From c735f738ef1b7dd6ed622413914706f018f77e77 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Tue, 26 May 2015 12:42:14 -0600 Subject: [PATCH] buffer: implement Uint8Array backed Buffer With V8 4.4 removing the external array data API currently used by Buffer, the new implementation uses the Uint8Array to back Buffer. Buffers now have a maximum size of Smi::kMaxLength, as defined by V8. Which is ~2 GB on 64 bit and ~1 GB on 32 bit. The flag --use-old-buffer allows using the old Buffer implementation. This flag will be removed once V8 4.4 has landed. The two JS Buffer implementations have been split into two files for simplicity. Use getter to return expected .parent/.offset values for backwards compatibility. PR-URL: https://github.com/nodejs/io.js/pull/1825 Reviewed-By: Ben Noordhuis --- lib/buffer.js | 1149 +--------------------------- lib/internal/buffer_new.js | 1020 ++++++++++++++++++++++++ lib/internal/buffer_old.js | 1140 +++++++++++++++++++++++++++ node.gyp | 3 +- src/env.h | 1 + src/node.cc | 36 +- src/node_buffer.cc | 393 ++++++++-- src/node_buffer.h | 6 +- src/node_internals.h | 2 + src/util-inl.h | 8 + src/util.h | 2 + test/parallel/test-buffer-slice.js | 12 - test/parallel/test-buffer.js | 10 +- 13 files changed, 2527 insertions(+), 1255 deletions(-) create mode 100644 lib/internal/buffer_new.js create mode 100644 lib/internal/buffer_old.js delete mode 100644 test/parallel/test-buffer-slice.js diff --git a/lib/buffer.js b/lib/buffer.js index 568a1f4b34ea26..641b542276066d 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 00000000000000..c082ed8bd57b1b --- /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 00000000000000..d892fd67a23069 --- /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; diff --git a/node.gyp b/node.gyp index dfa08ce6468f30..197f439f8d819b 100644 --- a/node.gyp +++ b/node.gyp @@ -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 b53ff87fb13114..d791efef85f595 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 82d081ce17aa90..3d80aee1ea2d91 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(data); + free(data); } @@ -2838,6 +2835,18 @@ void SetupProcessObject(Environment* env, READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate())); } + // --trace-sync-io + if (trace_sync_io) { + READONLY_PROPERTY(process, "traceSyncIO", True(env->isolate())); + // Don't env->set_trace_sync_io(true) because it will be enabled + // after LoadEnvironment() has run. + } + + // --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 exec_path_value; @@ -3062,13 +3071,14 @@ static void PrintHelp() { " -r, --require module to preload (option can be repeated)\n" " --no-deprecation silence deprecation warnings\n" " --throw-deprecation throw an exception anytime a deprecated " - "function is used\n" + " function is used\n" " --trace-deprecation show stack traces on deprecations\n" " --trace-sync-io show stack trace when use of sync IO\n" " is detected after the first tick\n" " --track-heap-objects track heap object allocations for heap " - "snapshots\n" + " 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" @@ -3206,6 +3216,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 be7cb50ffe992c..4f62e6b85153d1 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 name = (val).As(); \ + ArrayBuffer::Contents name##_c = name->Buffer()->GetContents(); \ + size_t name##_offset = name->ByteOffset(); \ + name##_length = name->ByteLength(); \ + name##_data = static_cast(name##_c.Data()) + name##_offset; \ + if (name##_length > 0) \ + CHECK_NE(name##_data, nullptr); + #define ARGS_THIS(argT, name) \ Local 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 val) { bool HasInstance(Handle 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 array = obj.As(); + Environment* env = Environment::GetCurrent(array->GetIsolate()); + return array->GetPrototype()->StrictEquals(env->buffer_prototype_object()); } @@ -86,8 +110,15 @@ char* Data(Handle val) { char* Data(Handle obj) { - CHECK(obj->HasIndexedPropertiesInExternalArrayData()); - return static_cast(obj->GetIndexedPropertiesExternalArrayData()); + if (using_old_buffer) { + CHECK(obj->HasIndexedPropertiesInExternalArrayData()); + return static_cast(obj->GetIndexedPropertiesExternalArrayData()); + } + + CHECK(obj->IsUint8Array()); + Local ui = obj.As(); + ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); + return static_cast(ab_c.Data()) + ui->ByteOffset(); } @@ -98,8 +129,14 @@ size_t Length(Handle val) { size_t Length(Handle obj) { - CHECK(obj->HasIndexedPropertiesInExternalArrayData()); - return obj->GetIndexedPropertiesExternalArrayDataLength(); + if (using_old_buffer) { + CHECK(obj->HasIndexedPropertiesInExternalArrayData()); + return obj->GetIndexedPropertiesExternalArrayDataLength(); + } + + CHECK(obj->IsUint8Array()); + Local ui = obj.As(); + return ui->ByteLength(); } @@ -107,11 +144,20 @@ Local New(Isolate* isolate, Handle string, enum encoding enc) { EscapableHandleScope scope(isolate); size_t length = StringBytes::Size(isolate, string, enc); + char* data = static_cast(malloc(length)); + + if (data == nullptr) + return Local(); - Local buf = New(isolate, length); - char* data = Buffer::Data(buf); - StringBytes::Write(isolate, data, length, string, enc); + size_t actual = StringBytes::Write(isolate, data, length, string, enc); + CHECK(actual <= length); + if (actual < length) { + data = static_cast(realloc(data, actual)); + CHECK_NE(data, nullptr); + } + + Local buf = Use(isolate, data, actual); return scope.Escape(buf); } @@ -123,19 +169,44 @@ Local 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 New(Environment* env, size_t length) { EscapableHandleScope scope(env->isolate()); - CHECK_LE(length, kMaxLength); + if (using_old_buffer) { + CHECK_LE(length, kMaxLength); - Local arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); + Local arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local 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(); + } + + 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(); + } else { + data = nullptr; + } + Local ab = + ArrayBuffer::New(env->isolate(), + data, + length, + ArrayBufferCreationMode::kInternalized); + Local ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + return scope.Escape(ui); } @@ -147,33 +218,59 @@ Local 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 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 arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local obj = + env->buffer_constructor_function()->NewInstance(1, &arg); + + char* new_data; + if (length > 0) { + new_data = static_cast(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 arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); + smalloc::Alloc(env, obj, new_data, length); + + return scope.Escape(obj); + } - // 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; + // V8 currently only allows a maximum Typed Array index of max Smi. + if (!IsValidSmi(length)) { + return Local(); + } + + void* new_data; if (length > 0) { - new_data = static_cast(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(); memcpy(new_data, data, length); } else { new_data = nullptr; } - smalloc::Alloc(env, obj, new_data, length); + Local ab = + ArrayBuffer::New(env->isolate(), + new_data, + length, + ArrayBufferCreationMode::kInternalized); + Local 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 New(Environment* env, void* hint) { EscapableHandleScope scope(env->isolate()); + // TODO(trevnorris): IMPLEMENT CHECK_LE(length, kMaxLength); Local arg = Uint32::NewFromUnsigned(env->isolate(), length); @@ -207,7 +305,7 @@ Local New(Environment* env, } -Local Use(Isolate* isolate, char* data, uint32_t length) { +Local Use(Isolate* isolate, char* data, size_t length) { Environment* env = Environment::GetCurrent(isolate); EscapableHandleScope handle_scope(env->isolate()); Local obj = Buffer::Use(env, data, length); @@ -215,17 +313,98 @@ Local Use(Isolate* isolate, char* data, uint32_t length) { } -Local Use(Environment* env, char* data, uint32_t length) { +Local 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 arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); + Local arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local 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 ab = + ArrayBuffer::New(env->isolate(), + data, + length, + ArrayBufferCreationMode::kInternalized); + Local ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + return scope.Escape(ui); +} + + +void Create(const FunctionCallbackInfo& 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 ab = + ArrayBuffer::New(isolate, + data, + length, + ArrayBufferCreationMode::kInternalized); + Local ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + args.GetReturnValue().Set(ui); +} + + +void CreateFromString(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsString()); + CHECK(args[1]->IsString()); + + enum encoding enc = ParseEncoding(args.GetIsolate(), + args[1].As(), + UTF8); + Local buf = New(args.GetIsolate(), args[0].As(), enc); + args.GetReturnValue().Set(buf); +} + + +void Slice(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsUint8Array()); + CHECK(args[1]->IsNumber()); + CHECK(args[2]->IsNumber()); + Environment* env = Environment::GetCurrent(args); + Local ab_ui = args[0].As(); + Local 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 ui = Uint8Array::New(ab, start, size); + ui->SetPrototype(env->buffer_prototype_object()); + args.GetReturnValue().Set(ui); } @@ -237,7 +416,12 @@ void StringSlice(const FunctionCallbackInfo& 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,11 +436,16 @@ void StringSlice(const FunctionCallbackInfo& args) { template <> void StringSlice(const FunctionCallbackInfo& 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); + } SLICE_START_END(args[0], args[1], ts_obj_length) @@ -334,14 +523,22 @@ void Copy(const FunctionCallbackInfo &args) { THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); - Local target = args[0]->ToObject(env->isolate()); + Local 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( + target_obj->GetIndexedPropertiesExternalArrayData()); + } else { + SPREAD_ARG(args.This(), ts_obj); + SPREAD_ARG(target_obj, target); + } - ARGS_THIS(args.This(), ts_obj); - size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength(); - char* target_data = static_cast( - target->GetIndexedPropertiesExternalArrayData()); size_t target_start; size_t source_start; size_t source_end; @@ -372,7 +569,12 @@ void Copy(const FunctionCallbackInfo &args) { void Fill(const FunctionCallbackInfo& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } size_t start = args[2]->Uint32Value(); size_t end = args[3]->Uint32Value(); @@ -417,7 +619,12 @@ void StringWrite(const FunctionCallbackInfo& 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"); @@ -495,7 +702,12 @@ template void ReadFloatGeneric(const FunctionCallbackInfo& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } uint32_t offset = args[1]->Uint32Value(); CHECK_LE(offset + sizeof(T), ts_obj_length); @@ -538,7 +750,12 @@ void ReadDoubleBE(const FunctionCallbackInfo& args) { template uint32_t WriteFloatGeneric(const FunctionCallbackInfo& args) { ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } T val = args[1]->NumberValue(); uint32_t offset = args[2]->Uint32Value(); @@ -595,26 +812,27 @@ void Compare(const FunctionCallbackInfo &args) { THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); - Local obj_a = args[0].As(); - char* obj_a_data = - static_cast(obj_a->GetIndexedPropertiesExternalArrayData()); - size_t obj_a_len = obj_a->GetIndexedPropertiesExternalArrayDataLength(); + ARGS_THIS_DEC(obj_a); + ARGS_THIS_DEC(obj_b); - Local obj_b = args[1].As(); - char* obj_b_data = - static_cast(obj_b->GetIndexedPropertiesExternalArrayData()); - size_t obj_b_len = obj_b->GetIndexedPropertiesExternalArrayDataLength(); + if (using_old_buffer) { + ARGS_THIS(args[0].As(), obj_a); + ARGS_THIS(args[1].As(), 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) @@ -649,7 +867,12 @@ void IndexOfString(const FunctionCallbackInfo& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } node::Utf8Value str(args.GetIsolate(), args[1]); int32_t offset_i32 = args[2]->Int32Value(); @@ -682,7 +905,12 @@ void IndexOfBuffer(const FunctionCallbackInfo& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } Local buf = args[1].As(); int32_t offset_i32 = args[2]->Int32Value(); @@ -721,7 +949,12 @@ void IndexOfNumber(const FunctionCallbackInfo& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } uint32_t needle = args[1]->Uint32Value(); int32_t offset_i32 = args[2]->Int32Value(); @@ -750,15 +983,20 @@ void IndexOfNumber(const FunctionCallbackInfo& args) { void SetupBufferJS(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsFunction()); - - Local bv = args[0].As(); - env->set_buffer_constructor_function(bv); - Local proto_v = bv->Get(env->prototype_string()); - - CHECK(proto_v->IsObject()); + Local proto; - Local proto = proto_v.As(); + if (using_old_buffer) { + CHECK(args[0]->IsFunction()); + Local bv = args[0].As(); + env->set_buffer_constructor_function(bv); + Local proto_v = bv->Get(env->prototype_string()); + CHECK(proto_v->IsObject()); + proto = proto_v.As(); + } else { + CHECK(args[0]->IsObject()); + proto = args[0].As(); + env->set_buffer_prototype_object(proto); + } env->SetMethod(proto, "asciiSlice", AsciiSlice); env->SetMethod(proto, "base64Slice", Base64Slice); @@ -777,9 +1015,11 @@ void SetupBufferJS(const FunctionCallbackInfo& 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); + } } @@ -789,7 +1029,10 @@ void Initialize(Handle 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 2e649970c4793a..4b1b2cd8591ee1 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 Use(v8::Isolate* isolate, char* data, - uint32_t len); + size_t len); NODE_DEPRECATED("Use Use(isolate, ...)", - inline v8::Local Use(char* data, uint32_t len) { + inline v8::Local Use(char* data, size_t len) { return Use(v8::Isolate::GetCurrent(), data, len); }) @@ -95,7 +95,7 @@ v8::Local New(Environment* env, size_t length, smalloc::FreeCallback callback, void* hint); -v8::Local Use(Environment* env, char* data, uint32_t length); +v8::Local 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 c99b2feeb0bdcd..e5f7458f78d59f 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 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 75bdb4784aaaf3..308c9b2787d54e 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -198,6 +198,14 @@ TypeName* Unwrap(v8::Local object) { return static_cast(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 ea17a155745993..3ca79c0ac15ece 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 53434eab8e3f8f..00000000000000 --- 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 02d902b199e23d..0e672d30b782d4 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 @@ -1073,10 +1071,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. @@ -1103,11 +1097,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) {