diff --git a/doc/api/buffer.markdown b/doc/api/buffer.markdown index f0c1c1c16ba1de..335ac4dcc31f69 100644 --- a/doc/api/buffer.markdown +++ b/doc/api/buffer.markdown @@ -80,6 +80,69 @@ Allocates a new buffer containing the given `str`. Returns true if the `encoding` is a valid encoding argument, or false otherwise. +### Class Method: Buffer.isBuffer(obj) + +* `obj` Object +* Return: Boolean + +Tests if `obj` is a `Buffer`. + +### Class Method: Buffer.byteLength(string, [encoding]) + +* `string` String +* `encoding` String, Optional, Default: 'utf8' +* Return: Number + +Gives the actual byte length of a string. `encoding` defaults to `'utf8'`. +This is not the same as `String.prototype.length` since that returns the +number of *characters* in a string. + +Example: + + str = '\u00bd + \u00bc = \u00be'; + + console.log(str + ": " + str.length + " characters, " + + Buffer.byteLength(str, 'utf8') + " bytes"); + + // ½ + ¼ = ¾: 9 characters, 12 bytes + +### Class Method: Buffer.concat(list, [totalLength]) + +* `list` {Array} List of Buffer objects to concat +* `totalLength` {Number} Total length of the buffers when concatenated + +Returns a buffer which is the result of concatenating all the buffers in +the list together. + +If the list has no items, or if the totalLength is 0, then it returns a +zero-length buffer. + +If the list has exactly one item, then the first item of the list is +returned. + +If the list has more than one item, then a new Buffer is created. + +If totalLength is not provided, it is read from the buffers in the list. +However, this adds an additional loop to the function, so it is faster +to provide the length explicitly. + +### buf.length + +* Number + +The size of the buffer in bytes. Note that this is not necessarily the size +of the contents. `length` refers to the amount of memory allocated for the +buffer object. It does not change when the contents of the buffer are changed. + + buf = new Buffer(1234); + + console.log(buf.length); + buf.write("some string", 0, "ascii"); + console.log(buf.length); + + // 1234 + // 1234 + ### buf.write(string, [offset], [length], [encoding]) * `string` String - data to be written to buffer @@ -155,69 +218,6 @@ Example: copy an ASCII string into a buffer, one byte at a time: // node.js -### Class Method: Buffer.isBuffer(obj) - -* `obj` Object -* Return: Boolean - -Tests if `obj` is a `Buffer`. - -### Class Method: Buffer.byteLength(string, [encoding]) - -* `string` String -* `encoding` String, Optional, Default: 'utf8' -* Return: Number - -Gives the actual byte length of a string. `encoding` defaults to `'utf8'`. -This is not the same as `String.prototype.length` since that returns the -number of *characters* in a string. - -Example: - - str = '\u00bd + \u00bc = \u00be'; - - console.log(str + ": " + str.length + " characters, " + - Buffer.byteLength(str, 'utf8') + " bytes"); - - // ½ + ¼ = ¾: 9 characters, 12 bytes - -### Class Method: Buffer.concat(list, [totalLength]) - -* `list` {Array} List of Buffer objects to concat -* `totalLength` {Number} Total length of the buffers when concatenated - -Returns a buffer which is the result of concatenating all the buffers in -the list together. - -If the list has no items, or if the totalLength is 0, then it returns a -zero-length buffer. - -If the list has exactly one item, then the first item of the list is -returned. - -If the list has more than one item, then a new Buffer is created. - -If totalLength is not provided, it is read from the buffers in the list. -However, this adds an additional loop to the function, so it is faster -to provide the length explicitly. - -### buf.length - -* Number - -The size of the buffer in bytes. Note that this is not necessarily the size -of the contents. `length` refers to the amount of memory allocated for the -buffer object. It does not change when the contents of the buffer are changed. - - buf = new Buffer(1234); - - console.log(buf.length); - buf.write("some string", 0, "ascii"); - console.log(buf.length); - - // 1234 - // 1234 - ### buf.copy(targetBuffer, [targetStart], [sourceStart], [sourceEnd]) * `targetBuffer` Buffer object - Buffer to copy into @@ -694,11 +694,8 @@ Note that this is a property on the buffer module returned by ## Class: SlowBuffer -This class is primarily for internal use. JavaScript programs should -use Buffer instead of using SlowBuffer. +Deprecated. SlowBuffer now returns an instance of Buffer. In order to avoid the overhead of allocating many C++ Buffer objects for small blocks of memory in the lifetime of a server, Node allocates memory -in 8Kb (8192 byte) chunks. If a buffer is smaller than this size, then it -will be backed by a parent SlowBuffer object. If it is larger than this, -then Node will allocate a SlowBuffer slab for it directly. +in 8Kb (8192 byte) chunks. This is now handled by Smalloc. diff --git a/lib/buffer.js b/lib/buffer.js index 8d93384d429274..a5abf368af6d03 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -19,299 +19,202 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -var SlowBuffer = process.binding('buffer').SlowBuffer; +var smalloc = process.binding('smalloc'); +var buffer = process.binding('buffer'); var assert = require('assert'); +var alloc = smalloc.alloc; +var sliceOnto = smalloc.sliceOnto; +var kMaxLength = smalloc.kMaxLength; +exports.Buffer = Buffer; +// backwards compatibility (DEPRECATE) +exports.SlowBuffer = Buffer; exports.INSPECT_MAX_BYTES = 50; -// Make SlowBuffer inherit from Buffer. -// This is an exception to the rule that __proto__ is not allowed in core. -SlowBuffer.prototype.__proto__ = Buffer.prototype; +// add methods to Buffer prototype +buffer.setupBufferJS(Buffer); +function Buffer(subject, encoding) { + if (!(this instanceof Buffer)) + return new Buffer(subject, encoding); -function clamp(index, len, defaultValue) { - if (typeof index !== 'number') return defaultValue; - index = ~~index; // Coerce to integer. - if (index >= len) return len; - if (index >= 0) return index; - index += len; - if (index >= 0) return index; - return 0; -} + var type = typeof subject; + switch (type) { + case 'number': + this.length = subject > 0 ? Math.floor(subject) : 0; + break; -function toHex(n) { - if (n < 16) return '0' + n.toString(16); - return n.toString(16); -} + case 'string': + this.length = Buffer.byteLength(subject, encoding = encoding || 'utf8'); + break; + case 'object': + this.length = +subject.length > 0 ? Math.floor(+subject.length) : 0; + break; -SlowBuffer.prototype.toString = function(encoding, start, end) { - encoding = String(encoding || 'utf8').toLowerCase(); - start = +start || 0; - if (typeof end !== 'number') end = this.length; + // undef first arg returns unallocated buffer, also assumes length passed. + // this is a stop-gap for now while look for better architecture. + // for internal use only. + case 'undefined': + this.length = encoding; + return; - // Fastpath empty strings - if (+end == start) { - return ''; + default: + throw new TypeError('must start with number, buffer, array or string'); } - switch (encoding) { - case 'hex': - return this.hexSlice(start, end); - - case 'utf8': - case 'utf-8': - return this.utf8Slice(start, end); + if (this.length > kMaxLength) + throw new RangeError('length > kMaxLength'); - case 'ascii': - return this.asciiSlice(start, end); + alloc(this, this.length); - case 'binary': - return this.binarySlice(start, end); + if (type !== 'number') { + if (type === 'string') { + // FIXME: the number of bytes hasn't changed, so why change the length? + this.length = this.write(subject, 0, encoding); + } else { + if (subject instanceof Buffer) + this.copy(subject, 0, 0, this.length); + else if (typeof subject.length === 'number' || Array.isArray(subject)) + for (var i = 0; i < this.length; i++) + this[i] = subject[i]; + } + } +} - case 'base64': - return this.base64Slice(start, end); - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return this.ucs2Slice(start, end); +// Static methods - default: - throw new TypeError('Unknown encoding: ' + encoding); - } +Buffer.isBuffer = function isBuffer(b) { + return b instanceof Buffer; }; -SlowBuffer.prototype.write = function(string, offset, length, encoding) { - // Support both (string, offset, length, encoding) - // and the legacy (string, encoding, offset, length) - if (isFinite(offset)) { - if (!isFinite(length)) { - encoding = length; - length = undefined; - } - } else { // legacy - var swap = encoding; - encoding = offset; - offset = length; - length = swap; - } - - offset = +offset || 0; - var remaining = this.length - offset; - if (!length) { - length = remaining; - } else { - length = +length; - if (length > remaining) { - length = remaining; - } - } - encoding = String(encoding || 'utf8').toLowerCase(); - - switch (encoding) { +Buffer.isEncoding = function(encoding) { + switch ((encoding + '').toLowerCase()) { case 'hex': - return this.hexWrite(string, offset, length); - case 'utf8': case 'utf-8': - return this.utf8Write(string, offset, length); - case 'ascii': - return this.asciiWrite(string, offset, length); - case 'binary': - return this.binaryWrite(string, offset, length); - case 'base64': - return this.base64Write(string, offset, length); - case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': - return this.ucs2Write(string, offset, length); + case 'raw': + return true; default: - throw new TypeError('Unknown encoding: ' + encoding); + return false; } }; -// slice(start, end) -SlowBuffer.prototype.slice = function(start, end) { - var len = this.length; - start = clamp(start, len, 0); - end = clamp(end, len, len); - return new Buffer(this, end - start, start); -}; - - -var zeroBuffer = new SlowBuffer(0); - -// Buffer -function Buffer(subject, encoding, offset) { - if (!(this instanceof Buffer)) { - return new Buffer(subject, encoding, offset); - } - - var type; - - // Are we slicing? - if (typeof offset === 'number') { - if (!Buffer.isBuffer(subject)) { - throw new TypeError('First argument must be a Buffer when slicing'); - } +Buffer.concat = function(list, length) { + if (!Array.isArray(list)) + throw new TypeError('Usage: Buffer.concat(list[, length])'); - this.length = +encoding > 0 ? Math.ceil(encoding) : 0; - this.parent = subject.parent ? subject.parent : subject; - this.offset = offset; + if (typeof length === 'undefined') { + length = 0; + for (var i = 0; i < list.length; i++) + length += list[i].length; } else { - // Find the length - switch (type = typeof subject) { - case 'number': - this.length = +subject > 0 ? Math.ceil(subject) : 0; - break; - - case 'string': - this.length = Buffer.byteLength(subject, encoding); - break; - - case 'object': // Assume object is array-ish - this.length = +subject.length > 0 ? Math.ceil(subject.length) : 0; - break; - - default: - throw new TypeError('First argument needs to be a number, ' + - 'array or string.'); - } + length = ~~length; + } - if (this.length > Buffer.poolSize) { - // Big buffer, just alloc one. - this.parent = new SlowBuffer(this.length); - this.offset = 0; + if (length < 0) length = 0; - } else if (this.length > 0) { - // Small buffer. - if (!pool || pool.length - pool.used < this.length) allocPool(); - this.parent = pool; - this.offset = pool.used; - pool.used += this.length; - if (pool.used & 7) pool.used = (pool.used + 8) & ~7; + if (list.length === 0) + return new Buffer(0); + else if (list.length === 1) + return list[0]; - } else { - // Zero-length buffer - this.parent = zeroBuffer; - this.offset = 0; - } + if (length < 0) + throw new RangeError('length is not a positive number'); - // optimize by branching logic for new allocations - if (typeof subject !== 'number') { - if (type === 'string') { - // We are a string - this.length = this.write(subject, 0, encoding); - // if subject is buffer then use built-in copy method - } else if (Buffer.isBuffer(subject)) { - if (subject.parent) - subject.parent.copy(this.parent, - this.offset, - subject.offset, - this.length + subject.offset); - else - subject.copy(this.parent, this.offset, 0, this.length); - } else if (isArrayIsh(subject)) { - for (var i = 0; i < this.length; i++) - this.parent[i + this.offset] = subject[i]; - } - } + 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; } - SlowBuffer.makeFastBuffer(this.parent, this, this.offset, this.length); -} + return buffer; +}; -function isArrayIsh(subject) { - return Array.isArray(subject) || - subject && typeof subject === 'object' && - typeof subject.length === 'number'; -} -exports.SlowBuffer = SlowBuffer; -exports.Buffer = Buffer; +// toString(encoding, start=0, end=buffer.length) +Buffer.prototype.toString = function(encoding, start, end) { + encoding = !!encoding ? (encoding + '').toLowerCase() : 'utf8'; + start = ~~start; + end = typeof end === 'undefined' ? this.length : ~~end; -Buffer.isEncoding = function(encoding) { - switch (encoding && encoding.toLowerCase()) { + if (start < 0) start = 0; + if (end > this.length) end = this.length; + if (end <= start) return ''; + + switch (encoding) { case 'hex': + return this.hexSlice(start, end); + case 'utf8': case 'utf-8': + return this.utf8Slice(start, end); + case 'ascii': + return this.asciiSlice(start, end); + case 'binary': + return this.binarySlice(start, end); + case 'base64': + return this.base64Slice(start, end); + case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': - case 'raw': - return true; + return this.ucs2Slice(start, end); default: - return false; + throw new TypeError('Unknown encoding: ' + encoding); } }; - -Buffer.poolSize = 8 * 1024; -var pool; - -function allocPool() { - pool = new SlowBuffer(Buffer.poolSize); - pool.used = 0; -} - - -// Static methods -Buffer.isBuffer = function isBuffer(b) { - return b instanceof Buffer; -}; - - // Inspect Buffer.prototype.inspect = function inspect() { - var out = [], - len = this.length, - name = this.constructor.name; - - for (var i = 0; i < len; i++) { - out[i] = toHex(this[i]); - if (i == exports.INSPECT_MAX_BYTES) { - out[i + 1] = '...'; - break; - } - } - - return '<' + name + ' ' + out.join(' ') + '>'; + var str = ''; + if (this.length > 0) + str = this.hexSlice(0, this.length).match(/.{2}/g).join(' '); + return '<' + this.constructor.name + ' ' + str + '>'; }; +// TODO(trevnorris): DEPRECATE Buffer.prototype.get = function get(offset) { + offset = ~~offset; if (offset < 0 || offset >= this.length) - throw new RangeError('offset is out of bounds'); - return this.parent[this.offset + offset]; + throw new RangeError('index out of range'); + return this[offset]; }; +// TODO(trevnorris): DEPRECATE Buffer.prototype.set = function set(offset, v) { + offset = ~~offset; if (offset < 0 || offset >= this.length) - throw new RangeError('offset is out of bounds'); - return this.parent[this.offset + offset] = v; + throw new RangeError('index out of range'); + return this[offset] = v; }; -// write(string, offset = 0, length = buffer.length-offset, encoding = 'utf8') +// TODO(trevnorris): fix these checks to follow new standard +// write(string, offset = 0, length = buffer.length, encoding = 'utf8') Buffer.prototype.write = function(string, offset, length, encoding) { // Support both (string, offset, length, encoding) // and the legacy (string, encoding, offset, length) @@ -320,6 +223,7 @@ Buffer.prototype.write = function(string, offset, length, encoding) { encoding = length; length = undefined; } + // TODO(trevnorris): DEPRECATE } else { // legacy var swap = encoding; encoding = offset; @@ -337,7 +241,11 @@ Buffer.prototype.write = function(string, offset, length, encoding) { length = remaining; } } - encoding = String(encoding || 'utf8').toLowerCase(); + + if (typeof encoding === 'undefined') + encoding = 'utf8'; + else + encoding = (encoding + '').toLowerCase(); if (string.length > 0 && (length < 0 || offset < 0)) throw new RangeError('attempt to write beyond buffer bounds'); @@ -345,32 +253,32 @@ Buffer.prototype.write = function(string, offset, length, encoding) { var ret; switch (encoding) { case 'hex': - ret = this.parent.hexWrite(string, this.offset + offset, length); + ret = this.hexWrite(string, offset, length); break; case 'utf8': case 'utf-8': - ret = this.parent.utf8Write(string, this.offset + offset, length); + ret = this.utf8Write(string, offset, length); break; case 'ascii': - ret = this.parent.asciiWrite(string, this.offset + offset, length); + ret = this.asciiWrite(string, offset, length); break; case 'binary': - ret = this.parent.binaryWrite(string, this.offset + offset, length); + ret = this.binaryWrite(string, offset, length); break; case 'base64': // Warning: maxLength not taken into account in base64Write - ret = this.parent.base64Write(string, this.offset + offset, length); + ret = this.base64Write(string, offset, length); break; case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': - ret = this.parent.ucs2Write(string, this.offset + offset, length); + ret = this.ucs2Write(string, offset, length); break; default: @@ -389,203 +297,48 @@ Buffer.prototype.toJSON = function() { }; -// toString(encoding, start=0, end=buffer.length) -Buffer.prototype.toString = function(encoding, start, end) { - encoding = String(encoding || 'utf8').toLowerCase(); - - if (typeof start !== 'number' || start < 0) { - start = 0; - } else if (start > this.length) { - start = this.length; - } - - if (typeof end !== 'number' || end > this.length) { - end = this.length; - } else if (end < 0) { - end = 0; - } - - start = start + this.offset; - end = end + this.offset; - - switch (encoding) { - case 'hex': - return this.parent.hexSlice(start, end); - - case 'utf8': - case 'utf-8': - return this.parent.utf8Slice(start, end); - - case 'ascii': - return this.parent.asciiSlice(start, end); - - case 'binary': - return this.parent.binarySlice(start, end); - - case 'base64': - return this.parent.base64Slice(start, end); - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return this.parent.ucs2Slice(start, end); - - default: - throw new TypeError('Unknown encoding: ' + encoding); - } -}; - - -// byteLength -Buffer.byteLength = SlowBuffer.byteLength; - - -// fill(value, start=0, end=buffer.length) -Buffer.prototype.fill = function fill(value, start, end) { - value || (value = 0); - start || (start = 0); - end || (end = this.length); - - if (typeof value === 'string') { - value = value.charCodeAt(0); - } - if (typeof value !== 'number' || isNaN(value)) { - throw new TypeError('value is not a number'); - } - - if (end < start) throw new RangeError('end < start'); - - // Fill 0 bytes; we're done - if (end === start) return 0; - if (this.length == 0) return 0; - - if (start < 0 || start >= this.length) { - throw new RangeError('start out of bounds'); - } - - if (end < 0 || end > this.length) { - throw new RangeError('end out of bounds'); - } - - this.parent.fill(value, - start + this.offset, - end + this.offset); - return this; -}; - - -Buffer.concat = function(list, length) { - if (!Array.isArray(list)) { - throw new TypeError('Usage: Buffer.concat(list, [length])'); - } - - if (list.length === 0) { - return new Buffer(0); - } else if (list.length === 1) { - return list[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 = typeof end === 'undefined' ? len : ~~end; + + if (start < 0) { + start += len; + if (start < 0) + start = 0; + } else if (start > len) { + start = len; } - if (typeof length !== 'number') { - length = 0; - for (var i = 0; i < list.length; i++) { - var buf = list[i]; - length += buf.length; - } + if (end < 0) { + end += len; + if (end < 0) + end = 0; + } else if (end > len) { + end = len; } - 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; -}; - - - - -// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) -Buffer.prototype.copy = function(target, target_start, start, end) { - // set undefined/NaN or out of bounds values equal to their default - if (!(target_start >= 0)) target_start = 0; - if (!(start >= 0)) start = 0; - if (!(end < this.length)) end = this.length; - - // Copy 0 bytes; we're done - if (end === start || - target.length === 0 || - this.length === 0 || - start > this.length) - return 0; - if (end < start) - throw new RangeError('sourceEnd < sourceStart'); - - if (target_start >= target.length) - throw new RangeError('targetStart out of bounds'); - - if (target.length - target_start < end - start) - end = target.length - target_start + start; - - return this.parent.copy(target.parent || target, - target_start + (target.offset || 0), - start + this.offset, - end + this.offset); -}; - - -// slice(start, end) -Buffer.prototype.slice = function(start, end) { - var len = this.length; - start = clamp(start, len, 0); - end = clamp(end, len, len); - return new Buffer(this.parent, end - start, start + this.offset); -}; - - -// Legacy methods for backwards compatibility. - -Buffer.prototype.utf8Slice = function(start, end) { - return this.toString('utf8', start, end); -}; + end = start; -Buffer.prototype.binarySlice = function(start, end) { - return this.toString('binary', start, end); -}; + var buf = new Buffer(); + buf.parent = sliceOnto(this, buf, start, end); + buf.length = end - start; -Buffer.prototype.asciiSlice = function(start, end) { - return this.toString('ascii', start, end); + return buf; }; -Buffer.prototype.utf8Write = function(string, offset) { - return this.write(string, offset, 'utf8'); -}; - -Buffer.prototype.binaryWrite = function(string, offset) { - return this.write(string, offset, 'binary'); -}; -Buffer.prototype.asciiWrite = function(string, offset) { - return this.write(string, offset, 'ascii'); -}; - - -/* - * Need to make sure that buffer isn't trying to write out of bounds. - * This check is far too slow internally for fast buffers. - */ function checkOffset(offset, ext, length) { - if ((offset % 1) !== 0 || offset < 0) - throw new RangeError('offset is not uint'); - if (offset + ext > length) - throw new RangeError('Trying to access beyond buffer length'); + if (offset < 0 || offset + ext > length) + throw new RangeError('index out of range'); } Buffer.prototype.readUInt8 = function(offset, noAssert) { + offset = ~~offset; if (!noAssert) checkOffset(offset, 1, this.length); return this[offset]; @@ -601,12 +354,12 @@ function readUInt16(buffer, offset, isBigEndian) { val = buffer[offset]; val |= buffer[offset + 1] << 8; } - return val; } Buffer.prototype.readUInt16LE = function(offset, noAssert) { + offset = ~~offset; if (!noAssert) checkOffset(offset, 2, this.length); return readUInt16(this, offset, false, noAssert); @@ -614,6 +367,7 @@ Buffer.prototype.readUInt16LE = function(offset, noAssert) { Buffer.prototype.readUInt16BE = function(offset, noAssert) { + offset = ~~offset; if (!noAssert) checkOffset(offset, 2, this.length); return readUInt16(this, offset, true, noAssert); @@ -622,7 +376,6 @@ Buffer.prototype.readUInt16BE = function(offset, noAssert) { function readUInt32(buffer, offset, isBigEndian, noAssert) { var val = 0; - if (isBigEndian) { val = buffer[offset + 1] << 16; val |= buffer[offset + 2] << 8; @@ -634,12 +387,12 @@ function readUInt32(buffer, offset, isBigEndian, noAssert) { val |= buffer[offset]; val = val + (buffer[offset + 3] << 24 >>> 0); } - return val; } Buffer.prototype.readUInt32LE = function(offset, noAssert) { + offset = ~~offset; if (!noAssert) checkOffset(offset, 4, this.length); return readUInt32(this, offset, false, noAssert); @@ -647,6 +400,7 @@ Buffer.prototype.readUInt32LE = function(offset, noAssert) { Buffer.prototype.readUInt32BE = function(offset, noAssert) { + offset = ~~offset; if (!noAssert) checkOffset(offset, 4, this.length); return readUInt32(this, offset, true, noAssert); @@ -700,6 +454,7 @@ Buffer.prototype.readUInt32BE = function(offset, noAssert) { */ Buffer.prototype.readInt8 = function(offset, noAssert) { + offset = ~~offset; if (!noAssert) checkOffset(offset, 1, this.length); if (!(this[offset] & 0x80)) @@ -710,7 +465,6 @@ Buffer.prototype.readInt8 = function(offset, noAssert) { function readInt16(buffer, offset, isBigEndian) { var val = readUInt16(buffer, offset, isBigEndian); - if (!(val & 0x8000)) return val; return (0xffff - val + 1) * -1; @@ -718,6 +472,7 @@ function readInt16(buffer, offset, isBigEndian) { Buffer.prototype.readInt16LE = function(offset, noAssert) { + offset = ~~offset; if (!noAssert) checkOffset(offset, 2, this.length); return readInt16(this, offset, false); @@ -725,6 +480,7 @@ Buffer.prototype.readInt16LE = function(offset, noAssert) { Buffer.prototype.readInt16BE = function(offset, noAssert) { + offset = ~~offset; if (!noAssert) checkOffset(offset, 2, this.length); return readInt16(this, offset, true); @@ -733,7 +489,6 @@ Buffer.prototype.readInt16BE = function(offset, noAssert) { function readInt32(buffer, offset, isBigEndian) { var val = readUInt32(buffer, offset, isBigEndian); - if (!(val & 0x80000000)) return (val); return (0xffffffff - val + 1) * -1; @@ -741,6 +496,7 @@ function readInt32(buffer, offset, isBigEndian) { Buffer.prototype.readInt32LE = function(offset, noAssert) { + offset = ~~offset; if (!noAssert) checkOffset(offset, 4, this.length); return readInt32(this, offset, false); @@ -748,50 +504,24 @@ Buffer.prototype.readInt32LE = function(offset, noAssert) { Buffer.prototype.readInt32BE = function(offset, noAssert) { + offset = ~~offset; if (!noAssert) checkOffset(offset, 4, this.length); return readInt32(this, offset, true); }; -Buffer.prototype.readFloatLE = function(offset, noAssert) { - if (!noAssert) - checkOffset(offset, 4, this.length); - return this.parent.readFloatLE(this.offset + offset, !!noAssert); -}; - - -Buffer.prototype.readFloatBE = function(offset, noAssert) { - if (!noAssert) - checkOffset(offset, 4, this.length); - return this.parent.readFloatBE(this.offset + offset, !!noAssert); -}; - - -Buffer.prototype.readDoubleLE = function(offset, noAssert) { - if (!noAssert) - checkOffset(offset, 8, this.length); - return this.parent.readDoubleLE(this.offset + offset, !!noAssert); -}; - - -Buffer.prototype.readDoubleBE = function(offset, noAssert) { - if (!noAssert) - checkOffset(offset, 8, this.length); - return this.parent.readDoubleBE(this.offset + offset, !!noAssert); -}; - function checkInt(buffer, value, offset, ext, max, min) { - if ((value % 1) !== 0 || value > max || value < min) + if (value > max || value < min) throw TypeError('value is out of bounds'); - if ((offset % 1) !== 0 || offset < 0) - throw TypeError('offset is not uint'); - if (offset + ext > buffer.length || buffer.length + offset < 0) - throw RangeError('Trying to write outside buffer length'); + if (offset < 0 || offset + ext > buffer.length || buffer.length + offset < 0) + throw RangeError('index out of range'); } Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { + value = +value; + offset = ~~offset; if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0); this[offset] = value; @@ -810,6 +540,8 @@ function writeUInt16(buffer, value, offset, isBigEndian) { Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { + value = +value; + offset = ~~offset; if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0); writeUInt16(this, value, offset, false); @@ -817,6 +549,8 @@ Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { + value = +value; + offset = ~~offset; if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0); writeUInt16(this, value, offset, true); @@ -839,6 +573,8 @@ function writeUInt32(buffer, value, offset, isBigEndian) { Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { + value = +value; + offset = ~~offset; if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0); writeUInt32(this, value, offset, false); @@ -846,6 +582,8 @@ Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { + value = +value; + offset = ~~offset; if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0); writeUInt32(this, value, offset, true); @@ -890,6 +628,8 @@ Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { */ Buffer.prototype.writeInt8 = function(value, offset, noAssert) { + value = +value; + offset = ~~offset; if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80); if (value < 0) value = 0xff + value + 1; @@ -898,6 +638,8 @@ Buffer.prototype.writeInt8 = function(value, offset, noAssert) { Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { + value = +value; + offset = ~~offset; if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000); if (value < 0) value = 0xffff + value + 1; @@ -906,6 +648,8 @@ Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { + value = +value; + offset = ~~offset; if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000); if (value < 0) value = 0xffff + value + 1; @@ -914,6 +658,8 @@ Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { + value = +value; + offset = ~~offset; if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); if (value < 0) value = 0xffffffff + value + 1; @@ -922,36 +668,10 @@ Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { + value = +value; + offset = ~~offset; if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); if (value < 0) value = 0xffffffff + value + 1; writeUInt32(this, value, offset, true); }; - - -Buffer.prototype.writeFloatLE = function(value, offset, noAssert) { - if (!noAssert) - checkOffset(offset, 4, this.length); - this.parent.writeFloatLE(value, this.offset + offset, !!noAssert); -}; - - -Buffer.prototype.writeFloatBE = function(value, offset, noAssert) { - if (!noAssert) - checkOffset(offset, 4, this.length); - this.parent.writeFloatBE(value, this.offset + offset, !!noAssert); -}; - - -Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) { - if (!noAssert) - checkOffset(offset, 8, this.length); - this.parent.writeDoubleLE(value, this.offset + offset, !!noAssert); -}; - - -Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) { - if (!noAssert) - checkOffset(offset, 8, this.length); - this.parent.writeDoubleBE(value, this.offset + offset, !!noAssert); -}; diff --git a/src/node.js b/src/node.js index 8d2767e7c0902c..66c8cbbe54f099 100644 --- a/src/node.js +++ b/src/node.js @@ -163,7 +163,6 @@ global.GLOBAL = global; global.root = global; global.Buffer = NativeModule.require('buffer').Buffer; - process.binding('buffer').setFastBufferConstructor(global.Buffer); process.domain = null; process._exiting = false; }; diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 687545a2052a01..4c59a055c02ca9 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -20,367 +20,369 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "node_buffer.h" - #include "node.h" +#include "node_internals.h" +#include "node_buffer.h" +#include "smalloc.h" #include "string_bytes.h" #include "v8.h" #include "v8-profiler.h" #include -#include // memcpy +#include #include -#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define BUFFER_CLASS_ID (0xBABE) +#define CHECK_NOT_OOB(r) \ + do { if (!(r)) return ThrowRangeError("out of range index"); } while (0) -namespace node { +#define ARGS_THIS(argT) \ + Local obj = argT; \ + size_t obj_length = obj->GetIndexedPropertiesExternalArrayDataLength(); \ + char* obj_data = static_cast( \ + obj->GetIndexedPropertiesExternalArrayData()); -using namespace v8; - -#define SLICE_ARGS(start_arg, end_arg) \ - if (!start_arg->IsInt32() || !end_arg->IsInt32()) { \ - return ThrowException(Exception::TypeError( \ - String::New("Bad argument."))); \ - } \ - int32_t start = start_arg->Int32Value(); \ - int32_t end = end_arg->Int32Value(); \ - if (start < 0 || end < 0) { \ - return ThrowException(Exception::TypeError( \ - String::New("Bad argument."))); \ - } \ - if (!(start <= end)) { \ - return ThrowException(Exception::Error( \ - String::New("Must have start <= end"))); \ - } \ - if ((size_t)end > parent->length_) { \ - return ThrowException(Exception::Error( \ - String::New("end cannot be longer than parent.length"))); \ - } +#define SLICE_START_END(start_arg, end_arg, end_max) \ + size_t start; \ + size_t end; \ + CHECK_NOT_OOB(ParseArrayIndex(start_arg, 0, &start)); \ + CHECK_NOT_OOB(ParseArrayIndex(end_arg, end_max, &end)); \ + if (end < start) end = start; \ + CHECK_NOT_OOB(end <= end_max); \ + size_t length = end - start; +namespace node { -static Persistent length_symbol; -static Persistent write_sym; -static Persistent fast_buffer_constructor; -Persistent Buffer::constructor_template; +namespace Buffer { -Handle Buffer::New(Handle string) { - HandleScope scope(node_isolate); +using v8::Arguments; +using v8::Function; +using v8::FunctionTemplate; +using v8::Handle; +using v8::HandleScope; +using v8::Local; +using v8::Number; +using v8::Object; +using v8::Persistent; +using v8::String; +using v8::Uint32; +using v8::Undefined; +using v8::Value; - // get Buffer from global scope. - Local global = v8::Context::GetCurrent()->Global(); - Local bv = global->Get(String::NewSymbol("Buffer")); - assert(bv->IsFunction()); - Local b = Local::Cast(bv); - Local argv[1] = { Local::New(node_isolate, string) }; - Local instance = b->NewInstance(1, argv); +Persistent p_buffer_fn; - return scope.Close(instance); + +bool HasInstance(Handle val) { + return val->IsObject() && HasInstance(val.As()); } -Buffer* Buffer::New(size_t length) { - HandleScope scope(node_isolate); +bool HasInstance(Handle obj) { + if (!obj->HasIndexedPropertiesInExternalArrayData()) + return false; + v8::ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType(); + return type == v8::kExternalUnsignedByteArray; +} - Local arg = Integer::NewFromUnsigned(length, node_isolate); - Local b = constructor_template->GetFunction()->NewInstance(1, &arg); - if (b.IsEmpty()) return NULL; - return ObjectWrap::Unwrap(b); +char* Data(Handle val) { + assert(val->IsObject()); + return Data(val.As()); } -Buffer* Buffer::New(const char* data, size_t length) { - HandleScope scope(node_isolate); +char* Data(Handle obj) { + assert(obj->HasIndexedPropertiesInExternalArrayData()); + return static_cast(obj->GetIndexedPropertiesExternalArrayData()); +} - Local arg = Integer::NewFromUnsigned(0, node_isolate); - Local obj = constructor_template->GetFunction()->NewInstance(1, &arg); - Buffer *buffer = ObjectWrap::Unwrap(obj); - buffer->Replace(const_cast(data), length, NULL, NULL); +size_t Length(Handle val) { + assert(val->IsObject()); + return Length(val.As()); +} + - return buffer; +size_t Length(Handle obj) { + assert(obj->HasIndexedPropertiesInExternalArrayData()); + return obj->GetIndexedPropertiesExternalArrayDataLength(); } -Buffer* Buffer::New(char *data, size_t length, - free_callback callback, void *hint) { +// TODO(trevnorris): would be more efficient to use StringBytes to calculate the +// length and write out the data beforehand then do the same as New(). +Local New(Handle string) { HandleScope scope(node_isolate); - Local arg = Integer::NewFromUnsigned(0, node_isolate); - Local obj = constructor_template->GetFunction()->NewInstance(1, &arg); + Handle argv[1] = { string }; + Local obj = p_buffer_fn->NewInstance(1, argv); - Buffer *buffer = ObjectWrap::Unwrap(obj); - buffer->Replace(data, length, callback, hint); - - return buffer; + return scope.Close(obj); } -Handle Buffer::New(const Arguments& args) { - if (!args.IsConstructCall()) { - return FromConstructorTemplate(constructor_template, args); - } - +// TODO(trevnorris): these have a flaw by needing to call the Buffer inst then +// Alloc. continue to look for a better architecture. +Local New(size_t length) { HandleScope scope(node_isolate); - if (!args[0]->IsUint32()) return ThrowTypeError("Bad argument"); + assert(length <= kMaxLength); - size_t length = args[0]->Uint32Value(); - if (length > Buffer::kMaxLength) { - return ThrowRangeError("length > kMaxLength"); - } - new Buffer(args.This(), length); + Handle argv[2]; + // this is safe b/c Undefined and length fits in an SMI, so there's no risk + // of GC reclaiming the values prematurely. + argv[0] = Undefined(node_isolate); + argv[1] = Uint32::New(length, node_isolate); + Local obj = p_buffer_fn->NewInstance(2, argv); - return args.This(); + smalloc::Alloc(obj, new char[length], length); + + return scope.Close(obj); } -Buffer::Buffer(Handle wrapper, size_t length) : ObjectWrap() { - Wrap(wrapper); +// TODO(trevnorris): for backwards compatibility this is left to copy the data, +// but for consistency w/ the other should use data. And a copy version renamed +// to something else. +Local New(const char* data, size_t length) { + HandleScope scope(node_isolate); - length_ = 0; - callback_ = NULL; - handle_.SetWrapperClassId(node_isolate, BUFFER_CLASS_ID); + assert(length <= kMaxLength); - Replace(NULL, length, NULL, NULL); -} + Handle argv[2]; + // this is safe b/c Undefined and length fits in an SMI, so there's no risk + // of GC reclaiming the values prematurely. + argv[0] = Undefined(node_isolate); + argv[1] = Uint32::New(length, node_isolate); + Local obj = p_buffer_fn->NewInstance(2, argv); + + char* new_data = new char[length]; + memcpy(new_data, data, length); + smalloc::Alloc(obj, new_data, length); -Buffer::~Buffer() { - Replace(NULL, 0, NULL, NULL); + return scope.Close(obj); } -// if replace doesn't have a callback, data must be copied -// const_cast in Buffer::New requires this -void Buffer::Replace(char *data, size_t length, - free_callback callback, void *hint) { +Local New(char* data, + size_t length, + smalloc::FreeCallback callback, + void* hint) { HandleScope scope(node_isolate); - if (callback_) { - callback_(data_, callback_hint_); - } else if (length_) { - delete [] data_; - node_isolate->AdjustAmountOfExternalAllocatedMemory( - -static_cast(sizeof(Buffer) + length_)); - } + assert(length <= kMaxLength); - length_ = length; - callback_ = callback; - callback_hint_ = hint; - - if (callback_) { - data_ = data; - } else if (length_) { - data_ = new char[length_]; - if (data) - memcpy(data_, data, length_); - node_isolate->AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer) + - length_); - } else { - data_ = NULL; - } + Handle argv[2]; + // this is safe b/c Undefined and length fits in an SMI, so there's no risk + // of GC reclaiming the values prematurely. + argv[0] = Undefined(node_isolate); + argv[1] = Uint32::New(length, node_isolate); + Local obj = p_buffer_fn->NewInstance(2, argv); + + smalloc::Alloc(obj, data, length, callback, hint); - handle_->SetIndexedPropertiesToExternalArrayData(data_, - kExternalUnsignedByteArray, - length_); - handle_->Set(length_symbol, Integer::NewFromUnsigned(length_, node_isolate)); + return scope.Close(obj); } -template -Handle Buffer::StringSlice(const Arguments& args) { +Local Use(char* data, uint32_t length) { HandleScope scope(node_isolate); - Buffer *parent = ObjectWrap::Unwrap(args.This()); - SLICE_ARGS(args[0], args[1]) - const char* src = parent->data_ + start; - size_t slen = (end - start); - return scope.Close(StringBytes::Encode(src, slen, encoding)); -} + assert(length <= kMaxLength); + Handle argv[2]; + // this is safe b/c Undefined and length fits in an SMI, so there's no risk + // of GC reclaiming the values prematurely. + argv[0] = Undefined(node_isolate); + argv[1] = Uint32::New(length, node_isolate); + Local obj = p_buffer_fn->NewInstance(2, argv); -Handle Buffer::BinarySlice(const Arguments& args) { - return Buffer::StringSlice(args); + smalloc::Alloc(obj, data, length); + + return scope.Close(obj); } -Handle Buffer::AsciiSlice(const Arguments& args) { - return Buffer::StringSlice(args); -} +template +Handle StringSlice(const Arguments& args) { + HandleScope scope(node_isolate); + ARGS_THIS(args.This()) + SLICE_START_END(args[0], args[1], obj_length) -Handle Buffer::Utf8Slice(const Arguments& args) { - return Buffer::StringSlice(args); + return scope.Close(StringBytes::Encode(obj_data + start, length, encoding)); } -Handle Buffer::Ucs2Slice(const Arguments& args) { - return Buffer::StringSlice(args); +Handle BinarySlice(const Arguments& args) { + return StringSlice(args); } - -Handle Buffer::HexSlice(const Arguments& args) { - return Buffer::StringSlice(args); +Handle AsciiSlice(const Arguments& args) { + return StringSlice(args); } -Handle Buffer::Base64Slice(const Arguments& args) { - return Buffer::StringSlice(args); +Handle Utf8Slice(const Arguments& args) { + return StringSlice(args); } -// buffer.fill(value, start, end); -Handle Buffer::Fill(const Arguments &args) { - HandleScope scope(node_isolate); +Handle Ucs2Slice(const Arguments& args) { + return StringSlice(args); +} - if (!args[0]->IsInt32()) { - return ThrowException(Exception::Error(String::New( - "value is not a number"))); - } - int value = (char)args[0]->Int32Value(); - Buffer *parent = ObjectWrap::Unwrap(args.This()); - SLICE_ARGS(args[1], args[2]) - memset( (void*)(parent->data_ + start), - value, - end - start); +Handle HexSlice(const Arguments& args) { + return StringSlice(args); +} - return Undefined(node_isolate); + +Handle Base64Slice(const Arguments& args) { + return StringSlice(args); } -// var bytesCopied = buffer.copy(target, targetStart, sourceStart, sourceEnd); -Handle Buffer::Copy(const Arguments &args) { +// bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]); +Handle Copy(const Arguments &args) { HandleScope scope(node_isolate); - Buffer *source = ObjectWrap::Unwrap(args.This()); + Local target = args[0]->ToObject(); - if (!Buffer::HasInstance(args[0])) { - return ThrowTypeError("First arg should be a Buffer"); - } + if (!HasInstance(target)) + return ThrowTypeError("first arg should be a Buffer"); - Local target = args[0]; - char* target_data = Buffer::Data(target); - size_t target_length = Buffer::Length(target); - size_t target_start = args[1]->IsUndefined() ? 0 : args[1]->Uint32Value(); - size_t source_start = args[2]->IsUndefined() ? 0 : args[2]->Uint32Value(); - size_t source_end = args[3]->IsUndefined() ? source->length_ - : args[3]->Uint32Value(); + ARGS_THIS(args.This()) + size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength(); + char* target_data = static_cast( + target->GetIndexedPropertiesExternalArrayData()); + size_t target_start; + size_t source_start; + size_t source_end; - if (source_end < source_start) { - return ThrowRangeError("sourceEnd < sourceStart"); - } + 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)); // Copy 0 bytes; we're done - if (source_end == source_start) { - return scope.Close(Integer::New(0, node_isolate)); - } - - if (target_start >= target_length) { - return ThrowRangeError("targetStart out of bounds"); - } + if (target_start >= target_length || source_start >= source_end) + return scope.Close(Uint32::New(0, node_isolate)); - if (source_start >= source->length_) { - return ThrowRangeError("sourceStart out of bounds"); - } + if (source_start > obj_length) + return ThrowRangeError("out of range index"); - if (source_end > source->length_) { - return ThrowRangeError("sourceEnd out of bounds"); - } + if (source_end - source_start > target_length - target_start) + source_end = source_start + target_length - target_start; size_t to_copy = MIN(MIN(source_end - source_start, target_length - target_start), - source->length_ - source_start); + obj_length - source_start); - // need to use slightly slower memmove is the ranges might overlap memmove((void *)(target_data + target_start), - (const void*)(source->data_ + source_start), + (const void*)(obj_data + source_start), to_copy); - return scope.Close(Integer::New(to_copy, node_isolate)); + return scope.Close(Uint32::New(to_copy, node_isolate)); } -Handle Buffer::Base64Write(const Arguments& args) { - return Buffer::StringWrite(args); -} +// buffer.fill(value[, start][, end]); +Handle Fill(const Arguments &args) { + HandleScope scope(node_isolate); -Handle Buffer::BinaryWrite(const Arguments& args) { - return Buffer::StringWrite(args); -} + ARGS_THIS(args.This()) + int value; -Handle Buffer::Utf8Write(const Arguments& args) { - return Buffer::StringWrite(args); -} + if (args[0]->IsString()) { + String::AsciiValue at(args[0]); + value = (*at)[0]; + } else { + value = (char)args[0]->Int32Value(); + } -Handle Buffer::Ucs2Write(const Arguments& args) { - return Buffer::StringWrite(args); -} + SLICE_START_END(args[1], args[2], obj_length) -Handle Buffer::HexWrite(const Arguments& args) { - return Buffer::StringWrite(args); -} + memset((void*)(obj_data + start), value, length); -Handle Buffer::AsciiWrite(const Arguments& args) { - return Buffer::StringWrite(args); + return args.This(); } + template -Handle Buffer::StringWrite(const Arguments& args) { +Handle StringWrite(const Arguments& args) { HandleScope scope(node_isolate); - Buffer* buffer = ObjectWrap::Unwrap(args.This()); + ARGS_THIS(args.This()) - if (!args[0]->IsString()) { + if (!args[0]->IsString()) return ThrowTypeError("Argument must be a string"); - } - - Local str = args[0].As(); - int length = str->Length(); + Local str = args[0]->ToString(); - if (length == 0) { - return scope.Close(Integer::New(0)); - } - - if (encoding == HEX && length % 2 != 0) + if (encoding == HEX && str->Length() % 2 != 0) return ThrowTypeError("Invalid hex string"); + size_t offset; + size_t max_length; - size_t offset = args[1]->Int32Value(); - size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset - : args[2]->Uint32Value(); - max_length = MIN(buffer->length_ - offset, max_length); + CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &offset)); + CHECK_NOT_OOB(ParseArrayIndex(args[2], obj_length - offset, &max_length)); - if (max_length == 0) { - // shortcut: nothing to write anyway - Local val = Integer::New(0); - return scope.Close(val); - } + max_length = MIN(obj_length - offset, max_length); + + if (max_length == 0) + return scope.Close(Uint32::New(0, node_isolate)); if (encoding == UCS2) max_length = max_length / 2; - if (offset >= buffer->length_) { - return ThrowTypeError("Offset is out of bounds"); - } + if (offset >= obj_length) + return ThrowRangeError("Offset is out of bounds"); - char* start = buffer->data_ + offset; - size_t written = StringBytes::Write(start, + size_t written = StringBytes::Write(obj_data + offset, max_length, str, encoding, NULL); - return scope.Close(Integer::New(written, node_isolate)); + return scope.Close(Uint32::New(written, node_isolate)); +} + + +Handle Base64Write(const Arguments& args) { + return StringWrite(args); +} + + +Handle BinaryWrite(const Arguments& args) { + return StringWrite(args); +} + + +Handle Utf8Write(const Arguments& args) { + return StringWrite(args); +} + + +Handle Ucs2Write(const Arguments& args) { + return StringWrite(args); +} + + +Handle HexWrite(const Arguments& args) { + return StringWrite(args); +} + + +Handle AsciiWrite(const Arguments& args) { + return StringWrite(args); } @@ -396,14 +398,13 @@ static inline void Swizzle(char* start, unsigned int len) { template Handle ReadFloatGeneric(const Arguments& args) { - size_t offset = args[0]->Uint32Value(); bool doAssert = !args[1]->BooleanValue(); + size_t offset; + + CHECK_NOT_OOB(ParseArrayIndex(args[0], 0, &offset)); if (doAssert) { - if (!args[0]->IsUint32()) - return ThrowTypeError("offset is not uint"); - size_t len = static_cast( - args.This()->GetIndexedPropertiesExternalArrayDataLength()); + size_t len = Length(args.This()); if (offset + sizeof(T) > len || offset + sizeof(T) < offset) return ThrowRangeError("Trying to read beyond buffer length"); } @@ -419,27 +420,26 @@ Handle ReadFloatGeneric(const Arguments& args) { memcpy(na.bytes, ptr, sizeof(na.bytes)); if (endianness != GetEndianness()) Swizzle(na.bytes, sizeof(na.bytes)); - // TODO: when Number::New is updated to accept an Isolate, make the change return Number::New(na.val); } -Handle Buffer::ReadFloatLE(const Arguments& args) { +Handle ReadFloatLE(const Arguments& args) { return ReadFloatGeneric(args); } -Handle Buffer::ReadFloatBE(const Arguments& args) { +Handle ReadFloatBE(const Arguments& args) { return ReadFloatGeneric(args); } -Handle Buffer::ReadDoubleLE(const Arguments& args) { +Handle ReadDoubleLE(const Arguments& args) { return ReadFloatGeneric(args); } -Handle Buffer::ReadDoubleBE(const Arguments& args) { +Handle ReadDoubleBE(const Arguments& args) { return ReadFloatGeneric(args); } @@ -448,19 +448,13 @@ template Handle WriteFloatGeneric(const Arguments& args) { bool doAssert = !args[2]->BooleanValue(); - if (doAssert) { - if (!args[0]->IsNumber()) - return ThrowTypeError("value not a number"); - if (!args[1]->IsUint32()) - return ThrowTypeError("offset is not uint"); - } - T val = static_cast(args[0]->NumberValue()); - size_t offset = args[1]->Uint32Value(); + size_t offset; + + CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &offset)); if (doAssert) { - size_t len = static_cast( - args.This()->GetIndexedPropertiesExternalArrayDataLength()); + size_t len = Length(args.This()); if (offset + sizeof(T) > len || offset + sizeof(T) < offset) return ThrowRangeError("Trying to write beyond buffer length"); } @@ -480,205 +474,122 @@ Handle WriteFloatGeneric(const Arguments& args) { } -Handle Buffer::WriteFloatLE(const Arguments& args) { +Handle WriteFloatLE(const Arguments& args) { return WriteFloatGeneric(args); } -Handle Buffer::WriteFloatBE(const Arguments& args) { +Handle WriteFloatBE(const Arguments& args) { return WriteFloatGeneric(args); } -Handle Buffer::WriteDoubleLE(const Arguments& args) { +Handle WriteDoubleLE(const Arguments& args) { return WriteFloatGeneric(args); } -Handle Buffer::WriteDoubleBE(const Arguments& args) { +Handle WriteDoubleBE(const Arguments& args) { return WriteFloatGeneric(args); } -// var nbytes = Buffer.byteLength("string", "utf8") -Handle Buffer::ByteLength(const Arguments &args) { +Handle ByteLength(const Arguments &args) { HandleScope scope(node_isolate); - if (!args[0]->IsString()) { + if (!args[0]->IsString()) return ThrowTypeError("Argument must be a string"); - } Local s = args[0]->ToString(); enum encoding e = ParseEncoding(args[1], UTF8); - return scope.Close(Integer::New(StringBytes::Size(s, e), node_isolate)); + return scope.Close(Uint32::New(StringBytes::Size(s, e), node_isolate)); } -Handle Buffer::MakeFastBuffer(const Arguments &args) { +// pass Buffer object to load prototype methods +Handle SetupBufferJS(const Arguments& args) { HandleScope scope(node_isolate); - if (!Buffer::HasInstance(args[0])) { - return ThrowTypeError("First argument must be a Buffer"); - } - - Buffer *buffer = ObjectWrap::Unwrap(args[0]->ToObject()); - Local fast_buffer = args[1]->ToObject(); - uint32_t offset = args[2]->Uint32Value(); - uint32_t length = args[3]->Uint32Value(); - - if (offset > buffer->length_) { - return ThrowRangeError("offset out of range"); - } - - if (offset + length > buffer->length_) { - return ThrowRangeError("length out of range"); - } - - // Check for wraparound. Safe because offset and length are unsigned. - if (offset + length < offset) { - return ThrowRangeError("offset or length out of range"); - } - - fast_buffer->SetIndexedPropertiesToExternalArrayData(buffer->data_ + offset, - kExternalUnsignedByteArray, - length); - - return Undefined(node_isolate); -} - - -bool Buffer::HasInstance(Handle val) { - if (!val->IsObject()) return false; - Local obj = val->ToObject(); - - ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType(); - if (type != kExternalUnsignedByteArray) - return false; - - // Also check for SlowBuffers that are empty. - if (constructor_template->HasInstance(obj)) - return true; - - assert(!fast_buffer_constructor.IsEmpty()); - return obj->GetConstructor()->StrictEquals(fast_buffer_constructor); -} - - -Handle SetFastBufferConstructor(const Arguments& args) { assert(args[0]->IsFunction()); - fast_buffer_constructor = Persistent::New(node_isolate, - args[0].As()); - return Undefined(node_isolate); -} - - -class RetainedBufferInfo: public RetainedObjectInfo { -public: - RetainedBufferInfo(Buffer* buffer); - virtual void Dispose(); - virtual bool IsEquivalent(RetainedObjectInfo* other); - virtual intptr_t GetHash(); - virtual const char* GetLabel(); - virtual intptr_t GetSizeInBytes(); -private: - Buffer* buffer_; - static const char label[]; -}; - -const char RetainedBufferInfo::label[] = "Buffer"; + Local bv = args[0].As(); + p_buffer_fn = Persistent::New(node_isolate, bv); + Local proto_v = bv->Get(String::New("prototype")); + + assert(proto_v->IsObject()); + + Local proto = proto_v.As(); + + bv->Set(String::New("byteLength"), + FunctionTemplate::New(ByteLength)->GetFunction()); + + proto->Set(String::New("asciiSlice"), + FunctionTemplate::New(AsciiSlice)->GetFunction()); + proto->Set(String::New("base64Slice"), + FunctionTemplate::New(Base64Slice)->GetFunction()); + proto->Set(String::New("binarySlice"), + FunctionTemplate::New(BinarySlice)->GetFunction()); + proto->Set(String::New("hexSlice"), + FunctionTemplate::New(HexSlice)->GetFunction()); + proto->Set(String::New("ucs2Slice"), + FunctionTemplate::New(Ucs2Slice)->GetFunction()); + proto->Set(String::New("utf8Slice"), + FunctionTemplate::New(Utf8Slice)->GetFunction()); + + proto->Set(String::New("asciiWrite"), + FunctionTemplate::New(AsciiWrite)->GetFunction()); + proto->Set(String::New("base64Write"), + FunctionTemplate::New(Base64Write)->GetFunction()); + proto->Set(String::New("binaryWrite"), + FunctionTemplate::New(BinaryWrite)->GetFunction()); + proto->Set(String::New("hexWrite"), + FunctionTemplate::New(HexWrite)->GetFunction()); + proto->Set(String::New("ucs2Write"), + FunctionTemplate::New(Ucs2Write)->GetFunction()); + proto->Set(String::New("utf8Write"), + FunctionTemplate::New(Utf8Write)->GetFunction()); + + proto->Set(String::New("readDoubleBE"), + FunctionTemplate::New(ReadDoubleBE)->GetFunction()); + proto->Set(String::New("readDoubleLE"), + FunctionTemplate::New(ReadDoubleLE)->GetFunction()); + proto->Set(String::New("readFloatBE"), + FunctionTemplate::New(ReadFloatBE)->GetFunction()); + proto->Set(String::New("readFloatLE"), + FunctionTemplate::New(ReadFloatLE)->GetFunction()); + + proto->Set(String::New("writeDoubleBE"), + FunctionTemplate::New(WriteDoubleBE)->GetFunction()); + proto->Set(String::New("writeDoubleLE"), + FunctionTemplate::New(WriteDoubleLE)->GetFunction()); + proto->Set(String::New("writeFloatBE"), + FunctionTemplate::New(WriteFloatBE)->GetFunction()); + proto->Set(String::New("writeFloatLE"), + FunctionTemplate::New(WriteFloatLE)->GetFunction()); + + proto->Set(String::New("copy"), + FunctionTemplate::New(Copy)->GetFunction()); + proto->Set(String::New("fill"), + FunctionTemplate::New(Fill)->GetFunction()); + + // for backwards compatibility + proto->Set(String::New("offset"), Uint32::New(0, node_isolate), v8::ReadOnly); -RetainedBufferInfo::RetainedBufferInfo(Buffer* buffer): buffer_(buffer) { -} - - -void RetainedBufferInfo::Dispose() { - buffer_ = NULL; - delete this; -} - - -bool RetainedBufferInfo::IsEquivalent(RetainedObjectInfo* other) { - return label == other->GetLabel() && - buffer_ == static_cast(other)->buffer_; -} - - -intptr_t RetainedBufferInfo::GetHash() { - return reinterpret_cast(buffer_); -} - - -const char* RetainedBufferInfo::GetLabel() { - return label; -} - - -intptr_t RetainedBufferInfo::GetSizeInBytes() { - return Buffer::Length(buffer_); -} - - -RetainedObjectInfo* WrapperInfo(uint16_t class_id, Handle wrapper) { - assert(class_id == BUFFER_CLASS_ID); - assert(Buffer::HasInstance(wrapper)); - Buffer* buffer = Buffer::Unwrap(wrapper.As()); - return new RetainedBufferInfo(buffer); + return Undefined(node_isolate); } -void Buffer::Initialize(Handle target) { +void Initialize(Handle target) { HandleScope scope(node_isolate); - length_symbol = NODE_PSYMBOL("length"); - - Local t = FunctionTemplate::New(Buffer::New); - constructor_template = Persistent::New(node_isolate, t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(String::NewSymbol("SlowBuffer")); - - NODE_SET_PROTOTYPE_METHOD(constructor_template, "binarySlice", Buffer::BinarySlice); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Slice", Buffer::Base64Slice); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "ucs2Slice", Buffer::Ucs2Slice); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "hexSlice", Buffer::HexSlice); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice); - // TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice); - - NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Write", Buffer::Utf8Write); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Write", Buffer::Base64Write); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "ucs2Write", Buffer::Ucs2Write); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "hexWrite", Buffer::HexWrite); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "readFloatLE", Buffer::ReadFloatLE); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "readFloatBE", Buffer::ReadFloatBE); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "readDoubleLE", Buffer::ReadDoubleLE); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "readDoubleBE", Buffer::ReadDoubleBE); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "writeFloatLE", Buffer::WriteFloatLE); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "writeFloatBE", Buffer::WriteFloatBE); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "writeDoubleLE", Buffer::WriteDoubleLE); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "writeDoubleBE", Buffer::WriteDoubleBE); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "fill", Buffer::Fill); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "copy", Buffer::Copy); - - NODE_SET_METHOD(constructor_template->GetFunction(), - "byteLength", - Buffer::ByteLength); - NODE_SET_METHOD(constructor_template->GetFunction(), - "makeFastBuffer", - Buffer::MakeFastBuffer); - - target->Set(String::NewSymbol("SlowBuffer"), constructor_template->GetFunction()); - target->Set(String::NewSymbol("setFastBufferConstructor"), - FunctionTemplate::New(SetFastBufferConstructor)->GetFunction()); - - v8::HeapProfiler* heap_profiler = node_isolate->GetHeapProfiler(); - heap_profiler->SetWrapperClassInfoProvider(BUFFER_CLASS_ID, WrapperInfo); + target->Set(String::New("setupBufferJS"), + FunctionTemplate::New(SetupBufferJS)->GetFunction()); } +} // namespace Buffer + } // namespace node NODE_MODULE(node_buffer, node::Buffer::Initialize) diff --git a/src/node_buffer.h b/src/node_buffer.h index 7b7cf8e586e199..b00badb35f5bd4 100644 --- a/src/node_buffer.h +++ b/src/node_buffer.h @@ -19,141 +19,44 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. +#include "smalloc.h" +#include "v8.h" + #ifndef NODE_BUFFER_H_ #define NODE_BUFFER_H_ -#include "node.h" -#include "node_object_wrap.h" -#include "v8.h" -#include - namespace node { -/* A buffer is a chunk of memory stored outside the V8 heap, mirrored by an - * object in javascript. The object is not totally opaque, one can access - * individual bytes with [] and slice it into substrings or sub-buffers - * without copying memory. - */ - -/* - The C++ API for Buffer changed radically between v0.2 and v0.3, in fact - it was the reason for bumping the version. In v0.2 JavaScript Buffers and - C++ Buffers were in one-to-one correspondence via ObjectWrap. We found - that it was faster to expose the C++ Buffers to JavaScript as a - "SlowBuffer" which is used as a private backend to pure JavaScript - "Buffer" objects - a 'Buffer' in v0.3 might look like this: - - { _parent: s, - _offset: 520, - length: 5 } - - Migrating code C++ Buffer code from v0.2 to v0.3 is difficult. Here are - some tips: - - buffer->data() calls should become Buffer::Data(buffer) calls. - - buffer->length() calls should become Buffer::Length(buffer) calls. - - There should not be any ObjectWrap::Unwrap() calls. You should - not be storing pointers to Buffer objects at all - as they are - now considered internal structures. Instead consider making a - JavaScript reference to the buffer. - - See the source code node-png as an example of a module which successfully - compiles on both v0.2 and v0.3 while making heavy use of the C++ Buffer - API. - - */ - - -class NODE_EXTERN Buffer: public ObjectWrap { - public: - // mirrors deps/v8/src/objects.h - static const unsigned int kMaxLength = 0x3fffffff; - - static v8::Persistent constructor_template; - - static bool HasInstance(v8::Handle val); - - static inline char* Data(v8::Handle val) { - assert(val->IsObject()); - void* data = val.As()->GetIndexedPropertiesExternalArrayData(); - return static_cast(data); - } - - static inline char* Data(Buffer *b) { - return Buffer::Data(b->handle_); - } - - static inline size_t Length(v8::Handle val) { - assert(val->IsObject()); - int len = val.As() - ->GetIndexedPropertiesExternalArrayDataLength(); - return static_cast(len); - } - - static inline size_t Length(Buffer *b) { - return Buffer::Length(b->handle_); - } - - - ~Buffer(); - - typedef void (*free_callback)(char *data, void *hint); - - // C++ API for constructing fast buffer - static v8::Handle New(v8::Handle string); - - static void Initialize(v8::Handle target); - - // public constructor - static Buffer* New(size_t length); - // public constructor - data is copied - static Buffer* New(const char *data, size_t len); - // public constructor - static Buffer* New(char *data, size_t length, - free_callback callback, void *hint); - - private: - static v8::Handle New(const v8::Arguments &args); - - template - static v8::Handle StringSlice(const v8::Arguments &args); - static v8::Handle BinarySlice(const v8::Arguments &args); - static v8::Handle AsciiSlice(const v8::Arguments &args); - static v8::Handle Base64Slice(const v8::Arguments &args); - static v8::Handle Utf8Slice(const v8::Arguments &args); - static v8::Handle Ucs2Slice(const v8::Arguments &args); - static v8::Handle HexSlice(const v8::Arguments &args); - - template - static v8::Handle StringWrite(const v8::Arguments &args); - static v8::Handle BinaryWrite(const v8::Arguments &args); - static v8::Handle Base64Write(const v8::Arguments &args); - static v8::Handle AsciiWrite(const v8::Arguments &args); - static v8::Handle Utf8Write(const v8::Arguments &args); - static v8::Handle Ucs2Write(const v8::Arguments &args); - static v8::Handle HexWrite(const v8::Arguments &args); - static v8::Handle ReadFloatLE(const v8::Arguments &args); - static v8::Handle ReadFloatBE(const v8::Arguments &args); - static v8::Handle ReadDoubleLE(const v8::Arguments &args); - static v8::Handle ReadDoubleBE(const v8::Arguments &args); - static v8::Handle WriteFloatLE(const v8::Arguments &args); - static v8::Handle WriteFloatBE(const v8::Arguments &args); - static v8::Handle WriteDoubleLE(const v8::Arguments &args); - static v8::Handle WriteDoubleBE(const v8::Arguments &args); - static v8::Handle ByteLength(const v8::Arguments &args); - static v8::Handle MakeFastBuffer(const v8::Arguments &args); - static v8::Handle Fill(const v8::Arguments &args); - static v8::Handle Copy(const v8::Arguments &args); - - Buffer(v8::Handle wrapper, size_t length); - void Replace(char *data, size_t length, free_callback callback, void *hint); - - size_t length_; - char* data_; - free_callback callback_; - void* callback_hint_; -}; - - -} // namespace node buffer +namespace Buffer { + +static const unsigned int kMaxLength = smalloc::kMaxLength; + +bool HasInstance(v8::Handle val); +bool HasInstance(v8::Handle val); +char* Data(v8::Handle val); +char* Data(v8::Handle val); +size_t Length(v8::Handle val); +size_t Length(v8::Handle val); + +// public constructor +v8::Local New(size_t length); +// public constructor from string +v8::Local New(v8::Handle string); +// public constructor - data is copied +// TODO(trevnorris): should be something like Copy() +v8::Local New(const char* data, size_t len); +// public constructor - data is used, callback is passed data on object gc +v8::Local New(char* data, + size_t length, + smalloc::FreeCallback callback, + void* hint); + +// public constructor - data is used. +// TODO(trevnorris): should be New() for consistency +v8::Local Use(char* data, uint32_t len); + +} // namespace Buffer + +} // namespace node #endif // NODE_BUFFER_H_ diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 0805956b1d9887..fffe6b6438938c 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -275,11 +275,6 @@ SSL_SESSION* SecureContext::GetSessionCallback(SSL* s, } -void SessionDataFree(char* data, void* hint) { - delete[] data; -} - - int SecureContext::NewSessionCallback(SSL* s, SSL_SESSION* sess) { HandleScope scope(node_isolate); @@ -297,8 +292,8 @@ int SecureContext::NewSessionCallback(SSL* s, SSL_SESSION* sess) { Handle argv[2] = { Buffer::New(reinterpret_cast(sess->session_id), - sess->session_id_length)->handle_, - Buffer::New(serialized, size, SessionDataFree, NULL)->handle_ + sess->session_id_length), + Buffer::Use(serialized, size) }; if (onnewsession_sym.IsEmpty()) { @@ -877,7 +872,7 @@ size_t ClientHelloParser::Write(const uint8_t* data, size_t len) { hello = Object::New(); hello->Set(sessionid_sym, Buffer::New(reinterpret_cast(session_id), - session_size)->handle_); + session_size)); argv[0] = hello; MakeCallback(conn_->handle_, onclienthello_sym, 1, argv); @@ -2292,11 +2287,11 @@ Handle CipherBase::Update(const Arguments& args) { return ThrowCryptoTypeError(ERR_get_error()); } - Buffer* buf = Buffer::New(reinterpret_cast(out), out_len); + Local buf = Buffer::New(reinterpret_cast(out), out_len); if (out) delete[] out; - return scope.Close(buf->handle_); + return scope.Close(buf); } @@ -2347,9 +2342,7 @@ Handle CipherBase::Final(const Arguments& args) { if (!r) return ThrowCryptoTypeError(ERR_get_error()); } - Buffer* buf = Buffer::New(reinterpret_cast(out_value), out_len); - - return scope.Close(buf->handle_); + return scope.Close(Buffer::New(reinterpret_cast(out_value), out_len)); } @@ -3547,11 +3540,6 @@ RandomBytesRequest::~RandomBytesRequest() { } -void RandomBytesFree(char* data, void* hint) { - delete[] data; -} - - template void RandomBytesWork(uv_work_t* work_req) { RandomBytesRequest* req = container_of(work_req, @@ -3588,10 +3576,8 @@ void RandomBytesCheck(RandomBytesRequest* req, Local argv[2]) { argv[1] = Local::New(node_isolate, Null(node_isolate)); } else { - // avoids the malloc + memcpy - Buffer* buffer = Buffer::New(req->data_, req->size_, RandomBytesFree, NULL); argv[0] = Local::New(node_isolate, Null(node_isolate)); - argv[1] = Local::New(node_isolate, buffer->handle_); + argv[1] = Buffer::Use(req->data_, req->size_); } } diff --git a/src/node_internals.h b/src/node_internals.h index 6055147b6c1f7c..9e2491a200f5be 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -74,6 +74,12 @@ inline static int snprintf(char* buf, unsigned int len, const char* fmt, ...) { # define ROUND_UP(a, b) ((a) % (b) ? ((a) + (b)) - ((a) % (b)) : (a)) #endif +#if defined(__GNUC__) && __GNUC__ >= 4 +# define MUST_USE_RESULT __attribute__((warn_unused_result)) +#else +# define MUST_USE_RESULT +#endif + // this would have been a template function were it not for the fact that g++ // sometimes fails to resolve it... #define THROW_ERROR(fun) \ @@ -137,6 +143,24 @@ inline bool IsBigEndian() { return GetEndianness() == kBigEndian; } +// parse index for external array data +inline MUST_USE_RESULT bool ParseArrayIndex(v8::Handle arg, + size_t def, + size_t* ret) { + if (arg->IsUndefined()) { + *ret = def; + return true; + } + + int32_t tmp_i = arg->Int32Value(); + + if (tmp_i < 0) + return false; + + *ret = static_cast(tmp_i); + return true; +} + } // namespace node #endif // SRC_NODE_INTERNALS_H_ diff --git a/src/slab_allocator.cc b/src/slab_allocator.cc index 476124cb6e48f3..1a98c8ecdb8269 100644 --- a/src/slab_allocator.cc +++ b/src/slab_allocator.cc @@ -71,10 +71,7 @@ void SlabAllocator::Initialize() { static Local NewSlab(unsigned int size) { HandleScope scope(node_isolate); - Local arg = Integer::NewFromUnsigned(ROUND_UP(size, 16), node_isolate); - Local buf = Buffer::constructor_template - ->GetFunction() - ->NewInstance(1, &arg); + Local buf = Buffer::New(ROUND_UP(size, 16)); return scope.Close(buf); } diff --git a/src/string_bytes.cc b/src/string_bytes.cc index db6bb8e5e61fd8..db810037d9ff03 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -624,7 +624,7 @@ Local StringBytes::Encode(const char* buf, Local val; switch (encoding) { case BUFFER: - return scope.Close(Buffer::New(buf, buflen)->handle_); + return scope.Close(Buffer::New(buf, buflen)); case ASCII: if (contains_non_ascii(buf, buflen)) { diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index c42898de88a6b8..75ece9b1da5f78 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -128,15 +128,14 @@ int TLSCallbacks::NewSessionCallback(SSL* s, SSL_SESSION* sess) { return 0; // Serialize session - Local buff = Local::New(Buffer::New(size)->handle_); + Local buff = Buffer::New(size); unsigned char* serialized = reinterpret_cast( Buffer::Data(buff)); memset(serialized, 0, size); i2d_SSL_SESSION(sess, &serialized); - Local session = Local::New( - Buffer::New(reinterpret_cast(sess->session_id), - sess->session_id_length)->handle_); + Local session = Buffer::New(reinterpret_cast(sess->session_id), + sess->session_id_length); Handle argv[2] = { session, buff }; MakeCallback(c->handle_, onnewsession_sym, ARRAY_SIZE(argv), argv); @@ -479,7 +478,7 @@ void TLSCallbacks::ClearOut() { do { read = SSL_read(ssl_, out, sizeof(out)); if (read > 0) { - Local buff = Local::New(Buffer::New(out, read)->handle_); + Local buff = Buffer::New(out, read); Handle argv[3] = { buff, Integer::New(0, node_isolate), @@ -775,7 +774,7 @@ void TLSCallbacks::ParseClientHello() { hello_obj = Object::New(); hello_obj->Set(sessionid_sym, Buffer::New(reinterpret_cast(session_id), - session_size)->handle_); + session_size)); argv[0] = hello_obj; MakeCallback(handle_, onclienthello_sym, 1, argv); diff --git a/test/simple/test-buffer-regress-GH-2659.js b/test/simple/test-buffer-regress-GH-2659.js deleted file mode 100644 index 19a1362fd77a2b..00000000000000 --- a/test/simple/test-buffer-regress-GH-2659.js +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var common = require('../common'); -var assert = require('assert'); - -// This is not a great test. It depends on a Node internal, namely the slab -// size. Maybe we should expose that in some way. Then again, maybe not... -for (var n = 1; n <= 8192; ++n) { - Buffer(n); - Buffer(0).write('', 'base64'); -} diff --git a/test/simple/test-buffer.js b/test/simple/test-buffer.js index a465df8cc0e1b8..b5a76d90f8ee2e 100644 --- a/test/simple/test-buffer.js +++ b/test/simple/test-buffer.js @@ -22,18 +22,11 @@ var common = require('../common'); var assert = require('assert'); -var SlowBuffer = require('buffer').SlowBuffer; var Buffer = require('buffer').Buffer; // counter to ensure unique value is always copied var cntr = 0; -// Regression test for segfault introduced in commit e501ce4. -['base64','binary','ucs2','utf8','ascii'].forEach(function(encoding) { - var buf = new SlowBuffer(0); - buf.write('', encoding); -}); - var b = Buffer(1024); // safe constructor console.log('b.length == %d', b.length); @@ -128,14 +121,6 @@ for (var i = 0; i < b.length; i++) { } -// copy from fast to slow buffer -var sb = new SlowBuffer(b.length); -var copied = b.copy(sb); -console.log('copied %d bytes from b into sb'); -for (var i = 0; i < sb.length; i++) { - assert.strictEqual(sb[i], b[i]); -} - var caught_error = null; // try to copy from before the beginning of b @@ -146,15 +131,10 @@ try { caught_error = err; } -// copy from b to c with negative sourceStart -b.fill(++cntr); -c.fill(++cntr); -var copied = b.copy(c, 0, -1); -assert.strictEqual(c.length, copied); -console.log('copied %d bytes from b into c w/ negative sourceStart', copied); -for (var i = 0; i < c.length; i++) { - assert.strictEqual(b[i], c[i]); -} +// copy throws at negative sourceStart +assert.throws(function() { + Buffer(5).copy(Buffer(5), 0, -1); +}, RangeError); // check sourceEnd resets to targetEnd if former is greater than the latter b.fill(++cntr); @@ -165,31 +145,17 @@ for (var i = 0; i < c.length; i++) { assert.strictEqual(b[i], c[i]); } -// copy from fast buffer to slow buffer without parameters -var sb = new SlowBuffer(b.length); -sb.fill(++cntr, 0, sb.length); -b.fill(++cntr); -var copied = b.copy(sb); -console.log('copied %d bytes from fast buffer to slow buffer', copied); -for (var i = 0 ; i < b.length; i++) { - assert.strictEqual(b[i], sb[i]); -} - // throw with negative sourceEnd console.log('test copy at negative sourceEnd'); assert.throws(function() { b.copy(c, 0, 0, -1); }, RangeError); -// throw when sourceStart is greater than sourceEnd -assert.throws(function() { - b.copy(c, 0, 100, 10); -}, RangeError); +// when sourceStart is greater than sourceEnd, zero copied +assert.equal(b.copy(c, 0, 100, 10), 0); -// throw attempting to copy after end of c -assert.throws(function() { - b.copy(c, 512, 0, 10); -}, RangeError); +// when targetStart > targetLength, zero copied +assert.equal(b.copy(c, 512, 0, 10), 0); var caught_error; @@ -218,8 +184,14 @@ new Buffer('', 'binary'); new Buffer(0); // try to write a 0-length string beyond the end of b -b.write('', 1024); -b.write('', 2048); +assert.throws(function() { + b.write('', 2048); +}, RangeError); + +// throw when writing to negative offset +assert.throws(function() { + b.write('a', -1); +}, RangeError); // throw when writing past bounds from the pool assert.throws(function() { @@ -591,12 +563,9 @@ assert.equal(b2, b3); assert.equal(b2, b4); -// Test slice on SlowBuffer GH-843 -var SlowBuffer = process.binding('buffer').SlowBuffer; - -function buildSlowBuffer(data) { +function buildBuffer(data) { if (Array.isArray(data)) { - var buffer = new SlowBuffer(data.length); + var buffer = new Buffer(data.length); data.forEach(function(v, k) { buffer[k] = v; }); @@ -605,10 +574,10 @@ function buildSlowBuffer(data) { return null; } -var x = buildSlowBuffer([0x81, 0xa3, 0x66, 0x6f, 0x6f, 0xa3, 0x62, 0x61, 0x72]); +var x = buildBuffer([0x81, 0xa3, 0x66, 0x6f, 0x6f, 0xa3, 0x62, 0x61, 0x72]); console.log(x.inspect()); -assert.equal('', x.inspect()); +assert.equal('', x.inspect()); var z = x.slice(4); console.log(z.inspect()); @@ -669,7 +638,7 @@ for (; i < 32; i++) assert.equal(1, b[i]); for (; i < b.length; i++) assert.equal(0, b[i]); ['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach(function(encoding) { - var b = new SlowBuffer(10); + var b = new Buffer(10); b.write('あいうえお', encoding); assert.equal(b.toString(encoding), 'あいうえお'); }); @@ -689,7 +658,7 @@ assert.equal(0xad, b[1]); assert.equal(0xbe, b[2]); assert.equal(0xef, b[3]); -// testing invalid encoding on SlowBuffer.toString +// testing invalid encoding on Buffer.toString caught_error = null; try { var copied = b.toString('invalid'); @@ -698,7 +667,7 @@ try { } assert.strictEqual('Unknown encoding: invalid', caught_error.message); -// testing invalid encoding on SlowBuffer.write +// testing invalid encoding on Buffer.write caught_error = null; try { var copied = b.write('some string', 0, 5, 'invalid'); @@ -708,11 +677,6 @@ try { assert.strictEqual('Unknown encoding: invalid', caught_error.message); -// This should not segfault the program. -assert.throws(function() { - new Buffer('"pong"', 0, 6, 8031, '127.0.0.1'); -}); - // #1210 Test UTF-8 string includes null character var buf = new Buffer('\0'); assert.equal(buf.length, 1); @@ -800,8 +764,8 @@ assert.equal(buf[4], 0); Buffer(3.3).toString(); // throws bad argument error in commit 43cb4ec assert.equal(Buffer(-1).length, 0); assert.equal(Buffer(NaN).length, 0); -assert.equal(Buffer(3.3).length, 4); -assert.equal(Buffer({length: 3.3}).length, 4); +assert.equal(Buffer(3.3).length, 3); +assert.equal(Buffer({length: 3.3}).length, 3); assert.equal(Buffer({length: 'BAM'}).length, 0); // Make sure that strings are not coerced to numbers. @@ -863,109 +827,79 @@ assert.throws(function() { }, RangeError); assert.throws(function() { new Buffer(0xFFFFFFFFF); -}, TypeError); +}, RangeError); // attempt to overflow buffers, similar to previous bug in array buffers assert.throws(function() { var buf = new Buffer(8); buf.readFloatLE(0xffffffff); -}, /Trying to access beyond buffer length/); +}, RangeError); assert.throws(function() { var buf = new Buffer(8); buf.writeFloatLE(0.0, 0xffffffff); -}, /Trying to access beyond buffer length/); +}, RangeError); assert.throws(function() { - var buf = new SlowBuffer(8); + var buf = new Buffer(8); buf.readFloatLE(0xffffffff); -}, /Trying to read beyond buffer length/); +}, RangeError); assert.throws(function() { - var buf = new SlowBuffer(8); + var buf = new Buffer(8); buf.writeFloatLE(0.0, 0xffffffff); -}, /Trying to write beyond buffer length/); +}, RangeError); // ensure negative values can't get past offset assert.throws(function() { var buf = new Buffer(8); buf.readFloatLE(-1); -}, /offset is not uint/); +}, RangeError); assert.throws(function() { var buf = new Buffer(8); buf.writeFloatLE(0.0, -1); -}, /offset is not uint/); +}, RangeError); assert.throws(function() { - var buf = new SlowBuffer(8); + var buf = new Buffer(8); buf.readFloatLE(-1); -}, /offset is not uint/); +}, RangeError); assert.throws(function() { - var buf = new SlowBuffer(8); + var buf = new Buffer(8); buf.writeFloatLE(0.0, -1); -}, /offset is not uint/); +}, RangeError); // offset checks var buf = new Buffer(0); -assert.throws(function() { buf.readUInt8(0); }, /beyond buffer length/); -assert.throws(function() { buf.readInt8(0); }, /beyond buffer length/); +assert.throws(function() { buf.readUInt8(0); }, RangeError); +assert.throws(function() { buf.readInt8(0); }, RangeError); [16, 32].forEach(function(bits) { var buf = new Buffer(bits / 8 - 1); - assert.throws( - function() { buf['readUInt' + bits + 'BE'](0); }, - /beyond buffer length/, - 'readUInt' + bits + 'BE' - ); - - assert.throws( - function() { buf['readUInt' + bits + 'LE'](0); }, - /beyond buffer length/, - 'readUInt' + bits + 'LE' - ); - - assert.throws( - function() { buf['readInt' + bits + 'BE'](0); }, - /beyond buffer length/, - 'readInt' + bits + 'BE()' - ); - - assert.throws( - function() { buf['readInt' + bits + 'LE'](0); }, - /beyond buffer length/, - 'readInt' + bits + 'LE()' - ); -}); + assert.throws(function() { buf['readUInt' + bits + 'BE'](0); }, + RangeError, + 'readUInt' + bits + 'BE'); -// SlowBuffer sanity checks. -assert.throws(function() { - var len = 0xfffff; - var sbuf = new SlowBuffer(len); - var buf = new Buffer(sbuf, len, 0); - SlowBuffer.makeFastBuffer(sbuf, buf, -len, len); // Should throw. - for (var i = 0; i < len; ++i) buf[i] = 0x42; // Try to force segfault. -}, RangeError); + assert.throws(function() { buf['readUInt' + bits + 'LE'](0); }, + RangeError, + 'readUInt' + bits + 'LE'); -assert.throws(function() { - var len = 0xfffff; - var sbuf = new SlowBuffer(len); - var buf = new Buffer(sbuf, len, -len); // Should throw. - for (var i = 0; i < len; ++i) buf[i] = 0x42; // Try to force segfault. -}, RangeError); + assert.throws(function() { buf['readInt' + bits + 'BE'](0); }, + RangeError, + 'readInt' + bits + 'BE()'); -assert.throws(function() { - var sbuf = new SlowBuffer(1); - var buf = new Buffer(sbuf, 1, 0); - buf.length = 0xffffffff; - buf.slice(0xffffff0, 0xffffffe); // Should throw. -}, Error); + assert.throws(function() { buf['readInt' + bits + 'LE'](0); }, + RangeError, + 'readInt' + bits + 'LE()'); +}); +// test Buffer slice (function() { var buf = new Buffer('0123456789'); assert.equal(buf.slice(-10, 10), '0123456789'); @@ -989,10 +923,3 @@ assert.equal(Buffer.byteLength('aaaa==', 'base64'), 3); assert.throws(function() { Buffer('', 'buffer'); }, TypeError); - -assert.doesNotThrow(function () { - var slow = new SlowBuffer(1); - assert(slow.write('', Buffer.poolSize * 10) === 0); - var fast = new Buffer(1); - assert(fast.write('', Buffer.poolSize * 10) === 0); -}); diff --git a/test/simple/test-readdouble.js b/test/simple/test-readdouble.js index 9fa23e090e4c4a..6749e5b2022746 100644 --- a/test/simple/test-readdouble.js +++ b/test/simple/test-readdouble.js @@ -22,7 +22,6 @@ /* * Tests to verify we're reading in doubles correctly */ -var SlowBuffer = process.binding('buffer').SlowBuffer; var common = require('../common'); var ASSERT = require('assert'); @@ -128,4 +127,3 @@ function test(clazz) { test(Buffer); -test(SlowBuffer); diff --git a/test/simple/test-readfloat.js b/test/simple/test-readfloat.js index 373fa209780cbf..586b992c5b8213 100644 --- a/test/simple/test-readfloat.js +++ b/test/simple/test-readfloat.js @@ -22,7 +22,6 @@ /* * Tests to verify we're reading in floats correctly */ -var SlowBuffer = process.binding('buffer').SlowBuffer; var common = require('../common'); var ASSERT = require('assert'); @@ -90,4 +89,3 @@ function test(clazz) { test(Buffer); -test(SlowBuffer); diff --git a/test/simple/test-readint.js b/test/simple/test-readint.js index f1bae2d130b5d2..f1b79e816a3ff8 100644 --- a/test/simple/test-readint.js +++ b/test/simple/test-readint.js @@ -22,7 +22,6 @@ /* * Tests to verify we're reading in signed integers correctly */ -var SlowBuffer = process.binding('buffer').SlowBuffer; var common = require('../common'); var ASSERT = require('assert'); @@ -115,8 +114,5 @@ function test32(clazz) { test8(Buffer); -test8(SlowBuffer); test16(Buffer); -test16(SlowBuffer); test32(Buffer); -test32(SlowBuffer); diff --git a/test/simple/test-readuint.js b/test/simple/test-readuint.js index e63bba70058ccd..2b11a61c929d22 100644 --- a/test/simple/test-readuint.js +++ b/test/simple/test-readuint.js @@ -23,7 +23,6 @@ * A battery of tests to help us read a series of uints */ -var SlowBuffer = process.binding('buffer').SlowBuffer; var common = require('../common'); var ASSERT = require('assert'); @@ -106,8 +105,5 @@ function test32(clazz) { test8(Buffer); -test8(SlowBuffer); test16(Buffer); -test16(SlowBuffer); test32(Buffer); -test32(SlowBuffer); diff --git a/test/simple/test-writedouble.js b/test/simple/test-writedouble.js index c4bdc1b10d2bfe..10b87d9b7f52f1 100644 --- a/test/simple/test-writedouble.js +++ b/test/simple/test-writedouble.js @@ -22,7 +22,6 @@ /* * Tests to verify we're writing doubles correctly */ -var SlowBuffer = process.binding('buffer').SlowBuffer; var common = require('../common'); var ASSERT = require('assert'); @@ -194,4 +193,3 @@ function test(clazz) { test(Buffer); -test(SlowBuffer); diff --git a/test/simple/test-writefloat.js b/test/simple/test-writefloat.js index f72cedeee96c95..4c736b73afef79 100644 --- a/test/simple/test-writefloat.js +++ b/test/simple/test-writefloat.js @@ -22,7 +22,6 @@ /* * Tests to verify we're writing floats correctly */ -var SlowBuffer = process.binding('buffer').SlowBuffer; var common = require('../common'); var ASSERT = require('assert'); @@ -132,4 +131,3 @@ function test(clazz) { test(Buffer); -test(SlowBuffer); diff --git a/test/simple/test-writeint.js b/test/simple/test-writeint.js index b7ebd6964f02f0..10540b6bd75312 100644 --- a/test/simple/test-writeint.js +++ b/test/simple/test-writeint.js @@ -22,7 +22,6 @@ /* * Tests to verify we're writing signed integers correctly */ -var SlowBuffer = process.binding('buffer').SlowBuffer; var common = require('../common'); var ASSERT = require('assert'); @@ -185,8 +184,5 @@ function test32(clazz) { test8(Buffer); -test8(SlowBuffer); test16(Buffer); -test16(SlowBuffer); test32(Buffer); -test32(SlowBuffer); diff --git a/test/simple/test-writeuint.js b/test/simple/test-writeuint.js index 91b5c6c9c64342..da0d7952812604 100644 --- a/test/simple/test-writeuint.js +++ b/test/simple/test-writeuint.js @@ -22,7 +22,6 @@ /* * A battery of tests to help us read a series of uints */ -var SlowBuffer = process.binding('buffer').SlowBuffer; var common = require('../common'); var ASSERT = require('assert'); @@ -144,8 +143,5 @@ function test32(clazz) { test8(Buffer); -test8(SlowBuffer); test16(Buffer); -test16(SlowBuffer); test32(Buffer); -test32(SlowBuffer);