Permalink
Browse files

Even more aggressive narwhal name spacing.

  • Loading branch information...
1 parent 78c837d commit 1129b4d30bd678a509828b25855c79d304516d04 @kriskowal committed Apr 12, 2010
View
761 lib/narwhal/binary.js
@@ -0,0 +1,761 @@
+
+/* Binary */
+// -- tlrobinson Tom Robinson
+// -- gozala Irakli Gozalishvili
+// -- tschaub
+// -- nrstott Nathan Stott
+
+var engine = require("./binary-engine"),
+ B_ALLOC = engine.B_ALLOC,
+ B_LENGTH = engine.B_LENGTH,
+ B_GET = engine.B_GET,
+ B_SET = engine.B_SET,
+ B_FILL = engine.B_FILL,
+ B_COPY = engine.B_COPY,
+ B_DECODE = engine.B_DECODE,
+ B_ENCODE = engine.B_ENCODE,
+ B_DECODE_DEFAULT = engine.B_DECODE_DEFAULT,
+ B_ENCODE_DEFAULT = engine.B_ENCODE_DEFAULT,
+ B_TRANSCODE = engine.B_TRANSCODE;
+
+var Binary = exports.Binary = function() {
+ // this._bytes
+ // this._offset
+ // this._length
+};
+
+Object.defineProperty(Binary.prototype, "length", {
+ "get": function () {
+ return this._length;
+ },
+ "enumerable": false,
+ "configurable": false
+});
+
+// toArray() - n array of the byte values
+// toArray(charset) - an array of the code points, decoded
+Binary.prototype.toArray = function(charset) {
+ if (arguments.length === 0) {
+ var array = new Array(this._length);
+
+ for (var i = 0; i < this._length; i++)
+ array[i] = this.get(i);
+
+ return array;
+ }
+ else if (arguments.length === 1) {
+ var string = B_DECODE(this._bytes, this._offset, this._length, charset),
+ length = string.length,
+ array = new Array(length);
+
+ for (var i = 0; i < length; i++)
+ array[i] = string.charCodeAt(i);
+
+ return array;
+ }
+ else
+ throw new Error("Illegal arguments to toArray()");
+};
+
+// toByteArray() - just a copy
+// toByteArray(sourceCharset, targetCharset) - transcoded
+Binary.prototype.toByteArray = function(sourceCodec, targetCodec) {
+ if (arguments.length < 2)
+ return new ByteArray(this);
+ else if (arguments.length === 2 && typeof sourceCodec === "string" && typeof targetCodec === "string") {
+ var bytes = B_TRANSCODE(this._bytes, this._offset, this._length, sourceCodec, targetCodec);
+ return new ByteArray(bytes, 0, B_LENGTH(bytes));
+ }
+
+ throw new Error("Illegal arguments to ByteArray toByteArray");
+};
+
+// toByteString() - byte for byte copy
+// toByteString(sourceCharset, targetCharset) - transcoded
+Binary.prototype.toByteString = function(sourceCodec, targetCodec) {
+ if (arguments.length < 2)
+ return new ByteString(this);
+ else if (arguments.length === 2 && typeof sourceCodec === "string" && typeof targetCodec === "string") {
+ var bytes = B_TRANSCODE(this._bytes, this._offset, this._length, sourceCodec, targetCodec);
+ return new ByteString(bytes, 0, B_LENGTH(bytes));
+ }
+
+ throw new Error("Illegal arguments to ByteArray toByteString");
+};
+
+// decodeToString()
+// decodeToString(charset) - returns a String from its decoded bytes in a given charset. If no charset is provided, or if the charset is "undefined", assumes the default system encoding.
+// decodeToString(number) - returns a String from its decoded bytes in a given base, like 64, 32, 16, 8, 2
+Binary.prototype.decodeToString = function(charset) {
+ if (charset) {
+ if (typeof charset == "number")
+ return require("base" + charset).encode(this);
+ else if (/^base/.test(charset))
+ return require(charset).encode(this);
+ else
+ return B_DECODE(this._bytes, this._offset, this._length, charset);
+ }
+ return B_DECODE_DEFAULT(this._bytes, this._offset, this._length);
+};
+
+// get(offset) - Return the byte at offset as a Number.
+Binary.prototype.get = function(offset) {
+ if (offset < 0 || offset >= this._length)
+ return NaN;
+
+ //var b = this._bytes[this._offset + offset];
+ //return (b >= 0) ? b : -1 * ((b ^ 0xFF) + 1);
+ return B_GET(this._bytes, this._offset + offset)
+};
+
+Binary.prototype.indexOf = function(byteValue, start, stop) {
+ // HACK: use ByteString's slice since we know we won't be modifying result
+ var array = ByteString.prototype.slice.apply(this, [start, stop]).toArray(),
+ result = array.indexOf(byteValue);
+ return (result < 0) ? -1 : result + (start || 0);
+};
+
+Binary.prototype.lastIndexOf = function(byteValue, start, stop) {
+ // HACK: use ByteString's slice since we know we won't be modifying result
+ var array = ByteString.prototype.slice.apply(this, [start, stop]).toArray(),
+ result = array.lastIndexOf(byteValue);
+ return (result < 0) ? -1 : result + (start || 0);
+};
+
+// valueOf()
+Binary.prototype.valueOf = function() {
+ return this;
+};
+
+/* ByteString */
+
+var ByteString = exports.ByteString = function() {
+ if (!(this instanceof ByteString)) {
+ if (arguments.length == 0)
+ return new ByteString();
+ if (arguments.length == 1)
+ return new ByteString(arguments[0]);
+ if (arguments.length == 2)
+ return new ByteString(arguments[0], arguments[1]);
+ if (arguments.length == 3)
+ return new ByteString(arguments[0], arguments[1], arguments[2]);
+ }
+
+ // ByteString() - Construct an empty byte string.
+ if (arguments.length === 0) {
+ this._bytes = B_ALLOC(0); // null;
+ this._offset = 0;
+ this._length = 0;
+ }
+ // ByteString(byteString) - Copies byteString.
+ else if (arguments.length === 1 && arguments[0] instanceof ByteString) {
+ return arguments[0];
+ }
+ // ByteString(byteArray) - Use the contents of byteArray.
+ else if (arguments.length === 1 && arguments[0] instanceof ByteArray) {
+ var copy = arguments[0].toByteArray();
+ this._bytes = copy._bytes;
+ this._offset = copy._offset;
+ this._length = copy._length;
+ }
+ // ByteString(arrayOfNumbers) - Use the numbers in arrayOfNumbers as the bytes.
+ else if (arguments.length === 1 && Array.isArray(arguments[0])) {
+ var array = arguments[0];
+ this._bytes = B_ALLOC(array.length);
+ for (var i = 0; i < array.length; i++) {
+ var b = array[i];
+ // If any element is outside the range 0...255, an exception (TODO) is thrown.
+ if (b < -0x80 || b > 0xFF)
+ throw new Error("ByteString constructor argument Array of integers must be -128 - 255 ("+b+")");
+ // Java "bytes" are interpreted as 2's complement
+ //this._bytes[i] = (b < 128) ? b : -1 * ((b ^ 0xFF) + 1);
+ B_SET(this._bytes, i, b);
+ }
+ this._offset = 0;
+ this._length = B_LENGTH(this._bytes);
+ }
+ // ByteString(string, charset) - Convert a string. The ByteString will contain string encoded with charset.
+ else if ((arguments.length === 1 || (arguments.length === 2 && arguments[1] === undefined)) && typeof arguments[0] === "string") {
+ this._bytes = B_ENCODE_DEFAULT(arguments[0]);
+ this._offset = 0;
+ this._length = B_LENGTH(this._bytes);
+ }
+ else if (arguments.length === 2 && typeof arguments[0] === "string" && typeof arguments[1] === "string") {
+ this._bytes = B_ENCODE(arguments[0], arguments[1]);
+ this._offset = 0;
+ this._length = B_LENGTH(this._bytes);
+ }
+ // private: ByteString(bytes, offset, length)
+ else if (arguments.length === 3 && typeof arguments[1] === "number" && typeof arguments[2] === "number") {
+ this._bytes = arguments[0];
+ this._offset = arguments[1];
+ this._length = arguments[2];
+ }
+ else {
+ var util = require("util");
+ throw new Error("Illegal arguments to ByteString constructor: " + util.repr(arguments));
+ }
+
+ if (engine.ByteStringWrapper)
+ return engine.ByteStringWrapper(this);
+ else
+ return this;
+};
+
+ByteString.prototype = new Binary();
+
+ByteString.prototype.__defineGetter__("length", function() {
+ return this._length;
+});
+ByteString.prototype.__defineSetter__("length", function(length) {
+});
+
+// toByteArray() - Returns a byte for byte copy in a ByteArray.
+// toByteArray(sourceCharset, targetCharset) - Returns a transcoded copy in a ByteArray.
+// - implemented on Binary
+
+// toByteString() - Returns itself, since there's no need to copy an immutable ByteString.
+// toByteString(sourceCharset, targetCharset) - Returns a transcoded copy.
+// - implemented on Binary
+
+// toArray() - Returns an array containing the bytes as numbers.
+// toArray(charset) - Returns an array containing the decoded Unicode code points.
+// - implemented on Binary
+
+// toString()
+ByteString.prototype.toString = function(charset) {
+ if (charset)
+ return this.decodeToString(charset);
+
+ return "[ByteString "+this._length+"]";
+};
+
+// decodeToString(charset) - Returns the decoded ByteArray as a string.
+// - implemented on Binary
+
+ByteString.prototype.byteAt =
+ByteString.prototype.charAt = function(offset) {
+ var byteValue = this.get(offset);
+
+ if (isNaN(byteValue))
+ return new ByteString();
+
+ return new ByteString([byteValue]);
+};
+
+// indexOf() - implemented on Binary
+// lastIndexOf() - implemented on Binary
+
+// charCodeAt(offset)
+ByteString.prototype.charCodeAt = Binary.prototype.get;
+
+// get(offset) - implemented on Binary
+
+// byteAt(offset) ByteString - implemented on Binary
+// charAt(offset) ByteString - implemented on Binary
+
+// split(delimiter, [options])
+ByteString.prototype.split = function(delimiters, options) {
+ var options = options || {},
+ count = options.count === undefined ? -1 : options.count,
+ includeDelimiter = options.includeDelimiter || false;
+
+ // standardize delimiters into an array of ByteStrings:
+ if (!Array.isArray(delimiters))
+ delimiters = [delimiters];
+
+ delimiters = delimiters.map(function(delimiter) {
+ if (typeof delimiter === "number")
+ delimiter = [delimiter];
+ return new ByteString(delimiter);
+ });
+
+ var components = [],
+ startOffset = this._offset,
+ currentOffset = this._offset;
+
+ // loop until there's no more bytes to consume
+ bytes_loop :
+ while (currentOffset < this._offset + this._length) {
+
+ // try each delimiter until we find a match
+ delimiters_loop :
+ for (var i = 0; i < delimiters.length; i++) {
+ var d = delimiters[i];
+
+ for (var j = 0; j < d._length; j++) {
+ // reached the end of the bytes, OR bytes not equal
+ if (currentOffset + j > this._offset + this._length ||
+ B_GET(this._bytes, currentOffset + j) !== B_GET(d._bytes, d._offset + j)) {
+ continue delimiters_loop;
+ }
+ }
+
+ // push the part before the delimiter
+ components.push(new ByteString(this._bytes, startOffset, currentOffset - startOffset));
+
+ // optionally push the delimiter
+ if (includeDelimiter)
+ components.push(new ByteString(this._bytes, currentOffset, d._length))
+
+ // reset the offsets
+ startOffset = currentOffset = currentOffset + d._length;
+
+ continue bytes_loop;
+ }
+
+ // if there was no match, increment currentOffset to try the next one
+ currentOffset++;
+ }
+
+ // push the remaining part, if any
+ if (currentOffset > startOffset)
+ components.push(new ByteString(this._bytes, startOffset, currentOffset - startOffset));
+
+ return components;
+};
+
+// slice()
+// slice(begin)
+// slice(begin, end)
+ByteString.prototype.slice = function(begin, end) {
+ if (begin === undefined)
+ begin = 0;
+ else if (begin < 0)
+ begin = this._length + begin;
+
+ if (end === undefined)
+ end = this._length;
+ else if (end < 0)
+ end = this._length + end;
+
+ begin = Math.min(this._length, Math.max(0, begin));
+ end = Math.min(this._length, Math.max(0, end));
+
+ return new ByteString(this._bytes, this._offset + begin, end - begin);
+};
+
+// substr(start)
+// substr(start, length)
+ByteString.prototype.substr = function(start, length) {
+ if (start !== undefined) {
+ if (length !== undefined)
+ return this.slice(start);
+ else
+ return this.slice(start, start + length);
+ }
+ return this.slice();
+};
+
+// substring(first)
+// substring(first, last)
+ByteString.prototype.substring = function(from, to) {
+ if (from !== undefined) {
+ if (to !== undefined)
+ return this.slice(Math.max(Math.min(from, this._length), 0));
+ else
+ return this.slice(Math.max(Math.min(from, this._length), 0),
+ Math.max(Math.min(to, this._length), 0));
+ }
+ return this.slice();
+};
+
+// [] ByteString - TODO
+
+// toSource()
+ByteString.prototype.toSource = function() {
+ return "ByteString(["+this.toArray().join(",")+"])";
+};
+
+/* ByteArray */
+
+// ByteArray() - New, empty ByteArray.
+// ByteArray(length) - New ByteArray filled with length zero bytes.
+// ByteArray(byteArray) - Copy byteArray.
+// ByteArray(byteString) - Copy contents of byteString.
+// ByteArray(arrayOfBytes) - Use numbers in arrayOfBytes as contents.
+// Throws an exception if any element is outside the range 0...255 (TODO).
+// ByteArray(string, charset) - Create a ByteArray from a Javascript string, the result being encoded with charset.
+var ByteArray = exports.ByteArray = function() {
+ if (!this instanceof ByteArray) {
+ if (arguments.length == 0)
+ return new ByteArray();
+ if (arguments.length == 1)
+ return new ByteArray(arguments[0]);
+ if (arguments.length == 2)
+ return new ByteArray(arguments[0], arguments[1]);
+ if (arguments.length == 3)
+ return new ByteArray(arguments[0], arguments[1], arguments[2]);
+ }
+
+ // ByteArray() - New, empty ByteArray.
+ if (arguments.length === 0) {
+ this._bytes = B_ALLOC(0); // null;
+ this._offset = 0;
+ this._length = 0;
+ }
+ // ByteArray(length) - New ByteArray filled with length zero bytes.
+ else if (arguments.length === 1 && typeof arguments[0] === "number") {
+ this._bytes = B_ALLOC(arguments[0]); // null;
+ this._offset = 0;
+ this._length = B_LENGTH(this._bytes);
+ }
+ // ByteArray(byteArray) - Copy byteArray.
+ // ByteArray(byteString) - Copy contents of byteString.
+ else if (arguments.length === 1 && (arguments[0] instanceof ByteArray || arguments[0] instanceof ByteString)) {
+ var byteArray = new ByteArray(arguments[0]._length);
+ B_COPY(arguments[0]._bytes, arguments[0]._offset, byteArray._bytes, byteArray._offset, byteArray._length);
+ return byteArray;
+ }
+ // ByteArray(arrayOfBytes) - Use numbers in arrayOfBytes as contents.
+ // Throws an exception if any element is outside the range 0...255 (TODO).
+ else if (arguments.length === 1 && Array.isArray(arguments[0])) {
+ var array = arguments[0];
+ this._bytes = B_ALLOC(array.length);
+ for (var i = 0; i < array.length; i++) {
+ var b = array[i];
+ // If any element is outside the range 0...255, an exception (TODO) is thrown.
+ if (b < 0 || b > 0xFF)
+ throw new Error("ByteString constructor argument Array of integers must be 0 - 255 ("+b+")");
+ // Java "bytes" are interpreted as 2's complement
+ //this._bytes[i] = (b < 128) ? b : -1 * ((b ^ 0xFF) + 1);
+ B_SET(this._bytes, i, b);
+ }
+ this._offset = 0;
+ this._length = B_LENGTH(this._bytes);
+ }
+ // ByteArray(string, charset) - Create a ByteArray from a Javascript string, the result being encoded with charset.
+ else if ((arguments.length === 1 || (arguments.length === 2 && arguments[1] === undefined)) && typeof arguments[0] === "string") {
+ this._bytes = B_ENCODE_DEFAULT(arguments[0]);
+ this._offset = 0;
+ this._length = B_LENGTH(this._bytes);
+ }
+ else if (arguments.length === 2 && typeof arguments[0] === "string" && typeof arguments[1] === "string") {
+ this._bytes = B_ENCODE(arguments[0], arguments[1]);
+ this._offset = 0;
+ this._length = B_LENGTH(this._bytes);
+ }
+ // private: ByteArray(bytes, offset, length)
+ else if (arguments.length === 3 && typeof arguments[1] === "number" && typeof arguments[2] === "number") {
+ this._bytes = arguments[0];
+ this._offset = arguments[1];
+ this._length = arguments[2];
+ }
+ else
+ throw new Error("Illegal arguments to ByteString constructor: [" +
+ Array.prototype.join.apply(arguments, [","]) + "] ("+arguments.length+")");
+
+ if (engine.ByteArrayWrapper)
+ return engine.ByteArrayWrapper(this);
+ else
+ return this;
+};
+
+ByteArray.prototype = new Binary();
+
+ByteArray.prototype.__defineGetter__("length", function() {
+ return this._length;
+});
+ByteArray.prototype.__defineSetter__("length", function(length) {
+ if (typeof length !== "number")
+ return;
+
+ // same length
+ if (length === this._length) {
+ return;
+ }
+ // new length is less, truncate
+ else if (length < this._length) {
+ this._length = length;
+ }
+ // new length is more, but fits without moving, just clear new bytes
+ else if (this._offset + length <= B_LENGTH(this._bytes)) {
+ B_FILL(this._bytes, this._length, this._offset + length - 1, 0);
+ this._length = length;
+ }
+ // new length is more, but fits if we shift to bottom, so do that.
+ else if (length <= B_LENGTH(this._bytes)) {
+ B_COPY(this._bytes, this._offset, this._bytes, 0, this._length);
+ this._offset = 0;
+ B_FILL(this._bytes, this._length, this._offset + length - 1, 0);
+ this._length = length;
+ }
+ // new length is more than the allocated bytes array, allocate a new one and copy the data
+ else {
+ var newBytes = B_ALLOC(length);
+ B_COPY(this._bytes, this._offset, newBytes, 0, this._length);
+ this._bytes = newBytes;
+ this._offset = 0;
+ this._length = length;
+ }
+});
+
+// FIXME: array notation for set and get
+ByteArray.prototype.set = function(index, b) {
+ // If any element is outside the range 0...255, an exception (TODO) is thrown.
+ if (b < 0 || b > 0xFF)
+ throw new Error("ByteString constructor argument Array of integers must be 0 - 255 ("+b+")");
+
+ if (index < 0 || index >= this._length)
+ throw new Error("Out of range");
+
+ // Java "bytes" are interpreted as 2's complement
+ //this._bytes[this._offset + index] = (b < 128) ? b : -1 * ((b ^ 0xFF) + 1);
+ B_SET(this._bytes, this._offset + index, b);
+};
+
+// toArray()
+// toArray(charset)
+// - implemented on Binary
+
+// toByteArray() - just a copy
+// toByteArray(sourceCharset, targetCharset) - transcoded
+// - implemented on Binary
+
+// toByteString() - byte for byte copy
+// toByteString(sourceCharset, targetCharset) - transcoded
+// - implemented on Binary
+
+// toString() - a string representation like "[ByteArray 10]"
+// toString(charset) - an alias for decodeToString(charset)
+ByteArray.prototype.toString = function(charset) {
+ if (charset)
+ return this.decodeToString(charset);
+
+ return "[ByteArray "+this._length+"]";
+};
+
+// decodeToString(charset) - implemented on Binary
+
+// byteAt(offset) ByteString - Return the byte at offset as a ByteString.
+// - implemented on Binary
+
+// get(offset) Number - Return the byte at offset as a Number.
+// - implemented on Binary
+
+// concat(other ByteArray|ByteString|Array)
+// TODO: I'm assuming Array means an array of ByteStrings/ByteArrays, not an array of integers.
+ByteArray.prototype.concat = function() {
+ var components = [this],
+ totalLength = this._length;
+
+ for (var i = 0; i < arguments.length; i++) {
+ var component = Array.isArray(arguments[i]) ? arguments[i] : [arguments[i]];
+
+ for (var j = 0; j < component.length; j++) {
+ var subcomponent = component[j];
+ if (!(subcomponent instanceof ByteString) && !(subcomponent instanceof ByteArray))
+ throw "Arguments to ByteArray.concat() must be ByteStrings, ByteArrays, or Arrays of those.";
+
+ components.push(subcomponent);
+ totalLength += subcomponent.length;
+ }
+ }
+
+ var result = new ByteArray(totalLength),
+ offset = 0;
+
+ components.forEach(function(component) {
+ B_COPY(component._bytes, component._offset, result._bytes, offset, component._length);
+ offset += component._length;
+ });
+
+ return result;
+};
+
+// pop() -> byte Number
+ByteArray.prototype.pop = function() {
+ if (this._length === 0)
+ return undefined;
+
+ this._length--;
+
+ return B_GET(this._bytes, this._offset + this._length);
+};
+
+// push(...variadic Numbers...)-> count Number
+ByteArray.prototype.push = function() {
+ var length, newLength = this.length += length = arguments.length;
+ try {
+ for (var i = 0; i < length; i++)
+ this.set(newLength - length + i, arguments[i]);
+ } catch (e) {
+ this.length -= length;
+ throw e;
+ }
+ return newLength;
+};
+
+// extendRight(...variadic Numbers / Arrays / ByteArrays / ByteStrings ...)
+ByteArray.prototype.extendRight = function() {
+ throw "NYI";
+};
+
+// shift() -> byte Number
+ByteArray.prototype.shift = function() {
+ if (this._length === 0)
+ return undefined;
+
+ this._length--;
+ this._offset++;
+
+ return B_GET(this._bytes, this._offset - 1);
+};
+
+// unshift(...variadic Numbers...) -> count Number
+ByteArray.prototype.unshift = function() {
+ var copy = this.slice();
+ this.length = 0;
+ try {
+ this.push.apply(this, arguments);
+ this.push.apply(this, copy.toArray());
+ return this.length;
+ } catch(e) {
+ B_COPY(copy._bytes, copy._offset, this._bytes, this._offset, copy.length);
+ this.length = copy.length;
+ throw e;
+ }
+};
+
+// extendLeft(...variadic Numbers / Arrays / ByteArrays / ByteStrings ...)
+ByteArray.prototype.extendLeft = function() {
+ throw "NYI";
+};
+
+// reverse() in place reversal
+ByteArray.prototype.reverse = function() {
+ // "limit" is halfway, rounded down. "top" is the last index.
+ var limit = Math.floor(this._length/2) + this._offset,
+ top = this._length - 1;
+
+ // swap each pair of bytes, up to the halfway point
+ for (var i = this._offset; i < limit; i++) {
+ var tmp = B_GET(this._bytes, i);
+ B_SET(this._bytes, i, B_GET(this._bytes, top - i));
+ B_SET(this._bytes, top - i, tmp);
+ }
+
+ return this;
+};
+
+// slice()
+ByteArray.prototype.slice = function() {
+ return new ByteArray(ByteString.prototype.slice.apply(this, arguments));
+};
+
+var numericCompareFunction = function(o1, o2) { return o1 - o2; };
+
+// sort([compareFunction])
+ByteArray.prototype.sort = function(compareFunction) {
+ // FIXME: inefficient?
+
+ var array = this.toArray();
+
+ if (arguments.length)
+ array.sort(compareFunction);
+ else
+ array.sort(numericCompareFunction);
+
+ for (var i = 0; i < array.length; i++)
+ this.set(i, array[i]);
+};
+
+// splice()
+ByteArray.prototype.splice = function(index, howMany /*, elem1, elem2 */) {
+ if (index === undefined) return;
+ if (index < 0) index += this.length;
+ if (howMany === undefined) howMany = this._length - index;
+ var end = index + howMany;
+ var remove = this.slice(index, end);
+ var keep = this.slice(end);
+ var inject = Array.prototype.slice.call(arguments, 2);
+ this._length = index;
+ this.push.apply(this, inject);
+ this.push.apply(this, keep.toArray());
+ return remove;
+};
+
+// indexOf() - implemented on Binary
+// lastIndexOf() - implemented on Binary
+
+// split() Returns an array of ByteArrays instead of ByteStrings.
+ByteArray.prototype.split = function() {
+ var components = ByteString.prototype.split.apply(this.toByteString(), arguments);
+
+ // convert ByteStrings to ByteArrays
+ for (var i = 0; i < components.length; i++) {
+ // we know we can use these byte buffers directly since we copied them above
+ components[i] = new ByteArray(components[i]._bytes, components[i]._offset, components[i]._length);
+ }
+
+ return components;
+};
+
+// filter(callback[, thisObject])
+ByteArray.prototype.filter = function(callback, thisObject) {
+ var result = new ByteArray(this._length);
+ for (var i = 0, length = this._length; i < length; i++) {
+ var value = this.get(i);
+ if (callback.apply(thisObject, [value, i, this]))
+ result.push(value);
+ }
+ return result;
+};
+
+// forEach(callback[, thisObject]);
+ByteArray.prototype.forEach = function(callback, thisObject) {
+ for (var i = 0, length = this._length; i < length; i++)
+ callback.apply(thisObject, [this.get(i), i, this]);
+};
+
+// every(callback[, thisObject])
+ByteArray.prototype.every = function(callback, thisObject) {
+ for (var i = 0, length = this._length; i < length; i++)
+ if (!callback.apply(thisObject, [this.get(i), i, this]))
+ return false;
+ return true;
+};
+
+// some(callback[, thisObject])
+ByteArray.prototype.some = function(callback, thisObject) {
+ for (var i = 0, length = this._length; i < length; i++)
+ if (callback.apply(thisObject, [this.get(i), i, this]))
+ return true;
+ return false;
+};
+
+// map(callback[, thisObject]);
+ByteArray.prototype.map = function(callback, thisObject) {
+ var result = new ByteArray(this._length);
+ for (var i = 0, length = this._length; i < length; i++)
+ result.set(i, callback.apply(thisObject, [this.get(i), i, this]));
+ return result;
+};
+
+// reduce(callback[, initialValue])
+ByteArray.prototype.reduce = function(callback, initialValue) {
+ var value = initialValue;
+ for (var i = 0, length = this._length; i < length; i++)
+ value = callback(value, this.get(i), i, this);
+ return value;
+};
+
+// reduceRight(callback[, initialValue])
+ByteArray.prototype.reduceRight = function(callback, initialValue) {
+ var value = initialValue;
+ for (var i = this._length-1; i > 0; i--)
+ value = callback(value, this.get(i), i, this);
+ return value;
+};
+
+// displace(begin, end, values/ByteStrings/ByteArrays/Arrays...) -> length
+// begin/end are specified like for slice. Can be used like splice but does not return the removed elements.
+ByteArray.prototype.displace = function(begin, end) {
+ throw "NYI";
+};
+
+// toSource() returns a string like "ByteArray([])" for a null byte-array.
+ByteArray.prototype.toSource = function() {
+ return "ByteArray(["+this.toArray().join(",")+"])";
+};
+
View
45 lib/narwhal/event-loop-hook.js
@@ -0,0 +1,45 @@
+
+// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
+
+/*whatsupdoc*/
+/*markup markdown*/
+
+/**
+ * This module is exclusively for the purpose of arranging
+ * for an "event-loop-engine" to begin dispatching events
+ * when the initial thread of execution runs to its
+ * completion.
+ *
+ * `narwhal/narwhal.js` checks whether this module has been
+ * loaded after the main module returns and calls the
+ * "emit" method. No module is intended to call "emit"
+ * manually.
+ *
+ * @module
+*/
+
+var observers = [];
+
+/**
+ * Arrange for an observer function to be called after the
+ * "main" module returns.
+ *
+ * @param observer {Function}
+ * @returns undefined
+ */
+exports.when = function (observer) {
+ observers.unshift(observer);
+};
+
+/**
+ * This method, called by the engine after the "main" module
+ * returns, invokes all of the observers (intended to be a
+ * single event loop runner registered by an
+ * "event-loop-engine") in the order they were requested.
+ */
+exports.emit = function () {
+ observers.forEach(function (observer) {
+ observer();
+ });
+};
+
View
40 lib/narwhal/event-loop-setup.js
@@ -0,0 +1,40 @@
+
+var eventLoop;
+
+/**
+ * Configures the system to use a particular event loop
+ * implementation. The event loop must support the event
+ * loop API:
+ *
+ * - setTimeout
+ * - clearTimeout
+ * - setInterval
+ * - clearInterval
+ * - enqueue is optional; if it is not provided, the
+ * event-loop module will provide one in terms of
+ * setTimeout(task, 0).
+ *
+ * @throws Error if an event loop has already been
+ * configured.
+ *
+ */
+exports.setEventLoop = function (_eventLoop) {
+ if (eventLoop)
+ throw new Error("An event loop has already been loaded.");
+ eventLoop = _eventLoop;
+};
+
+/**
+ * Returns an event loop implementation. If no event loop
+ * has been configured, configures the "event-loop-engine"
+ * module as the default implementation. After this method
+ * has been called, the event loop cannot be changed.
+ * The "event-loop" module itself uses this method the first
+ * time it is required.
+ */
+exports.getEventLoop = function () {
+ if (!eventLoop)
+ eventLoop = require("./event-loop-engine");
+ return eventLoop;
+};
+
View
20 lib/narwhal/event-loop.js
@@ -0,0 +1,20 @@
+
+var ENGINE = require("./event-loop-setup").getEventLoop();
+
+exports.enqueue = ENGINE.enqueue || function (task) {
+ exports.setTimeout(function () {
+ // uses a closure to ensure that any additional
+ // parameters are laundered
+ task();
+ }, 0);
+};
+
+exports.setTimeout = ENGINE.setTimeout;
+exports.clearTimeout = ENGINE.clearTimeout;
+exports.setInterval = ENGINE.setInterval;
+exports.clearInterval = ENGINE.clearInterval;
+
+// optional
+exports.hasPendingEvents = ENGINE.hasPendingEvents;
+exports.processNextEvent = ENGINE.processNextEvent;
+
View
233 lib/narwhal/os.js
@@ -0,0 +1,233 @@
+
+// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
+// -- Richard Penwell (penwellr) MIT Licence - March 1, 2010
+// * Contributed "parse"
+
+/**
+ * Provides operating system call support, particularly POSIX calls
+ * when possible.
+ *
+ * @module
+ * @extends os-engine
+ */
+
+var engine = require("./os-engine");
+for (var name in engine) {
+ if (Object.prototype.hasOwnProperty.call(engine, name)) {
+ exports[name] = engine[name];
+ }
+}
+
+var system = require("system");
+
+/**
+ * executes a given command, attached to this process's
+ * IO streams, and returns the exit status.
+ *
+ * @param {Array or String} command uses "/bin/sh -c" if the command
+ * is a string.
+ * @returns Number exit status
+ */
+if (!exports.system) {
+ exports.system = function (command, options) {
+ var process = exports.popen(command, options);
+ return process.communicate(
+ system.stdin,
+ system.stdout,
+ system.stderr
+ ).status;
+ };
+}
+
+/**
+ * Supported in Rhino and Node. On Rhino, acceptance of the command
+ * object and the search argument are not yet supported.
+ *
+ * @param {String || Array || {args, env, search}} command or
+ * "system" like
+ * object
+ * @param {Object} env
+ * @param {Boolean} search defaults to true
+ * @throws {Error} if execution resumes with the old program image.
+ */
+if (!exports.exec && exports.exec0) {
+ exports.exec = function (command, env, search) {
+ var args;
+ if (typeof command === "string") {
+ args = ["/bin/sh", "-c", command];
+ command = args[0];
+ } else if (Array.isArray(command)) {
+ args = command;
+ command = args[0];
+ } else {
+ args = command.args;
+ env = command.env;
+ if (command.search !== undefined)
+ search = command.search;
+ command = command.command || args[0];
+ }
+ if (search === undefined)
+ search = true;
+ exports.exec0(command, args, env, search);
+ // should not get here; exec0 should throw
+ throw new Error("exec() failed");
+ };
+}
+
+/**
+ * Strictly built on Posix `execv`, `execvp`, and `execvP`,
+ * where the command is provided as the first argument and
+ * args[0] is the original path used to execute the command.
+ *
+ * @param {String} command
+ * @param {Array * String} args optional
+ * @param {Object * String} env optional
+ * @param {Boolean} search optionally whether to use the `env.PATH`
+ * to search for `command`
+ * @name exec0
+ */
+
+/**
+ * executes a given command quietly and returns
+ * the exit status.
+ *
+ * @param {Array or String} command uses "/bin/sh -c" if the command
+ * is a string.
+ * @returns Number exit status
+ */
+exports.status = function (command) {
+ var process = exports.popen(command);
+ var result = process.communicate();
+ return result.status;
+};
+
+/**
+ * executes a given command and returns the
+ * standard output. If the exit status is non-zero,
+ * throws an Error.
+ *
+ * @param {Array or String} command uses "/bin/sh -c" if the command
+ * is a string.
+ * @returns String the standard output of the command
+ */
+exports.command = function (command) {
+ var process = exports.popen(command);
+ var result = process.communicate();
+ if (result.status !== 0)
+ throw new Error("(" + result.status + ") " + result.stderr.read());
+ return result.stdout.read();
+};
+
+/**
+ * enquotes a string such that it is guaranteed to be a single
+ * argument with no interpolated values for a shell.
+ *
+ * /!\ WARNING: as yet, this implementation only handles
+ * enquoting for Unix shell script style arguments. Further
+ * development is necessary to enquote and escape arguments
+ * on Windows.
+ */
+exports.enquote = function (word) {
+ return "'" + String(word).replace(/'/g, "'\"'\"'") + "'";
+};
+
+/**
+ * parses command line arguments
+ * @param command {String} a command composed of space delimited,
+ * quoted, or backslash escaped arguments.
+ * @returns an Array of unquoted arguments.
+ *
+ * /!\ WARNING: this does not handle all of the edge cases
+ * of command line argument parsing, nor is suitable for
+ * general purpose argument enquoting on all platforms. It
+ * also will never be able to handle environment variable
+ * interpolation or other forms of shell quote expansion.
+ * This utility is used by Narwhal to pare arguments from
+ * system.env.NARWHAL_OPT.
+ */
+
+var STATE_NORMAL = 0; // waiting for non whitespace/quote
+var STATE_ARG = 1; // nextArg is an argument, even if empty
+var STATE_IN_QUOTE = 2; // within a ' or " quote
+
+exports.parse = function (argString) {
+ var args = [];
+
+ var nextArg = "";
+ var state = STATE_NORMAL;
+ var escapeNext = false;
+ var delimiter;
+
+ var tokens = argString.split("");
+ while (tokens.length > 0) {
+ var token = tokens.shift();
+
+ if (state === STATE_NORMAL || state === STATE_ARG) {
+ if (!escapeNext && token === "\\") {
+ escapeNext = true;
+ }
+ else if (escapeNext) {
+ state = STATE_ARG;
+ escapeNext = false;
+ nextArg += token;
+ }
+ else if (token === "'" || token === '"') {
+ delimiter = token;
+ state = STATE_IN_QUOTE;
+ }
+ else if (token === " ") {
+ if (state === STATE_ARG) {
+ args.push(nextArg);
+ nextArg = "";
+ }
+ state = STATE_NORMAL;
+ }
+ else {
+ nextArg += token;
+ state = STATE_ARG;
+ }
+ }
+ else if (state === STATE_IN_QUOTE) {
+ if (!escapeNext && token === "\\") {
+ escapeNext = true;
+ }
+ else if (delimiter === token) {
+ if (escapeNext) {
+ nextArg += token;
+ escapeNext = false;
+ } else {
+ state = STATE_ARG;
+ }
+ }
+ else {
+ if (escapeNext) {
+ // if not a quote (above) or other special character that needs to be escaped then include the backslash
+ if (token !== "\\")
+ nextArg += "\\";
+ nextArg += token;
+ escapeNext = false;
+ } else {
+ nextArg += token;
+ }
+ }
+ }
+ else {
+ throw "wtf " + state;
+ }
+ }
+
+ if (state === STATE_IN_QUOTE) {
+ if (token === delimiter) {
+ args.push(nextArg.slice(0,-1) + "\\");
+ }
+ else {
+ // throw "Invalid or not yet implemented case"
+ }
+ }
+ else if (state === STATE_ARG) {
+ args.push(nextArg);
+ }
+
+ return args;
+};
+
View
2 lib/narwhal/promise-util.js
@@ -7,7 +7,7 @@
var QUTIL = exports;
var UTIL = require("narwhal/util");
var Q = require("narwhal/promise");
-var EL = require("event-loop");
+var EL = require("narwhal/event-loop");
UTIL.update(QUTIL, Q);
View
2 lib/narwhal/promise.js
@@ -35,7 +35,7 @@ if (typeof setTimeout === "function") {
setTimeout(task, 0);
};
} else {
- enqueue = require("event-loop").enqueue;
+ enqueue = require("./event-loop").enqueue;
}
/**
View
2 lib/narwhal/term.js
@@ -15,6 +15,8 @@ var terms = [
];
exports.Stream = function (system) {
+ if (!system.stdout)
+ throw new Error("narwhal/term can not be loaded until system.stdout has been constructed");
var self = Object.create(system.stdout);
var output = system.stdout;
var errput = system.stderr;

0 comments on commit 1129b4d

Please sign in to comment.