From 266526c20844957ce3622aaab88929cb90897fa3 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Tue, 26 May 2015 14:34:12 -0600 Subject: [PATCH 1/7] buffer: allow ARGS_THIS to accept a name Allowing the name to be passed to the ARGS_THIS macro will make it easier to share code with the Uint8Array implementation. --- src/node_buffer.cc | 123 +++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 54 deletions(-) diff --git a/src/node_buffer.cc b/src/node_buffer.cc index fca08599e50feb..c4c1b5b9ab89de 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -18,13 +18,17 @@ 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 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; \ @@ -222,24 +226,27 @@ Local Use(Environment* env, char* data, uint32_t length) { template void StringSlice(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); + Isolate* isolate = env->isolate(); + ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args.This()) - SLICE_START_END(args[0], args[1], obj_length) + ARGS_THIS(args.This(), ts_obj); + 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) + ARGS_THIS(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; @@ -307,8 +314,9 @@ void Copy(const FunctionCallbackInfo &args) { return env->ThrowTypeError("first arg should be a Buffer"); Local target = args[0]->ToObject(env->isolate()); + ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args.This()) + ARGS_THIS(args.This(), ts_obj); size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength(); char* target_data = static_cast( target->GetIndexedPropertiesExternalArrayData()); @@ -318,13 +326,13 @@ void Copy(const FunctionCallbackInfo &args) { 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 +340,49 @@ 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); + ARGS_THIS(args[0].As(), 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 +391,9 @@ 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()) + ARGS_THIS(args.This(), ts_obj); if (!args[0]->IsString()) return env->ThrowTypeError("Argument must be a string"); @@ -397,18 +407,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 +469,11 @@ 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); + ARGS_THIS(args[0].As(), 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 +481,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 +512,12 @@ void ReadDoubleBE(const FunctionCallbackInfo& args) { template uint32_t WriteFloatGeneric(const FunctionCallbackInfo& args) { - ARGS_THIS(args[0].As()) + ARGS_THIS_DEC(ts_obj); + ARGS_THIS(args[0].As(), 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 +525,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)); @@ -603,28 +615,29 @@ void IndexOfString(const FunctionCallbackInfo& args) { ASSERT(args[1]->IsString()); ASSERT(args[2]->IsNumber()); - ARGS_THIS(args[0].As()); + ARGS_THIS_DEC(ts_obj); + ARGS_THIS(args[0].As(), 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 +647,8 @@ void IndexOfBuffer(const FunctionCallbackInfo& args) { ASSERT(args[1]->IsObject()); ASSERT(args[2]->IsNumber()); - ARGS_THIS(args[0].As()); + ARGS_THIS_DEC(ts_obj); + ARGS_THIS(args[0].As(), ts_obj); Local buf = args[1].As(); int32_t offset_i32 = args[2]->Int32Value(); size_t buf_length = buf->GetIndexedPropertiesExternalArrayDataLength(); @@ -646,22 +660,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 +685,28 @@ void IndexOfNumber(const FunctionCallbackInfo& args) { ASSERT(args[1]->IsNumber()); ASSERT(args[2]->IsNumber()); - ARGS_THIS(args[0].As()); + ARGS_THIS_DEC(ts_obj); + ARGS_THIS(args[0].As(), 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); } From dca709ee1303fb49b81a55d7bc4fc81b20a0c275 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Tue, 26 May 2015 12:42:14 -0600 Subject: [PATCH 2/7] buffer: implement Uint8Array backed Buffer With V8 4.4 removing the external array data API currently used by Buffer, the new implementation uses the Uint8Array to back Buffer. Buffers now have a maximum size of Smi::kMaxLength, as defined by V8. Which is ~2 GB on 64 bit and ~1 GB on 32 bit. The flag --use-old-buffer allows using the old Buffer implementation. This flag will be removed once V8 4.4 has landed. The two JS Buffer implementations have been split into two files for simplicity. Use getter to return expected .parent/.offset values for backwards compatibility. --- lib/buffer.js | 1150 +--------------------------- lib/internal/buffer_new.js | 1020 ++++++++++++++++++++++++ lib/internal/buffer_old.js | 1149 +++++++++++++++++++++++++++ node.gyp | 2 + src/env.h | 1 + src/node.cc | 25 +- src/node_buffer.cc | 392 ++++++++-- src/node_buffer.h | 6 +- src/node_internals.h | 2 + src/util-inl.h | 8 + src/util.h | 2 + test/parallel/test-buffer-slice.js | 12 - test/parallel/test-buffer.js | 10 +- 13 files changed, 2527 insertions(+), 1252 deletions(-) create mode 100644 lib/internal/buffer_new.js create mode 100644 lib/internal/buffer_old.js delete mode 100644 test/parallel/test-buffer-slice.js diff --git a/lib/buffer.js b/lib/buffer.js index 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/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 c4c1b5b9ab89de..9c8947336fb997 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -5,6 +5,8 @@ #include "env-inl.h" #include "smalloc.h" #include "string_bytes.h" +#include "util.h" +#include "util-inl.h" #include "v8-profiler.h" #include "v8.h" @@ -22,6 +24,16 @@ size_t name##_length; \ char* name##_data; +#define SPREAD_ARG(val, name) \ + CHECK((val)->IsUint8Array()); \ + Local name = (val).As(); \ + ArrayBuffer::Contents name##_c = name->Buffer()->GetContents(); \ + size_t name##_offset = name->ByteOffset(); \ + name##_length = name->ByteLength(); \ + name##_data = static_cast(name##_c.Data()) + name##_offset; \ + if (name##_length > 0) \ + CHECK_NE(name##_data, nullptr); + #define ARGS_THIS(argT, name) \ Local name = argT; \ name##_length = name->GetIndexedPropertiesExternalArrayDataLength(); \ @@ -42,6 +54,8 @@ namespace node { namespace Buffer { +using v8::ArrayBuffer; +using v8::ArrayBufferCreationMode; using v8::Context; using v8::EscapableHandleScope; using v8::Function; @@ -55,6 +69,7 @@ using v8::Number; using v8::Object; using v8::String; using v8::Uint32; +using v8::Uint8Array; using v8::Value; @@ -64,10 +79,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()); } @@ -80,8 +104,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(); } @@ -92,8 +123,14 @@ size_t Length(Handle val) { size_t Length(Handle obj) { - CHECK(obj->HasIndexedPropertiesInExternalArrayData()); - return obj->GetIndexedPropertiesExternalArrayDataLength(); + if (using_old_buffer) { + CHECK(obj->HasIndexedPropertiesInExternalArrayData()); + return obj->GetIndexedPropertiesExternalArrayDataLength(); + } + + CHECK(obj->IsUint8Array()); + Local ui = obj.As(); + return ui->ByteLength(); } @@ -101,11 +138,20 @@ Local New(Isolate* isolate, Handle string, enum encoding enc) { EscapableHandleScope scope(isolate); size_t length = StringBytes::Size(isolate, string, enc); + char* data = static_cast(malloc(length)); + + if (data == nullptr) + return Local(); - Local buf = New(isolate, length); - char* data = Buffer::Data(buf); - StringBytes::Write(isolate, data, length, string, enc); + size_t actual = StringBytes::Write(isolate, data, length, string, enc); + CHECK(actual <= length); + if (actual < length) { + data = static_cast(realloc(data, actual)); + CHECK_NE(data, nullptr); + } + + Local buf = Use(isolate, data, actual); return scope.Escape(buf); } @@ -117,19 +163,44 @@ Local New(Isolate* isolate, size_t length) { } -// TODO(trevnorris): these have a flaw by needing to call the Buffer inst then -// Alloc. continue to look for a better architecture. Local New(Environment* env, size_t length) { EscapableHandleScope scope(env->isolate()); - CHECK_LE(length, kMaxLength); + if (using_old_buffer) { + CHECK_LE(length, kMaxLength); - Local arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); + Local arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local obj = + env->buffer_constructor_function()->NewInstance(1, &arg); - smalloc::Alloc(env, obj, length); + smalloc::Alloc(env, obj, length); - return scope.Escape(obj); + return scope.Escape(obj); + } + + // V8 currently only allows a maximum Typed Array index of max Smi. + if (!IsValidSmi(length)) { + return Local(); + } + + void* data; + if (length > 0) { + data = malloc(length); + // NOTE: API change. Must check .IsEmpty() on the return object to see if + // the data was able to be allocated. + if (data == nullptr) + return Local(); + } else { + data = nullptr; + } + Local ab = + ArrayBuffer::New(env->isolate(), + data, + length, + ArrayBufferCreationMode::kInternalized); + Local ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + return scope.Escape(ui); } @@ -141,33 +212,59 @@ Local New(Isolate* isolate, const char* data, size_t length) { } -// TODO(trevnorris): for backwards compatibility this is left to copy the data, -// but for consistency w/ the other should use data. And a copy version renamed -// to something else. +// Make a copy of "data". Why this isn't called "Copy", we'll never know. Local New(Environment* env, const char* data, size_t length) { EscapableHandleScope scope(env->isolate()); - CHECK_LE(length, kMaxLength); + if (using_old_buffer) { + CHECK_LE(length, kMaxLength); + + Local arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local obj = + env->buffer_constructor_function()->NewInstance(1, &arg); + + char* new_data; + if (length > 0) { + new_data = static_cast(malloc(length)); + if (new_data == nullptr) + FatalError("node::Buffer::New(const char*, size_t)", "Out Of Memory"); + memcpy(new_data, data, length); + } else { + new_data = nullptr; + } - Local arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); + smalloc::Alloc(env, obj, new_data, length); + + return scope.Escape(obj); + } - // TODO(trevnorris): done like this to handle HasInstance since only checks - // if external array data has been set, but would like to use a better - // approach if v8 provided one. - char* new_data; + // V8 currently only allows a maximum Typed Array index of max Smi. + if (!IsValidSmi(length)) { + return Local(); + } + + void* new_data; if (length > 0) { - new_data = static_cast(malloc(length)); + CHECK_NE(data, nullptr); + new_data = malloc(length); + // NOTE: API change. Must check .IsEmpty() on the return object to see if + // the data was able to be allocated. if (new_data == nullptr) - FatalError("node::Buffer::New(const char*, size_t)", "Out Of Memory"); + return Local(); memcpy(new_data, data, length); } else { new_data = nullptr; } - smalloc::Alloc(env, obj, new_data, length); + Local ab = + ArrayBuffer::New(env->isolate(), + new_data, + length, + ArrayBufferCreationMode::kInternalized); + Local ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); - return scope.Escape(obj); + return scope.Escape(ui); } @@ -190,6 +287,7 @@ Local New(Environment* env, void* hint) { EscapableHandleScope scope(env->isolate()); + // TODO(trevnorris): IMPLEMENT CHECK_LE(length, kMaxLength); Local arg = Uint32::NewFromUnsigned(env->isolate(), length); @@ -201,7 +299,7 @@ Local New(Environment* env, } -Local Use(Isolate* isolate, char* data, uint32_t length) { +Local Use(Isolate* isolate, char* data, size_t length) { Environment* env = Environment::GetCurrent(isolate); EscapableHandleScope handle_scope(env->isolate()); Local obj = Buffer::Use(env, data, length); @@ -209,17 +307,98 @@ Local Use(Isolate* isolate, char* data, uint32_t length) { } -Local Use(Environment* env, char* data, uint32_t length) { +Local Use(Environment* env, char* data, size_t length) { EscapableHandleScope scope(env->isolate()); - CHECK_LE(length, kMaxLength); + if (using_old_buffer) { + CHECK_LE(length, kMaxLength); - Local arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); + Local arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local obj = + env->buffer_constructor_function()->NewInstance(1, &arg); - smalloc::Alloc(env, obj, data, length); + smalloc::Alloc(env, obj, data, length); - return scope.Escape(obj); + return scope.Escape(obj); + } + + if (length > 0) { + CHECK_NE(data, nullptr); + CHECK(IsValidSmi(length)); + } + + Local ab = + ArrayBuffer::New(env->isolate(), + data, + length, + ArrayBufferCreationMode::kInternalized); + Local ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + return scope.Escape(ui); +} + + +void Create(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsNumber()); + + int64_t length = args[0]->IntegerValue(); + + if (!IsValidSmi(length)) { + return env->ThrowRangeError("invalid Buffer length"); + } + + void* data; + if (length > 0) { + data = malloc(length); + if (data == nullptr) + return env->ThrowRangeError("invalid Buffer length"); + } else { + data = nullptr; + } + + Local ab = + ArrayBuffer::New(isolate, + data, + length, + ArrayBufferCreationMode::kInternalized); + Local ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + args.GetReturnValue().Set(ui); +} + + +void CreateFromString(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsString()); + CHECK(args[1]->IsString()); + + enum encoding enc = ParseEncoding(args.GetIsolate(), + args[1].As(), + UTF8); + Local buf = New(args.GetIsolate(), args[0].As(), enc); + args.GetReturnValue().Set(buf); +} + + +void Slice(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsUint8Array()); + CHECK(args[1]->IsNumber()); + CHECK(args[2]->IsNumber()); + Environment* env = Environment::GetCurrent(args); + Local ab_ui = args[0].As(); + Local ab = ab_ui->Buffer(); + ArrayBuffer::Contents ab_c = ab->GetContents(); + size_t offset = ab_ui->ByteOffset(); + size_t start = args[1]->NumberValue() + offset; + size_t end = args[2]->NumberValue() + offset; + CHECK_GE(end, start); + size_t size = end - start; + CHECK_GE(ab_c.ByteLength(), start + size); + Local ui = Uint8Array::New(ab, start, size); + ui->SetPrototype(env->buffer_prototype_object()); + args.GetReturnValue().Set(ui); } @@ -229,7 +408,12 @@ void StringSlice(const FunctionCallbackInfo& args) { Isolate* isolate = env->isolate(); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args.This(), ts_obj); + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + } else { + SPREAD_ARG(args.This(), ts_obj); + } + SLICE_START_END(args[0], args[1], ts_obj_length) args.GetReturnValue().Set( @@ -242,7 +426,12 @@ void StringSlice(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args.This(), ts_obj); + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + } else { + SPREAD_ARG(args.This(), ts_obj); + } + SLICE_START_END(args[0], args[1], ts_obj_length) length /= 2; @@ -313,13 +502,20 @@ 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(), ts_obj); - size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength(); - char* target_data = static_cast( - target->GetIndexedPropertiesExternalArrayData()); size_t target_start; size_t source_start; size_t source_end; @@ -349,7 +545,12 @@ void Copy(const FunctionCallbackInfo &args) { void Fill(const FunctionCallbackInfo& args) { ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } size_t start = args[2]->Uint32Value(); size_t end = args[3]->Uint32Value(); @@ -393,7 +594,11 @@ void StringWrite(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args.This(), ts_obj); + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + } else { + SPREAD_ARG(args.This(), ts_obj); + } if (!args[0]->IsString()) return env->ThrowTypeError("Argument must be a string"); @@ -470,7 +675,12 @@ static inline void Swizzle(char* start, unsigned int len) { template void ReadFloatGeneric(const FunctionCallbackInfo& args) { ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } uint32_t offset = args[1]->Uint32Value(); CHECK_LE(offset + sizeof(T), ts_obj_length); @@ -513,7 +723,12 @@ void ReadDoubleBE(const FunctionCallbackInfo& args) { template uint32_t WriteFloatGeneric(const FunctionCallbackInfo& args) { ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } T val = args[1]->NumberValue(); uint32_t offset = args[2]->Uint32Value(); @@ -562,26 +777,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) @@ -616,7 +832,13 @@ void IndexOfString(const FunctionCallbackInfo& args) { ASSERT(args[2]->IsNumber()); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } + node::Utf8Value str(args.GetIsolate(), args[1]); int32_t offset_i32 = args[2]->Int32Value(); uint32_t offset; @@ -648,7 +870,13 @@ void IndexOfBuffer(const FunctionCallbackInfo& args) { ASSERT(args[2]->IsNumber()); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } + Local buf = args[1].As(); int32_t offset_i32 = args[2]->Int32Value(); size_t buf_length = buf->GetIndexedPropertiesExternalArrayDataLength(); @@ -686,7 +914,13 @@ void IndexOfNumber(const FunctionCallbackInfo& args) { ASSERT(args[2]->IsNumber()); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } + uint32_t needle = args[1]->Uint32Value(); int32_t offset_i32 = args[2]->Int32Value(); uint32_t offset; @@ -714,15 +948,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); @@ -741,9 +980,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); + } } @@ -753,7 +994,10 @@ void Initialize(Handle target, Environment* env = Environment::GetCurrent(context); env->SetMethod(target, "setupBufferJS", SetupBufferJS); + env->SetMethod(target, "create", Create); + env->SetMethod(target, "createFromString", CreateFromString); + env->SetMethod(target, "slice", Slice); env->SetMethod(target, "byteLengthUtf8", ByteLengthUtf8); env->SetMethod(target, "compare", Compare); env->SetMethod(target, "fill", Fill); diff --git a/src/node_buffer.h b/src/node_buffer.h index 2e649970c4793a..4b1b2cd8591ee1 100644 --- a/src/node_buffer.h +++ b/src/node_buffer.h @@ -63,9 +63,9 @@ NODE_DEPRECATED("Use New(isolate, ...)", // TODO(trevnorris): should be New() for consistency NODE_EXTERN v8::Local Use(v8::Isolate* isolate, char* data, - uint32_t len); + size_t len); NODE_DEPRECATED("Use Use(isolate, ...)", - inline v8::Local Use(char* data, uint32_t len) { + inline v8::Local Use(char* data, size_t len) { return Use(v8::Isolate::GetCurrent(), data, len); }) @@ -95,7 +95,7 @@ v8::Local New(Environment* env, size_t length, smalloc::FreeCallback callback, void* hint); -v8::Local Use(Environment* env, char* data, uint32_t length); +v8::Local Use(Environment* env, char* data, size_t length); #endif // defined(NODE_WANT_INTERNALS) } // namespace Buffer diff --git a/src/node_internals.h b/src/node_internals.h index c99b2feeb0bdcd..e5f7458f78d59f 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -17,6 +17,8 @@ namespace node { // Forward declaration class Environment; +extern bool using_old_buffer; + // If persistent.IsWeak() == false, then do not call persistent.Reset() // while the returned Local is still in scope, it will destroy the // reference to the object. diff --git a/src/util-inl.h b/src/util-inl.h index 75bdb4784aaaf3..308c9b2787d54e 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -198,6 +198,14 @@ TypeName* Unwrap(v8::Local object) { return static_cast(pointer); } +inline bool IsValidSmi(int64_t value) { + if (sizeof(int32_t) == sizeof(intptr_t)) { + return value >= -0x40000000LL && value <= 0x3fffffffLL; + } else { + return value >= -0x80000000LL && value <= 0x7fffffffLL; + } +} + } // namespace node #endif // SRC_UTIL_INL_H_ diff --git a/src/util.h b/src/util.h index ea17a155745993..3ca79c0ac15ece 100644 --- a/src/util.h +++ b/src/util.h @@ -195,6 +195,8 @@ class Utf8Value { char str_st_[1024]; }; +inline bool IsValidSmi(int64_t value); + } // namespace node #endif // SRC_UTIL_H_ diff --git a/test/parallel/test-buffer-slice.js b/test/parallel/test-buffer-slice.js deleted file mode 100644 index 53434eab8e3f8f..00000000000000 --- a/test/parallel/test-buffer-slice.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; -var common = require('../common'); -var assert = require('assert'); - -var Buffer = require('buffer').Buffer; - -var buff = new Buffer(Buffer.poolSize + 1); -var slicedBuffer = buff.slice(); -assert.equal(slicedBuffer.parent, - buff, - 'slicedBufffer should have its parent set to the original ' + - ' buffer'); diff --git a/test/parallel/test-buffer.js b/test/parallel/test-buffer.js index 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) { From b9fcd80c7b0b5664ddd48ac539077dda934683a1 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Mon, 1 Jun 2015 12:29:17 -0600 Subject: [PATCH 3/7] buffer: finish implementing FreeCallback Passing a FreeCallback to Buffer::New() now uses externalized ArrayBuffer's. --- src/node_buffer.cc | 120 +++++++++++++++++++++++++++++++++++++++++---- src/node_buffer.h | 8 +-- 2 files changed, 116 insertions(+), 12 deletions(-) diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 9c8947336fb997..c08de6d067e6bc 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -13,6 +13,8 @@ #include #include +#define BUFFER_ID 0xB0E4 + #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define CHECK_NOT_OOB(r) \ @@ -67,12 +69,103 @@ using v8::Isolate; using v8::Local; 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()); } @@ -271,7 +364,7 @@ Local New(Environment* env, const char* data, size_t length) { Local New(Isolate* isolate, char* data, size_t length, - smalloc::FreeCallback callback, + FreeCallback callback, void* hint) { Environment* env = Environment::GetCurrent(isolate); EscapableHandleScope handle_scope(env->isolate()); @@ -283,19 +376,28 @@ Local New(Isolate* isolate, Local New(Environment* env, char* data, size_t length, - smalloc::FreeCallback callback, + FreeCallback callback, void* hint) { EscapableHandleScope scope(env->isolate()); - // TODO(trevnorris): IMPLEMENT - CHECK_LE(length, kMaxLength); - - Local arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); + 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); + } - smalloc::Alloc(env, obj, data, length, callback, hint); + if (!IsValidSmi(length)) { + return Local(); + } - return scope.Escape(obj); + Local ab = ArrayBuffer::New(env->isolate(), data, length); + Local ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + CallbackInfo::New(env->isolate(), ui, callback, hint); + return scope.Escape(ui); } diff --git a/src/node_buffer.h b/src/node_buffer.h index 4b1b2cd8591ee1..568b68f83dbd5c 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); @@ -49,12 +51,12 @@ NODE_DEPRECATED("Use New(isolate, ...)", NODE_EXTERN v8::Local New(v8::Isolate* isolate, char* data, size_t length, - smalloc::FreeCallback callback, + FreeCallback callback, void* hint); NODE_DEPRECATED("Use New(isolate, ...)", inline v8::Local New(char* data, size_t length, - smalloc::FreeCallback callback, + FreeCallback callback, void* hint) { return New(v8::Isolate::GetCurrent(), data, length, callback, hint); }) @@ -93,7 +95,7 @@ v8::Local New(Environment* env, const char* data, size_t len); v8::Local New(Environment* env, char* data, size_t length, - smalloc::FreeCallback callback, + FreeCallback callback, void* hint); v8::Local Use(Environment* env, char* data, size_t length); #endif // defined(NODE_WANT_INTERNALS) From 05d334133da31d20695baae28b716017db727fd6 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Tue, 2 Jun 2015 10:18:01 -0600 Subject: [PATCH 4/7] buffer: switch to using Maybe<> API Use the new Maybe<> syntax for v8::Object::SetPrototype(). --- src/node_buffer.cc | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/node_buffer.cc b/src/node_buffer.cc index c08de6d067e6bc..34f27c4e0d91a3 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -67,6 +67,7 @@ using v8::Handle; using v8::HandleScope; using v8::Isolate; using v8::Local; +using v8::Maybe; using v8::Number; using v8::Object; using v8::Persistent; @@ -292,7 +293,13 @@ Local New(Environment* env, size_t length) { length, ArrayBufferCreationMode::kInternalized); Local ui = Uint8Array::New(ab, 0, length); - ui->SetPrototype(env->buffer_prototype_object()); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (!mb.FromMaybe(false)) { + FatalError("node::Buffer::New(Environment*, size_t)", + "Could not set Object prototype"); + UNREACHABLE(); + } return scope.Escape(ui); } @@ -355,7 +362,13 @@ Local New(Environment* env, const char* data, size_t length) { length, ArrayBufferCreationMode::kInternalized); Local ui = Uint8Array::New(ab, 0, length); - ui->SetPrototype(env->buffer_prototype_object()); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (!mb.FromMaybe(false)) { + FatalError("node::Buffer::New(Environment*, char*, size_t)", + "Could not set Object prototype"); + UNREACHABLE(); + } return scope.Escape(ui); } @@ -395,7 +408,14 @@ Local New(Environment* env, Local ab = ArrayBuffer::New(env->isolate(), data, length); Local ui = Uint8Array::New(ab, 0, length); - ui->SetPrototype(env->buffer_prototype_object()); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (!mb.FromMaybe(false)) { + FatalError("node::Buffer::New(Environment*, char*, size_t," + " FreeCallback, void*)", + "Could not set Object prototype"); + UNREACHABLE(); + } CallbackInfo::New(env->isolate(), ui, callback, hint); return scope.Escape(ui); } @@ -435,7 +455,13 @@ Local Use(Environment* env, char* data, size_t length) { length, ArrayBufferCreationMode::kInternalized); Local ui = Uint8Array::New(ab, 0, length); - ui->SetPrototype(env->buffer_prototype_object()); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (!mb.FromMaybe(false)) { + FatalError("node::Buffer::Use(Environment*, char*, size_t)", + "Could not set Object prototype"); + UNREACHABLE(); + } return scope.Escape(ui); } @@ -467,8 +493,10 @@ void Create(const FunctionCallbackInfo& args) { length, ArrayBufferCreationMode::kInternalized); Local ui = Uint8Array::New(ab, 0, length); - ui->SetPrototype(env->buffer_prototype_object()); - args.GetReturnValue().Set(ui); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (mb.FromMaybe(false)) + args.GetReturnValue().Set(ui); } @@ -499,8 +527,10 @@ void Slice(const FunctionCallbackInfo& args) { size_t size = end - start; CHECK_GE(ab_c.ByteLength(), start + size); Local ui = Uint8Array::New(ab, start, size); - ui->SetPrototype(env->buffer_prototype_object()); - args.GetReturnValue().Set(ui); + Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (mb.FromMaybe(false)) + args.GetReturnValue().Set(ui); } From d2bcd9863657979d3283ff281172bbe0a904eeb7 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Tue, 2 Jun 2015 11:09:03 -0600 Subject: [PATCH 5/7] buffer: switch API to return MaybeLocal<> --- src/js_stream.cc | 20 ++++--- src/node_buffer.cc | 132 ++++++++++++++++++++++++-------------------- src/node_buffer.h | 66 +++++++++++----------- src/node_crypto.cc | 65 +++++++++++++--------- src/spawn_sync.cc | 2 +- src/stream_wrap.cc | 3 +- src/string_bytes.cc | 6 +- src/tls_wrap.cc | 2 +- src/udp_wrap.cc | 2 +- 9 files changed, 166 insertions(+), 132 deletions(-) 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_buffer.cc b/src/node_buffer.cc index 34f27c4e0d91a3..67305479641e68 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -68,6 +68,7 @@ using v8::HandleScope; using v8::Isolate; using v8::Local; using v8::Maybe; +using v8::MaybeLocal; using v8::Number; using v8::Object; using v8::Persistent; @@ -228,7 +229,9 @@ size_t Length(Handle obj) { } -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); @@ -245,19 +248,26 @@ Local New(Isolate* isolate, Handle string, enum encoding enc) { CHECK_NE(data, nullptr); } - Local buf = Use(isolate, data, actual); - return scope.Escape(buf); + Local buf; + if (Use(isolate, data, actual).ToLocal(&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(); } -Local New(Environment* env, size_t length) { +MaybeLocal New(Environment* env, size_t length) { EscapableHandleScope scope(env->isolate()); if (using_old_buffer) { @@ -280,13 +290,12 @@ Local New(Environment* env, size_t length) { void* data; if (length > 0) { data = malloc(length); - // NOTE: API change. Must check .IsEmpty() on the return object to see if - // the data was able to be allocated. if (data == nullptr) return Local(); } else { data = nullptr; } + Local ab = ArrayBuffer::New(env->isolate(), data, @@ -295,25 +304,27 @@ Local New(Environment* env, size_t length) { Local ui = Uint8Array::New(ab, 0, length); Maybe mb = ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (!mb.FromMaybe(false)) { - FatalError("node::Buffer::New(Environment*, size_t)", - "Could not set Object prototype"); - UNREACHABLE(); - } - return scope.Escape(ui); + 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 New(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(); } // Make a copy of "data". Why this isn't called "Copy", we'll never know. -Local New(Environment* env, const char* data, size_t length) { +MaybeLocal New(Environment* env, const char* data, size_t length) { EscapableHandleScope scope(env->isolate()); if (using_old_buffer) { @@ -347,8 +358,6 @@ Local New(Environment* env, const char* data, size_t length) { if (length > 0) { CHECK_NE(data, nullptr); new_data = malloc(length); - // NOTE: API change. Must check .IsEmpty() on the return object to see if - // the data was able to be allocated. if (new_data == nullptr) return Local(); memcpy(new_data, data, length); @@ -364,33 +373,34 @@ Local New(Environment* env, const char* data, size_t length) { Local ui = Uint8Array::New(ab, 0, length); Maybe mb = ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (!mb.FromMaybe(false)) { - FatalError("node::Buffer::New(Environment*, char*, size_t)", - "Could not set Object prototype"); - UNREACHABLE(); - } + if (mb.FromMaybe(false)) + return scope.Escape(ui); - 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, - 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, - FreeCallback callback, - void* hint) { +MaybeLocal New(Environment* env, + char* data, + size_t length, + FreeCallback callback, + void* hint) { EscapableHandleScope scope(env->isolate()); if (using_old_buffer) { @@ -410,26 +420,26 @@ Local New(Environment* env, Local ui = Uint8Array::New(ab, 0, length); Maybe mb = ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (!mb.FromMaybe(false)) { - FatalError("node::Buffer::New(Environment*, char*, size_t," - " FreeCallback, void*)", - "Could not set Object prototype"); - UNREACHABLE(); - } + + if (!mb.FromMaybe(false)) + return Local(); + CallbackInfo::New(env->isolate(), ui, callback, hint); return scope.Escape(ui); } -Local Use(Isolate* isolate, char* data, size_t length) { +MaybeLocal Use(Isolate* isolate, char* data, size_t length) { Environment* env = Environment::GetCurrent(isolate); EscapableHandleScope handle_scope(env->isolate()); - Local obj = Buffer::Use(env, data, length); - return handle_scope.Escape(obj); + Local obj; + if (Buffer::Use(env, data, length).ToLocal(&obj)) + return handle_scope.Escape(obj); + return Local(); } -Local Use(Environment* env, char* data, size_t length) { +MaybeLocal Use(Environment* env, char* data, size_t length) { EscapableHandleScope scope(env->isolate()); if (using_old_buffer) { @@ -457,12 +467,9 @@ Local Use(Environment* env, char* data, size_t length) { Local ui = Uint8Array::New(ab, 0, length); Maybe mb = ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (!mb.FromMaybe(false)) { - FatalError("node::Buffer::Use(Environment*, char*, size_t)", - "Could not set Object prototype"); - UNREACHABLE(); - } - return scope.Escape(ui); + if (mb.FromMaybe(false)) + return scope.Escape(ui); + return Local(); } @@ -495,8 +502,9 @@ void Create(const FunctionCallbackInfo& args) { Local ui = Uint8Array::New(ab, 0, length); Maybe mb = ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (mb.FromMaybe(false)) - args.GetReturnValue().Set(ui); + if (!mb.FromMaybe(false)) + return env->ThrowError("Unable to set Object prototype"); + args.GetReturnValue().Set(ui); } @@ -507,8 +515,9 @@ void CreateFromString(const FunctionCallbackInfo& args) { enum encoding enc = ParseEncoding(args.GetIsolate(), args[1].As(), UTF8); - Local buf = New(args.GetIsolate(), args[0].As(), enc); - args.GetReturnValue().Set(buf); + Local buf; + if (New(args.GetIsolate(), args[0].As(), enc).ToLocal(&buf)) + args.GetReturnValue().Set(buf); } @@ -529,8 +538,9 @@ void Slice(const FunctionCallbackInfo& args) { Local ui = Uint8Array::New(ab, start, size); Maybe mb = ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (mb.FromMaybe(false)) - args.GetReturnValue().Set(ui); + if (!mb.FromMaybe(false)) + env->ThrowError("Unable to set Object prototype"); + args.GetReturnValue().Set(ui); } diff --git a/src/node_buffer.h b/src/node_buffer.h index 568b68f83dbd5c..d59c44692b12fb 100644 --- a/src/node_buffer.h +++ b/src/node_buffer.h @@ -24,50 +24,52 @@ NODE_EXTERN size_t Length(v8::Handle val); NODE_EXTERN size_t Length(v8::Handle val); // public constructor -NODE_EXTERN v8::Local New(v8::Isolate* isolate, size_t length); +NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, size_t length); NODE_DEPRECATED("Use New(isolate, ...)", - inline v8::Local New(size_t length) { + inline v8::MaybeLocal New(size_t length) { return New(v8::Isolate::GetCurrent(), length); }) // public constructor from string -NODE_EXTERN v8::Local New(v8::Isolate* isolate, - v8::Handle string, - enum encoding enc = UTF8); +NODE_EXTERN v8::MaybeLocal 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) { + inline v8::MaybeLocal 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_EXTERN v8::MaybeLocal 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) { + inline v8::MaybeLocal New(const char* data, + size_t len) { return New(v8::Isolate::GetCurrent(), data, len); }) // 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, - FreeCallback callback, - void* hint); +NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, + char* data, + size_t length, + FreeCallback callback, + void* hint); NODE_DEPRECATED("Use New(isolate, ...)", - inline v8::Local New(char* data, - size_t length, - FreeCallback callback, - void* hint) { + inline v8::MaybeLocal New(char* data, + size_t length, + FreeCallback callback, + void* hint) { return New(v8::Isolate::GetCurrent(), data, length, callback, hint); }) // public constructor - data is used. // TODO(trevnorris): should be New() for consistency -NODE_EXTERN v8::Local Use(v8::Isolate* isolate, - char* data, - size_t len); +NODE_EXTERN v8::MaybeLocal Use(v8::Isolate* isolate, + char* data, + size_t len); NODE_DEPRECATED("Use Use(isolate, ...)", - inline v8::Local Use(char* data, size_t len) { + inline v8::MaybeLocal Use(char* data, size_t len) { return Use(v8::Isolate::GetCurrent(), data, len); }) @@ -90,14 +92,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, - FreeCallback callback, - void* hint); -v8::Local Use(Environment* env, char* data, size_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..2c40bb96dacd9b 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::Use(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::Use(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::Use(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::Use(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::Use(req->env(), data, size).ToLocalChecked(); } } 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..3097eac4859761 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::Use(env, base, nread).ToLocalChecked(); + wrap->EmitData(nread, obj, pending_obj); } diff --git a/src/string_bytes.cc b/src/string_bytes.cc index 0042e9ac1c3f8f..b26465c0fdce7b 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,10 @@ Local StringBytes::Encode(Isolate* isolate, Local val; switch (encoding) { case BUFFER: - return scope.Escape(Buffer::New(isolate, buf, buflen)); + { + Local vbuf = Buffer::New(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..21cf01466dbd75 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::Use(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..8319436ec8ffb3 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::Use(env, base, nread).ToLocalChecked(); argv[3] = AddressToJS(env, addr); wrap->MakeCallback(env->onmessage_string(), ARRAY_SIZE(argv), argv); } From 0f36e80ac773a7e85dc05e7057c340402c233bd6 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Tue, 2 Jun 2015 12:53:30 -0600 Subject: [PATCH 6/7] buffer: update native API * Remove the deprecated API. * Change Buffer::New() that did a copy of the data to Buffer::Copy() * Change Buffer::Use() to Buffer::New() --- src/node_buffer.cc | 8 ++++---- src/node_buffer.h | 41 ++++++++--------------------------------- src/node_crypto.cc | 10 +++++----- src/stream_wrap.cc | 2 +- src/string_bytes.cc | 3 ++- src/tls_wrap.cc | 2 +- src/udp_wrap.cc | 2 +- 7 files changed, 22 insertions(+), 46 deletions(-) diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 67305479641e68..1955658f21c3d4 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -249,7 +249,7 @@ MaybeLocal New(Isolate* isolate, } Local buf; - if (Use(isolate, data, actual).ToLocal(&buf)) + if (New(isolate, data, actual).ToLocal(&buf)) return scope.Escape(buf); // Object failed to be created. Clean up resources. @@ -313,7 +313,7 @@ MaybeLocal New(Environment* env, size_t length) { } -MaybeLocal 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; @@ -429,11 +429,11 @@ MaybeLocal New(Environment* env, } -MaybeLocal Use(Isolate* isolate, char* data, size_t length) { +MaybeLocal New(Isolate* isolate, char* data, size_t length) { Environment* env = Environment::GetCurrent(isolate); EscapableHandleScope handle_scope(env->isolate()); Local obj; - if (Buffer::Use(env, data, length).ToLocal(&obj)) + if (Buffer::New(env, data, length).ToLocal(&obj)) return handle_scope.Escape(obj); return Local(); } diff --git a/src/node_buffer.h b/src/node_buffer.h index d59c44692b12fb..ec442d58b3735e 100644 --- a/src/node_buffer.h +++ b/src/node_buffer.h @@ -23,55 +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::MaybeLocal New(v8::Isolate* isolate, size_t length); -NODE_DEPRECATED("Use New(isolate, ...)", - inline v8::MaybeLocal New(size_t length) { - return New(v8::Isolate::GetCurrent(), length); -}) + // public constructor from string NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, v8::Handle string, enum encoding enc = UTF8); -NODE_DEPRECATED("Use New(isolate, ...)", - inline v8::MaybeLocal 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::MaybeLocal New(v8::Isolate* isolate, - const char* data, - size_t len); -NODE_DEPRECATED("Use New(isolate, ...)", - inline v8::MaybeLocal New(const char* data, - size_t len) { - return New(v8::Isolate::GetCurrent(), data, len); -}) + // public constructor - data is used, callback is passed data on object gc NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, char* data, size_t length, FreeCallback callback, void* hint); -NODE_DEPRECATED("Use New(isolate, ...)", - inline v8::MaybeLocal New(char* data, - size_t length, - FreeCallback callback, - void* hint) { - return New(v8::Isolate::GetCurrent(), data, length, callback, hint); -}) // public constructor - data is used. -// TODO(trevnorris): should be New() for consistency -NODE_EXTERN v8::MaybeLocal Use(v8::Isolate* isolate, +NODE_EXTERN v8::MaybeLocal New(v8::Isolate* isolate, char* data, size_t len); -NODE_DEPRECATED("Use Use(isolate, ...)", - inline v8::MaybeLocal Use(char* data, size_t len) { - return Use(v8::Isolate::GetCurrent(), data, len); -}) // This is verbose to be explicit with inline commenting static inline bool IsWithinBounds(size_t off, size_t len, size_t max) { diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 2c40bb96dacd9b..6674327231f600 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -2819,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).ToLocalChecked(); + Local buf = Buffer::New(env, out, out_len).ToLocalChecked(); args.GetReturnValue().Set(buf); } else { env->ThrowError("Attempting to get auth tag in unsupported state"); @@ -4293,7 +4293,7 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { return env->ThrowError("Failed to compute ECDH key"); } - Local buf = Buffer::Use(env, out, out_len).ToLocalChecked(); + Local buf = Buffer::New(env, out, out_len).ToLocalChecked(); args.GetReturnValue().Set(buf); } @@ -4331,7 +4331,7 @@ void ECDH::GetPublicKey(const FunctionCallbackInfo& args) { } Local buf = - Buffer::Use(env, reinterpret_cast(out), size).ToLocalChecked(); + Buffer::New(env, reinterpret_cast(out), size).ToLocalChecked(); args.GetReturnValue().Set(buf); } @@ -4358,7 +4358,7 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo& args) { } Local buf = - Buffer::Use(env, reinterpret_cast(out), size).ToLocalChecked(); + Buffer::New(env, reinterpret_cast(out), size).ToLocalChecked(); args.GetReturnValue().Set(buf); } @@ -4757,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(), data, size).ToLocalChecked(); + argv[1] = Buffer::New(req->env(), data, size).ToLocalChecked(); } } diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 3097eac4859761..ae941f5edbd90b 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -223,7 +223,7 @@ void StreamWrap::OnReadImpl(ssize_t nread, CHECK_EQ(pending, UV_UNKNOWN_HANDLE); } - Local obj = Buffer::Use(env, base, nread).ToLocalChecked(); + 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 b26465c0fdce7b..a1806e3e3a0d6f 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -683,7 +683,8 @@ Local StringBytes::Encode(Isolate* isolate, switch (encoding) { case BUFFER: { - Local vbuf = Buffer::New(isolate, buf, buflen).ToLocalChecked(); + Local vbuf = + Buffer::Copy(isolate, buf, buflen).ToLocalChecked(); return scope.Escape(vbuf); } diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 21cf01466dbd75..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).ToLocalChecked(); + 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 8319436ec8ffb3..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).ToLocalChecked(); + argv[2] = Buffer::New(env, base, nread).ToLocalChecked(); argv[3] = AddressToJS(env, addr); wrap->MakeCallback(env->onmessage_string(), ARRAY_SIZE(argv), argv); } From a1a8dfb38c2e2ddbda8c9bda9d0bf030c1fac53e Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Tue, 2 Jun 2015 13:10:47 -0600 Subject: [PATCH 7/7] crypto: remove kMaxLength on randomBytes() New Buffer implementation allows greater than kMaxLength to be created. So instead check if the passed value is a valid Smi. --- src/node_crypto.cc | 10 +++++++--- test/parallel/test-crypto-random.js | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 6674327231f600..7cdf18edbf20b4 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4785,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/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);