From 64ccd8a4fb3cef95788ff9dd7f63d876ae925e3a Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Mar 2013 11:41:33 -0700 Subject: [PATCH] some tests passing, moved over to use slow buffer as the new basis --- index.js | 1098 +++++++++++------------------------------------ old_buffer.js | 1011 +++++++++++++++++++++++++++++++++++++++++++ slow_buffer.js | 321 -------------- test/indexes.js | 2 +- 4 files changed, 1264 insertions(+), 1168 deletions(-) create mode 100644 old_buffer.js delete mode 100644 slow_buffer.js diff --git a/index.js b/index.js index 55a26f5..e1e1c5f 100644 --- a/index.js +++ b/index.js @@ -1,26 +1,9 @@ -var assert = require('assert'); -var SlowBuffer = require('./slow_buffer'); - -exports.INSPECT_MAX_BYTES = 50; - -function coerce(length) { - // Coerce length to a number (possibly NaN), round up - // in case it's fractional (e.g. 123.456) then do a - // double negate to coerce a NaN to 0. Easy, right? - length = ~~Math.ceil(+length); - return length < 0 ? 0 : length; -} +exports.Buffer = SlowBuffer; +var Buffer = SlowBuffer; -function toHex(n) { - if (n < 16) return '0' + n.toString(16); - return n.toString(16); -} - -// Buffer - -function Buffer(subject, encoding, offset) { - if (!(this instanceof Buffer)) { - return new Buffer(subject, encoding, offset); +function SlowBuffer(subject, encoding, offset) { + if (!(this instanceof SlowBuffer)) { + return new SlowBuffer(subject, encoding, offset); } var type; @@ -28,7 +11,6 @@ function Buffer(subject, encoding, offset) { // Are we slicing? if (typeof offset === 'number') { this.length = coerce(encoding); - this.parent = subject; this.offset = offset; } else { // Find the length @@ -38,7 +20,7 @@ function Buffer(subject, encoding, offset) { break; case 'string': - this.length = Buffer.byteLength(subject, encoding); + this.length = SlowBuffer.byteLength(subject, encoding); break; case 'object': // Assume object is an array @@ -50,230 +32,209 @@ function Buffer(subject, encoding, offset) { 'array or string.'); } - if (this.length > Buffer.poolSize) { - // Big buffer, just alloc one. - this.parent = new SlowBuffer(this.length); - this.offset = 0; - - } else { - // Small buffer. - if (!pool || pool.length - pool.used < this.length) allocPool(); - this.parent = pool; - this.offset = pool.used; - pool.used += this.length; - } - // Treat array-ish objects as a byte array. if (isArrayIsh(subject)) { for (var i = 0; i < this.length; i++) { - if (subject instanceof Buffer) { - this.parent[i + this.offset] = subject.readUInt8(i); + if (subject instanceof SlowBuffer) { this[i] = subject.readUInt8(i); } else { - this.parent[i + this.offset] = subject[i]; this[i] = subject[i]; } } } else if (type == 'string') { // We are a string this.length = this.write(subject, 0, encoding); - for (var i = 0; i < this.length; i++) { - this[i] = this.parent[i]; - } } } - } -function isArrayIsh(subject) { - return Array.isArray(subject) || Buffer.isBuffer(subject) || - subject && typeof subject === 'object' && - typeof subject.length === 'number'; +var assert = require('assert'); + +exports.INSPECT_MAX_BYTES = 50; + + +function toHex(n) { + if (n < 16) return '0' + n.toString(16); + return n.toString(16); } -exports.SlowBuffer = SlowBuffer; -exports.Buffer = Buffer; +function utf8ToBytes(str) { + var byteArray = []; + for (var i = 0; i < str.length; i++) + if (str.charCodeAt(i) <= 0x7F) + byteArray.push(str.charCodeAt(i)); + else { + var h = encodeURIComponent(str.charAt(i)).substr(1).split('%'); + for (var j = 0; j < h.length; j++) + byteArray.push(parseInt(h[j], 16)); + } + + return byteArray; +} -Buffer.poolSize = 8 * 1024; -var pool; +function asciiToBytes(str) { + var byteArray = [] + for (var i = 0; i < str.length; i++ ) + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push( str.charCodeAt(i) & 0xFF ); -function allocPool() { - pool = new SlowBuffer(Buffer.poolSize); - pool.used = 0; + return byteArray; } +function base64ToBytes(str) { + return require("base64-js").toByteArray(str); +} -// Static methods -Buffer.isBuffer = function isBuffer(b) { - return b instanceof Buffer || b instanceof SlowBuffer; -}; +SlowBuffer.byteLength = function (str, encoding) { + switch (encoding || "utf8") { + case 'hex': + return str.length / 2; -Buffer.concat = function (list, totalLength) { - if (!Array.isArray(list)) { - throw new Error("Usage: Buffer.concat(list, [totalLength])\n \ - list should be an Array."); - } + case 'utf8': + case 'utf-8': + return utf8ToBytes(str).length; - if (list.length === 0) { - return new Buffer(0); - } else if (list.length === 1) { - return list[0]; - } + case 'ascii': + case 'binary': + return str.length; - if (typeof totalLength !== 'number') { - totalLength = 0; - for (var i = 0; i < list.length; i++) { - var buf = list[i]; - totalLength += buf.length; - } - } + case 'base64': + return base64ToBytes(str).length; - var buffer = new Buffer(totalLength); - var pos = 0; - for (var i = 0; i < list.length; i++) { - var buf = list[i]; - buf.copy(buffer, pos); - pos += buf.length; + default: + throw new Error('Unknown encoding'); } - return buffer; }; -// Inspect -Buffer.prototype.inspect = function inspect() { - var out = [], - len = this.length; - - for (var i = 0; i < len; i++) { - out[i] = toHex(this.parent[i + this.offset]); - if (i == exports.INSPECT_MAX_BYTES) { - out[i + 1] = '...'; +function blitBuffer(src, dst, offset, length) { + var pos, i = 0; + while (i < length) { + if ((i+offset >= dst.length) || (i >= src.length)) break; - } + + dst[i + offset] = src[i]; + i++; } + return i; +} - return ''; +SlowBuffer.prototype.utf8Write = function (string, offset, length) { + var bytes, pos; + return SlowBuffer._charsWritten = blitBuffer(utf8ToBytes(string), this, offset, length); }; - -Buffer.prototype.get = function get(i) { - if (i < 0 || i >= this.length) throw new Error('oob'); - return this.parent[this.offset + i]; +SlowBuffer.prototype.asciiWrite = function (string, offset, length) { + var bytes, pos; + return SlowBuffer._charsWritten = blitBuffer(asciiToBytes(string), this, offset, length); }; +SlowBuffer.prototype.binaryWrite = SlowBuffer.prototype.asciiWrite; -Buffer.prototype.set = function set(i, v) { - if (i < 0 || i >= this.length) throw new Error('oob'); - return this.parent[this.offset + i] = v; +SlowBuffer.prototype.base64Write = function (string, offset, length) { + var bytes, pos; + return SlowBuffer._charsWritten = blitBuffer(base64ToBytes(string), this, offset, length); }; +SlowBuffer.prototype.base64Slice = function (start, end) { + var bytes = Array.prototype.slice.apply(this, arguments) + return require("base64-js").fromByteArray(bytes); +} -// write(string, offset = 0, length = buffer.length-offset, encoding = 'utf8') -Buffer.prototype.write = function(string, offset, length, encoding) { - // Support both (string, offset, length, encoding) - // and the legacy (string, encoding, offset, length) - if (isFinite(offset)) { - if (!isFinite(length)) { - encoding = length; - length = undefined; - } - } else { // legacy - var swap = encoding; - encoding = offset; - offset = length; - length = swap; +function decodeUtf8Char(str) { + try { + return decodeURIComponent(str); + } catch (err) { + return String.fromCharCode(0xFFFD); // UTF 8 invalid char } +} - offset = +offset || 0; - var remaining = this.length - offset; - if (!length) { - length = remaining; - } else { - length = +length; - if (length > remaining) { - length = remaining; - } +SlowBuffer.prototype.utf8Slice = function () { + var bytes = Array.prototype.slice.apply(this, arguments); + var res = ""; + var tmp = ""; + var i = 0; + while (i < bytes.length) { + if (bytes[i] <= 0x7F) { + res += decodeUtf8Char(tmp) + String.fromCharCode(bytes[i]); + tmp = ""; + } else + tmp += "%" + bytes[i].toString(16); + + i++; } - encoding = String(encoding || 'utf8').toLowerCase(); - var ret; - switch (encoding) { - case 'hex': - ret = this.parent.hexWrite(string, this.offset + offset, length); - break; + return res + decodeUtf8Char(tmp); +} - case 'utf8': - case 'utf-8': - ret = this.parent.utf8Write(string, this.offset + offset, length); - break; +SlowBuffer.prototype.asciiSlice = function () { + var bytes = Array.prototype.slice.apply(this, arguments); + var ret = ""; + for (var i = 0; i < bytes.length; i++) + ret += String.fromCharCode(bytes[i]); + return ret; +} - case 'ascii': - ret = this.parent.asciiWrite(string, this.offset + offset, length); - break; +SlowBuffer.prototype.binarySlice = SlowBuffer.prototype.asciiSlice; - case 'binary': - ret = this.parent.binaryWrite(string, this.offset + offset, length); +SlowBuffer.prototype.inspect = function() { + var out = [], + len = this.length; + for (var i = 0; i < len; i++) { + out[i] = toHex(this[i]); + if (i == exports.INSPECT_MAX_BYTES) { + out[i + 1] = '...'; break; + } + } + return ''; +}; - case 'base64': - // Warning: maxLength not taken into account in base64Write - ret = this.parent.base64Write(string, this.offset + offset, length); - break; - case 'ucs2': - case 'ucs-2': - ret = this.parent.ucs2Write(string, this.offset + offset, length); - break; +SlowBuffer.prototype.hexSlice = function(start, end) { + var len = this.length; - default: - throw new Error('Unknown encoding'); - } - - Buffer._charsWritten = SlowBuffer._charsWritten; + if (!start || start < 0) start = 0; + if (!end || end < 0 || end > len) end = len; - return ret; + var out = ''; + for (var i = start; i < end; i++) { + out += toHex(this[i]); + } + return out; }; -// toString(encoding, start=0, end=buffer.length) -Buffer.prototype.toString = function(encoding, start, end) { +SlowBuffer.prototype.toString = function(encoding, start, end) { encoding = String(encoding || 'utf8').toLowerCase(); + start = +start || 0; + if (typeof end == 'undefined') end = this.length; - if (typeof start == 'undefined' || start < 0) { - start = 0; - } else if (start > this.length) { - start = this.length; - } - - if (typeof end == 'undefined' || end > this.length) { - end = this.length; - } else if (end < 0) { - end = 0; + // Fastpath empty strings + if (+end == start) { + return ''; } - start = start + this.offset; - end = end + this.offset; - switch (encoding) { case 'hex': - return this.parent.hexSlice(start, end); + return this.hexSlice(start, end); case 'utf8': case 'utf-8': - return this.parent.utf8Slice(start, end); + return this.utf8Slice(start, end); case 'ascii': - return this.parent.asciiSlice(start, end); + return this.asciiSlice(start, end); case 'binary': - return this.parent.binarySlice(start, end); + return this.binarySlice(start, end); case 'base64': - return this.parent.base64Slice(start, end); + return this.base64Slice(start, end); case 'ucs2': case 'ucs-2': - return this.parent.ucs2Slice(start, end); + return this.ucs2Slice(start, end); default: throw new Error('Unknown encoding'); @@ -281,731 +242,176 @@ Buffer.prototype.toString = function(encoding, start, end) { }; -// byteLength -Buffer.byteLength = SlowBuffer.byteLength; - - -// fill(value, start=0, end=buffer.length) -Buffer.prototype.fill = function fill(value, start, end) { - value || (value = 0); - start || (start = 0); - end || (end = this.length); - - if (typeof value === 'string') { - value = value.charCodeAt(0); - } - if (!(typeof value === 'number') || isNaN(value)) { - throw new Error('value is not a number'); - } - - if (end < start) throw new Error('end < start'); - - // Fill 0 bytes; we're done - if (end === start) return 0; - if (this.length == 0) return 0; - - if (start < 0 || start >= this.length) { - throw new Error('start out of bounds'); - } - - if (end < 0 || end > this.length) { - throw new Error('end out of bounds'); - } - - return this.parent.fill(value, - start + this.offset, - end + this.offset); -}; - - -// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) -Buffer.prototype.copy = function(target, target_start, start, end) { - var source = this; - start || (start = 0); - end || (end = this.length); - target_start || (target_start = 0); - - if (end < start) throw new Error('sourceEnd < sourceStart'); - - // Copy 0 bytes; we're done - if (end === start) return 0; - if (target.length == 0 || source.length == 0) return 0; - - if (target_start < 0 || target_start >= target.length) { - throw new Error('targetStart out of bounds'); - } - - if (start < 0 || start >= source.length) { - throw new Error('sourceStart out of bounds'); - } - - if (end < 0 || end > source.length) { - throw new Error('sourceEnd out of bounds'); - } - - // Are we oob? - if (end > this.length) { - end = this.length; - } - - if (target.length - target_start < end - start) { - end = target.length - target_start + start; - } - - return this.parent.copy(target.parent, - target_start + target.offset, - start + this.offset, - end + this.offset); -}; - - -// slice(start, end) -Buffer.prototype.slice = function(start, end) { - if (end === undefined) end = this.length; - if (end > this.length) throw new Error('oob'); - if (start > end) throw new Error('oob'); - - return new Buffer(this.parent, end - start, +start + this.offset); -}; - - -// Legacy methods for backwards compatibility. - -Buffer.prototype.utf8Slice = function(start, end) { - return this.toString('utf8', start, end); -}; - -Buffer.prototype.binarySlice = function(start, end) { - return this.toString('binary', start, end); -}; - -Buffer.prototype.asciiSlice = function(start, end) { - return this.toString('ascii', start, end); -}; - -Buffer.prototype.utf8Write = function(string, offset) { - return this.write(string, offset, 'utf8'); -}; - -Buffer.prototype.binaryWrite = function(string, offset) { - return this.write(string, offset, 'binary'); -}; - -Buffer.prototype.asciiWrite = function(string, offset) { - return this.write(string, offset, 'ascii'); -}; - -Buffer.prototype.readUInt8 = function(offset, noAssert) { - var buffer = this; - - if (!noAssert) { - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset < buffer.length, - 'Trying to read beyond buffer length'); - } - - if (offset >= buffer.length) return; - - return buffer.parent[buffer.offset + offset]; -}; - -function readUInt16(buffer, offset, isBigEndian, noAssert) { - var val = 0; - - - if (!noAssert) { - assert.ok(typeof (isBigEndian) === 'boolean', - 'missing or invalid endian'); - - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset + 1 < buffer.length, - 'Trying to read beyond buffer length'); - } - - if (offset >= buffer.length) return 0; - - if (isBigEndian) { - val = buffer.parent[buffer.offset + offset] << 8; - if (offset + 1 < buffer.length) { - val |= buffer.parent[buffer.offset + offset + 1]; - } +SlowBuffer.prototype.hexWrite = function(string, offset, length) { + offset = +offset || 0; + var remaining = this.length - offset; + if (!length) { + length = remaining; } else { - val = buffer.parent[buffer.offset + offset]; - if (offset + 1 < buffer.length) { - val |= buffer.parent[buffer.offset + offset + 1] << 8; + length = +length; + if (length > remaining) { + length = remaining; } } - return val; -} - -Buffer.prototype.readUInt16LE = function(offset, noAssert) { - return readUInt16(this, offset, false, noAssert); -}; - -Buffer.prototype.readUInt16BE = function(offset, noAssert) { - return readUInt16(this, offset, true, noAssert); -}; - -function readUInt32(buffer, offset, isBigEndian, noAssert) { - var val = 0; - - if (!noAssert) { - assert.ok(typeof (isBigEndian) === 'boolean', - 'missing or invalid endian'); - - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset + 3 < buffer.length, - 'Trying to read beyond buffer length'); + // must be an even number of digits + var strLen = string.length; + if (strLen % 2) { + throw new Error('Invalid hex string'); } - - if (offset >= buffer.length) return 0; - - if (isBigEndian) { - if (offset + 1 < buffer.length) - val = buffer.parent[buffer.offset + offset + 1] << 16; - if (offset + 2 < buffer.length) - val |= buffer.parent[buffer.offset + offset + 2] << 8; - if (offset + 3 < buffer.length) - val |= buffer.parent[buffer.offset + offset + 3]; - val = val + (buffer.parent[buffer.offset + offset] << 24 >>> 0); - } else { - if (offset + 2 < buffer.length) - val = buffer.parent[buffer.offset + offset + 2] << 16; - if (offset + 1 < buffer.length) - val |= buffer.parent[buffer.offset + offset + 1] << 8; - val |= buffer.parent[buffer.offset + offset]; - if (offset + 3 < buffer.length) - val = val + (buffer.parent[buffer.offset + offset + 3] << 24 >>> 0); + if (length > strLen / 2) { + length = strLen / 2; } - - return val; -} - -Buffer.prototype.readUInt32LE = function(offset, noAssert) { - return readUInt32(this, offset, false, noAssert); -}; - -Buffer.prototype.readUInt32BE = function(offset, noAssert) { - return readUInt32(this, offset, true, noAssert); -}; - - -/* - * Signed integer types, yay team! A reminder on how two's complement actually - * works. The first bit is the signed bit, i.e. tells us whether or not the - * number should be positive or negative. If the two's complement value is - * positive, then we're done, as it's equivalent to the unsigned representation. - * - * Now if the number is positive, you're pretty much done, you can just leverage - * the unsigned translations and return those. Unfortunately, negative numbers - * aren't quite that straightforward. - * - * At first glance, one might be inclined to use the traditional formula to - * translate binary numbers between the positive and negative values in two's - * complement. (Though it doesn't quite work for the most negative value) - * Mainly: - * - invert all the bits - * - add one to the result - * - * Of course, this doesn't quite work in Javascript. Take for example the value - * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of - * course, Javascript will do the following: - * - * > ~0xff80 - * -65409 - * - * Whoh there, Javascript, that's not quite right. But wait, according to - * Javascript that's perfectly correct. When Javascript ends up seeing the - * constant 0xff80, it has no notion that it is actually a signed number. It - * assumes that we've input the unsigned value 0xff80. Thus, when it does the - * binary negation, it casts it into a signed value, (positive 0xff80). Then - * when you perform binary negation on that, it turns it into a negative number. - * - * Instead, we're going to have to use the following general formula, that works - * in a rather Javascript friendly way. I'm glad we don't support this kind of - * weird numbering scheme in the kernel. - * - * (BIT-MAX - (unsigned)val + 1) * -1 - * - * The astute observer, may think that this doesn't make sense for 8-bit numbers - * (really it isn't necessary for them). However, when you get 16-bit numbers, - * you do. Let's go back to our prior example and see how this will look: - * - * (0xffff - 0xff80 + 1) * -1 - * (0x007f + 1) * -1 - * (0x0080) * -1 - */ -Buffer.prototype.readInt8 = function(offset, noAssert) { - var buffer = this; - var neg; - - if (!noAssert) { - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset < buffer.length, - 'Trying to read beyond buffer length'); + for (var i = 0; i < length; i++) { + var byte = parseInt(string.substr(i * 2, 2), 16); + if (isNaN(byte)) throw new Error('Invalid hex string'); + this[offset + i] = byte; } - - if (offset >= buffer.length) return; - - neg = buffer.parent[buffer.offset + offset] & 0x80; - if (!neg) { - return (buffer.parent[buffer.offset + offset]); - } - - return ((0xff - buffer.parent[buffer.offset + offset] + 1) * -1); + SlowBuffer._charsWritten = i * 2; + return i; }; -function readInt16(buffer, offset, isBigEndian, noAssert) { - var neg, val; - - if (!noAssert) { - assert.ok(typeof (isBigEndian) === 'boolean', - 'missing or invalid endian'); - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset + 1 < buffer.length, - 'Trying to read beyond buffer length'); +SlowBuffer.prototype.write = function(string, offset, length, encoding) { + // Support both (string, offset, length, encoding) + // and the legacy (string, encoding, offset, length) + if (isFinite(offset)) { + if (!isFinite(length)) { + encoding = length; + length = undefined; + } + } else { // legacy + var swap = encoding; + encoding = offset; + offset = length; + length = swap; } - val = readUInt16(buffer, offset, isBigEndian, noAssert); - neg = val & 0x8000; - if (!neg) { - return val; + offset = +offset || 0; + var remaining = this.length - offset; + if (!length) { + length = remaining; + } else { + length = +length; + if (length > remaining) { + length = remaining; + } } + encoding = String(encoding || 'utf8').toLowerCase(); - return (0xffff - val + 1) * -1; -} - -Buffer.prototype.readInt16LE = function(offset, noAssert) { - return readInt16(this, offset, false, noAssert); -}; + switch (encoding) { + case 'hex': + return this.hexWrite(string, offset, length); -Buffer.prototype.readInt16BE = function(offset, noAssert) { - return readInt16(this, offset, true, noAssert); -}; + case 'utf8': + case 'utf-8': + return this.utf8Write(string, offset, length); -function readInt32(buffer, offset, isBigEndian, noAssert) { - var neg, val; + case 'ascii': + return this.asciiWrite(string, offset, length); - if (!noAssert) { - assert.ok(typeof (isBigEndian) === 'boolean', - 'missing or invalid endian'); + case 'binary': + return this.binaryWrite(string, offset, length); - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); + case 'base64': + return this.base64Write(string, offset, length); - assert.ok(offset + 3 < buffer.length, - 'Trying to read beyond buffer length'); - } + case 'ucs2': + case 'ucs-2': + return this.ucs2Write(string, offset, length); - val = readUInt32(buffer, offset, isBigEndian, noAssert); - neg = val & 0x80000000; - if (!neg) { - return (val); + default: + throw new Error('Unknown encoding'); } - - return (0xffffffff - val + 1) * -1; -} - -Buffer.prototype.readInt32LE = function(offset, noAssert) { - return readInt32(this, offset, false, noAssert); }; -Buffer.prototype.readInt32BE = function(offset, noAssert) { - return readInt32(this, offset, true, noAssert); -}; -function readFloat(buffer, offset, isBigEndian, noAssert) { - if (!noAssert) { - assert.ok(typeof (isBigEndian) === 'boolean', - 'missing or invalid endian'); +// slice(start, end) +SlowBuffer.prototype.slice = function(start, end) { + if (end === undefined) end = this.length; - assert.ok(offset + 3 < buffer.length, - 'Trying to read beyond buffer length'); + if (end > this.length) { + throw new Error('oob'); } - - return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, - 23, 4); -} - -Buffer.prototype.readFloatLE = function(offset, noAssert) { - return readFloat(this, offset, false, noAssert); -}; - -Buffer.prototype.readFloatBE = function(offset, noAssert) { - return readFloat(this, offset, true, noAssert); -}; - -function readDouble(buffer, offset, isBigEndian, noAssert) { - if (!noAssert) { - assert.ok(typeof (isBigEndian) === 'boolean', - 'missing or invalid endian'); - - assert.ok(offset + 7 < buffer.length, - 'Trying to read beyond buffer length'); + if (start > end) { + throw new Error('oob'); } - return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, - 52, 8); -} - -Buffer.prototype.readDoubleLE = function(offset, noAssert) { - return readDouble(this, offset, false, noAssert); -}; - -Buffer.prototype.readDoubleBE = function(offset, noAssert) { - return readDouble(this, offset, true, noAssert); + return new Buffer(this, end - start, +start); }; - -/* - * We have to make sure that the value is a valid integer. This means that it is - * non-negative. It has no fractional component and that it does not exceed the - * maximum allowed value. - * - * value The number to check for validity - * - * max The maximum value - */ -function verifuint(value, max) { - assert.ok(typeof (value) == 'number', - 'cannot write a non-number as a number'); - - assert.ok(value >= 0, - 'specified a negative value for writing an unsigned value'); - - assert.ok(value <= max, 'value is larger than maximum value for type'); - - assert.ok(Math.floor(value) === value, 'value has a fractional component'); -} - -Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { - var buffer = this; - - if (!noAssert) { - assert.ok(value !== undefined && value !== null, - 'missing value'); - - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset < buffer.length, - 'trying to write beyond buffer length'); - - verifuint(value, 0xff); +SlowBuffer.prototype.copy = function(target, targetstart, sourcestart, sourceend) { + var temp = []; + for (var i=sourcestart; i>> - (isBigEndian ? 1 - i : i) * 8; +SlowBuffer.prototype.fill = function(value, start, end) { + if (end > this.length) { + throw new Error('oob'); } - -} - -Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { - writeUInt16(this, value, offset, false, noAssert); -}; - -Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { - writeUInt16(this, value, offset, true, noAssert); -}; - -function writeUInt32(buffer, value, offset, isBigEndian, noAssert) { - if (!noAssert) { - assert.ok(value !== undefined && value !== null, - 'missing value'); - - assert.ok(typeof (isBigEndian) === 'boolean', - 'missing or invalid endian'); - - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset + 3 < buffer.length, - 'trying to write beyond buffer length'); - - verifuint(value, 0xffffffff); + if (start > end) { + throw new Error('oob'); } - for (var i = 0; i < Math.min(buffer.length - offset, 4); i++) { - buffer.parent[buffer.offset + offset + i] = - (value >>> (isBigEndian ? 3 - i : i) * 8) & 0xff; + for (var i = start; i < end; i++) { + this[i] = value; } } -Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { - writeUInt32(this, value, offset, false, noAssert); -}; - -Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { - writeUInt32(this, value, offset, true, noAssert); -}; - - -/* - * We now move onto our friends in the signed number category. Unlike unsigned - * numbers, we're going to have to worry a bit more about how we put values into - * arrays. Since we are only worrying about signed 32-bit values, we're in - * slightly better shape. Unfortunately, we really can't do our favorite binary - * & in this system. It really seems to do the wrong thing. For example: - * - * > -32 & 0xff - * 224 - * - * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of - * this aren't treated as a signed number. Ultimately a bad thing. - * - * What we're going to want to do is basically create the unsigned equivalent of - * our representation and pass that off to the wuint* functions. To do that - * we're going to do the following: - * - * - if the value is positive - * we can pass it directly off to the equivalent wuint - * - if the value is negative - * we do the following computation: - * mb + val + 1, where - * mb is the maximum unsigned value in that byte size - * val is the Javascript negative integer - * - * - * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If - * you do out the computations: - * - * 0xffff - 128 + 1 - * 0xffff - 127 - * 0xff80 - * - * You can then encode this value as the signed version. This is really rather - * hacky, but it should work and get the job done which is our goal here. - */ - -/* - * A series of checks to make sure we actually have a signed 32-bit number - */ -function verifsint(value, max, min) { - assert.ok(typeof (value) == 'number', - 'cannot write a non-number as a number'); - - assert.ok(value <= max, 'value larger than maximum allowed value'); - - assert.ok(value >= min, 'value smaller than minimum allowed value'); - - assert.ok(Math.floor(value) === value, 'value has a fractional component'); -} - -function verifIEEE754(value, max, min) { - assert.ok(typeof (value) == 'number', - 'cannot write a non-number as a number'); - - assert.ok(value <= max, 'value larger than maximum allowed value'); - - assert.ok(value >= min, 'value smaller than minimum allowed value'); -} - -Buffer.prototype.writeInt8 = function(value, offset, noAssert) { - var buffer = this; - - if (!noAssert) { - assert.ok(value !== undefined && value !== null, - 'missing value'); - - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset < buffer.length, - 'Trying to write beyond buffer length'); - - verifsint(value, 0x7f, -0x80); - } - - if (value >= 0) { - buffer.writeUInt8(value, offset, noAssert); - } else { - buffer.writeUInt8(0xff + value + 1, offset, noAssert); - } +// Static methods +Buffer.isBuffer = function isBuffer(b) { + return b instanceof Buffer || b instanceof SlowBuffer; }; -function writeInt16(buffer, value, offset, isBigEndian, noAssert) { - if (!noAssert) { - assert.ok(value !== undefined && value !== null, - 'missing value'); - - assert.ok(typeof (isBigEndian) === 'boolean', - 'missing or invalid endian'); - - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset + 1 < buffer.length, - 'Trying to write beyond buffer length'); - - verifsint(value, 0x7fff, -0x8000); +Buffer.concat = function (list, totalLength) { + if (!Array.isArray(list)) { + throw new Error("Usage: Buffer.concat(list, [totalLength])\n \ + list should be an Array."); } - if (value >= 0) { - writeUInt16(buffer, value, offset, isBigEndian, noAssert); - } else { - writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert); + if (list.length === 0) { + return new Buffer(0); + } else if (list.length === 1) { + return list[0]; } -} - -Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { - writeInt16(this, value, offset, false, noAssert); -}; - -Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { - writeInt16(this, value, offset, true, noAssert); -}; -function writeInt32(buffer, value, offset, isBigEndian, noAssert) { - if (!noAssert) { - assert.ok(value !== undefined && value !== null, - 'missing value'); - - assert.ok(typeof (isBigEndian) === 'boolean', - 'missing or invalid endian'); - - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset + 3 < buffer.length, - 'Trying to write beyond buffer length'); - - verifsint(value, 0x7fffffff, -0x80000000); + if (typeof totalLength !== 'number') { + totalLength = 0; + for (var i = 0; i < list.length; i++) { + var buf = list[i]; + totalLength += buf.length; + } } - if (value >= 0) { - writeUInt32(buffer, value, offset, isBigEndian, noAssert); - } else { - writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert); + var buffer = new Buffer(totalLength); + var pos = 0; + for (var i = 0; i < list.length; i++) { + var buf = list[i]; + buf.copy(buffer, pos); + pos += buf.length; } -} - -Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { - writeInt32(this, value, offset, false, noAssert); -}; - -Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { - writeInt32(this, value, offset, true, noAssert); + return buffer; }; -function writeFloat(buffer, value, offset, isBigEndian, noAssert) { - if (!noAssert) { - assert.ok(value !== undefined && value !== null, - 'missing value'); - - assert.ok(typeof (isBigEndian) === 'boolean', - 'missing or invalid endian'); - - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset + 3 < buffer.length, - 'Trying to write beyond buffer length'); - - verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38); - } +// helpers - require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, - 23, 4); +function coerce(length) { + // Coerce length to a number (possibly NaN), round up + // in case it's fractional (e.g. 123.456) then do a + // double negate to coerce a NaN to 0. Easy, right? + length = ~~Math.ceil(+length); + return length < 0 ? 0 : length; } -Buffer.prototype.writeFloatLE = function(value, offset, noAssert) { - writeFloat(this, value, offset, false, noAssert); -}; - -Buffer.prototype.writeFloatBE = function(value, offset, noAssert) { - writeFloat(this, value, offset, true, noAssert); -}; - -function writeDouble(buffer, value, offset, isBigEndian, noAssert) { - if (!noAssert) { - assert.ok(value !== undefined && value !== null, - 'missing value'); - - assert.ok(typeof (isBigEndian) === 'boolean', - 'missing or invalid endian'); - - assert.ok(offset !== undefined && offset !== null, - 'missing offset'); - - assert.ok(offset + 7 < buffer.length, - 'Trying to write beyond buffer length'); - - verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308); - } - - require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, - 52, 8); +function isArrayIsh(subject) { + return Array.isArray(subject) || Buffer.isBuffer(subject) || + subject && typeof subject === 'object' && + typeof subject.length === 'number'; } - -Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) { - writeDouble(this, value, offset, false, noAssert); -}; - -Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) { - writeDouble(this, value, offset, true, noAssert); -}; - -SlowBuffer.prototype.readUInt8 = Buffer.prototype.readUInt8; -SlowBuffer.prototype.readUInt16LE = Buffer.prototype.readUInt16LE; -SlowBuffer.prototype.readUInt16BE = Buffer.prototype.readUInt16BE; -SlowBuffer.prototype.readUInt32LE = Buffer.prototype.readUInt32LE; -SlowBuffer.prototype.readUInt32BE = Buffer.prototype.readUInt32BE; -SlowBuffer.prototype.readInt8 = Buffer.prototype.readInt8; -SlowBuffer.prototype.readInt16LE = Buffer.prototype.readInt16LE; -SlowBuffer.prototype.readInt16BE = Buffer.prototype.readInt16BE; -SlowBuffer.prototype.readInt32LE = Buffer.prototype.readInt32LE; -SlowBuffer.prototype.readInt32BE = Buffer.prototype.readInt32BE; -SlowBuffer.prototype.readFloatLE = Buffer.prototype.readFloatLE; -SlowBuffer.prototype.readFloatBE = Buffer.prototype.readFloatBE; -SlowBuffer.prototype.readDoubleLE = Buffer.prototype.readDoubleLE; -SlowBuffer.prototype.readDoubleBE = Buffer.prototype.readDoubleBE; -SlowBuffer.prototype.writeUInt8 = Buffer.prototype.writeUInt8; -SlowBuffer.prototype.writeUInt16LE = Buffer.prototype.writeUInt16LE; -SlowBuffer.prototype.writeUInt16BE = Buffer.prototype.writeUInt16BE; -SlowBuffer.prototype.writeUInt32LE = Buffer.prototype.writeUInt32LE; -SlowBuffer.prototype.writeUInt32BE = Buffer.prototype.writeUInt32BE; -SlowBuffer.prototype.writeInt8 = Buffer.prototype.writeInt8; -SlowBuffer.prototype.writeInt16LE = Buffer.prototype.writeInt16LE; -SlowBuffer.prototype.writeInt16BE = Buffer.prototype.writeInt16BE; -SlowBuffer.prototype.writeInt32LE = Buffer.prototype.writeInt32LE; -SlowBuffer.prototype.writeInt32BE = Buffer.prototype.writeInt32BE; -SlowBuffer.prototype.writeFloatLE = Buffer.prototype.writeFloatLE; -SlowBuffer.prototype.writeFloatBE = Buffer.prototype.writeFloatBE; -SlowBuffer.prototype.writeDoubleLE = Buffer.prototype.writeDoubleLE; -SlowBuffer.prototype.writeDoubleBE = Buffer.prototype.writeDoubleBE; diff --git a/old_buffer.js b/old_buffer.js new file mode 100644 index 0000000..55a26f5 --- /dev/null +++ b/old_buffer.js @@ -0,0 +1,1011 @@ +var assert = require('assert'); +var SlowBuffer = require('./slow_buffer'); + +exports.INSPECT_MAX_BYTES = 50; + +function coerce(length) { + // Coerce length to a number (possibly NaN), round up + // in case it's fractional (e.g. 123.456) then do a + // double negate to coerce a NaN to 0. Easy, right? + length = ~~Math.ceil(+length); + return length < 0 ? 0 : length; +} + +function toHex(n) { + if (n < 16) return '0' + n.toString(16); + return n.toString(16); +} + +// Buffer + +function Buffer(subject, encoding, offset) { + if (!(this instanceof Buffer)) { + return new Buffer(subject, encoding, offset); + } + + var type; + + // Are we slicing? + if (typeof offset === 'number') { + this.length = coerce(encoding); + this.parent = subject; + this.offset = offset; + } else { + // Find the length + switch (type = typeof subject) { + case 'number': + this.length = coerce(subject); + break; + + case 'string': + this.length = Buffer.byteLength(subject, encoding); + break; + + case 'object': // Assume object is an array + this.length = coerce(subject.length); + break; + + default: + throw new Error('First argument needs to be a number, ' + + 'array or string.'); + } + + if (this.length > Buffer.poolSize) { + // Big buffer, just alloc one. + this.parent = new SlowBuffer(this.length); + this.offset = 0; + + } else { + // Small buffer. + if (!pool || pool.length - pool.used < this.length) allocPool(); + this.parent = pool; + this.offset = pool.used; + pool.used += this.length; + } + + // Treat array-ish objects as a byte array. + if (isArrayIsh(subject)) { + for (var i = 0; i < this.length; i++) { + if (subject instanceof Buffer) { + this.parent[i + this.offset] = subject.readUInt8(i); + this[i] = subject.readUInt8(i); + } + else { + this.parent[i + this.offset] = subject[i]; + this[i] = subject[i]; + } + } + } else if (type == 'string') { + // We are a string + this.length = this.write(subject, 0, encoding); + for (var i = 0; i < this.length; i++) { + this[i] = this.parent[i]; + } + } + } + +} + +function isArrayIsh(subject) { + return Array.isArray(subject) || Buffer.isBuffer(subject) || + subject && typeof subject === 'object' && + typeof subject.length === 'number'; +} + +exports.SlowBuffer = SlowBuffer; +exports.Buffer = Buffer; + +Buffer.poolSize = 8 * 1024; +var pool; + +function allocPool() { + pool = new SlowBuffer(Buffer.poolSize); + pool.used = 0; +} + + +// Static methods +Buffer.isBuffer = function isBuffer(b) { + return b instanceof Buffer || b instanceof SlowBuffer; +}; + +Buffer.concat = function (list, totalLength) { + if (!Array.isArray(list)) { + throw new Error("Usage: Buffer.concat(list, [totalLength])\n \ + list should be an Array."); + } + + if (list.length === 0) { + return new Buffer(0); + } else if (list.length === 1) { + return list[0]; + } + + if (typeof totalLength !== 'number') { + totalLength = 0; + for (var i = 0; i < list.length; i++) { + var buf = list[i]; + totalLength += buf.length; + } + } + + var buffer = new Buffer(totalLength); + var pos = 0; + for (var i = 0; i < list.length; i++) { + var buf = list[i]; + buf.copy(buffer, pos); + pos += buf.length; + } + return buffer; +}; + +// Inspect +Buffer.prototype.inspect = function inspect() { + var out = [], + len = this.length; + + for (var i = 0; i < len; i++) { + out[i] = toHex(this.parent[i + this.offset]); + if (i == exports.INSPECT_MAX_BYTES) { + out[i + 1] = '...'; + break; + } + } + + return ''; +}; + + +Buffer.prototype.get = function get(i) { + if (i < 0 || i >= this.length) throw new Error('oob'); + return this.parent[this.offset + i]; +}; + + +Buffer.prototype.set = function set(i, v) { + if (i < 0 || i >= this.length) throw new Error('oob'); + return this.parent[this.offset + i] = v; +}; + + +// write(string, offset = 0, length = buffer.length-offset, encoding = 'utf8') +Buffer.prototype.write = function(string, offset, length, encoding) { + // Support both (string, offset, length, encoding) + // and the legacy (string, encoding, offset, length) + if (isFinite(offset)) { + if (!isFinite(length)) { + encoding = length; + length = undefined; + } + } else { // legacy + var swap = encoding; + encoding = offset; + offset = length; + length = swap; + } + + offset = +offset || 0; + var remaining = this.length - offset; + if (!length) { + length = remaining; + } else { + length = +length; + if (length > remaining) { + length = remaining; + } + } + encoding = String(encoding || 'utf8').toLowerCase(); + + var ret; + switch (encoding) { + case 'hex': + ret = this.parent.hexWrite(string, this.offset + offset, length); + break; + + case 'utf8': + case 'utf-8': + ret = this.parent.utf8Write(string, this.offset + offset, length); + break; + + case 'ascii': + ret = this.parent.asciiWrite(string, this.offset + offset, length); + break; + + case 'binary': + ret = this.parent.binaryWrite(string, this.offset + offset, length); + break; + + case 'base64': + // Warning: maxLength not taken into account in base64Write + ret = this.parent.base64Write(string, this.offset + offset, length); + break; + + case 'ucs2': + case 'ucs-2': + ret = this.parent.ucs2Write(string, this.offset + offset, length); + break; + + default: + throw new Error('Unknown encoding'); + } + + Buffer._charsWritten = SlowBuffer._charsWritten; + + return ret; +}; + + +// toString(encoding, start=0, end=buffer.length) +Buffer.prototype.toString = function(encoding, start, end) { + encoding = String(encoding || 'utf8').toLowerCase(); + + if (typeof start == 'undefined' || start < 0) { + start = 0; + } else if (start > this.length) { + start = this.length; + } + + if (typeof end == 'undefined' || end > this.length) { + end = this.length; + } else if (end < 0) { + end = 0; + } + + start = start + this.offset; + end = end + this.offset; + + switch (encoding) { + case 'hex': + return this.parent.hexSlice(start, end); + + case 'utf8': + case 'utf-8': + return this.parent.utf8Slice(start, end); + + case 'ascii': + return this.parent.asciiSlice(start, end); + + case 'binary': + return this.parent.binarySlice(start, end); + + case 'base64': + return this.parent.base64Slice(start, end); + + case 'ucs2': + case 'ucs-2': + return this.parent.ucs2Slice(start, end); + + default: + throw new Error('Unknown encoding'); + } +}; + + +// byteLength +Buffer.byteLength = SlowBuffer.byteLength; + + +// fill(value, start=0, end=buffer.length) +Buffer.prototype.fill = function fill(value, start, end) { + value || (value = 0); + start || (start = 0); + end || (end = this.length); + + if (typeof value === 'string') { + value = value.charCodeAt(0); + } + if (!(typeof value === 'number') || isNaN(value)) { + throw new Error('value is not a number'); + } + + if (end < start) throw new Error('end < start'); + + // Fill 0 bytes; we're done + if (end === start) return 0; + if (this.length == 0) return 0; + + if (start < 0 || start >= this.length) { + throw new Error('start out of bounds'); + } + + if (end < 0 || end > this.length) { + throw new Error('end out of bounds'); + } + + return this.parent.fill(value, + start + this.offset, + end + this.offset); +}; + + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function(target, target_start, start, end) { + var source = this; + start || (start = 0); + end || (end = this.length); + target_start || (target_start = 0); + + if (end < start) throw new Error('sourceEnd < sourceStart'); + + // Copy 0 bytes; we're done + if (end === start) return 0; + if (target.length == 0 || source.length == 0) return 0; + + if (target_start < 0 || target_start >= target.length) { + throw new Error('targetStart out of bounds'); + } + + if (start < 0 || start >= source.length) { + throw new Error('sourceStart out of bounds'); + } + + if (end < 0 || end > source.length) { + throw new Error('sourceEnd out of bounds'); + } + + // Are we oob? + if (end > this.length) { + end = this.length; + } + + if (target.length - target_start < end - start) { + end = target.length - target_start + start; + } + + return this.parent.copy(target.parent, + target_start + target.offset, + start + this.offset, + end + this.offset); +}; + + +// slice(start, end) +Buffer.prototype.slice = function(start, end) { + if (end === undefined) end = this.length; + if (end > this.length) throw new Error('oob'); + if (start > end) throw new Error('oob'); + + return new Buffer(this.parent, end - start, +start + this.offset); +}; + + +// Legacy methods for backwards compatibility. + +Buffer.prototype.utf8Slice = function(start, end) { + return this.toString('utf8', start, end); +}; + +Buffer.prototype.binarySlice = function(start, end) { + return this.toString('binary', start, end); +}; + +Buffer.prototype.asciiSlice = function(start, end) { + return this.toString('ascii', start, end); +}; + +Buffer.prototype.utf8Write = function(string, offset) { + return this.write(string, offset, 'utf8'); +}; + +Buffer.prototype.binaryWrite = function(string, offset) { + return this.write(string, offset, 'binary'); +}; + +Buffer.prototype.asciiWrite = function(string, offset) { + return this.write(string, offset, 'ascii'); +}; + +Buffer.prototype.readUInt8 = function(offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return; + + return buffer.parent[buffer.offset + offset]; +}; + +function readUInt16(buffer, offset, isBigEndian, noAssert) { + var val = 0; + + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return 0; + + if (isBigEndian) { + val = buffer.parent[buffer.offset + offset] << 8; + if (offset + 1 < buffer.length) { + val |= buffer.parent[buffer.offset + offset + 1]; + } + } else { + val = buffer.parent[buffer.offset + offset]; + if (offset + 1 < buffer.length) { + val |= buffer.parent[buffer.offset + offset + 1] << 8; + } + } + + return val; +} + +Buffer.prototype.readUInt16LE = function(offset, noAssert) { + return readUInt16(this, offset, false, noAssert); +}; + +Buffer.prototype.readUInt16BE = function(offset, noAssert) { + return readUInt16(this, offset, true, noAssert); +}; + +function readUInt32(buffer, offset, isBigEndian, noAssert) { + var val = 0; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return 0; + + if (isBigEndian) { + if (offset + 1 < buffer.length) + val = buffer.parent[buffer.offset + offset + 1] << 16; + if (offset + 2 < buffer.length) + val |= buffer.parent[buffer.offset + offset + 2] << 8; + if (offset + 3 < buffer.length) + val |= buffer.parent[buffer.offset + offset + 3]; + val = val + (buffer.parent[buffer.offset + offset] << 24 >>> 0); + } else { + if (offset + 2 < buffer.length) + val = buffer.parent[buffer.offset + offset + 2] << 16; + if (offset + 1 < buffer.length) + val |= buffer.parent[buffer.offset + offset + 1] << 8; + val |= buffer.parent[buffer.offset + offset]; + if (offset + 3 < buffer.length) + val = val + (buffer.parent[buffer.offset + offset + 3] << 24 >>> 0); + } + + return val; +} + +Buffer.prototype.readUInt32LE = function(offset, noAssert) { + return readUInt32(this, offset, false, noAssert); +}; + +Buffer.prototype.readUInt32BE = function(offset, noAssert) { + return readUInt32(this, offset, true, noAssert); +}; + + +/* + * Signed integer types, yay team! A reminder on how two's complement actually + * works. The first bit is the signed bit, i.e. tells us whether or not the + * number should be positive or negative. If the two's complement value is + * positive, then we're done, as it's equivalent to the unsigned representation. + * + * Now if the number is positive, you're pretty much done, you can just leverage + * the unsigned translations and return those. Unfortunately, negative numbers + * aren't quite that straightforward. + * + * At first glance, one might be inclined to use the traditional formula to + * translate binary numbers between the positive and negative values in two's + * complement. (Though it doesn't quite work for the most negative value) + * Mainly: + * - invert all the bits + * - add one to the result + * + * Of course, this doesn't quite work in Javascript. Take for example the value + * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of + * course, Javascript will do the following: + * + * > ~0xff80 + * -65409 + * + * Whoh there, Javascript, that's not quite right. But wait, according to + * Javascript that's perfectly correct. When Javascript ends up seeing the + * constant 0xff80, it has no notion that it is actually a signed number. It + * assumes that we've input the unsigned value 0xff80. Thus, when it does the + * binary negation, it casts it into a signed value, (positive 0xff80). Then + * when you perform binary negation on that, it turns it into a negative number. + * + * Instead, we're going to have to use the following general formula, that works + * in a rather Javascript friendly way. I'm glad we don't support this kind of + * weird numbering scheme in the kernel. + * + * (BIT-MAX - (unsigned)val + 1) * -1 + * + * The astute observer, may think that this doesn't make sense for 8-bit numbers + * (really it isn't necessary for them). However, when you get 16-bit numbers, + * you do. Let's go back to our prior example and see how this will look: + * + * (0xffff - 0xff80 + 1) * -1 + * (0x007f + 1) * -1 + * (0x0080) * -1 + */ +Buffer.prototype.readInt8 = function(offset, noAssert) { + var buffer = this; + var neg; + + if (!noAssert) { + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return; + + neg = buffer.parent[buffer.offset + offset] & 0x80; + if (!neg) { + return (buffer.parent[buffer.offset + offset]); + } + + return ((0xff - buffer.parent[buffer.offset + offset] + 1) * -1); +}; + +function readInt16(buffer, offset, isBigEndian, noAssert) { + var neg, val; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to read beyond buffer length'); + } + + val = readUInt16(buffer, offset, isBigEndian, noAssert); + neg = val & 0x8000; + if (!neg) { + return val; + } + + return (0xffff - val + 1) * -1; +} + +Buffer.prototype.readInt16LE = function(offset, noAssert) { + return readInt16(this, offset, false, noAssert); +}; + +Buffer.prototype.readInt16BE = function(offset, noAssert) { + return readInt16(this, offset, true, noAssert); +}; + +function readInt32(buffer, offset, isBigEndian, noAssert) { + var neg, val; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + val = readUInt32(buffer, offset, isBigEndian, noAssert); + neg = val & 0x80000000; + if (!neg) { + return (val); + } + + return (0xffffffff - val + 1) * -1; +} + +Buffer.prototype.readInt32LE = function(offset, noAssert) { + return readInt32(this, offset, false, noAssert); +}; + +Buffer.prototype.readInt32BE = function(offset, noAssert) { + return readInt32(this, offset, true, noAssert); +}; + +function readFloat(buffer, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, + 23, 4); +} + +Buffer.prototype.readFloatLE = function(offset, noAssert) { + return readFloat(this, offset, false, noAssert); +}; + +Buffer.prototype.readFloatBE = function(offset, noAssert) { + return readFloat(this, offset, true, noAssert); +}; + +function readDouble(buffer, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset + 7 < buffer.length, + 'Trying to read beyond buffer length'); + } + + return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, + 52, 8); +} + +Buffer.prototype.readDoubleLE = function(offset, noAssert) { + return readDouble(this, offset, false, noAssert); +}; + +Buffer.prototype.readDoubleBE = function(offset, noAssert) { + return readDouble(this, offset, true, noAssert); +}; + + +/* + * We have to make sure that the value is a valid integer. This means that it is + * non-negative. It has no fractional component and that it does not exceed the + * maximum allowed value. + * + * value The number to check for validity + * + * max The maximum value + */ +function verifuint(value, max) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value >= 0, + 'specified a negative value for writing an unsigned value'); + + assert.ok(value <= max, 'value is larger than maximum value for type'); + + assert.ok(Math.floor(value) === value, 'value has a fractional component'); +} + +Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xff); + } + + if (offset < buffer.length) { + buffer.parent[buffer.offset + offset] = value; + } +}; + +function writeUInt16(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xffff); + } + + for (var i = 0; i < Math.min(buffer.length - offset, 2); i++) { + buffer.parent[buffer.offset + offset + i] = + (value & (0xff << (8 * (isBigEndian ? 1 - i : i)))) >>> + (isBigEndian ? 1 - i : i) * 8; + } + +} + +Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { + writeUInt16(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { + writeUInt16(this, value, offset, true, noAssert); +}; + +function writeUInt32(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xffffffff); + } + + for (var i = 0; i < Math.min(buffer.length - offset, 4); i++) { + buffer.parent[buffer.offset + offset + i] = + (value >>> (isBigEndian ? 3 - i : i) * 8) & 0xff; + } +} + +Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { + writeUInt32(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { + writeUInt32(this, value, offset, true, noAssert); +}; + + +/* + * We now move onto our friends in the signed number category. Unlike unsigned + * numbers, we're going to have to worry a bit more about how we put values into + * arrays. Since we are only worrying about signed 32-bit values, we're in + * slightly better shape. Unfortunately, we really can't do our favorite binary + * & in this system. It really seems to do the wrong thing. For example: + * + * > -32 & 0xff + * 224 + * + * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of + * this aren't treated as a signed number. Ultimately a bad thing. + * + * What we're going to want to do is basically create the unsigned equivalent of + * our representation and pass that off to the wuint* functions. To do that + * we're going to do the following: + * + * - if the value is positive + * we can pass it directly off to the equivalent wuint + * - if the value is negative + * we do the following computation: + * mb + val + 1, where + * mb is the maximum unsigned value in that byte size + * val is the Javascript negative integer + * + * + * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If + * you do out the computations: + * + * 0xffff - 128 + 1 + * 0xffff - 127 + * 0xff80 + * + * You can then encode this value as the signed version. This is really rather + * hacky, but it should work and get the job done which is our goal here. + */ + +/* + * A series of checks to make sure we actually have a signed 32-bit number + */ +function verifsint(value, max, min) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value <= max, 'value larger than maximum allowed value'); + + assert.ok(value >= min, 'value smaller than minimum allowed value'); + + assert.ok(Math.floor(value) === value, 'value has a fractional component'); +} + +function verifIEEE754(value, max, min) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value <= max, 'value larger than maximum allowed value'); + + assert.ok(value >= min, 'value smaller than minimum allowed value'); +} + +Buffer.prototype.writeInt8 = function(value, offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7f, -0x80); + } + + if (value >= 0) { + buffer.writeUInt8(value, offset, noAssert); + } else { + buffer.writeUInt8(0xff + value + 1, offset, noAssert); + } +}; + +function writeInt16(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7fff, -0x8000); + } + + if (value >= 0) { + writeUInt16(buffer, value, offset, isBigEndian, noAssert); + } else { + writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert); + } +} + +Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { + writeInt16(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { + writeInt16(this, value, offset, true, noAssert); +}; + +function writeInt32(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7fffffff, -0x80000000); + } + + if (value >= 0) { + writeUInt32(buffer, value, offset, isBigEndian, noAssert); + } else { + writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert); + } +} + +Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { + writeInt32(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { + writeInt32(this, value, offset, true, noAssert); +}; + +function writeFloat(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to write beyond buffer length'); + + verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38); + } + + require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, + 23, 4); +} + +Buffer.prototype.writeFloatLE = function(value, offset, noAssert) { + writeFloat(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeFloatBE = function(value, offset, noAssert) { + writeFloat(this, value, offset, true, noAssert); +}; + +function writeDouble(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 7 < buffer.length, + 'Trying to write beyond buffer length'); + + verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308); + } + + require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, + 52, 8); +} + +Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) { + writeDouble(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) { + writeDouble(this, value, offset, true, noAssert); +}; + +SlowBuffer.prototype.readUInt8 = Buffer.prototype.readUInt8; +SlowBuffer.prototype.readUInt16LE = Buffer.prototype.readUInt16LE; +SlowBuffer.prototype.readUInt16BE = Buffer.prototype.readUInt16BE; +SlowBuffer.prototype.readUInt32LE = Buffer.prototype.readUInt32LE; +SlowBuffer.prototype.readUInt32BE = Buffer.prototype.readUInt32BE; +SlowBuffer.prototype.readInt8 = Buffer.prototype.readInt8; +SlowBuffer.prototype.readInt16LE = Buffer.prototype.readInt16LE; +SlowBuffer.prototype.readInt16BE = Buffer.prototype.readInt16BE; +SlowBuffer.prototype.readInt32LE = Buffer.prototype.readInt32LE; +SlowBuffer.prototype.readInt32BE = Buffer.prototype.readInt32BE; +SlowBuffer.prototype.readFloatLE = Buffer.prototype.readFloatLE; +SlowBuffer.prototype.readFloatBE = Buffer.prototype.readFloatBE; +SlowBuffer.prototype.readDoubleLE = Buffer.prototype.readDoubleLE; +SlowBuffer.prototype.readDoubleBE = Buffer.prototype.readDoubleBE; +SlowBuffer.prototype.writeUInt8 = Buffer.prototype.writeUInt8; +SlowBuffer.prototype.writeUInt16LE = Buffer.prototype.writeUInt16LE; +SlowBuffer.prototype.writeUInt16BE = Buffer.prototype.writeUInt16BE; +SlowBuffer.prototype.writeUInt32LE = Buffer.prototype.writeUInt32LE; +SlowBuffer.prototype.writeUInt32BE = Buffer.prototype.writeUInt32BE; +SlowBuffer.prototype.writeInt8 = Buffer.prototype.writeInt8; +SlowBuffer.prototype.writeInt16LE = Buffer.prototype.writeInt16LE; +SlowBuffer.prototype.writeInt16BE = Buffer.prototype.writeInt16BE; +SlowBuffer.prototype.writeInt32LE = Buffer.prototype.writeInt32LE; +SlowBuffer.prototype.writeInt32BE = Buffer.prototype.writeInt32BE; +SlowBuffer.prototype.writeFloatLE = Buffer.prototype.writeFloatLE; +SlowBuffer.prototype.writeFloatBE = Buffer.prototype.writeFloatBE; +SlowBuffer.prototype.writeDoubleLE = Buffer.prototype.writeDoubleLE; +SlowBuffer.prototype.writeDoubleBE = Buffer.prototype.writeDoubleBE; diff --git a/slow_buffer.js b/slow_buffer.js deleted file mode 100644 index beeafc0..0000000 --- a/slow_buffer.js +++ /dev/null @@ -1,321 +0,0 @@ -module.exports = SlowBuffer; - -function SlowBuffer (size) { - this.length = size; -}; - -var assert = require('assert'); - -exports.INSPECT_MAX_BYTES = 50; - - -function toHex(n) { - if (n < 16) return '0' + n.toString(16); - return n.toString(16); -} - -function utf8ToBytes(str) { - var byteArray = []; - for (var i = 0; i < str.length; i++) - if (str.charCodeAt(i) <= 0x7F) - byteArray.push(str.charCodeAt(i)); - else { - var h = encodeURIComponent(str.charAt(i)).substr(1).split('%'); - for (var j = 0; j < h.length; j++) - byteArray.push(parseInt(h[j], 16)); - } - - return byteArray; -} - -function asciiToBytes(str) { - var byteArray = [] - for (var i = 0; i < str.length; i++ ) - // Node's code seems to be doing this and not & 0x7F.. - byteArray.push( str.charCodeAt(i) & 0xFF ); - - return byteArray; -} - -function base64ToBytes(str) { - return require("base64-js").toByteArray(str); -} - -SlowBuffer.byteLength = function (str, encoding) { - switch (encoding || "utf8") { - case 'hex': - return str.length / 2; - - case 'utf8': - case 'utf-8': - return utf8ToBytes(str).length; - - case 'ascii': - case 'binary': - return str.length; - - case 'base64': - return base64ToBytes(str).length; - - default: - throw new Error('Unknown encoding'); - } -}; - -function blitBuffer(src, dst, offset, length) { - var pos, i = 0; - while (i < length) { - if ((i+offset >= dst.length) || (i >= src.length)) - break; - - dst[i + offset] = src[i]; - i++; - } - return i; -} - -SlowBuffer.prototype.utf8Write = function (string, offset, length) { - var bytes, pos; - return SlowBuffer._charsWritten = blitBuffer(utf8ToBytes(string), this, offset, length); -}; - -SlowBuffer.prototype.asciiWrite = function (string, offset, length) { - var bytes, pos; - return SlowBuffer._charsWritten = blitBuffer(asciiToBytes(string), this, offset, length); -}; - -SlowBuffer.prototype.binaryWrite = SlowBuffer.prototype.asciiWrite; - -SlowBuffer.prototype.base64Write = function (string, offset, length) { - var bytes, pos; - return SlowBuffer._charsWritten = blitBuffer(base64ToBytes(string), this, offset, length); -}; - -SlowBuffer.prototype.base64Slice = function (start, end) { - var bytes = Array.prototype.slice.apply(this, arguments) - return require("base64-js").fromByteArray(bytes); -} - -function decodeUtf8Char(str) { - try { - return decodeURIComponent(str); - } catch (err) { - return String.fromCharCode(0xFFFD); // UTF 8 invalid char - } -} - -SlowBuffer.prototype.utf8Slice = function () { - var bytes = Array.prototype.slice.apply(this, arguments); - var res = ""; - var tmp = ""; - var i = 0; - while (i < bytes.length) { - if (bytes[i] <= 0x7F) { - res += decodeUtf8Char(tmp) + String.fromCharCode(bytes[i]); - tmp = ""; - } else - tmp += "%" + bytes[i].toString(16); - - i++; - } - - return res + decodeUtf8Char(tmp); -} - -SlowBuffer.prototype.asciiSlice = function () { - var bytes = Array.prototype.slice.apply(this, arguments); - var ret = ""; - for (var i = 0; i < bytes.length; i++) - ret += String.fromCharCode(bytes[i]); - return ret; -} - -SlowBuffer.prototype.binarySlice = SlowBuffer.prototype.asciiSlice; - -SlowBuffer.prototype.inspect = function() { - var out = [], - len = this.length; - for (var i = 0; i < len; i++) { - out[i] = toHex(this[i]); - if (i == exports.INSPECT_MAX_BYTES) { - out[i + 1] = '...'; - break; - } - } - return ''; -}; - - -SlowBuffer.prototype.hexSlice = function(start, end) { - var len = this.length; - - if (!start || start < 0) start = 0; - if (!end || end < 0 || end > len) end = len; - - var out = ''; - for (var i = start; i < end; i++) { - out += toHex(this[i]); - } - return out; -}; - - -SlowBuffer.prototype.toString = function(encoding, start, end) { - encoding = String(encoding || 'utf8').toLowerCase(); - start = +start || 0; - if (typeof end == 'undefined') end = this.length; - - // Fastpath empty strings - if (+end == start) { - return ''; - } - - switch (encoding) { - case 'hex': - return this.hexSlice(start, end); - - case 'utf8': - case 'utf-8': - return this.utf8Slice(start, end); - - case 'ascii': - return this.asciiSlice(start, end); - - case 'binary': - return this.binarySlice(start, end); - - case 'base64': - return this.base64Slice(start, end); - - case 'ucs2': - case 'ucs-2': - return this.ucs2Slice(start, end); - - default: - throw new Error('Unknown encoding'); - } -}; - - -SlowBuffer.prototype.hexWrite = function(string, offset, length) { - offset = +offset || 0; - var remaining = this.length - offset; - if (!length) { - length = remaining; - } else { - length = +length; - if (length > remaining) { - length = remaining; - } - } - - // must be an even number of digits - var strLen = string.length; - if (strLen % 2) { - throw new Error('Invalid hex string'); - } - if (length > strLen / 2) { - length = strLen / 2; - } - for (var i = 0; i < length; i++) { - var byte = parseInt(string.substr(i * 2, 2), 16); - if (isNaN(byte)) throw new Error('Invalid hex string'); - this[offset + i] = byte; - } - SlowBuffer._charsWritten = i * 2; - return i; -}; - - -SlowBuffer.prototype.write = function(string, offset, length, encoding) { - // Support both (string, offset, length, encoding) - // and the legacy (string, encoding, offset, length) - if (isFinite(offset)) { - if (!isFinite(length)) { - encoding = length; - length = undefined; - } - } else { // legacy - var swap = encoding; - encoding = offset; - offset = length; - length = swap; - } - - offset = +offset || 0; - var remaining = this.length - offset; - if (!length) { - length = remaining; - } else { - length = +length; - if (length > remaining) { - length = remaining; - } - } - encoding = String(encoding || 'utf8').toLowerCase(); - - switch (encoding) { - case 'hex': - return this.hexWrite(string, offset, length); - - case 'utf8': - case 'utf-8': - return this.utf8Write(string, offset, length); - - case 'ascii': - return this.asciiWrite(string, offset, length); - - case 'binary': - return this.binaryWrite(string, offset, length); - - case 'base64': - return this.base64Write(string, offset, length); - - case 'ucs2': - case 'ucs-2': - return this.ucs2Write(string, offset, length); - - default: - throw new Error('Unknown encoding'); - } -}; - - -// slice(start, end) -SlowBuffer.prototype.slice = function(start, end) { - if (end === undefined) end = this.length; - - if (end > this.length) { - throw new Error('oob'); - } - if (start > end) { - throw new Error('oob'); - } - - return new Buffer(this, end - start, +start); -}; - -SlowBuffer.prototype.copy = function(target, targetstart, sourcestart, sourceend) { - var temp = []; - for (var i=sourcestart; i this.length) { - throw new Error('oob'); - } - if (start > end) { - throw new Error('oob'); - } - - for (var i = start; i < end; i++) { - this[i] = value; - } -} diff --git a/test/indexes.js b/test/indexes.js index 8802ab3..d5a2f20 100644 --- a/test/indexes.js +++ b/test/indexes.js @@ -1,4 +1,4 @@ -var B = require('../index.js').Buffer; +var B = require('../').Buffer; var test = require('tap').test; test('indexes from a string', function(t) { -- 2.34.1