diff options
-rw-r--r-- | lib/buffer.js | 1149 | ||||
-rw-r--r-- | lib/internal/buffer_new.js | 1020 | ||||
-rw-r--r-- | lib/internal/buffer_old.js | 1140 | ||||
-rw-r--r-- | node.gyp | 3 | ||||
-rw-r--r-- | src/env.h | 1 | ||||
-rw-r--r-- | src/node.cc | 25 | ||||
-rw-r--r-- | src/node_buffer.cc | 395 | ||||
-rw-r--r-- | src/node_buffer.h | 6 | ||||
-rw-r--r-- | src/node_internals.h | 2 | ||||
-rw-r--r-- | src/util-inl.h | 8 | ||||
-rw-r--r-- | src/util.h | 2 | ||||
-rw-r--r-- | test/parallel/test-buffer-slice.js | 12 | ||||
-rw-r--r-- | test/parallel/test-buffer.js | 10 |
13 files changed, 2521 insertions, 1252 deletions
diff --git a/lib/buffer.js b/lib/buffer.js index 568a1f4b34e..641b5422760 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -1,1149 +1,8 @@ /* eslint-disable require-buffer */ 'use strict'; -const binding = process.binding('buffer'); -const smalloc = process.binding('smalloc'); -const internalUtil = require('internal/util'); -const alloc = smalloc.alloc; -const truncate = smalloc.truncate; -const sliceOnto = smalloc.sliceOnto; -const kMaxLength = smalloc.kMaxLength; - -exports.Buffer = Buffer; -exports.SlowBuffer = SlowBuffer; -exports.INSPECT_MAX_BYTES = 50; - - -Buffer.poolSize = 8 * 1024; -var poolSize, poolOffset, allocPool; - - -function createPool() { - poolSize = Buffer.poolSize; - allocPool = alloc({}, poolSize); - poolOffset = 0; -} -createPool(); - -function Buffer(arg) { - if (!(this instanceof Buffer)) { - // Avoid going through an ArgumentsAdaptorTrampoline in the common case. - if (arguments.length > 1) - return new Buffer(arg, arguments[1]); - - return new Buffer(arg); - } - - this.length = 0; - this.parent = undefined; - - // Common case. - if (typeof(arg) === 'number') { - fromNumber(this, arg); - return; - } - - // Slightly less common case. - if (typeof(arg) === 'string') { - fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8'); - return; - } - - // Unusual. - fromObject(this, arg); -} - -function fromNumber(that, length) { - allocate(that, length < 0 ? 0 : checked(length) | 0); -} - -function fromString(that, string, encoding) { - if (typeof(encoding) !== 'string' || encoding === '') - encoding = 'utf8'; - - // Assumption: byteLength() return value is always < kMaxLength. - var length = byteLength(string, encoding) | 0; - allocate(that, length); - - var actual = that.write(string, encoding) | 0; - if (actual !== length) { - // Fix up for truncated base64 input. Don't bother returning - // the unused two or three bytes to the pool. - that.length = actual; - truncate(that, actual); - } -} - -function fromObject(that, object) { - if (object instanceof Buffer) - return fromBuffer(that, object); - - if (Array.isArray(object)) - return fromArray(that, object); - - if (object == null) - throw new TypeError('must start with number, buffer, array or string'); - - if (object.buffer instanceof ArrayBuffer) - return fromTypedArray(that, object); - - if (object.length) - return fromArrayLike(that, object); - - return fromJsonObject(that, object); -} - -function fromBuffer(that, buffer) { - var length = checked(buffer.length) | 0; - allocate(that, length); - buffer.copy(that, 0, 0, length); -} - -function fromArray(that, array) { - var length = checked(array.length) | 0; - allocate(that, length); - for (var i = 0; i < length; i += 1) - that[i] = array[i] & 255; -} - -// Duplicate of fromArray() to keep fromArray() monomorphic. -function fromTypedArray(that, array) { - var length = checked(array.length) | 0; - allocate(that, length); - // Truncating the elements is probably not what people expect from typed - // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior - // of the old Buffer constructor. - for (var i = 0; i < length; i += 1) - that[i] = array[i] & 255; -} - -function fromArrayLike(that, array) { - var length = checked(array.length) | 0; - allocate(that, length); - for (var i = 0; i < length; i += 1) - that[i] = array[i] & 255; -} - -// Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object. -// Returns a zero-length buffer for inputs that don't conform to the spec. -function fromJsonObject(that, object) { - var array; - var length = 0; - - if (object.type === 'Buffer' && Array.isArray(object.data)) { - array = object.data; - length = checked(array.length) | 0; - } - allocate(that, length); - - for (var i = 0; i < length; i += 1) - that[i] = array[i] & 255; -} - -function allocate(that, length) { - var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1; - if (fromPool) - that.parent = palloc(that, length); - else - alloc(that, length); - that.length = length; -} - -function palloc(that, length) { - if (length > poolSize - poolOffset) - createPool(); - - var start = poolOffset; - var end = start + length; - var buf = sliceOnto(allocPool, that, start, end); - poolOffset = end; - - // Ensure aligned slices - if (poolOffset & 0x7) { - poolOffset |= 0x7; - poolOffset++; - } - - return buf; -} - -function checked(length) { - // Note: cannot use `length < kMaxLength` here because that fails when - // length is NaN (which is otherwise coerced to zero.) - if (length >= kMaxLength) { - throw new RangeError('Attempt to allocate Buffer larger than maximum ' + - 'size: 0x' + kMaxLength.toString(16) + ' bytes'); - } - return length >>> 0; -} - -function SlowBuffer(length) { - length = length >>> 0; - if (length > kMaxLength) { - throw new RangeError('Attempt to allocate Buffer larger than maximum ' + - 'size: 0x' + kMaxLength.toString(16) + ' bytes'); - } - var b = new NativeBuffer(length); - alloc(b, length); - return b; -} - - -// Bypass all checks for instantiating unallocated Buffer required for -// Objects created in C++. Significantly faster than calling the Buffer -// function. -function NativeBuffer(length) { - this.length = length >>> 0; - // Set this to keep the object map the same. - this.parent = undefined; -} -NativeBuffer.prototype = Buffer.prototype; - - -// add methods to Buffer prototype -binding.setupBufferJS(NativeBuffer); - - -// Static methods - -Buffer.isBuffer = function isBuffer(b) { - return b instanceof Buffer; -}; - - -Buffer.compare = function compare(a, b) { - if (!(a instanceof Buffer) || - !(b instanceof Buffer)) - throw new TypeError('Arguments must be Buffers'); - - if (a === b) - return 0; - - return binding.compare(a, b); -}; - - -Buffer.isEncoding = function(encoding) { - switch ((encoding + '').toLowerCase()) { - case 'hex': - case 'utf8': - case 'utf-8': - case 'ascii': - case 'binary': - case 'base64': - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - case 'raw': - return true; - - default: - return false; - } -}; - - -Buffer.concat = function(list, length) { - if (!Array.isArray(list)) - throw new TypeError('list argument must be an Array of Buffers.'); - - if (list.length === 0) - return new Buffer(0); - else if (list.length === 1) - return list[0]; - - if (length === undefined) { - length = 0; - for (var i = 0; i < list.length; i++) - length += list[i].length; - } else { - length = length >>> 0; - } - - var buffer = new Buffer(length); - var pos = 0; - for (var i = 0; i < list.length; i++) { - var buf = list[i]; - buf.copy(buffer, pos); - pos += buf.length; - } - - return buffer; -}; - - -function base64ByteLength(str, bytes) { - // Handle padding - if (str.charCodeAt(bytes - 1) === 0x3D) - bytes--; - if (bytes > 1 && str.charCodeAt(bytes - 1) === 0x3D) - bytes--; - - // Base64 ratio: 3/4 - return (bytes * 3) >>> 2; -} - - -function byteLength(string, encoding) { - if (typeof string !== 'string') - string = '' + string; - - var len = string.length; - if (len === 0) - return 0; - - // Use a for loop to avoid recursion - var loweredCase = false; - for (;;) { - switch (encoding) { - case 'ascii': - case 'binary': - // Deprecated - case 'raw': - case 'raws': - return len; - - case 'utf8': - case 'utf-8': - return binding.byteLengthUtf8(string); - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return len * 2; - - case 'hex': - return len >>> 1; - - case 'base64': - return base64ByteLength(string, len); - - default: - // The C++ binding defaulted to UTF8, we should too. - if (loweredCase) - return binding.byteLengthUtf8(string); - - encoding = ('' + encoding).toLowerCase(); - loweredCase = true; - } - } -} - -Buffer.byteLength = byteLength; - -function slowToString(encoding, start, end) { - var loweredCase = false; - - start = start >>> 0; - end = end === undefined || end === Infinity ? this.length : end >>> 0; - - if (!encoding) encoding = 'utf8'; - if (start < 0) start = 0; - if (end > this.length) end = this.length; - if (end <= start) return ''; - - while (true) { - 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': - case 'utf16le': - case 'utf-16le': - return this.ucs2Slice(start, end); - - default: - if (loweredCase) - throw new TypeError('Unknown encoding: ' + encoding); - encoding = (encoding + '').toLowerCase(); - loweredCase = true; - } - } -} - - -Buffer.prototype.toString = function() { - const length = this.length | 0; - if (arguments.length === 0) - return this.utf8Slice(0, length); - return slowToString.apply(this, arguments); -}; - - -Buffer.prototype.equals = function equals(b) { - if (!(b instanceof Buffer)) - throw new TypeError('Argument must be a Buffer'); - - if (this === b) - return true; - - return binding.compare(this, b) === 0; -}; - - -// Inspect -Buffer.prototype.inspect = function inspect() { - var str = ''; - var max = exports.INSPECT_MAX_BYTES; - if (this.length > 0) { - str = this.toString('hex', 0, max).match(/.{2}/g).join(' '); - if (this.length > max) - str += ' ... '; - } - return '<' + this.constructor.name + ' ' + str + '>'; -}; - - -Buffer.prototype.compare = function compare(b) { - if (!(b instanceof Buffer)) - throw new TypeError('Argument must be a Buffer'); - - if (this === b) - return 0; - - return binding.compare(this, b); -}; - - -Buffer.prototype.indexOf = function indexOf(val, byteOffset) { - if (byteOffset > 0x7fffffff) - byteOffset = 0x7fffffff; - else if (byteOffset < -0x80000000) - byteOffset = -0x80000000; - byteOffset >>= 0; - - if (typeof val === 'string') - return binding.indexOfString(this, val, byteOffset); - if (val instanceof Buffer) - return binding.indexOfBuffer(this, val, byteOffset); - if (typeof val === 'number') - return binding.indexOfNumber(this, val, byteOffset); - - throw new TypeError('val must be string, number or Buffer'); -}; - - -Buffer.prototype.fill = function fill(val, start, end) { - start = start >> 0; - end = (end === undefined) ? this.length : end >> 0; - - if (start < 0 || end > this.length) - throw new RangeError('out of range index'); - if (end <= start) - return this; - - if (typeof val !== 'string') { - val = val >>> 0; - } else if (val.length === 1) { - var code = val.charCodeAt(0); - if (code < 256) - val = code; - } - - binding.fill(this, val, start, end); - - return this; -}; - - -// XXX remove in v0.13 -Buffer.prototype.get = internalUtil.deprecate(function get(offset) { - offset = ~~offset; - if (offset < 0 || offset >= this.length) - throw new RangeError('index out of range'); - return this[offset]; -}, 'Buffer.get is deprecated. Use array indexes instead.'); - - -// XXX remove in v0.13 -Buffer.prototype.set = internalUtil.deprecate(function set(offset, v) { - offset = ~~offset; - if (offset < 0 || offset >= this.length) - throw new RangeError('index out of range'); - return this[offset] = v; -}, 'Buffer.set is deprecated. Use array indexes instead.'); - - -// TODO(trevnorris): fix these checks to follow new standard -// write(string, offset = 0, length = buffer.length, encoding = 'utf8') -var writeWarned = false; -const writeMsg = 'Buffer.write(string, encoding, offset, length) is ' + - 'deprecated. Use write(string[, offset[, length]]' + - '[, encoding]) instead.'; -Buffer.prototype.write = function(string, offset, length, encoding) { - // Buffer#write(string); - if (offset === undefined) { - encoding = 'utf8'; - length = this.length; - offset = 0; - - // Buffer#write(string, encoding) - } else if (length === undefined && typeof offset === 'string') { - encoding = offset; - length = this.length; - offset = 0; - - // Buffer#write(string, offset[, length][, encoding]) - } else if (isFinite(offset)) { - offset = offset >>> 0; - if (isFinite(length)) { - length = length >>> 0; - if (encoding === undefined) - encoding = 'utf8'; - } else { - encoding = length; - length = undefined; - } - - // XXX legacy write(string, encoding, offset, length) - remove in v0.13 - } else { - writeWarned = internalUtil.printDeprecationMessage(writeMsg, writeWarned); - var swap = encoding; - encoding = offset; - offset = length >>> 0; - length = swap; - } - - var remaining = this.length - offset; - if (length === undefined || length > remaining) - length = remaining; - - if (string.length > 0 && (length < 0 || offset < 0)) - throw new RangeError('attempt to write outside buffer bounds'); - - if (!encoding) - encoding = 'utf8'; - - var loweredCase = false; - for (;;) { - 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': - // Warning: maxLength not taken into account in base64Write - return this.base64Write(string, offset, length); - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return this.ucs2Write(string, offset, length); - - default: - if (loweredCase) - throw new TypeError('Unknown encoding: ' + encoding); - encoding = ('' + encoding).toLowerCase(); - loweredCase = true; - } - } -}; - - -Buffer.prototype.toJSON = function() { - return { - type: 'Buffer', - data: Array.prototype.slice.call(this, 0) - }; -}; - - -// TODO(trevnorris): currently works like Array.prototype.slice(), which -// doesn't follow the new standard for throwing on out of range indexes. -Buffer.prototype.slice = function(start, end) { - var len = this.length; - start = ~~start; - end = end === undefined ? len : ~~end; - - if (start < 0) { - start += len; - if (start < 0) - start = 0; - } else if (start > len) { - start = len; - } - - if (end < 0) { - end += len; - if (end < 0) - end = 0; - } else if (end > len) { - end = len; - } - - if (end < start) - end = start; - - var buf = new NativeBuffer(); - sliceOnto(this, buf, start, end); - buf.length = end - start; - if (buf.length > 0) - buf.parent = this.parent === undefined ? this : this.parent; - - return buf; -}; - - -function checkOffset(offset, ext, length) { - if (offset + ext > length) - throw new RangeError('index out of range'); -} - - -Buffer.prototype.readUIntLE = function(offset, byteLength, noAssert) { - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) - checkOffset(offset, byteLength, this.length); - - var val = this[offset]; - var mul = 1; - var i = 0; - while (++i < byteLength && (mul *= 0x100)) - val += this[offset + i] * mul; - - return val; -}; - - -Buffer.prototype.readUIntBE = function(offset, byteLength, noAssert) { - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) - checkOffset(offset, byteLength, this.length); - - var val = this[offset + --byteLength]; - var mul = 1; - while (byteLength > 0 && (mul *= 0x100)) - val += this[offset + --byteLength] * mul; - - return val; -}; - - -Buffer.prototype.readUInt8 = function(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 1, this.length); - return this[offset]; -}; - - -Buffer.prototype.readUInt16LE = function(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 2, this.length); - return this[offset] | (this[offset + 1] << 8); -}; - - -Buffer.prototype.readUInt16BE = function(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 2, this.length); - return (this[offset] << 8) | this[offset + 1]; -}; - - -Buffer.prototype.readUInt32LE = function(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 4, this.length); - - return ((this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16)) + - (this[offset + 3] * 0x1000000); -}; - - -Buffer.prototype.readUInt32BE = function(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 4, this.length); - - return (this[offset] * 0x1000000) + - ((this[offset + 1] << 16) | - (this[offset + 2] << 8) | - this[offset + 3]); -}; - - -Buffer.prototype.readIntLE = function(offset, byteLength, noAssert) { - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) - checkOffset(offset, byteLength, this.length); - - var val = this[offset]; - var mul = 1; - var i = 0; - while (++i < byteLength && (mul *= 0x100)) - val += this[offset + i] * mul; - mul *= 0x80; - - if (val >= mul) - val -= Math.pow(2, 8 * byteLength); - - return val; -}; - - -Buffer.prototype.readIntBE = function(offset, byteLength, noAssert) { - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) - checkOffset(offset, byteLength, this.length); - - var i = byteLength; - var mul = 1; - var val = this[offset + --i]; - while (i > 0 && (mul *= 0x100)) - val += this[offset + --i] * mul; - mul *= 0x80; - - if (val >= mul) - val -= Math.pow(2, 8 * byteLength); - - return val; -}; - - -Buffer.prototype.readInt8 = function(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 1, this.length); - var val = this[offset]; - return !(val & 0x80) ? val : (0xff - val + 1) * -1; -}; - - -Buffer.prototype.readInt16LE = function(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 2, this.length); - var val = this[offset] | (this[offset + 1] << 8); - return (val & 0x8000) ? val | 0xFFFF0000 : val; -}; - - -Buffer.prototype.readInt16BE = function(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 2, this.length); - var val = this[offset + 1] | (this[offset] << 8); - return (val & 0x8000) ? val | 0xFFFF0000 : val; -}; - - -Buffer.prototype.readInt32LE = function(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 4, this.length); - - return (this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16) | - (this[offset + 3] << 24); -}; - - -Buffer.prototype.readInt32BE = function(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 4, this.length); - - return (this[offset] << 24) | - (this[offset + 1] << 16) | - (this[offset + 2] << 8) | - (this[offset + 3]); -}; - - -Buffer.prototype.readFloatLE = function readFloatLE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 4, this.length); - return binding.readFloatLE(this, offset); -}; - - -Buffer.prototype.readFloatBE = function readFloatBE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 4, this.length); - return binding.readFloatBE(this, offset); -}; - - -Buffer.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 8, this.length); - return binding.readDoubleLE(this, offset); -}; - - -Buffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 8, this.length); - return binding.readDoubleBE(this, offset); -}; - - -function checkInt(buffer, value, offset, ext, max, min) { - if (!(buffer instanceof Buffer)) - throw new TypeError('buffer must be a Buffer instance'); - if (value > max || value < min) - throw new TypeError('value is out of bounds'); - if (offset + ext > buffer.length) - throw new RangeError('index out of range'); -} - - -Buffer.prototype.writeUIntLE = function(value, offset, byteLength, noAssert) { - value = +value; - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) - checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0); - - var mul = 1; - var i = 0; - this[offset] = value; - while (++i < byteLength && (mul *= 0x100)) - this[offset + i] = (value / mul) >>> 0; - - return offset + byteLength; -}; - - -Buffer.prototype.writeUIntBE = function(value, offset, byteLength, noAssert) { - value = +value; - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) - checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0); - - var i = byteLength - 1; - var mul = 1; - this[offset + i] = value; - while (--i >= 0 && (mul *= 0x100)) - this[offset + i] = (value / mul) >>> 0; - - return offset + byteLength; -}; - - -Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) - checkInt(this, value, offset, 1, 0xff, 0); - this[offset] = value; - return offset + 1; -}; - - -Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) - checkInt(this, value, offset, 2, 0xffff, 0); - this[offset] = value; - this[offset + 1] = (value >>> 8); - return offset + 2; -}; - - -Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) - checkInt(this, value, offset, 2, 0xffff, 0); - this[offset] = (value >>> 8); - this[offset + 1] = value; - return offset + 2; -}; - - -Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) - checkInt(this, value, offset, 4, 0xffffffff, 0); - this[offset + 3] = (value >>> 24); - this[offset + 2] = (value >>> 16); - this[offset + 1] = (value >>> 8); - this[offset] = value; - return offset + 4; -}; - - -Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) - checkInt(this, value, offset, 4, 0xffffffff, 0); - this[offset] = (value >>> 24); - this[offset + 1] = (value >>> 16); - this[offset + 2] = (value >>> 8); - this[offset + 3] = value; - return offset + 4; -}; - - -Buffer.prototype.writeIntLE = function(value, offset, byteLength, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) { - checkInt(this, - value, - offset, - byteLength, - Math.pow(2, 8 * byteLength - 1) - 1, - -Math.pow(2, 8 * byteLength - 1)); - } - - var i = 0; - var mul = 1; - var sub = value < 0 ? 1 : 0; - this[offset] = value; - while (++i < byteLength && (mul *= 0x100)) - this[offset + i] = ((value / mul) >> 0) - sub; - - return offset + byteLength; -}; - - -Buffer.prototype.writeIntBE = function(value, offset, byteLength, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) { - checkInt(this, - value, - offset, - byteLength, - Math.pow(2, 8 * byteLength - 1) - 1, - -Math.pow(2, 8 * byteLength - 1)); - } - - var i = byteLength - 1; - var mul = 1; - var sub = value < 0 ? 1 : 0; - this[offset + i] = value; - while (--i >= 0 && (mul *= 0x100)) - this[offset + i] = ((value / mul) >> 0) - sub; - - return offset + byteLength; -}; - - -Buffer.prototype.writeInt8 = function(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) - checkInt(this, value, offset, 1, 0x7f, -0x80); - this[offset] = value; - return offset + 1; -}; - - -Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) - checkInt(this, value, offset, 2, 0x7fff, -0x8000); - this[offset] = value; - this[offset + 1] = (value >>> 8); - return offset + 2; -}; - - -Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) - checkInt(this, value, offset, 2, 0x7fff, -0x8000); - this[offset] = (value >>> 8); - this[offset + 1] = value; - return offset + 2; -}; - - -Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) - checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); - this[offset] = value; - this[offset + 1] = (value >>> 8); - this[offset + 2] = (value >>> 16); - this[offset + 3] = (value >>> 24); - return offset + 4; -}; - - -Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) - checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); - this[offset] = (value >>> 24); - this[offset + 1] = (value >>> 16); - this[offset + 2] = (value >>> 8); - this[offset + 3] = value; - return offset + 4; -}; - - -function checkFloat(buffer, value, offset, ext) { - if (!(buffer instanceof Buffer)) - throw new TypeError('buffer must be a Buffer instance'); - if (offset + ext > buffer.length) - throw new RangeError('index out of range'); +if (process.useOldBuffer) { + module.exports = require('internal/buffer_old'); +} else { + module.exports = require('internal/buffer_new'); } - - -Buffer.prototype.writeFloatLE = function writeFloatLE(val, offset, noAssert) { - val = +val; - offset = offset >>> 0; - if (!noAssert) - checkFloat(this, val, offset, 4); - binding.writeFloatLE(this, val, offset); - return offset + 4; -}; - - -Buffer.prototype.writeFloatBE = function writeFloatBE(val, offset, noAssert) { - val = +val; - offset = offset >>> 0; - if (!noAssert) - checkFloat(this, val, offset, 4); - binding.writeFloatBE(this, val, offset); - return offset + 4; -}; - - -Buffer.prototype.writeDoubleLE = function writeDoubleLE(val, offset, noAssert) { - val = +val; - offset = offset >>> 0; - if (!noAssert) - checkFloat(this, val, offset, 8); - binding.writeDoubleLE(this, val, offset); - return offset + 8; -}; - - -Buffer.prototype.writeDoubleBE = function writeDoubleBE(val, offset, noAssert) { - val = +val; - offset = offset >>> 0; - if (!noAssert) - checkFloat(this, val, offset, 8); - binding.writeDoubleBE(this, val, offset); - return offset + 8; -}; - -// ES6 iterator - -var ITERATOR_KIND_KEYS = 1; -var ITERATOR_KIND_ENTRIES = 3; - -function BufferIteratorResult(value, done) { - this.value = value; - this.done = done; -} - -var resultCache = new Array(256); - -for (var i = 0; i < 256; i++) - resultCache[i] = Object.freeze(new BufferIteratorResult(i, false)); - -var finalResult = Object.freeze(new BufferIteratorResult(undefined, true)); - -function BufferIterator(buffer, kind) { - this._buffer = buffer; - this._kind = kind; - this._index = 0; -} - -BufferIterator.prototype.next = function() { - var buffer = this._buffer; - var kind = this._kind; - var index = this._index; - - if (index >= buffer.length) - return finalResult; - - this._index++; - - if (kind === ITERATOR_KIND_ENTRIES) - return new BufferIteratorResult([index, buffer[index]], false); - - return new BufferIteratorResult(index, false); -}; - -function BufferValueIterator(buffer) { - BufferIterator.call(this, buffer, null); -} - -BufferValueIterator.prototype.next = function() { - var buffer = this._buffer; - var index = this._index; - - if (index >= buffer.length) - return finalResult; - - this._index++; - - return resultCache[buffer[index]]; -}; - - -BufferIterator.prototype[Symbol.iterator] = function() { - return this; -}; - -BufferValueIterator.prototype[Symbol.iterator] = - BufferIterator.prototype[Symbol.iterator]; - -Buffer.prototype.keys = function() { - return new BufferIterator(this, ITERATOR_KIND_KEYS); -}; - -Buffer.prototype.entries = function() { - return new BufferIterator(this, ITERATOR_KIND_ENTRIES); -}; - -Buffer.prototype.values = function() { - return new BufferValueIterator(this); -}; - -Buffer.prototype[Symbol.iterator] = Buffer.prototype.values; diff --git a/lib/internal/buffer_new.js b/lib/internal/buffer_new.js new file mode 100644 index 00000000000..c082ed8bd57 --- /dev/null +++ b/lib/internal/buffer_new.js @@ -0,0 +1,1020 @@ +'use strict'; + +const binding = process.binding('buffer'); +const internalUtil = require('internal/util'); + +exports.Buffer = Buffer; +exports.SlowBuffer = SlowBuffer; +exports.INSPECT_MAX_BYTES = 50; + + +Buffer.poolSize = 8 * 1024; +var poolSize, poolOffset, allocPool; + + +function createPool() { + poolSize = Buffer.poolSize; + allocPool = binding.create(poolSize); + poolOffset = 0; +} + + +function Buffer(arg) { + // Common case. + if (typeof arg === 'number') { + // If less than zero, or NaN. + if (arg < 0 || arg !== arg) + arg = 0; + return allocate(arg); + } + + // Slightly less common case. + if (typeof arg === 'string') { + return fromString(arg, arguments[1]); + } + + // Unusual. + return fromObject(arg); +}; + +Buffer.prototype.__proto__ = Uint8Array.prototype; +Buffer.__proto__ = Uint8Array; + + +binding.setupBufferJS(Buffer.prototype); +// Buffer prototype must be past before creating our first pool. +createPool(); + + +function SlowBuffer(length) { + if (length < 0) + length = 0; + return binding.create(length); +}; + +SlowBuffer.prototype.__proto__ = Buffer.prototype; +SlowBuffer.__proto__ = Buffer; + + +function allocate(size) { + if (size === 0) + return binding.create(0); + if (size < (Buffer.poolSize >>> 1)) { + if (size > (poolSize - poolOffset)) + createPool(); + var b = binding.slice(allocPool, poolOffset, poolOffset + size); + poolOffset += size; + return b; + } else { + return binding.create(size); + } +} + + +function fromString(string, encoding) { + if (typeof encoding !== 'string' || encoding === '') + encoding = 'utf8'; + + var length = byteLength(string, encoding); + if (length >= (Buffer.poolSize >>> 1)) + return binding.createFromString(string, encoding); + + if (length > (poolSize - poolOffset)) + createPool(); + var actual = allocPool.write(string, poolOffset, encoding); + var b = binding.slice(allocPool, poolOffset, poolOffset + actual); + poolOffset += actual; + return b; +} + + +function fromObject(obj) { + if (obj instanceof Buffer) { + var b = allocate(obj.length); + obj.copy(b, 0, 0, obj.length); + return b; + } + + if (Array.isArray(obj)) { + var length = obj.length; + var b = allocate(length); + for (var i = 0; i < length; i++) + b[i] = obj[i] & 255; + return b; + } + + // TODO(trevnorris): This will fail for an actual ArrayBuffer. + if (obj.buffer instanceof ArrayBuffer || obj.length) { + var length; + if (typeof obj.length !== 'number' || obj.length !== obj.length) + length = 0; + else + length = obj.length; + var b = allocate(length); + for (var i = 0; i < length; i++) { + b[i] = obj[i] & 255; + } + return b; + } + + if (obj.type === 'Buffer' && Array.isArray(obj.data)) { + var array = obj.data; + var b = allocate(array.length); + for (var i = 0; i < array.length; i++) + b[i] = array[i] & 255; + return b; + } + + throw new TypeError('must start with number, buffer, array or string'); +} + + +// Static methods + +Buffer.isBuffer = function isBuffer(b) { + return b instanceof Buffer; +}; + + +Buffer.compare = function compare(a, b) { + if (!(a instanceof Buffer) || + !(b instanceof Buffer)) { + throw new TypeError('Arguments must be Buffers'); + } + + if (a === b) { + return 0; + } + + return binding.compare(a, b); +}; + + +Buffer.isEncoding = function(encoding) { + var loweredCase = false; + for (;;) { + switch (encoding) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + case 'raw': + return true; + + default: + if (loweredCase) + return false; + encoding = ('' + encoding).toLowerCase(); + loweredCase = true; + } + } +}; + + +Buffer.concat = function(list, length) { + if (!Array.isArray(list)) + throw new TypeError('list argument must be an Array of Buffers.'); + + if (list.length === 0) + return new Buffer(0); + else if (list.length === 1) + return list[0]; + + if (length === undefined) { + length = 0; + for (var i = 0; i < list.length; i++) + length += list[i].length; + } else { + length = length >>> 0; + } + + var buffer = new Buffer(length); + var pos = 0; + for (var i = 0; i < list.length; i++) { + var buf = list[i]; + buf.copy(buffer, pos); + pos += buf.length; + } + + return buffer; +}; + + +function base64ByteLength(str, bytes) { + // Handle padding + if (str.charCodeAt(bytes - 1) === 0x3D) + bytes--; + if (bytes > 1 && str.charCodeAt(bytes - 1) === 0x3D) + bytes--; + + // Base64 ratio: 3/4 + return (bytes * 3) >>> 2; +} + + +function byteLength(string, encoding) { + if (typeof string !== 'string') + string = '' + string; + + var len = string.length; + if (len === 0) + return 0; + + // Use a for loop to avoid recursion + var loweredCase = false; + for (;;) { + switch (encoding) { + case 'ascii': + case 'binary': + // Deprecated + case 'raw': + case 'raws': + return len; + + case 'utf8': + case 'utf-8': + return binding.byteLengthUtf8(string); + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2; + + case 'hex': + return len >>> 1; + + case 'base64': + return base64ByteLength(string, len); + + default: + // The C++ binding defaulted to UTF8, we should too. + if (loweredCase) + return binding.byteLengthUtf8(string); + + encoding = ('' + encoding).toLowerCase(); + loweredCase = true; + } + } +} + +Buffer.byteLength = byteLength; + + +// For backwards compatibility. +Object.defineProperty(Buffer.prototype, 'parent', { + enumerable: true, + get: function() { + if (this.byteLength === 0 || + this.byteLength === this.buffer.byteLength) { + return undefined; + } + return this.buffer; + } +}); +Object.defineProperty(Buffer.prototype, 'offset', { + enumerable: true, + get: function() { + return this.byteOffset; + } +}); + + +// toString(encoding, start=0, end=buffer.length) +Buffer.prototype.toString = function(encoding, start, end) { + var loweredCase = false; + + start = start >>> 0; + end = end === undefined || end === Infinity ? this.length : end >>> 0; + + if (!encoding) encoding = 'utf8'; + if (start < 0) start = 0; + if (end > this.length) end = this.length; + if (end <= start) return ''; + + while (true) { + 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': + case 'utf16le': + case 'utf-16le': + return this.ucs2Slice(start, end); + + default: + if (loweredCase) + throw new TypeError('Unknown encoding: ' + encoding); + encoding = (encoding + '').toLowerCase(); + loweredCase = true; + } + } +}; + + +Buffer.prototype.equals = function equals(b) { + if (!(b instanceof Buffer)) + throw new TypeError('Argument must be a Buffer'); + + if (this === b) + return true; + + return binding.compare(this, b) === 0; +}; + + +// Inspect +Buffer.prototype.inspect = function inspect() { + var str = ''; + var max = exports.INSPECT_MAX_BYTES; + if (this.length > 0) { + str = this.toString('hex', 0, max).match(/.{2}/g).join(' '); + if (this.length > max) + str += ' ... '; + } + return '<' + this.constructor.name + ' ' + str + '>'; +}; + + +Buffer.prototype.compare = function compare(b) { + if (!(b instanceof Buffer)) + throw new TypeError('Argument must be a Buffer'); + + if (this === b) + return 0; + + return binding.compare(this, b); +}; + + +Buffer.prototype.indexOf = function indexOf(val, byteOffset) { + if (byteOffset > 0x7fffffff) + byteOffset = 0x7fffffff; + else if (byteOffset < -0x80000000) + byteOffset = -0x80000000; + byteOffset >>= 0; + + if (typeof val === 'string') + return binding.indexOfString(this, val, byteOffset); + if (val instanceof Buffer) + return binding.indexOfBuffer(this, val, byteOffset); + if (typeof val === 'number') + return binding.indexOfNumber(this, val, byteOffset); + + throw new TypeError('val must be string, number or Buffer'); +}; + + +Buffer.prototype.fill = function fill(val, start, end) { + start = start >> 0; + end = (end === undefined) ? this.length : end >> 0; + + if (start < 0 || end > this.length) + throw new RangeError('out of range index'); + if (end <= start) + return this; + + if (typeof val !== 'string') { + val = val >>> 0; + } else if (val.length === 1) { + var code = val.charCodeAt(0); + if (code < 256) + val = code; + } + + binding.fill(this, val, start, end); + + return this; +}; + + +// XXX remove in v0.13 +Buffer.prototype.get = internalUtil.deprecate(function get(offset) { + offset = ~~offset; + if (offset < 0 || offset >= this.length) + throw new RangeError('index out of range'); + return this[offset]; +}, '.get() is deprecated. Access using array indexes instead.'); + + +// XXX remove in v0.13 +Buffer.prototype.set = internalUtil.deprecate(function set(offset, v) { + offset = ~~offset; + if (offset < 0 || offset >= this.length) + throw new RangeError('index out of range'); + return this[offset] = v; +}, '.set() is deprecated. Set using array indexes instead.'); + + +// TODO(trevnorris): fix these checks to follow new standard +// write(string, offset = 0, length = buffer.length, encoding = 'utf8') +var writeWarned = false; +const writeMsg = '.write(string, encoding, offset, length) is deprecated.' + + ' Use write(string[, offset[, length]][, encoding]) instead.'; +Buffer.prototype.write = function(string, offset, length, encoding) { + // Buffer#write(string); + if (offset === undefined) { + encoding = 'utf8'; + length = this.length; + offset = 0; + + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset; + length = this.length; + offset = 0; + + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset >>> 0; + if (isFinite(length)) { + length = length >>> 0; + if (encoding === undefined) + encoding = 'utf8'; + } else { + encoding = length; + length = undefined; + } + + // XXX legacy write(string, encoding, offset, length) - remove in v0.13 + } else { + if (!writeWarned) { + if (process.throwDeprecation) + throw new Error(writeMsg); + else if (process.traceDeprecation) + console.trace(writeMsg); + else + console.error(writeMsg); + writeWarned = true; + } + + var swap = encoding; + encoding = offset; + offset = length >>> 0; + length = swap; + } + + var remaining = this.length - offset; + if (length === undefined || length > remaining) + length = remaining; + + if (string.length > 0 && (length < 0 || offset < 0)) + throw new RangeError('attempt to write outside buffer bounds'); + + if (!encoding) + encoding = 'utf8'; + + var loweredCase = false; + for (;;) { + 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': + // Warning: maxLength not taken into account in base64Write + return this.base64Write(string, offset, length); + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return this.ucs2Write(string, offset, length); + + default: + if (loweredCase) + throw new TypeError('Unknown encoding: ' + encoding); + encoding = ('' + encoding).toLowerCase(); + loweredCase = true; + } + } +}; + + +Buffer.prototype.toJSON = function() { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this, 0) + }; +}; + + +// TODO(trevnorris): currently works like Array.prototype.slice(), which +// doesn't follow the new standard for throwing on out of range indexes. +Buffer.prototype.slice = function slice(start, end) { + var len = this.length; + start = ~~start; + end = end === undefined ? len : ~~end; + + if (start < 0) { + start += len; + if (start < 0) + start = 0; + } else if (start > len) { + start = len; + } + + if (end < 0) { + end += len; + if (end < 0) + end = 0; + } else if (end > len) { + end = len; + } + + if (end < start) + end = start; + + return binding.slice(this, start, end); +}; + + +function checkOffset(offset, ext, length) { + if (offset + ext > length) + throw new RangeError('index out of range'); +} + + +Buffer.prototype.readUIntLE = function(offset, byteLength, noAssert) { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkOffset(offset, byteLength, this.length); + + var val = this[offset]; + var mul = 1; + var i = 0; + while (++i < byteLength && (mul *= 0x100)) + val += this[offset + i] * mul; + + return val; +}; + + +Buffer.prototype.readUIntBE = function(offset, byteLength, noAssert) { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkOffset(offset, byteLength, this.length); + + var val = this[offset + --byteLength]; + var mul = 1; + while (byteLength > 0 && (mul *= 0x100)) + val += this[offset + --byteLength] * mul; + + return val; +}; + + +Buffer.prototype.readUInt8 = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 1, this.length); + return this[offset]; +}; + + +Buffer.prototype.readUInt16LE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 2, this.length); + return this[offset] | (this[offset + 1] << 8); +}; + + +Buffer.prototype.readUInt16BE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 2, this.length); + return (this[offset] << 8) | this[offset + 1]; +}; + + +Buffer.prototype.readUInt32LE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + + return ((this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16)) + + (this[offset + 3] * 0x1000000); +}; + + +Buffer.prototype.readUInt32BE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + + return (this[offset] * 0x1000000) + + ((this[offset + 1] << 16) | + (this[offset + 2] << 8) | + this[offset + 3]); +}; + + +Buffer.prototype.readIntLE = function(offset, byteLength, noAssert) { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkOffset(offset, byteLength, this.length); + + var val = this[offset]; + var mul = 1; + var i = 0; + while (++i < byteLength && (mul *= 0x100)) + val += this[offset + i] * mul; + mul *= 0x80; + + if (val >= mul) + val -= Math.pow(2, 8 * byteLength); + + return val; +}; + + +Buffer.prototype.readIntBE = function(offset, byteLength, noAssert) { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkOffset(offset, byteLength, this.length); + + var i = byteLength; + var mul = 1; + var val = this[offset + --i]; + while (i > 0 && (mul *= 0x100)) + val += this[offset + --i] * mul; + mul *= 0x80; + + if (val >= mul) + val -= Math.pow(2, 8 * byteLength); + + return val; +}; + + +Buffer.prototype.readInt8 = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 1, this.length); + var val = this[offset]; + return !(val & 0x80) ? val : (0xff - val + 1) * -1; +}; + + +Buffer.prototype.readInt16LE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 2, this.length); + var val = this[offset] | (this[offset + 1] << 8); + return (val & 0x8000) ? val | 0xFFFF0000 : val; +}; + + +Buffer.prototype.readInt16BE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 2, this.length); + var val = this[offset + 1] | (this[offset] << 8); + return (val & 0x8000) ? val | 0xFFFF0000 : val; +}; + + +Buffer.prototype.readInt32LE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + + return (this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16) | + (this[offset + 3] << 24); +}; + + +Buffer.prototype.readInt32BE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + + return (this[offset] << 24) | + (this[offset + 1] << 16) | + (this[offset + 2] << 8) | + (this[offset + 3]); +}; + + +Buffer.prototype.readFloatLE = function readFloatLE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + return binding.readFloatLE(this, offset); +}; + + +Buffer.prototype.readFloatBE = function readFloatBE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + return binding.readFloatBE(this, offset); +}; + + +Buffer.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 8, this.length); + return binding.readDoubleLE(this, offset); +}; + + +Buffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 8, this.length); + return binding.readDoubleBE(this, offset); +}; + + +function checkInt(buffer, value, offset, ext, max, min) { + if (!(buffer instanceof Buffer)) + throw new TypeError('buffer must be a Buffer instance'); + if (value > max || value < min) + throw new TypeError('value is out of bounds'); + if (offset + ext > buffer.length) + throw new RangeError('index out of range'); +} + + +Buffer.prototype.writeUIntLE = function(value, offset, byteLength, noAssert) { + value = +value; + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0); + + var mul = 1; + var i = 0; + this[offset] = value; + while (++i < byteLength && (mul *= 0x100)) + this[offset + i] = (value / mul) >>> 0; + + return offset + byteLength; +}; + + +Buffer.prototype.writeUIntBE = function(value, offset, byteLength, noAssert) { + value = +value; + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0); + + var i = byteLength - 1; + var mul = 1; + this[offset + i] = value; + while (--i >= 0 && (mul *= 0x100)) + this[offset + i] = (value / mul) >>> 0; + + return offset + byteLength; +}; + + +Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 1, 0xff, 0); + this[offset] = value; + return offset + 1; +}; + + +Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 2, 0xffff, 0); + this[offset] = value; + this[offset + 1] = (value >>> 8); + return offset + 2; +}; + + +Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 2, 0xffff, 0); + this[offset] = (value >>> 8); + this[offset + 1] = value; + return offset + 2; +}; + + +Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 4, 0xffffffff, 0); + this[offset + 3] = (value >>> 24); + this[offset + 2] = (value >>> 16); + this[offset + 1] = (value >>> 8); + this[offset] = value; + return offset + 4; +}; + + +Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 4, 0xffffffff, 0); + this[offset] = (value >>> 24); + this[offset + 1] = (value >>> 16); + this[offset + 2] = (value >>> 8); + this[offset + 3] = value; + return offset + 4; +}; + + +Buffer.prototype.writeIntLE = function(value, offset, byteLength, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) { + checkInt(this, + value, + offset, + byteLength, + Math.pow(2, 8 * byteLength - 1) - 1, + -Math.pow(2, 8 * byteLength - 1)); + } + + var i = 0; + var mul = 1; + var sub = value < 0 ? 1 : 0; + this[offset] = value; + while (++i < byteLength && (mul *= 0x100)) + this[offset + i] = ((value / mul) >> 0) - sub; + + return offset + byteLength; +}; + + +Buffer.prototype.writeIntBE = function(value, offset, byteLength, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) { + checkInt(this, + value, + offset, + byteLength, + Math.pow(2, 8 * byteLength - 1) - 1, + -Math.pow(2, 8 * byteLength - 1)); + } + + var i = byteLength - 1; + var mul = 1; + var sub = value < 0 ? 1 : 0; + this[offset + i] = value; + while (--i >= 0 && (mul *= 0x100)) + this[offset + i] = ((value / mul) >> 0) - sub; + + return offset + byteLength; +}; + + +Buffer.prototype.writeInt8 = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 1, 0x7f, -0x80); + this[offset] = value; + return offset + 1; +}; + + +Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 2, 0x7fff, -0x8000); + this[offset] = value; + this[offset + 1] = (value >>> 8); + return offset + 2; +}; + + +Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 2, 0x7fff, -0x8000); + this[offset] = (value >>> 8); + this[offset + 1] = value; + return offset + 2; +}; + + +Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); + this[offset] = value; + this[offset + 1] = (value >>> 8); + this[offset + 2] = (value >>> 16); + this[offset + 3] = (value >>> 24); + return offset + 4; +}; + + +Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); + this[offset] = (value >>> 24); + this[offset + 1] = (value >>> 16); + this[offset + 2] = (value >>> 8); + this[offset + 3] = value; + return offset + 4; +}; + + +function checkFloat(buffer, value, offset, ext) { + if (!(buffer instanceof Buffer)) + throw new TypeError('buffer must be a Buffer instance'); + if (offset + ext > buffer.length) + throw new RangeError('index out of range'); +} + + +Buffer.prototype.writeFloatLE = function writeFloatLE(val, offset, noAssert) { + val = +val; + offset = offset >>> 0; + if (!noAssert) + checkFloat(this, val, offset, 4); + binding.writeFloatLE(this, val, offset); + return offset + 4; +}; + + +Buffer.prototype.writeFloatBE = function writeFloatBE(val, offset, noAssert) { + val = +val; + offset = offset >>> 0; + if (!noAssert) + checkFloat(this, val, offset, 4); + binding.writeFloatBE(this, val, offset); + return offset + 4; +}; + + +Buffer.prototype.writeDoubleLE = function writeDoubleLE(val, offset, noAssert) { + val = +val; + offset = offset >>> 0; + if (!noAssert) + checkFloat(this, val, offset, 8); + binding.writeDoubleLE(this, val, offset); + return offset + 8; +}; + + +Buffer.prototype.writeDoubleBE = function writeDoubleBE(val, offset, noAssert) { + val = +val; + offset = offset >>> 0; + if (!noAssert) + checkFloat(this, val, offset, 8); + binding.writeDoubleBE(this, val, offset); + return offset + 8; +}; diff --git a/lib/internal/buffer_old.js b/lib/internal/buffer_old.js new file mode 100644 index 00000000000..d892fd67a23 --- /dev/null +++ b/lib/internal/buffer_old.js @@ -0,0 +1,1140 @@ +'use strict'; + +const binding = process.binding('buffer'); +const smalloc = process.binding('smalloc'); +const internalUtil = require('internal/util'); +const alloc = smalloc.alloc; +const truncate = smalloc.truncate; +const sliceOnto = smalloc.sliceOnto; +const kMaxLength = smalloc.kMaxLength; + +exports.Buffer = Buffer; +exports.SlowBuffer = SlowBuffer; +exports.INSPECT_MAX_BYTES = 50; + + +Buffer.poolSize = 8 * 1024; +var poolSize, poolOffset, allocPool; + + +function createPool() { + poolSize = Buffer.poolSize; + allocPool = alloc({}, poolSize); + poolOffset = 0; +} +createPool(); + +function Buffer(arg) { + if (!(this instanceof Buffer)) { + // Avoid going through an ArgumentsAdaptorTrampoline in the common case. + if (arguments.length > 1) + return new Buffer(arg, arguments[1]); + + return new Buffer(arg); + } + + this.length = 0; + this.parent = undefined; + + // Common case. + if (typeof(arg) === 'number') { + fromNumber(this, arg); + return; + } + + // Slightly less common case. + if (typeof(arg) === 'string') { + fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8'); + return; + } + + // Unusual. + fromObject(this, arg); +} + +function fromNumber(that, length) { + allocate(that, length < 0 ? 0 : checked(length) | 0); +} + +function fromString(that, string, encoding) { + if (typeof(encoding) !== 'string' || encoding === '') + encoding = 'utf8'; + + // Assumption: byteLength() return value is always < kMaxLength. + var length = byteLength(string, encoding) | 0; + allocate(that, length); + + var actual = that.write(string, encoding) | 0; + if (actual !== length) { + // Fix up for truncated base64 input. Don't bother returning + // the unused two or three bytes to the pool. + that.length = actual; + truncate(that, actual); + } +} + +function fromObject(that, object) { + if (object instanceof Buffer) + return fromBuffer(that, object); + + if (Array.isArray(object)) + return fromArray(that, object); + + if (object == null) + throw new TypeError('must start with number, buffer, array or string'); + + if (object.buffer instanceof ArrayBuffer) + return fromTypedArray(that, object); + + if (object.length) + return fromArrayLike(that, object); + + return fromJsonObject(that, object); +} + +function fromBuffer(that, buffer) { + var length = checked(buffer.length) | 0; + allocate(that, length); + buffer.copy(that, 0, 0, length); +} + +function fromArray(that, array) { + var length = checked(array.length) | 0; + allocate(that, length); + for (var i = 0; i < length; i += 1) + that[i] = array[i] & 255; +} + +// Duplicate of fromArray() to keep fromArray() monomorphic. +function fromTypedArray(that, array) { + var length = checked(array.length) | 0; + allocate(that, length); + // Truncating the elements is probably not what people expect from typed + // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior + // of the old Buffer constructor. + for (var i = 0; i < length; i += 1) + that[i] = array[i] & 255; +} + +function fromArrayLike(that, array) { + var length = checked(array.length) | 0; + allocate(that, length); + for (var i = 0; i < length; i += 1) + that[i] = array[i] & 255; +} + +// Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object. +// Returns a zero-length buffer for inputs that don't conform to the spec. +function fromJsonObject(that, object) { + var array; + var length = 0; + + if (object.type === 'Buffer' && Array.isArray(object.data)) { + array = object.data; + length = checked(array.length) | 0; + } + allocate(that, length); + + for (var i = 0; i < length; i += 1) + that[i] = array[i] & 255; +} + +function allocate(that, length) { + var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1; + if (fromPool) + that.parent = palloc(that, length); + else + alloc(that, length); + that.length = length; +} + +function palloc(that, length) { + if (length > poolSize - poolOffset) + createPool(); + + var start = poolOffset; + var end = start + length; + var buf = sliceOnto(allocPool, that, start, end); + poolOffset = end; + + // Ensure aligned slices + if (poolOffset & 0x7) { + poolOffset |= 0x7; + poolOffset++; + } + + return buf; +} + +function checked(length) { + // Note: cannot use `length < kMaxLength` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= kMaxLength) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + kMaxLength.toString(16) + ' bytes'); + } + return length >>> 0; +} + +function SlowBuffer(length) { + length = length >>> 0; + if (length > kMaxLength) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + kMaxLength.toString(16) + ' bytes'); + } + var b = new NativeBuffer(length); + alloc(b, length); + return b; +} + + +// Bypass all checks for instantiating unallocated Buffer required for +// Objects created in C++. Significantly faster than calling the Buffer +// function. +function NativeBuffer(length) { + this.length = length >>> 0; + // Set this to keep the object map the same. + this.parent = undefined; +} +NativeBuffer.prototype = Buffer.prototype; + + +// add methods to Buffer prototype +binding.setupBufferJS(NativeBuffer); + + +// Static methods + +Buffer.isBuffer = function isBuffer(b) { + return b instanceof Buffer; +}; + + +Buffer.compare = function compare(a, b) { + if (!(a instanceof Buffer) || + !(b instanceof Buffer)) + throw new TypeError('Arguments must be Buffers'); + + if (a === b) + return 0; + + return binding.compare(a, b); +}; + + +Buffer.isEncoding = function(encoding) { + switch ((encoding + '').toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + case 'raw': + return true; + + default: + return false; + } +}; + + +Buffer.concat = function(list, length) { + if (!Array.isArray(list)) + throw new TypeError('list argument must be an Array of Buffers.'); + + if (list.length === 0) + return new Buffer(0); + else if (list.length === 1) + return list[0]; + + if (length === undefined) { + length = 0; + for (var i = 0; i < list.length; i++) + length += list[i].length; + } else { + length = length >>> 0; + } + + var buffer = new Buffer(length); + var pos = 0; + for (var i = 0; i < list.length; i++) { + var buf = list[i]; + buf.copy(buffer, pos); + pos += buf.length; + } + + return buffer; +}; + + +function base64ByteLength(str, bytes) { + // Handle padding + if (str.charCodeAt(bytes - 1) === 0x3D) + bytes--; + if (bytes > 1 && str.charCodeAt(bytes - 1) === 0x3D) + bytes--; + + // Base64 ratio: 3/4 + return (bytes * 3) >>> 2; +} + + +function byteLength(string, encoding) { + if (typeof string !== 'string') + string = '' + string; + + var len = string.length; + if (len === 0) + return 0; + + // Use a for loop to avoid recursion + var loweredCase = false; + for (;;) { + switch (encoding) { + case 'ascii': + case 'binary': + // Deprecated + case 'raw': + case 'raws': + return len; + + case 'utf8': + case 'utf-8': + return binding.byteLengthUtf8(string); + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2; + + case 'hex': + return len >>> 1; + + case 'base64': + return base64ByteLength(string, len); + + default: + // The C++ binding defaulted to UTF8, we should too. + if (loweredCase) + return binding.byteLengthUtf8(string); + + encoding = ('' + encoding).toLowerCase(); + loweredCase = true; + } + } +} + +Buffer.byteLength = byteLength; + +// toString(encoding, start=0, end=buffer.length) +Buffer.prototype.toString = function(encoding, start, end) { + var loweredCase = false; + + start = start >>> 0; + end = end === undefined || end === Infinity ? this.length : end >>> 0; + + if (!encoding) encoding = 'utf8'; + if (start < 0) start = 0; + if (end > this.length) end = this.length; + if (end <= start) return ''; + + while (true) { + 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': + case 'utf16le': + case 'utf-16le': + return this.ucs2Slice(start, end); + + default: + if (loweredCase) + throw new TypeError('Unknown encoding: ' + encoding); + encoding = (encoding + '').toLowerCase(); + loweredCase = true; + } + } +}; + + +Buffer.prototype.equals = function equals(b) { + if (!(b instanceof Buffer)) + throw new TypeError('Argument must be a Buffer'); + + if (this === b) + return true; + + return binding.compare(this, b) === 0; +}; + + +// Inspect +Buffer.prototype.inspect = function inspect() { + var str = ''; + var max = exports.INSPECT_MAX_BYTES; + if (this.length > 0) { + str = this.toString('hex', 0, max).match(/.{2}/g).join(' '); + if (this.length > max) + str += ' ... '; + } + return '<' + this.constructor.name + ' ' + str + '>'; +}; + + +Buffer.prototype.compare = function compare(b) { + if (!(b instanceof Buffer)) + throw new TypeError('Argument must be a Buffer'); + + if (this === b) + return 0; + + return binding.compare(this, b); +}; + + +Buffer.prototype.indexOf = function indexOf(val, byteOffset) { + if (byteOffset > 0x7fffffff) + byteOffset = 0x7fffffff; + else if (byteOffset < -0x80000000) + byteOffset = -0x80000000; + byteOffset >>= 0; + + if (typeof val === 'string') + return binding.indexOfString(this, val, byteOffset); + if (val instanceof Buffer) + return binding.indexOfBuffer(this, val, byteOffset); + if (typeof val === 'number') + return binding.indexOfNumber(this, val, byteOffset); + + throw new TypeError('val must be string, number or Buffer'); +}; + + +Buffer.prototype.fill = function fill(val, start, end) { + start = start >> 0; + end = (end === undefined) ? this.length : end >> 0; + + if (start < 0 || end > this.length) + throw new RangeError('out of range index'); + if (end <= start) + return this; + + if (typeof val !== 'string') { + val = val >>> 0; + } else if (val.length === 1) { + var code = val.charCodeAt(0); + if (code < 256) + val = code; + } + + binding.fill(this, val, start, end); + + return this; +}; + + +// XXX remove in v0.13 +Buffer.prototype.get = internalUtil.deprecate(function get(offset) { + offset = ~~offset; + if (offset < 0 || offset >= this.length) + throw new RangeError('index out of range'); + return this[offset]; +}, '.get() is deprecated. Access using array indexes instead.'); + + +// XXX remove in v0.13 +Buffer.prototype.set = internalUtil.deprecate(function set(offset, v) { + offset = ~~offset; + if (offset < 0 || offset >= this.length) + throw new RangeError('index out of range'); + return this[offset] = v; +}, '.set() is deprecated. Set using array indexes instead.'); + + +// TODO(trevnorris): fix these checks to follow new standard +// write(string, offset = 0, length = buffer.length, encoding = 'utf8') +var writeWarned = false; +const writeMsg = '.write(string, encoding, offset, length) is deprecated.' + + ' Use write(string[, offset[, length]][, encoding]) instead.'; +Buffer.prototype.write = function(string, offset, length, encoding) { + // Buffer#write(string); + if (offset === undefined) { + encoding = 'utf8'; + length = this.length; + offset = 0; + + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset; + length = this.length; + offset = 0; + + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset >>> 0; + if (isFinite(length)) { + length = length >>> 0; + if (encoding === undefined) + encoding = 'utf8'; + } else { + encoding = length; + length = undefined; + } + + // XXX legacy write(string, encoding, offset, length) - remove in v0.13 + } else { + writeWarned = internalUtil.printDeprecationMessage(writeMsg, writeWarned); + var swap = encoding; + encoding = offset; + offset = length >>> 0; + length = swap; + } + + var remaining = this.length - offset; + if (length === undefined || length > remaining) + length = remaining; + + if (string.length > 0 && (length < 0 || offset < 0)) + throw new RangeError('attempt to write outside buffer bounds'); + + if (!encoding) + encoding = 'utf8'; + + var loweredCase = false; + for (;;) { + 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': + // Warning: maxLength not taken into account in base64Write + return this.base64Write(string, offset, length); + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return this.ucs2Write(string, offset, length); + + default: + if (loweredCase) + throw new TypeError('Unknown encoding: ' + encoding); + encoding = ('' + encoding).toLowerCase(); + loweredCase = true; + } + } +}; + + +Buffer.prototype.toJSON = function() { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this, 0) + }; +}; + + +// TODO(trevnorris): currently works like Array.prototype.slice(), which +// doesn't follow the new standard for throwing on out of range indexes. +Buffer.prototype.slice = function(start, end) { + var len = this.length; + start = ~~start; + end = end === undefined ? len : ~~end; + + if (start < 0) { + start += len; + if (start < 0) + start = 0; + } else if (start > len) { + start = len; + } + + if (end < 0) { + end += len; + if (end < 0) + end = 0; + } else if (end > len) { + end = len; + } + + if (end < start) + end = start; + + var buf = new NativeBuffer(); + sliceOnto(this, buf, start, end); + buf.length = end - start; + if (buf.length > 0) + buf.parent = this.parent === undefined ? this : this.parent; + + return buf; +}; + + +function checkOffset(offset, ext, length) { + if (offset + ext > length) + throw new RangeError('index out of range'); +} + + +Buffer.prototype.readUIntLE = function(offset, byteLength, noAssert) { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkOffset(offset, byteLength, this.length); + + var val = this[offset]; + var mul = 1; + var i = 0; + while (++i < byteLength && (mul *= 0x100)) + val += this[offset + i] * mul; + + return val; +}; + + +Buffer.prototype.readUIntBE = function(offset, byteLength, noAssert) { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkOffset(offset, byteLength, this.length); + + var val = this[offset + --byteLength]; + var mul = 1; + while (byteLength > 0 && (mul *= 0x100)) + val += this[offset + --byteLength] * mul; + + return val; +}; + + +Buffer.prototype.readUInt8 = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 1, this.length); + return this[offset]; +}; + + +Buffer.prototype.readUInt16LE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 2, this.length); + return this[offset] | (this[offset + 1] << 8); +}; + + +Buffer.prototype.readUInt16BE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 2, this.length); + return (this[offset] << 8) | this[offset + 1]; +}; + + +Buffer.prototype.readUInt32LE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + + return ((this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16)) + + (this[offset + 3] * 0x1000000); +}; + + +Buffer.prototype.readUInt32BE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + + return (this[offset] * 0x1000000) + + ((this[offset + 1] << 16) | + (this[offset + 2] << 8) | + this[offset + 3]); +}; + + +Buffer.prototype.readIntLE = function(offset, byteLength, noAssert) { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkOffset(offset, byteLength, this.length); + + var val = this[offset]; + var mul = 1; + var i = 0; + while (++i < byteLength && (mul *= 0x100)) + val += this[offset + i] * mul; + mul *= 0x80; + + if (val >= mul) + val -= Math.pow(2, 8 * byteLength); + + return val; +}; + + +Buffer.prototype.readIntBE = function(offset, byteLength, noAssert) { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkOffset(offset, byteLength, this.length); + + var i = byteLength; + var mul = 1; + var val = this[offset + --i]; + while (i > 0 && (mul *= 0x100)) + val += this[offset + --i] * mul; + mul *= 0x80; + + if (val >= mul) + val -= Math.pow(2, 8 * byteLength); + + return val; +}; + + +Buffer.prototype.readInt8 = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 1, this.length); + var val = this[offset]; + return !(val & 0x80) ? val : (0xff - val + 1) * -1; +}; + + +Buffer.prototype.readInt16LE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 2, this.length); + var val = this[offset] | (this[offset + 1] << 8); + return (val & 0x8000) ? val | 0xFFFF0000 : val; +}; + + +Buffer.prototype.readInt16BE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 2, this.length); + var val = this[offset + 1] | (this[offset] << 8); + return (val & 0x8000) ? val | 0xFFFF0000 : val; +}; + + +Buffer.prototype.readInt32LE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + + return (this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16) | + (this[offset + 3] << 24); +}; + + +Buffer.prototype.readInt32BE = function(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + + return (this[offset] << 24) | + (this[offset + 1] << 16) | + (this[offset + 2] << 8) | + (this[offset + 3]); +}; + + +Buffer.prototype.readFloatLE = function readFloatLE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + return binding.readFloatLE(this, offset); +}; + + +Buffer.prototype.readFloatBE = function readFloatBE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 4, this.length); + return binding.readFloatBE(this, offset); +}; + + +Buffer.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 8, this.length); + return binding.readDoubleLE(this, offset); +}; + + +Buffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) + checkOffset(offset, 8, this.length); + return binding.readDoubleBE(this, offset); +}; + + +function checkInt(buffer, value, offset, ext, max, min) { + if (!(buffer instanceof Buffer)) + throw new TypeError('buffer must be a Buffer instance'); + if (value > max || value < min) + throw new TypeError('value is out of bounds'); + if (offset + ext > buffer.length) + throw new RangeError('index out of range'); +} + + +Buffer.prototype.writeUIntLE = function(value, offset, byteLength, noAssert) { + value = +value; + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0); + + var mul = 1; + var i = 0; + this[offset] = value; + while (++i < byteLength && (mul *= 0x100)) + this[offset + i] = (value / mul) >>> 0; + + return offset + byteLength; +}; + + +Buffer.prototype.writeUIntBE = function(value, offset, byteLength, noAssert) { + value = +value; + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) + checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0); + + var i = byteLength - 1; + var mul = 1; + this[offset + i] = value; + while (--i >= 0 && (mul *= 0x100)) + this[offset + i] = (value / mul) >>> 0; + + return offset + byteLength; +}; + + +Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 1, 0xff, 0); + this[offset] = value; + return offset + 1; +}; + + +Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 2, 0xffff, 0); + this[offset] = value; + this[offset + 1] = (value >>> 8); + return offset + 2; +}; + + +Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 2, 0xffff, 0); + this[offset] = (value >>> 8); + this[offset + 1] = value; + return offset + 2; +}; + + +Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 4, 0xffffffff, 0); + this[offset + 3] = (value >>> 24); + this[offset + 2] = (value >>> 16); + this[offset + 1] = (value >>> 8); + this[offset] = value; + return offset + 4; +}; + + +Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 4, 0xffffffff, 0); + this[offset] = (value >>> 24); + this[offset + 1] = (value >>> 16); + this[offset + 2] = (value >>> 8); + this[offset + 3] = value; + return offset + 4; +}; + + +Buffer.prototype.writeIntLE = function(value, offset, byteLength, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) { + checkInt(this, + value, + offset, + byteLength, + Math.pow(2, 8 * byteLength - 1) - 1, + -Math.pow(2, 8 * byteLength - 1)); + } + + var i = 0; + var mul = 1; + var sub = value < 0 ? 1 : 0; + this[offset] = value; + while (++i < byteLength && (mul *= 0x100)) + this[offset + i] = ((value / mul) >> 0) - sub; + + return offset + byteLength; +}; + + +Buffer.prototype.writeIntBE = function(value, offset, byteLength, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) { + checkInt(this, + value, + offset, + byteLength, + Math.pow(2, 8 * byteLength - 1) - 1, + -Math.pow(2, 8 * byteLength - 1)); + } + + var i = byteLength - 1; + var mul = 1; + var sub = value < 0 ? 1 : 0; + this[offset + i] = value; + while (--i >= 0 && (mul *= 0x100)) + this[offset + i] = ((value / mul) >> 0) - sub; + + return offset + byteLength; +}; + + +Buffer.prototype.writeInt8 = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 1, 0x7f, -0x80); + this[offset] = value; + return offset + 1; +}; + + +Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 2, 0x7fff, -0x8000); + this[offset] = value; + this[offset + 1] = (value >>> 8); + return offset + 2; +}; + + +Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 2, 0x7fff, -0x8000); + this[offset] = (value >>> 8); + this[offset + 1] = value; + return offset + 2; +}; + + +Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); + this[offset] = value; + this[offset + 1] = (value >>> 8); + this[offset + 2] = (value >>> 16); + this[offset + 3] = (value >>> 24); + return offset + 4; +}; + + +Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) + checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); + this[offset] = (value >>> 24); + this[offset + 1] = (value >>> 16); + this[offset + 2] = (value >>> 8); + this[offset + 3] = value; + return offset + 4; +}; + + +function checkFloat(buffer, value, offset, ext) { + if (!(buffer instanceof Buffer)) + throw new TypeError('buffer must be a Buffer instance'); + if (offset + ext > buffer.length) + throw new RangeError('index out of range'); +} + + +Buffer.prototype.writeFloatLE = function writeFloatLE(val, offset, noAssert) { + val = +val; + offset = offset >>> 0; + if (!noAssert) + checkFloat(this, val, offset, 4); + binding.writeFloatLE(this, val, offset); + return offset + 4; +}; + + +Buffer.prototype.writeFloatBE = function writeFloatBE(val, offset, noAssert) { + val = +val; + offset = offset >>> 0; + if (!noAssert) + checkFloat(this, val, offset, 4); + binding.writeFloatBE(this, val, offset); + return offset + 4; +}; + + +Buffer.prototype.writeDoubleLE = function writeDoubleLE(val, offset, noAssert) { + val = +val; + offset = offset >>> 0; + if (!noAssert) + checkFloat(this, val, offset, 8); + binding.writeDoubleLE(this, val, offset); + return offset + 8; +}; + + +Buffer.prototype.writeDoubleBE = function writeDoubleBE(val, offset, noAssert) { + val = +val; + offset = offset >>> 0; + if (!noAssert) + checkFloat(this, val, offset, 8); + binding.writeDoubleBE(this, val, offset); + return offset + 8; +}; + +// ES6 iterator + +var ITERATOR_KIND_KEYS = 1; +var ITERATOR_KIND_ENTRIES = 3; + +function BufferIteratorResult(value, done) { + this.value = value; + this.done = done; +} + +var resultCache = new Array(256); + +for (var i = 0; i < 256; i++) + resultCache[i] = Object.freeze(new BufferIteratorResult(i, false)); + +var finalResult = Object.freeze(new BufferIteratorResult(undefined, true)); + +function BufferIterator(buffer, kind) { + this._buffer = buffer; + this._kind = kind; + this._index = 0; +} + +BufferIterator.prototype.next = function() { + var buffer = this._buffer; + var kind = this._kind; + var index = this._index; + + if (index >= buffer.length) + return finalResult; + + this._index++; + + if (kind === ITERATOR_KIND_ENTRIES) + return new BufferIteratorResult([index, buffer[index]], false); + + return new BufferIteratorResult(index, false); +}; + +function BufferValueIterator(buffer) { + BufferIterator.call(this, buffer, null); +} + +BufferValueIterator.prototype.next = function() { + var buffer = this._buffer; + var index = this._index; + + if (index >= buffer.length) + return finalResult; + + this._index++; + + return resultCache[buffer[index]]; +}; + + +BufferIterator.prototype[Symbol.iterator] = function() { + return this; +}; + +BufferValueIterator.prototype[Symbol.iterator] = + BufferIterator.prototype[Symbol.iterator]; + +Buffer.prototype.keys = function() { + return new BufferIterator(this, ITERATOR_KIND_KEYS); +}; + +Buffer.prototype.entries = function() { + return new BufferIterator(this, ITERATOR_KIND_ENTRIES); +}; + +Buffer.prototype.values = function() { + return new BufferValueIterator(this); +}; + +Buffer.prototype[Symbol.iterator] = Buffer.prototype.values; @@ -69,8 +69,9 @@ 'lib/v8.js', 'lib/vm.js', 'lib/zlib.js', - 'lib/internal/child_process.js', + 'lib/internal/buffer_old.js', + 'lib/internal/buffer_new.js', 'lib/internal/freelist.js', 'lib/internal/smalloc.js', 'lib/internal/socket_list.js', diff --git a/src/env.h b/src/env.h index 72895219a93..84beabcdbfa 100644 --- a/src/env.h +++ b/src/env.h @@ -231,6 +231,7 @@ namespace node { V(async_hooks_post_function, v8::Function) \ V(binding_cache_object, v8::Object) \ V(buffer_constructor_function, v8::Function) \ + V(buffer_prototype_object, v8::Object) \ V(context, v8::Context) \ V(domain_array, v8::Array) \ V(fs_stats_constructor_function, v8::Function) \ diff --git a/src/node.cc b/src/node.cc index 631a595c6a2..4d699e8f288 100644 --- a/src/node.cc +++ b/src/node.cc @@ -5,6 +5,7 @@ #include "node_http_parser.h" #include "node_javascript.h" #include "node_version.h" +#include "node_internals.h" #if defined HAVE_PERFCTR #include "node_counters.h" @@ -147,6 +148,8 @@ static uv_async_t dispatch_debug_messages_async; static Isolate* node_isolate = nullptr; static v8::Platform* default_platform; +bool using_old_buffer = false; + class ArrayBufferAllocator : public ArrayBuffer::Allocator { public: // Impose an upper limit to avoid out of memory errors that bring down @@ -166,23 +169,17 @@ ArrayBufferAllocator ArrayBufferAllocator::the_singleton; void* ArrayBufferAllocator::Allocate(size_t length) { - if (length > kMaxLength) - return nullptr; - char* data = new char[length]; - memset(data, 0, length); - return data; + return calloc(length, 1); } void* ArrayBufferAllocator::AllocateUninitialized(size_t length) { - if (length > kMaxLength) - return nullptr; - return new char[length]; + return malloc(length); } void ArrayBufferAllocator::Free(void* data, size_t length) { - delete[] static_cast<char*>(data); + free(data); } @@ -2836,6 +2833,11 @@ void SetupProcessObject(Environment* env, READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate())); } + // --use-old_buffer + if (using_old_buffer) { + READONLY_PROPERTY(process, "useOldBuffer", True(env->isolate())); + } + size_t exec_path_len = 2 * PATH_MAX; char* exec_path = new char[exec_path_len]; Local<String> exec_path_value; @@ -3067,6 +3069,7 @@ static void PrintHelp() { " --track-heap-objects track heap object allocations for heap " "snapshots\n" " --v8-options print v8 command line options\n" + " --use-old-buffer Revert to old Buffer implementation\n" #if defined(NODE_HAVE_I18N_SUPPORT) " --icu-data-dir=dir set ICU data load path to dir\n" " (overrides NODE_ICU_DATA)\n" @@ -3204,6 +3207,10 @@ static void ParseArgs(int* argc, #endif } else if (strcmp(arg, "--expose-internals") == 0 || strcmp(arg, "--expose_internals") == 0) { + } else if (strcmp(arg, "--use-old-buffer") == 0 || + strcmp(arg, "--use_old_buffer") == 0) { + using_old_buffer = true; + // consumed in js } else { // V8 option. Pass through as-is. diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 3f429817e13..d3398075d06 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -5,6 +5,8 @@ #include "env-inl.h" #include "smalloc.h" #include "string_bytes.h" +#include "util.h" +#include "util-inl.h" #include "v8-profiler.h" #include "v8.h" @@ -28,6 +30,16 @@ size_t name##_length; \ char* name##_data; +#define SPREAD_ARG(val, name) \ + CHECK((val)->IsUint8Array()); \ + Local<Uint8Array> name = (val).As<Uint8Array>(); \ + ArrayBuffer::Contents name##_c = name->Buffer()->GetContents(); \ + size_t name##_offset = name->ByteOffset(); \ + name##_length = name->ByteLength(); \ + name##_data = static_cast<char*>(name##_c.Data()) + name##_offset; \ + if (name##_length > 0) \ + CHECK_NE(name##_data, nullptr); + #define ARGS_THIS(argT, name) \ Local<Object> name = argT; \ name##_length = name->GetIndexedPropertiesExternalArrayDataLength(); \ @@ -48,6 +60,8 @@ namespace node { namespace Buffer { +using v8::ArrayBuffer; +using v8::ArrayBufferCreationMode; using v8::Context; using v8::EscapableHandleScope; using v8::Function; @@ -61,6 +75,7 @@ using v8::Number; using v8::Object; using v8::String; using v8::Uint32; +using v8::Uint8Array; using v8::Value; @@ -70,10 +85,19 @@ bool HasInstance(Handle<Value> val) { bool HasInstance(Handle<Object> obj) { - if (!obj->HasIndexedPropertiesInExternalArrayData()) + if (using_old_buffer) { + if (!obj->HasIndexedPropertiesInExternalArrayData()) + return false; + v8::ExternalArrayType type = + obj->GetIndexedPropertiesExternalArrayDataType(); + return type == v8::kExternalUint8Array; + } + + if (!obj->IsUint8Array()) return false; - v8::ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType(); - return type == v8::kExternalUint8Array; + Local<Uint8Array> array = obj.As<Uint8Array>(); + Environment* env = Environment::GetCurrent(array->GetIsolate()); + return array->GetPrototype()->StrictEquals(env->buffer_prototype_object()); } @@ -86,8 +110,15 @@ char* Data(Handle<Value> val) { char* Data(Handle<Object> obj) { - CHECK(obj->HasIndexedPropertiesInExternalArrayData()); - return static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData()); + if (using_old_buffer) { + CHECK(obj->HasIndexedPropertiesInExternalArrayData()); + return static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData()); + } + + CHECK(obj->IsUint8Array()); + Local<Uint8Array> ui = obj.As<Uint8Array>(); + ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); + return static_cast<char*>(ab_c.Data()) + ui->ByteOffset(); } @@ -98,8 +129,14 @@ size_t Length(Handle<Value> val) { size_t Length(Handle<Object> obj) { - CHECK(obj->HasIndexedPropertiesInExternalArrayData()); - return obj->GetIndexedPropertiesExternalArrayDataLength(); + if (using_old_buffer) { + CHECK(obj->HasIndexedPropertiesInExternalArrayData()); + return obj->GetIndexedPropertiesExternalArrayDataLength(); + } + + CHECK(obj->IsUint8Array()); + Local<Uint8Array> ui = obj.As<Uint8Array>(); + return ui->ByteLength(); } @@ -107,11 +144,20 @@ Local<Object> New(Isolate* isolate, Handle<String> string, enum encoding enc) { EscapableHandleScope scope(isolate); size_t length = StringBytes::Size(isolate, string, enc); + char* data = static_cast<char*>(malloc(length)); - Local<Object> buf = New(isolate, length); - char* data = Buffer::Data(buf); - StringBytes::Write(isolate, data, length, string, enc); + if (data == nullptr) + return Local<Object>(); + size_t actual = StringBytes::Write(isolate, data, length, string, enc); + CHECK(actual <= length); + + if (actual < length) { + data = static_cast<char*>(realloc(data, actual)); + CHECK_NE(data, nullptr); + } + + Local<Object> buf = Use(isolate, data, actual); return scope.Escape(buf); } @@ -123,19 +169,44 @@ Local<Object> New(Isolate* isolate, size_t length) { } -// TODO(trevnorris): these have a flaw by needing to call the Buffer inst then -// Alloc. continue to look for a better architecture. Local<Object> New(Environment* env, size_t length) { EscapableHandleScope scope(env->isolate()); - CHECK_LE(length, kMaxLength); + if (using_old_buffer) { + CHECK_LE(length, kMaxLength); - Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg); + Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local<Object> obj = + env->buffer_constructor_function()->NewInstance(1, &arg); - smalloc::Alloc(env, obj, length); + smalloc::Alloc(env, obj, length); - return scope.Escape(obj); + return scope.Escape(obj); + } + + // V8 currently only allows a maximum Typed Array index of max Smi. + if (!IsValidSmi(length)) { + return Local<Object>(); + } + + void* data; + if (length > 0) { + data = malloc(length); + // NOTE: API change. Must check .IsEmpty() on the return object to see if + // the data was able to be allocated. + if (data == nullptr) + return Local<Object>(); + } else { + data = nullptr; + } + Local<ArrayBuffer> ab = + ArrayBuffer::New(env->isolate(), + data, + length, + ArrayBufferCreationMode::kInternalized); + Local<Uint8Array> ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + return scope.Escape(ui); } @@ -147,33 +218,59 @@ Local<Object> New(Isolate* isolate, const char* data, size_t length) { } -// TODO(trevnorris): for backwards compatibility this is left to copy the data, -// but for consistency w/ the other should use data. And a copy version renamed -// to something else. +// Make a copy of "data". Why this isn't called "Copy", we'll never know. Local<Object> New(Environment* env, const char* data, size_t length) { EscapableHandleScope scope(env->isolate()); - CHECK_LE(length, kMaxLength); + if (using_old_buffer) { + CHECK_LE(length, kMaxLength); + + Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local<Object> obj = + env->buffer_constructor_function()->NewInstance(1, &arg); + + char* new_data; + if (length > 0) { + new_data = static_cast<char*>(malloc(length)); + if (new_data == nullptr) + FatalError("node::Buffer::New(const char*, size_t)", "Out Of Memory"); + memcpy(new_data, data, length); + } else { + new_data = nullptr; + } - Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg); + smalloc::Alloc(env, obj, new_data, length); - // TODO(trevnorris): done like this to handle HasInstance since only checks - // if external array data has been set, but would like to use a better - // approach if v8 provided one. - char* new_data; + return scope.Escape(obj); + } + + // V8 currently only allows a maximum Typed Array index of max Smi. + if (!IsValidSmi(length)) { + return Local<Object>(); + } + + void* new_data; if (length > 0) { - new_data = static_cast<char*>(malloc(length)); + CHECK_NE(data, nullptr); + new_data = malloc(length); + // NOTE: API change. Must check .IsEmpty() on the return object to see if + // the data was able to be allocated. if (new_data == nullptr) - FatalError("node::Buffer::New(const char*, size_t)", "Out Of Memory"); + return Local<Object>(); memcpy(new_data, data, length); } else { new_data = nullptr; } - smalloc::Alloc(env, obj, new_data, length); + Local<ArrayBuffer> ab = + ArrayBuffer::New(env->isolate(), + new_data, + length, + ArrayBufferCreationMode::kInternalized); + Local<Uint8Array> ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); - return scope.Escape(obj); + return scope.Escape(ui); } @@ -196,6 +293,7 @@ Local<Object> New(Environment* env, void* hint) { EscapableHandleScope scope(env->isolate()); + // TODO(trevnorris): IMPLEMENT CHECK_LE(length, kMaxLength); Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); @@ -207,7 +305,7 @@ Local<Object> New(Environment* env, } -Local<Object> Use(Isolate* isolate, char* data, uint32_t length) { +Local<Object> Use(Isolate* isolate, char* data, size_t length) { Environment* env = Environment::GetCurrent(isolate); EscapableHandleScope handle_scope(env->isolate()); Local<Object> obj = Buffer::Use(env, data, length); @@ -215,17 +313,98 @@ Local<Object> Use(Isolate* isolate, char* data, uint32_t length) { } -Local<Object> Use(Environment* env, char* data, uint32_t length) { +Local<Object> Use(Environment* env, char* data, size_t length) { EscapableHandleScope scope(env->isolate()); - CHECK_LE(length, kMaxLength); + if (using_old_buffer) { + CHECK_LE(length, kMaxLength); - Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg); + Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local<Object> obj = + env->buffer_constructor_function()->NewInstance(1, &arg); - smalloc::Alloc(env, obj, data, length); + smalloc::Alloc(env, obj, data, length); - return scope.Escape(obj); + return scope.Escape(obj); + } + + if (length > 0) { + CHECK_NE(data, nullptr); + CHECK(IsValidSmi(length)); + } + + Local<ArrayBuffer> ab = + ArrayBuffer::New(env->isolate(), + data, + length, + ArrayBufferCreationMode::kInternalized); + Local<Uint8Array> ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + return scope.Escape(ui); +} + + +void Create(const FunctionCallbackInfo<Value>& args) { + Isolate* isolate = args.GetIsolate(); + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsNumber()); + + int64_t length = args[0]->IntegerValue(); + + if (!IsValidSmi(length)) { + return env->ThrowRangeError("invalid Buffer length"); + } + + void* data; + if (length > 0) { + data = malloc(length); + if (data == nullptr) + return env->ThrowRangeError("invalid Buffer length"); + } else { + data = nullptr; + } + + Local<ArrayBuffer> ab = + ArrayBuffer::New(isolate, + data, + length, + ArrayBufferCreationMode::kInternalized); + Local<Uint8Array> ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + args.GetReturnValue().Set(ui); +} + + +void CreateFromString(const FunctionCallbackInfo<Value>& args) { + CHECK(args[0]->IsString()); + CHECK(args[1]->IsString()); + + enum encoding enc = ParseEncoding(args.GetIsolate(), + args[1].As<String>(), + UTF8); + Local<Object> buf = New(args.GetIsolate(), args[0].As<String>(), enc); + args.GetReturnValue().Set(buf); +} + + +void Slice(const FunctionCallbackInfo<Value>& args) { + CHECK(args[0]->IsUint8Array()); + CHECK(args[1]->IsNumber()); + CHECK(args[2]->IsNumber()); + Environment* env = Environment::GetCurrent(args); + Local<Uint8Array> ab_ui = args[0].As<Uint8Array>(); + Local<ArrayBuffer> ab = ab_ui->Buffer(); + ArrayBuffer::Contents ab_c = ab->GetContents(); + size_t offset = ab_ui->ByteOffset(); + size_t start = args[1]->NumberValue() + offset; + size_t end = args[2]->NumberValue() + offset; + CHECK_GE(end, start); + size_t size = end - start; + CHECK_GE(ab_c.ByteLength(), start + size); + Local<Uint8Array> ui = Uint8Array::New(ab, start, size); + ui->SetPrototype(env->buffer_prototype_object()); + args.GetReturnValue().Set(ui); } @@ -237,7 +416,12 @@ void StringSlice(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args.This(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + } else { + SPREAD_ARG(args.This(), ts_obj); + } if (ts_obj_length == 0) return args.GetReturnValue().SetEmptyString(); @@ -252,10 +436,16 @@ void StringSlice(const FunctionCallbackInfo<Value>& args) { template <> void StringSlice<UCS2>(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); - ARGS_THIS_DEC(ts_obj); THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); - ARGS_THIS(args.This(), ts_obj); + + ARGS_THIS_DEC(ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + } else { + SPREAD_ARG(args.This(), ts_obj); + } if (ts_obj_length == 0) return args.GetReturnValue().SetEmptyString(); @@ -329,11 +519,22 @@ void Copy(const FunctionCallbackInfo<Value> &args) { THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); - Local<Object> target_obj = args[0].As<Object>(); - size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength(); - char* target_data = static_cast<char*>( - target->GetIndexedPropertiesExternalArrayData()); + Local<Object> target_obj = args[0]->ToObject(env->isolate()); + + ARGS_THIS_DEC(ts_obj); + ARGS_THIS_DEC(target); + + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + target_length = target_obj->GetIndexedPropertiesExternalArrayDataLength(); + target_data = static_cast<char*>( + target_obj->GetIndexedPropertiesExternalArrayData()); + } else { + SPREAD_ARG(args.This(), ts_obj); + SPREAD_ARG(target_obj, target); + } + size_t target_start; size_t source_start; size_t source_end; @@ -364,7 +565,12 @@ void Copy(const FunctionCallbackInfo<Value> &args) { void Fill(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } size_t start = args[2]->Uint32Value(); size_t end = args[3]->Uint32Value(); @@ -409,7 +615,12 @@ void StringWrite(const FunctionCallbackInfo<Value>& args) { ARGS_THIS_DEC(ts_obj); THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); - ARGS_THIS(args.This(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + } else { + SPREAD_ARG(args.This(), ts_obj); + } if (!args[0]->IsString()) return env->ThrowTypeError("Argument must be a string"); @@ -487,7 +698,12 @@ template <typename T, enum Endianness endianness> void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } uint32_t offset = args[1]->Uint32Value(); CHECK_LE(offset + sizeof(T), ts_obj_length); @@ -530,7 +746,12 @@ void ReadDoubleBE(const FunctionCallbackInfo<Value>& args) { template <typename T, enum Endianness endianness> uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) { ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } T val = args[1]->NumberValue(); uint32_t offset = args[2]->Uint32Value(); @@ -588,26 +809,27 @@ void Compare(const FunctionCallbackInfo<Value> &args) { THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); - Local<Object> obj_a = args[0].As<Object>(); - char* obj_a_data = - static_cast<char*>(obj_a->GetIndexedPropertiesExternalArrayData()); - size_t obj_a_len = obj_a->GetIndexedPropertiesExternalArrayDataLength(); + ARGS_THIS_DEC(obj_a); + ARGS_THIS_DEC(obj_b); - Local<Object> obj_b = args[1].As<Object>(); - char* obj_b_data = - static_cast<char*>(obj_b->GetIndexedPropertiesExternalArrayData()); - size_t obj_b_len = obj_b->GetIndexedPropertiesExternalArrayDataLength(); + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), obj_a); + ARGS_THIS(args[1].As<Object>(), obj_b); + } else { + SPREAD_ARG(args[0], obj_a); + SPREAD_ARG(args[1], obj_b); + } - size_t cmp_length = MIN(obj_a_len, obj_b_len); + size_t cmp_length = MIN(obj_a_length, obj_b_length); int32_t val = memcmp(obj_a_data, obj_b_data, cmp_length); // Normalize val to be an integer in the range of [1, -1] since // implementations of memcmp() can vary by platform. if (val == 0) { - if (obj_a_len > obj_b_len) + if (obj_a_length > obj_b_length) val = 1; - else if (obj_a_len < obj_b_len) + else if (obj_a_length < obj_b_length) val = -1; } else { if (val > 0) @@ -642,7 +864,12 @@ void IndexOfString(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } node::Utf8Value str(args.GetIsolate(), args[1]); int32_t offset_i32 = args[2]->Int32Value(); @@ -675,7 +902,12 @@ void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } Local<Object> buf = args[1].As<Object>(); int32_t offset_i32 = args[2]->Int32Value(); @@ -714,7 +946,12 @@ void IndexOfNumber(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } uint32_t needle = args[1]->Uint32Value(); int32_t offset_i32 = args[2]->Int32Value(); @@ -743,15 +980,20 @@ void IndexOfNumber(const FunctionCallbackInfo<Value>& args) { void SetupBufferJS(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsFunction()); - - Local<Function> bv = args[0].As<Function>(); - env->set_buffer_constructor_function(bv); - Local<Value> proto_v = bv->Get(env->prototype_string()); - - CHECK(proto_v->IsObject()); + Local<Object> proto; - Local<Object> proto = proto_v.As<Object>(); + if (using_old_buffer) { + CHECK(args[0]->IsFunction()); + Local<Function> bv = args[0].As<Function>(); + env->set_buffer_constructor_function(bv); + Local<Value> proto_v = bv->Get(env->prototype_string()); + CHECK(proto_v->IsObject()); + proto = proto_v.As<Object>(); + } else { + CHECK(args[0]->IsObject()); + proto = args[0].As<Object>(); + env->set_buffer_prototype_object(proto); + } env->SetMethod(proto, "asciiSlice", AsciiSlice); env->SetMethod(proto, "base64Slice", Base64Slice); @@ -770,9 +1012,11 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) { env->SetMethod(proto, "copy", Copy); // for backwards compatibility - proto->ForceSet(env->offset_string(), - Uint32::New(env->isolate(), 0), - v8::ReadOnly); + if (using_old_buffer) { + proto->ForceSet(env->offset_string(), + Uint32::New(env->isolate(), 0), + v8::ReadOnly); + } } @@ -782,7 +1026,10 @@ void Initialize(Handle<Object> target, Environment* env = Environment::GetCurrent(context); env->SetMethod(target, "setupBufferJS", SetupBufferJS); + env->SetMethod(target, "create", Create); + env->SetMethod(target, "createFromString", CreateFromString); + env->SetMethod(target, "slice", Slice); env->SetMethod(target, "byteLengthUtf8", ByteLengthUtf8); env->SetMethod(target, "compare", Compare); env->SetMethod(target, "fill", Fill); diff --git a/src/node_buffer.h b/src/node_buffer.h index 2e649970c47..4b1b2cd8591 100644 --- a/src/node_buffer.h +++ b/src/node_buffer.h @@ -63,9 +63,9 @@ NODE_DEPRECATED("Use New(isolate, ...)", // TODO(trevnorris): should be New() for consistency NODE_EXTERN v8::Local<v8::Object> Use(v8::Isolate* isolate, char* data, - uint32_t len); + size_t len); NODE_DEPRECATED("Use Use(isolate, ...)", - inline v8::Local<v8::Object> Use(char* data, uint32_t len) { + inline v8::Local<v8::Object> Use(char* data, size_t len) { return Use(v8::Isolate::GetCurrent(), data, len); }) @@ -95,7 +95,7 @@ v8::Local<v8::Object> New(Environment* env, size_t length, smalloc::FreeCallback callback, void* hint); -v8::Local<v8::Object> Use(Environment* env, char* data, uint32_t length); +v8::Local<v8::Object> Use(Environment* env, char* data, size_t length); #endif // defined(NODE_WANT_INTERNALS) } // namespace Buffer diff --git a/src/node_internals.h b/src/node_internals.h index c99b2feeb0b..e5f7458f78d 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -17,6 +17,8 @@ namespace node { // Forward declaration class Environment; +extern bool using_old_buffer; + // If persistent.IsWeak() == false, then do not call persistent.Reset() // while the returned Local<T> is still in scope, it will destroy the // reference to the object. diff --git a/src/util-inl.h b/src/util-inl.h index 75bdb4784aa..308c9b2787d 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -198,6 +198,14 @@ TypeName* Unwrap(v8::Local<v8::Object> object) { return static_cast<TypeName*>(pointer); } +inline bool IsValidSmi(int64_t value) { + if (sizeof(int32_t) == sizeof(intptr_t)) { + return value >= -0x40000000LL && value <= 0x3fffffffLL; + } else { + return value >= -0x80000000LL && value <= 0x7fffffffLL; + } +} + } // namespace node #endif // SRC_UTIL_INL_H_ diff --git a/src/util.h b/src/util.h index ea17a155745..3ca79c0ac15 100644 --- a/src/util.h +++ b/src/util.h @@ -195,6 +195,8 @@ class Utf8Value { char str_st_[1024]; }; +inline bool IsValidSmi(int64_t value); + } // namespace node #endif // SRC_UTIL_H_ diff --git a/test/parallel/test-buffer-slice.js b/test/parallel/test-buffer-slice.js deleted file mode 100644 index 53434eab8e3..00000000000 --- a/test/parallel/test-buffer-slice.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; -var common = require('../common'); -var assert = require('assert'); - -var Buffer = require('buffer').Buffer; - -var buff = new Buffer(Buffer.poolSize + 1); -var slicedBuffer = buff.slice(); -assert.equal(slicedBuffer.parent, - buff, - 'slicedBufffer should have its parent set to the original ' + - ' buffer'); diff --git a/test/parallel/test-buffer.js b/test/parallel/test-buffer.js index 27a211bc2da..443da055600 100644 --- a/test/parallel/test-buffer.js +++ b/test/parallel/test-buffer.js @@ -329,8 +329,6 @@ assert.equal(b.parent, d.parent); var b = new SlowBuffer(5); var c = b.slice(0, 4); var d = c.slice(0, 2); -assert.equal(b, c.parent); -assert.equal(b, d.parent); // Bug regression test @@ -1075,10 +1073,6 @@ assert.equal(buf.readInt8(0), -1); // try to slice a zero length Buffer // see https://github.com/joyent/node/issues/5881 SlowBuffer(0).slice(0, 1); - // make sure a zero length slice doesn't set the .parent attribute - assert.equal(Buffer(5).slice(0, 0).parent, undefined); - // and make sure a proper slice does have a parent - assert.ok(typeof Buffer(5).slice(0, 5).parent === 'object'); })(); // Regression test for #5482: should throw but not assert in C++ land. @@ -1105,11 +1099,11 @@ assert.throws(function() { assert.throws(function() { - new Buffer(smalloc.kMaxLength + 1); + new Buffer((-1 >>> 0) + 1); }, RangeError); assert.throws(function() { - new SlowBuffer(smalloc.kMaxLength + 1); + new SlowBuffer((-1 >>> 0) + 1); }, RangeError); if (common.hasCrypto) { |