diff --git a/lib/buffer.js b/lib/buffer.js index 1b9c68465d6b03..d4fef2bf27b817 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -1,1149 +1,7 @@ 'use strict'; -const binding = process.binding('buffer'); -const smalloc = process.binding('smalloc'); -const util = require('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 = util.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 = util.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(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'); +if (process.useOldBuffer) { + module.exports = require('internal/buffer_old'); +} else { + module.exports = require('internal/buffer_new'); } - - -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/lib/internal/buffer_new.js b/lib/internal/buffer_new.js new file mode 100644 index 00000000000000..0b7eba28d8de8d --- /dev/null +++ b/lib/internal/buffer_new.js @@ -0,0 +1,1020 @@ +'use strict'; + +const binding = process.binding('buffer'); +const util = require('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 = util.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 = util.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..1b9c68465d6b03 --- /dev/null +++ b/lib/internal/buffer_old.js @@ -0,0 +1,1149 @@ +'use strict'; + +const binding = process.binding('buffer'); +const smalloc = process.binding('smalloc'); +const util = require('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 = util.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 = util.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(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 70c9841a89a176..ac210072f0807f 100644 --- a/node.gyp +++ b/node.gyp @@ -70,6 +70,8 @@ 'lib/vm.js', 'lib/zlib.js', + 'lib/internal/buffer_old.js', + 'lib/internal/buffer_new.js', 'lib/internal/freelist.js', 'lib/internal/smalloc.js', 'lib/internal/repl.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/js_stream.cc b/src/js_stream.cc index 6b7c4063e05a2a..5833851c56a10e 100644 --- a/src/js_stream.cc +++ b/src/js_stream.cc @@ -18,6 +18,7 @@ using v8::FunctionTemplate; using v8::Handle; using v8::HandleScope; using v8::Local; +using v8::MaybeLocal; using v8::Object; using v8::Value; @@ -88,8 +89,11 @@ int JSStream::DoWrite(WriteWrap* w, HandleScope scope(env()->isolate()); Local bufs_arr = Array::New(env()->isolate(), count); - for (size_t i = 0; i < count; i++) - bufs_arr->Set(i, Buffer::New(env(), bufs[i].base, bufs[i].len)); + Local buf; + for (size_t i = 0; i < count; i++) { + buf = Buffer::New(env(), bufs[i].base, bufs[i].len).ToLocalChecked(); + bufs_arr->Set(i, buf); + } Local argv[] = { w->object(), @@ -134,11 +138,13 @@ void JSStream::DoAlloc(const FunctionCallbackInfo& args) { uv_buf_t buf; wrap->OnAlloc(args[0]->Int32Value(), &buf); - args.GetReturnValue().Set(Buffer::New(wrap->env(), - buf.base, - buf.len, - FreeCallback, - nullptr)); + Local vbuf = Buffer::New( + wrap->env(), + buf.base, + buf.len, + FreeCallback, + nullptr).ToLocalChecked(); + return args.GetReturnValue().Set(vbuf); } diff --git a/src/node.cc b/src/node.cc index f47dd722056a8d..48644806f90d17 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" @@ -146,6 +147,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 @@ -165,23 +168,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); } @@ -2844,6 +2841,11 @@ void SetupProcessObject(Environment* env, // 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; @@ -3072,6 +3074,7 @@ static void PrintHelp() { " --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" + " --use-old-buffer Revert to old Buffer implementation\n" " --v8-options print v8 command line options\n" #if defined(NODE_HAVE_I18N_SUPPORT) " --icu-data-dir=dir set ICU data load path to dir\n" @@ -3208,6 +3211,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 fca08599e50feb..1955658f21c3d4 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -5,12 +5,16 @@ #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" #include #include +#define BUFFER_ID 0xB0E4 + #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define CHECK_NOT_OOB(r) \ @@ -18,13 +22,27 @@ if (!(r)) return env->ThrowRangeError("out of range index"); \ } while (0) -#define ARGS_THIS(argT) \ - Local obj = argT; \ - size_t obj_length = obj->GetIndexedPropertiesExternalArrayDataLength(); \ - char* obj_data = static_cast( \ - obj->GetIndexedPropertiesExternalArrayData()); \ - if (obj_length > 0) \ - CHECK_NE(obj_data, nullptr); +#define ARGS_THIS_DEC(name) \ + 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(); \ + name##_data = static_cast( \ + name->GetIndexedPropertiesExternalArrayData()); \ + if (name##_length > 0) \ + CHECK_NE(name##_data, nullptr); #define SLICE_START_END(start_arg, end_arg, end_max) \ size_t start; \ @@ -38,6 +56,8 @@ namespace node { namespace Buffer { +using v8::ArrayBuffer; +using v8::ArrayBufferCreationMode; using v8::Context; using v8::EscapableHandleScope; using v8::Function; @@ -47,12 +67,106 @@ using v8::Handle; using v8::HandleScope; using v8::Isolate; using v8::Local; +using v8::Maybe; +using v8::MaybeLocal; using v8::Number; using v8::Object; +using v8::Persistent; using v8::String; using v8::Uint32; +using v8::Uint8Array; using v8::Value; +using v8::WeakCallbackData; + + +class CallbackInfo { + public: + static inline void Free(char* data, void* hint); + static inline CallbackInfo* New(Isolate* isolate, + Handle object, + FreeCallback callback, + void* hint = 0); + inline void Dispose(Isolate* isolate); + inline Persistent* persistent(); + private: + static void WeakCallback(const WeakCallbackData&); + inline void WeakCallback(Isolate* isolate, Local object); + inline CallbackInfo(Isolate* isolate, + Handle object, + FreeCallback callback, + void* hint); + ~CallbackInfo(); + Persistent persistent_; + FreeCallback const callback_; + void* const hint_; + DISALLOW_COPY_AND_ASSIGN(CallbackInfo); +}; + + +void CallbackInfo::Free(char* data, void*) { + ::free(data); +} + + +CallbackInfo* CallbackInfo::New(Isolate* isolate, + Handle object, + FreeCallback callback, + void* hint) { + return new CallbackInfo(isolate, object, callback, hint); +} + + +void CallbackInfo::Dispose(Isolate* isolate) { + WeakCallback(isolate, PersistentToLocal(isolate, persistent_)); +} + + +Persistent* CallbackInfo::persistent() { + return &persistent_; +} + + +CallbackInfo::CallbackInfo(Isolate* isolate, + Handle object, + FreeCallback callback, + void* hint) + : persistent_(isolate, object), + callback_(callback), + hint_(hint) { + persistent_.SetWeak(this, WeakCallback); + persistent_.SetWrapperClassId(BUFFER_ID); + persistent_.MarkIndependent(); + isolate->AdjustAmountOfExternalAllocatedMemory(sizeof(*this)); +} + +CallbackInfo::~CallbackInfo() { + persistent_.Reset(); +} + + +void CallbackInfo::WeakCallback( + const WeakCallbackData& data) { + data.GetParameter()->WeakCallback(data.GetIsolate(), data.GetValue()); +} + + +void CallbackInfo::WeakCallback(Isolate* isolate, Local object) { + ARGS_THIS_DEC(obj); + SPREAD_ARG(object, obj); + CHECK_EQ(obj_offset, 0); + CHECK_EQ(obj_c.ByteLength(), obj_length); + + obj->Buffer()->Neuter(); + callback_(obj_data, hint_); + int64_t change_in_bytes = -static_cast(sizeof(*this)); + isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes); + + delete this; +} + + +// Buffer methods bool HasInstance(Handle val) { return val->IsObject() && HasInstance(val.As()); @@ -60,10 +174,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()); } @@ -76,8 +199,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(); } @@ -88,158 +218,366 @@ 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(); } -Local New(Isolate* isolate, Handle string, enum encoding enc) { +MaybeLocal 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(); + + size_t actual = StringBytes::Write(isolate, data, length, string, enc); + CHECK(actual <= length); - Local buf = New(isolate, length); - char* data = Buffer::Data(buf); - StringBytes::Write(isolate, data, length, string, enc); + if (actual < length) { + data = static_cast(realloc(data, actual)); + CHECK_NE(data, nullptr); + } + + Local buf; + if (New(isolate, data, actual).ToLocal(&buf)) + return scope.Escape(buf); - return scope.Escape(buf); + // Object failed to be created. Clean up resources. + free(data); + return Local(); } -Local New(Isolate* isolate, size_t length) { +MaybeLocal New(Isolate* isolate, size_t length) { EscapableHandleScope handle_scope(isolate); - Local obj = Buffer::New(Environment::GetCurrent(isolate), length); - return handle_scope.Escape(obj); + Local obj; + if (Buffer::New(Environment::GetCurrent(isolate), length).ToLocal(&obj)) + return handle_scope.Escape(obj); + return Local(); } -// 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) { +MaybeLocal 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); + 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); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (mb.FromMaybe(false)) + return scope.Escape(ui); + + // Object failed to be created. Clean up resources. + free(data); + return Local(); } -Local New(Isolate* isolate, const char* data, size_t length) { +MaybeLocal Copy(Isolate* isolate, const char* data, size_t length) { Environment* env = Environment::GetCurrent(isolate); EscapableHandleScope handle_scope(env->isolate()); - Local obj = Buffer::New(env, data, length); - return handle_scope.Escape(obj); + Local obj; + if (Buffer::New(env, data, length).ToLocal(&obj)) + return handle_scope.Escape(obj); + return Local(); } -// 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. -Local New(Environment* env, const char* data, size_t length) { +// Make a copy of "data". Why this isn't called "Copy", we'll never know. +MaybeLocal 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); - // TODO(trevnorris): done like this to handle HasInstance since only checks - // if external array data has been set, but would like to use a better - // approach if v8 provided one. - char* new_data; + return scope.Escape(obj); + } + + // V8 currently only allows a maximum Typed Array index of max Smi. + if (!IsValidSmi(length)) { + return Local(); + } + + void* new_data; if (length > 0) { - new_data = static_cast(malloc(length)); + CHECK_NE(data, nullptr); + new_data = malloc(length); 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); - - return scope.Escape(obj); + Local ab = + ArrayBuffer::New(env->isolate(), + new_data, + length, + ArrayBufferCreationMode::kInternalized); + Local ui = Uint8Array::New(ab, 0, length); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (mb.FromMaybe(false)) + return scope.Escape(ui); + + // Object failed to be created. Clean up resources. + free(new_data); + return Local(); } -Local New(Isolate* isolate, - char* data, - size_t length, - smalloc::FreeCallback callback, - void* hint) { +MaybeLocal New(Isolate* isolate, + char* data, + size_t length, + FreeCallback callback, + void* hint) { Environment* env = Environment::GetCurrent(isolate); EscapableHandleScope handle_scope(env->isolate()); - Local obj = Buffer::New(env, data, length, callback, hint); - return handle_scope.Escape(obj); + Local obj; + if (Buffer::New(env, data, length, callback, hint).ToLocal(&obj)) + return handle_scope.Escape(obj); + return Local(); } -Local New(Environment* env, - char* data, - size_t length, - smalloc::FreeCallback callback, - void* hint) { +MaybeLocal New(Environment* env, + char* data, + size_t length, + FreeCallback callback, + void* hint) { 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); + smalloc::Alloc(env, obj, data, length, callback, hint); + return scope.Escape(obj); + } + + if (!IsValidSmi(length)) { + return Local(); + } - Local arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); + Local ab = ArrayBuffer::New(env->isolate(), data, length); + Local ui = Uint8Array::New(ab, 0, length); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); - smalloc::Alloc(env, obj, data, length, callback, hint); + if (!mb.FromMaybe(false)) + return Local(); - return scope.Escape(obj); + CallbackInfo::New(env->isolate(), ui, callback, hint); + return scope.Escape(ui); } -Local Use(Isolate* isolate, char* data, uint32_t length) { +MaybeLocal New(Isolate* isolate, char* data, size_t length) { Environment* env = Environment::GetCurrent(isolate); EscapableHandleScope handle_scope(env->isolate()); - Local obj = Buffer::Use(env, data, length); - return handle_scope.Escape(obj); + Local obj; + if (Buffer::New(env, data, length).ToLocal(&obj)) + return handle_scope.Escape(obj); + return Local(); } -Local Use(Environment* env, char* data, uint32_t length) { +MaybeLocal 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); + + smalloc::Alloc(env, obj, data, length); + + 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); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (mb.FromMaybe(false)) + return scope.Escape(ui); + return Local(); +} + + +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); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (!mb.FromMaybe(false)) + return env->ThrowError("Unable to set Object prototype"); + args.GetReturnValue().Set(ui); +} + - Local arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); +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; + if (New(args.GetIsolate(), args[0].As(), enc).ToLocal(&buf)) + args.GetReturnValue().Set(buf); +} - smalloc::Alloc(env, obj, data, length); - return scope.Escape(obj); +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); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (!mb.FromMaybe(false)) + env->ThrowError("Unable to set Object prototype"); + args.GetReturnValue().Set(ui); } template void StringSlice(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); + Isolate* isolate = env->isolate(); + ARGS_THIS_DEC(ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + } else { + SPREAD_ARG(args.This(), ts_obj); + } - ARGS_THIS(args.This()) - SLICE_START_END(args[0], args[1], obj_length) + SLICE_START_END(args[0], args[1], ts_obj_length) args.GetReturnValue().Set( - StringBytes::Encode(env->isolate(), obj_data + start, length, encoding)); + StringBytes::Encode(isolate, ts_obj_data + start, length, encoding)); } template <> void StringSlice(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); + ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args.This()) - SLICE_START_END(args[0], args[1], obj_length) + 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) length /= 2; - const char* data = obj_data + start; + const char* data = ts_obj_data + start; const uint16_t* buf; bool release = false; @@ -306,25 +644,33 @@ void Copy(const FunctionCallbackInfo &args) { if (!HasInstance(args[0])) return env->ThrowTypeError("first arg should be a Buffer"); - 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()) - 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; CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &target_start)); CHECK_NOT_OOB(ParseArrayIndex(args[2], 0, &source_start)); - CHECK_NOT_OOB(ParseArrayIndex(args[3], obj_length, &source_end)); + CHECK_NOT_OOB(ParseArrayIndex(args[3], ts_obj_length, &source_end)); // Copy 0 bytes; we're done if (target_start >= target_length || source_start >= source_end) return args.GetReturnValue().Set(0); - if (source_start > obj_length) + if (source_start > ts_obj_length) return env->ThrowRangeError("out of range index"); if (source_end - source_start > target_length - target_start) @@ -332,48 +678,54 @@ void Copy(const FunctionCallbackInfo &args) { uint32_t to_copy = MIN(MIN(source_end - source_start, target_length - target_start), - obj_length - source_start); + ts_obj_length - source_start); - memmove(target_data + target_start, obj_data + source_start, to_copy); + memmove(target_data + target_start, ts_obj_data + source_start, to_copy); args.GetReturnValue().Set(to_copy); } void Fill(const FunctionCallbackInfo& args) { - ARGS_THIS(args[0].As()) + ARGS_THIS_DEC(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(); size_t length = end - start; - CHECK(length + start <= obj_length); + CHECK(length + start <= ts_obj_length); if (args[1]->IsNumber()) { int value = args[1]->Uint32Value() & 255; - memset(obj_data + start, value, length); + memset(ts_obj_data + start, value, length); return; } node::Utf8Value str(args.GetIsolate(), args[1]); size_t str_length = str.length(); size_t in_there = str_length; - char* ptr = obj_data + start + str_length; + char* ptr = ts_obj_data + start + str_length; if (str_length == 0) return; - memcpy(obj_data + start, *str, MIN(str_length, length)); + memcpy(ts_obj_data + start, *str, MIN(str_length, length)); if (str_length >= length) return; while (in_there < length - in_there) { - memcpy(ptr, obj_data + start, in_there); + memcpy(ptr, ts_obj_data + start, in_there); ptr += in_there; in_there *= 2; } if (in_there < length) { - memcpy(ptr, obj_data + start, length - in_there); + memcpy(ptr, ts_obj_data + start, length - in_there); in_there = length; } } @@ -382,8 +734,13 @@ void Fill(const FunctionCallbackInfo& args) { template void StringWrite(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); + ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args.This()) + 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"); @@ -397,18 +754,18 @@ void StringWrite(const FunctionCallbackInfo& args) { size_t max_length; CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &offset)); - CHECK_NOT_OOB(ParseArrayIndex(args[2], obj_length - offset, &max_length)); + CHECK_NOT_OOB(ParseArrayIndex(args[2], ts_obj_length - offset, &max_length)); - max_length = MIN(obj_length - offset, max_length); + max_length = MIN(ts_obj_length - offset, max_length); if (max_length == 0) return args.GetReturnValue().Set(0); - if (offset >= obj_length) + if (offset >= ts_obj_length) return env->ThrowRangeError("Offset is out of bounds"); uint32_t written = StringBytes::Write(env->isolate(), - obj_data + offset, + ts_obj_data + offset, max_length, str, encoding, @@ -459,10 +816,16 @@ static inline void Swizzle(char* start, unsigned int len) { template void ReadFloatGeneric(const FunctionCallbackInfo& args) { - ARGS_THIS(args[0].As()); + ARGS_THIS_DEC(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), obj_length); + CHECK_LE(offset + sizeof(T), ts_obj_length); union NoAlias { T val; @@ -470,7 +833,7 @@ void ReadFloatGeneric(const FunctionCallbackInfo& args) { }; union NoAlias na; - const char* ptr = static_cast(obj_data) + offset; + const char* ptr = static_cast(ts_obj_data) + offset; memcpy(na.bytes, ptr, sizeof(na.bytes)); if (endianness != GetEndianness()) Swizzle(na.bytes, sizeof(na.bytes)); @@ -501,11 +864,17 @@ void ReadDoubleBE(const FunctionCallbackInfo& args) { template uint32_t WriteFloatGeneric(const FunctionCallbackInfo& args) { - ARGS_THIS(args[0].As()) + ARGS_THIS_DEC(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(); - CHECK_LE(offset + sizeof(T), obj_length); + CHECK_LE(offset + sizeof(T), ts_obj_length); union NoAlias { T val; @@ -513,7 +882,7 @@ uint32_t WriteFloatGeneric(const FunctionCallbackInfo& args) { }; union NoAlias na = { val }; - char* ptr = static_cast(obj_data) + offset; + char* ptr = static_cast(ts_obj_data) + offset; if (endianness != GetEndianness()) Swizzle(na.bytes, sizeof(na.bytes)); memcpy(ptr, na.bytes, sizeof(na.bytes)); @@ -550,26 +919,27 @@ void ByteLengthUtf8(const FunctionCallbackInfo &args) { void Compare(const FunctionCallbackInfo &args) { - 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) @@ -603,28 +973,35 @@ void IndexOfString(const FunctionCallbackInfo& args) { ASSERT(args[1]->IsString()); ASSERT(args[2]->IsNumber()); - ARGS_THIS(args[0].As()); + ARGS_THIS_DEC(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(); uint32_t offset; if (offset_i32 < 0) { - if (offset_i32 + static_cast(obj_length) < 0) + if (offset_i32 + static_cast(ts_obj_length) < 0) offset = 0; else - offset = static_cast(obj_length + offset_i32); + offset = static_cast(ts_obj_length + offset_i32); } else { offset = static_cast(offset_i32); } if (str.length() == 0 || - obj_length == 0 || + ts_obj_length == 0 || (offset != 0 && str.length() + offset <= str.length()) || - str.length() + offset > obj_length) + str.length() + offset > ts_obj_length) return args.GetReturnValue().Set(-1); int32_t r = - IndexOf(obj_data + offset, obj_length - offset, *str, str.length()); + IndexOf(ts_obj_data + offset, ts_obj_length - offset, *str, str.length()); args.GetReturnValue().Set(r == -1 ? -1 : static_cast(r + offset)); } @@ -634,7 +1011,14 @@ void IndexOfBuffer(const FunctionCallbackInfo& args) { ASSERT(args[1]->IsObject()); ASSERT(args[2]->IsNumber()); - ARGS_THIS(args[0].As()); + ARGS_THIS_DEC(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(); size_t buf_length = buf->GetIndexedPropertiesExternalArrayDataLength(); @@ -646,22 +1030,22 @@ void IndexOfBuffer(const FunctionCallbackInfo& args) { CHECK_NE(buf_data, nullptr); if (offset_i32 < 0) { - if (offset_i32 + static_cast(obj_length) < 0) + if (offset_i32 + static_cast(ts_obj_length) < 0) offset = 0; else - offset = static_cast(obj_length + offset_i32); + offset = static_cast(ts_obj_length + offset_i32); } else { offset = static_cast(offset_i32); } if (buf_length == 0 || - obj_length == 0 || + ts_obj_length == 0 || (offset != 0 && buf_length + offset <= buf_length) || - buf_length + offset > obj_length) + buf_length + offset > ts_obj_length) return args.GetReturnValue().Set(-1); int32_t r = - IndexOf(obj_data + offset, obj_length - offset, buf_data, buf_length); + IndexOf(ts_obj_data + offset, ts_obj_length - offset, buf_data, buf_length); args.GetReturnValue().Set(r == -1 ? -1 : static_cast(r + offset)); } @@ -671,27 +1055,34 @@ void IndexOfNumber(const FunctionCallbackInfo& args) { ASSERT(args[1]->IsNumber()); ASSERT(args[2]->IsNumber()); - ARGS_THIS(args[0].As()); + ARGS_THIS_DEC(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(); uint32_t offset; if (offset_i32 < 0) { - if (offset_i32 + static_cast(obj_length) < 0) + if (offset_i32 + static_cast(ts_obj_length) < 0) offset = 0; else - offset = static_cast(obj_length + offset_i32); + offset = static_cast(ts_obj_length + offset_i32); } else { offset = static_cast(offset_i32); } - if (obj_length == 0 || offset + 1 > obj_length) + if (ts_obj_length == 0 || offset + 1 > ts_obj_length) return args.GetReturnValue().Set(-1); - void* ptr = memchr(obj_data + offset, needle, obj_length - offset); + void* ptr = memchr(ts_obj_data + offset, needle, ts_obj_length - offset); char* ptr_char = static_cast(ptr); args.GetReturnValue().Set( - ptr ? static_cast(ptr_char - obj_data) : -1); + ptr ? static_cast(ptr_char - ts_obj_data) : -1); } @@ -699,15 +1090,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); @@ -726,9 +1122,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); + } } @@ -738,7 +1136,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..ec442d58b3735e 100644 --- a/src/node_buffer.h +++ b/src/node_buffer.h @@ -14,6 +14,8 @@ namespace Buffer { static const unsigned int kMaxLength = smalloc::kMaxLength; +NODE_EXTERN typedef void (*FreeCallback)(char* data, void* hint); + NODE_EXTERN bool HasInstance(v8::Handle val); NODE_EXTERN bool HasInstance(v8::Handle val); NODE_EXTERN char* Data(v8::Handle val); @@ -21,53 +23,30 @@ NODE_EXTERN char* Data(v8::Handle val); NODE_EXTERN size_t Length(v8::Handle val); NODE_EXTERN size_t Length(v8::Handle val); +// public constructor - data is copied +NODE_EXTERN v8::MaybeLocal Copy(v8::Isolate* isolate, + const char* data, + size_t len); + // public constructor -NODE_EXTERN v8::Local New(v8::Isolate* isolate, size_t length); -NODE_DEPRECATED("Use New(isolate, ...)", - inline v8::Local New(size_t length) { - return New(v8::Isolate::GetCurrent(), length); -}) +NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, size_t length); + // public constructor from string -NODE_EXTERN v8::Local New(v8::Isolate* isolate, - v8::Handle string, - enum encoding enc = UTF8); -NODE_DEPRECATED("Use New(isolate, ...)", - inline v8::Local New(v8::Handle string, - enum encoding enc = UTF8) { - return New(v8::Isolate::GetCurrent(), string, enc); -}) -// public constructor - data is copied -// TODO(trevnorris): should be something like Copy() -NODE_EXTERN v8::Local New(v8::Isolate* isolate, - const char* data, - size_t len); -NODE_DEPRECATED("Use New(isolate, ...)", - inline v8::Local New(const char* data, size_t len) { - return New(v8::Isolate::GetCurrent(), data, len); -}) +NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, + v8::Handle string, + enum encoding enc = UTF8); + // public constructor - data is used, callback is passed data on object gc -NODE_EXTERN v8::Local New(v8::Isolate* isolate, - char* data, - size_t length, - smalloc::FreeCallback callback, - void* hint); -NODE_DEPRECATED("Use New(isolate, ...)", - inline v8::Local New(char* data, - size_t length, - smalloc::FreeCallback callback, - void* hint) { - return New(v8::Isolate::GetCurrent(), data, length, callback, hint); -}) +NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, + char* data, + size_t length, + FreeCallback callback, + void* hint); // public constructor - data is used. -// TODO(trevnorris): should be New() for consistency -NODE_EXTERN v8::Local Use(v8::Isolate* isolate, - char* data, - uint32_t len); -NODE_DEPRECATED("Use Use(isolate, ...)", - inline v8::Local Use(char* data, uint32_t len) { - return Use(v8::Isolate::GetCurrent(), data, len); -}) +NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, + char* data, + size_t len); // This is verbose to be explicit with inline commenting static inline bool IsWithinBounds(size_t off, size_t len, size_t max) { @@ -88,14 +67,14 @@ static inline bool IsWithinBounds(size_t off, size_t len, size_t max) { // src/node_internals.h due to a circular dependency issue with // the smalloc.h and node_internals.h headers. #if defined(NODE_WANT_INTERNALS) -v8::Local New(Environment* env, size_t size); -v8::Local New(Environment* env, const char* data, size_t len); -v8::Local New(Environment* env, - char* data, - size_t length, - smalloc::FreeCallback callback, - void* hint); -v8::Local Use(Environment* env, char* data, uint32_t length); +v8::MaybeLocal New(Environment* env, size_t size); +v8::MaybeLocal New(Environment* env, const char* data, size_t len); +v8::MaybeLocal New(Environment* env, + char* data, + size_t length, + FreeCallback callback, + void* hint); +v8::MaybeLocal Use(Environment* env, char* data, size_t length); #endif // defined(NODE_WANT_INTERNALS) } // namespace Buffer diff --git a/src/node_crypto.cc b/src/node_crypto.cc index e2c478a510be84..7cdf18edbf20b4 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -73,6 +73,7 @@ using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; +using v8::MaybeLocal; using v8::Null; using v8::Object; using v8::Persistent; @@ -910,7 +911,7 @@ void SecureContext::GetTicketKeys(const FunctionCallbackInfo& args) { SecureContext* wrap = Unwrap(args.Holder()); - Local buff = Buffer::New(wrap->env(), 48); + Local buff = Buffer::New(wrap->env(), 48).ToLocalChecked(); if (SSL_CTX_get_tlsext_ticket_keys(wrap->ctx_, Buffer::Data(buff), Buffer::Length(buff)) != 1) { @@ -974,7 +975,7 @@ void SecureContext::GetCertificate(const FunctionCallbackInfo& args) { return args.GetReturnValue().Set(Null(env->isolate())); int size = i2d_X509(cert, nullptr); - Local buff = Buffer::New(env, size); + Local buff = Buffer::New(env, size).ToLocalChecked(); unsigned char* serialized = reinterpret_cast( Buffer::Data(buff)); i2d_X509(cert, &serialized); @@ -1077,15 +1078,16 @@ int SSLWrap::NewSessionCallback(SSL* s, SSL_SESSION* sess) { return 0; // Serialize session - Local buff = Buffer::New(env, size); + Local buff = Buffer::New(env, size).ToLocalChecked(); unsigned char* serialized = reinterpret_cast( Buffer::Data(buff)); memset(serialized, 0, size); i2d_SSL_SESSION(sess, &serialized); - Local session = Buffer::New(env, - reinterpret_cast(sess->session_id), - sess->session_id_length); + Local session = Buffer::New( + env, + reinterpret_cast(sess->session_id), + sess->session_id_length).ToLocalChecked(); Local argv[] = { session, buff }; w->new_session_wait_ = true; w->MakeCallback(env->onnewsession_string(), ARRAY_SIZE(argv), argv); @@ -1106,7 +1108,7 @@ void SSLWrap::OnClientHello(void* arg, Local buff = Buffer::New( env, reinterpret_cast(hello.session_id()), - hello.session_size()); + hello.session_size()).ToLocalChecked(); hello_obj->Set(env->session_id_string(), buff); if (hello.servername() == nullptr) { hello_obj->Set(env->servername_string(), String::Empty(env->isolate())); @@ -1317,7 +1319,7 @@ static Local X509ToObject(Environment* env, X509* cert) { // Raw DER certificate int size = i2d_X509(cert, nullptr); - Local buff = Buffer::New(env, size); + Local buff = Buffer::New(env, size).ToLocalChecked(); unsigned char* serialized = reinterpret_cast( Buffer::Data(buff)); i2d_X509(cert, &serialized); @@ -1564,11 +1566,12 @@ void SSLWrap::GetTLSTicket(const FunctionCallbackInfo& args) { if (sess == nullptr || sess->tlsext_tick == nullptr) return; - Local buf = Buffer::New(env, - reinterpret_cast(sess->tlsext_tick), - sess->tlsext_ticklen); + Local buff = Buffer::New( + env, + reinterpret_cast(sess->tlsext_tick), + sess->tlsext_ticklen).ToLocalChecked(); - args.GetReturnValue().Set(buf); + args.GetReturnValue().Set(buff); } @@ -1848,7 +1851,7 @@ int SSLWrap::TLSExtStatusCallback(SSL* s, void* arg) { arg = Buffer::New( env, reinterpret_cast(const_cast(resp)), - len); + len).ToLocalChecked(); } w->MakeCallback(env->onocspresponse_string(), 1, &arg); @@ -2816,7 +2819,7 @@ void CipherBase::GetAuthTag(const FunctionCallbackInfo& args) { unsigned int out_len = 0; if (cipher->GetAuthTag(&out, &out_len)) { - Local buf = Buffer::Use(env, out, out_len); + Local buf = Buffer::New(env, out, out_len).ToLocalChecked(); args.GetReturnValue().Set(buf); } else { env->ThrowError("Attempting to get auth tag in unsupported state"); @@ -2933,7 +2936,8 @@ void CipherBase::Update(const FunctionCallbackInfo& args) { "Trying to add data in unsupported state"); } - Local buf = Buffer::New(env, reinterpret_cast(out), out_len); + Local buf = + Buffer::New(env, reinterpret_cast(out), out_len).ToLocalChecked(); if (out) delete[] out; @@ -3008,8 +3012,11 @@ void CipherBase::Final(const FunctionCallbackInfo& args) { } } - args.GetReturnValue().Set( - Buffer::New(env, reinterpret_cast(out_value), out_len)); + Local buf = Buffer::New( + env, + reinterpret_cast(out_value), + out_len).ToLocalChecked(); + args.GetReturnValue().Set(buf); delete[] out_value; } @@ -3787,8 +3794,11 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { } } - args.GetReturnValue().Set( - Buffer::New(env, reinterpret_cast(out_value), out_len)); + Local vbuf = Buffer::New( + env, + reinterpret_cast(out_value), + out_len).ToLocalChecked(); + args.GetReturnValue().Set(vbuf); delete[] out_value; } @@ -4283,7 +4293,8 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { return env->ThrowError("Failed to compute ECDH key"); } - args.GetReturnValue().Set(Buffer::Use(env, out, out_len)); + Local buf = Buffer::New(env, out, out_len).ToLocalChecked(); + args.GetReturnValue().Set(buf); } @@ -4319,9 +4330,9 @@ void ECDH::GetPublicKey(const FunctionCallbackInfo& args) { return env->ThrowError("Failed to get public key"); } - args.GetReturnValue().Set(Buffer::Use(env, - reinterpret_cast(out), - size)); + Local buf = + Buffer::New(env, reinterpret_cast(out), size).ToLocalChecked(); + args.GetReturnValue().Set(buf); } @@ -4346,9 +4357,9 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo& args) { return env->ThrowError("Failed to convert ECDH private key to Buffer"); } - args.GetReturnValue().Set(Buffer::Use(env, - reinterpret_cast(out), - size)); + Local buf = + Buffer::New(env, reinterpret_cast(out), size).ToLocalChecked(); + args.GetReturnValue().Set(buf); } @@ -4746,7 +4757,7 @@ void RandomBytesCheck(RandomBytesRequest* req, Local argv[2]) { size_t size; req->return_memory(&data, &size); argv[0] = Null(req->env()->isolate()); - argv[1] = Buffer::Use(req->env()->isolate(), data, size); + argv[1] = Buffer::New(req->env(), data, size).ToLocalChecked(); } } @@ -4774,9 +4785,13 @@ void RandomBytes(const FunctionCallbackInfo& args) { return env->ThrowTypeError("size must be a number >= 0"); } - const uint32_t size = args[0]->Uint32Value(); - if (size > Buffer::kMaxLength) { - return env->ThrowTypeError("size > Buffer::kMaxLength"); + const int64_t size = args[0]->IntegerValue(); + if (using_old_buffer) { + if (size > Buffer::kMaxLength) + return env->ThrowTypeError("size > Buffer::kMaxLength"); + } else { + if (!IsValidSmi(size)) + return env->ThrowRangeError("size is not a valid Smi"); } Local obj = Object::New(env->isolate()); 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/spawn_sync.cc b/src/spawn_sync.cc index 79428fcd73ccc5..97bccfd4b789a6 100644 --- a/src/spawn_sync.cc +++ b/src/spawn_sync.cc @@ -167,7 +167,7 @@ void SyncProcessStdioPipe::Close() { Local SyncProcessStdioPipe::GetOutputAsBuffer(Environment* env) const { size_t length = OutputLength(); - Local js_buffer = Buffer::New(env, length); + Local js_buffer = Buffer::New(env, length).ToLocalChecked(); CopyOutput(Buffer::Data(js_buffer)); return js_buffer; } diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 540639d458050c..ae941f5edbd90b 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -223,7 +223,8 @@ void StreamWrap::OnReadImpl(ssize_t nread, CHECK_EQ(pending, UV_UNKNOWN_HANDLE); } - wrap->EmitData(nread, Buffer::Use(env, base, nread), pending_obj); + Local obj = Buffer::New(env, base, nread).ToLocalChecked(); + wrap->EmitData(nread, obj, pending_obj); } diff --git a/src/string_bytes.cc b/src/string_bytes.cc index 0042e9ac1c3f8f..a1806e3e3a0d6f 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -19,6 +19,7 @@ using v8::Handle; using v8::HandleScope; using v8::Isolate; using v8::Local; +using v8::Object; using v8::String; using v8::Value; @@ -681,7 +682,11 @@ Local StringBytes::Encode(Isolate* isolate, Local val; switch (encoding) { case BUFFER: - return scope.Escape(Buffer::New(isolate, buf, buflen)); + { + Local vbuf = + Buffer::Copy(isolate, buf, buflen).ToLocalChecked(); + return scope.Escape(vbuf); + } case ASCII: if (contains_non_ascii(buf, buflen)) { diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 30768640eb99e2..c224c6fcc81da4 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -656,7 +656,7 @@ void TLSWrap::OnReadSelf(ssize_t nread, TLSWrap* wrap = static_cast(ctx); Local buf_obj; if (buf != nullptr) - buf_obj = Buffer::Use(wrap->env(), buf->base, buf->len); + buf_obj = Buffer::New(wrap->env(), buf->base, buf->len).ToLocalChecked(); wrap->EmitData(nread, buf_obj, Local()); } diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 3183d1f9f5367c..5affcaf0cf238a 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -407,7 +407,7 @@ void UDPWrap::OnRecv(uv_udp_t* handle, } char* base = static_cast(realloc(buf->base, nread)); - argv[2] = Buffer::Use(env, base, nread); + argv[2] = Buffer::New(env, base, nread).ToLocalChecked(); argv[3] = AddressToJS(env, addr); wrap->MakeCallback(env->onmessage_string(), ARRAY_SIZE(argv), argv); } 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 1d02148734e38a..ff0d09a2facca8 100644 --- a/test/parallel/test-buffer.js +++ b/test/parallel/test-buffer.js @@ -314,8 +314,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); @@ -1059,10 +1057,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. @@ -1089,11 +1083,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) { diff --git a/test/parallel/test-crypto-random.js b/test/parallel/test-crypto-random.js index cac0c9b86cf2c1..538b4ecb8686b4 100644 --- a/test/parallel/test-crypto-random.js +++ b/test/parallel/test-crypto-random.js @@ -53,5 +53,5 @@ function checkCall(cb, desc) { // #5126, "FATAL ERROR: v8::Object::SetIndexedPropertiesToExternalArrayData() // length exceeds max acceptable value" assert.throws(function() { - crypto.randomBytes(0x3fffffff + 1); + crypto.randomBytes((-1 >>> 0) + 1); }, TypeError);