Browse Source

buffer: floating point read/write improvements

Improvements:
* floating point operations are approx 4x's faster
* Now write quiet NaN's
* all read/write on floating point now done in C, so no more need for
  lib/buffer_ieee754.js
* float values have more accurate min/max value checks
* add additional benchmarks for buffers read/write
* created benchmark/_bench_timer.js which is a simple library that
  can be included into any benchmark and provides an intelligent tracker
  for sync and async tests
* add benchmarks for DataView set methods
* add checks and tests to make sure offset is greater than 0
v0.9.7-release
Trevor Norris 12 years ago
committed by isaacs
parent
commit
22b84e6216
  1. 32
      LICENSE
  2. 88
      benchmark/_bench_timer.js
  3. 97
      benchmark/buffer_read.js
  4. 103
      benchmark/buffer_write.js
  5. 104
      benchmark/dataview_set.js
  6. 116
      lib/buffer.js
  7. 116
      lib/buffer_ieee754.js
  8. 1
      node.gyp
  9. 141
      src/node_buffer.cc
  10. 8
      src/node_buffer.h
  11. 44
      test/simple/test-buffer.js
  12. 8
      test/simple/test-writedouble.js
  13. 30
      test/simple/test-writefloat.js

32
LICENSE

@ -252,38 +252,6 @@ maintained libraries. The externally maintained libraries used by Node are:
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
- lib/buffer_ieee754.js. Its license follows:
"""
// Copyright (c) 2008, Fair Oaks Labs, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of Fair Oaks Labs, Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
"""
- lib/punycode.js is copyright 2011 Mathias Bynens <http://mathiasbynens.be/>
and released under the MIT license.
"""

88
benchmark/_bench_timer.js

@ -0,0 +1,88 @@
/*
* This is a simple addition to allow for higher resolution timers.
* It can be used to track time for both synchronous or asynchronous
* calls. For synchronous calls pass a callback function like so:
*
* var timer = require('./_bench_timer');
*
* timer('myTest', function() {
* for (var i = 0; i < 1e6; i++)
* // ... run something here
* }
* });
*
* For asynchronous timers just pass the name. Then run it again with
* the same name to finish it:
*
* timer('checkAsync');
* setTimeout(function() {
* timer('checkAsync');
* }, 300);
*
* When this happens all currently queued benchmarks will be paused
* until the asynchronous benchmark has completed.
*
* If the benchmark has been run with --expose_gc then the garbage
* collector will be run between each test.
*
* The setTimeout delay can also be changed by passing a value to
* timer.delay.
*/
var store = {};
var order = [];
var maxLength = 0;
var processing = false;
var asyncQueue = 0;
var GCd = typeof gc !== 'function' ? false : true;
function timer(name, fn) {
if (maxLength < name.length)
maxLength = name.length;
if (!fn) {
processing = false;
if (!store[name]) {
asyncQueue++;
store[name] = process.hrtime();
return;
}
displayTime(name, process.hrtime(store[name]));
asyncQueue--;
} else {
store[name] = fn;
order.push(name);
}
if (!processing && asyncQueue <= 0) {
processing = true;
setTimeout(run, timer.delay);
}
}
timer.delay = 100;
function run() {
if (asyncQueue > 0 || order.length <= 0)
return;
if (GCd) gc();
setTimeout(function() {
var name = order.shift();
var fn = store[name];
var ini = process.hrtime();
fn();
ini = process.hrtime(ini);
displayTime(name, ini);
run();
}, timer.delay);
}
function displayTime(name, ini) {
name += ': ';
while (name.length < maxLength + 2)
name += ' ';
console.log(name + '%s \u00b5s',
(~~((ini[0] * 1e6) + (ini[1] / 1e3)))
.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"));
}
module.exports = timer;

97
benchmark/buffer_read.js

@ -0,0 +1,97 @@
const LEN = 1e7;
const noAssert = process.argv[3] == 'true' ? true
: process.argv[3] == 'false' ? false
: undefined;
var timer = require('./_bench_timer');
var buff = (process.argv[2] == 'slow') ?
(new require('buffer').SlowBuffer(8)) :
(new Buffer(8));
var i;
buff.writeDoubleLE(0, 0, noAssert);
timer('readUInt8', function() {
for (i = 0; i < LEN; i++) {
buff.readUInt8(0, noAssert);
}
});
timer('readUInt16LE', function() {
for (i = 0; i < LEN; i++) {
buff.readUInt16LE(0, noAssert);
}
});
timer('readUInt16BE', function() {
for (i = 0; i < LEN; i++) {
buff.readUInt16BE(0, noAssert);
}
});
timer('readUInt32LE', function() {
for (i = 0; i < LEN; i++) {
buff.readUInt32LE(0, noAssert);
}
});
timer('readUInt32BE', function() {
for (i = 0; i < LEN; i++) {
buff.readUInt32BE(0, noAssert);
}
});
timer('readInt8', function() {
for (i = 0; i < LEN; i++) {
buff.readInt8(0, noAssert);
}
});
timer('readInt16LE', function() {
for (i = 0; i < LEN; i++) {
buff.readInt16LE(0, noAssert);
}
});
timer('readInt16BE', function() {
for (i = 0; i < LEN; i++) {
buff.readInt16BE(0, noAssert);
}
});
timer('readInt32LE', function() {
for (i = 0; i < LEN; i++) {
buff.readInt32LE(0, noAssert);
}
});
timer('readInt32BE', function() {
for (i = 0; i < LEN; i++) {
buff.readInt32BE(0, noAssert);
}
});
timer('readFloatLE', function() {
for (i = 0; i < LEN; i++) {
buff.readFloatLE(0, noAssert);
}
});
timer('readFloatBE', function() {
for (i = 0; i < LEN; i++) {
buff.readFloatBE(0, noAssert);
}
});
timer('readDoubleLE', function() {
for (i = 0; i < LEN; i++) {
buff.readDoubleLE(0, noAssert);
}
});
timer('readDoubleBE', function() {
for (i = 0; i < LEN; i++) {
buff.readDoubleBE(0, noAssert);
}
});

103
benchmark/buffer_write.js

@ -0,0 +1,103 @@
const LEN = 1e7;
const INT8 = 0x7f;
const INT16 = 0x7fff;
const INT32 = 0x7fffffff;
const UINT8 = INT8 * 2;
const UINT16 = INT16 * 2;
const UINT32 = INT32 * 2;
const noAssert = process.argv[3] == 'true' ? true
: process.argv[3] == 'false' ? false
: undefined;
var timer = require('./_bench_timer');
var buff = (process.argv[2] == 'slow') ?
(new require('buffer').SlowBuffer(8)) :
(new Buffer(8));
var i;
timer('writeUInt8', function() {
for (i = 0; i < LEN; i++) {
buff.writeUInt8(i % UINT8, 0, noAssert);
}
});
timer('writeUInt16LE', function() {
for (i = 0; i < LEN; i++) {
buff.writeUInt16LE(i % UINT16, 0, noAssert);
}
});
timer('writeUInt16BE', function() {
for (i = 0; i < LEN; i++) {
buff.writeUInt16BE(i % UINT16, 0, noAssert);
}
});
timer('writeUInt32LE', function() {
for (i = 0; i < LEN; i++) {
buff.writeUInt32LE(i % UINT32, 0, noAssert);
}
});
timer('writeUInt32BE', function() {
for (i = 0; i < LEN; i++) {
buff.writeUInt32BE(i % UINT32, 0, noAssert);
}
});
timer('writeInt8', function() {
for (i = 0; i < LEN; i++) {
buff.writeInt8(i % INT8, 0, noAssert);
}
});
timer('writeInt16LE', function() {
for (i = 0; i < LEN; i++) {
buff.writeInt16LE(i % INT16, 0, noAssert);
}
});
timer('writeInt16BE', function() {
for (i = 0; i < LEN; i++) {
buff.writeInt16BE(i % INT16, 0, noAssert);
}
});
timer('writeInt32LE', function() {
for (i = 0; i < LEN; i++) {
buff.writeInt32LE(i % INT32, 0, noAssert);
}
});
timer('writeInt32BE', function() {
for (i = 0; i < LEN; i++) {
buff.writeInt32BE(i % INT32, 0, noAssert);
}
});
timer('writeFloatLE', function() {
for (i = 0; i < LEN; i++) {
buff.writeFloatLE(i * 0.1, 0, noAssert);
}
});
timer('writeFloatBE', function() {
for (i = 0; i < LEN; i++) {
buff.writeFloatBE(i * 0.1, 0, noAssert);
}
});
timer('writeDoubleLE', function() {
for (i = 0; i < LEN; i++) {
buff.writeDoubleLE(i * 0.1, 0, noAssert);
}
});
timer('writeDoubleBE', function() {
for (i = 0; i < LEN; i++) {
buff.writeDoubleBE(i * 0.1, 0, noAssert);
}
});

104
benchmark/dataview_set.js

@ -0,0 +1,104 @@
const LEN = 1e7;
const INT8 = 0x7f;
const INT16 = 0x7fff;
const INT32 = 0x7fffffff;
const UINT8 = INT8 * 2;
const UINT16 = INT16 * 2;
const UINT32 = INT32 * 2;
const noAssert = process.argv[3] == 'true' ? true
: process.argv[3] == 'false' ? false
: undefined;
var timer = require('./_bench_timer');
var buff = (process.argv[2] == 'slow') ?
(new require('buffer').SlowBuffer(8)) :
(new Buffer(8));
var dv = new DataView(buff);
var i;
timer('setUint8', function() {
for (i = 0; i < LEN; i++) {
dv.setUint8(0, i % UINT8);
}
});
timer('setUint16 - LE', function() {
for (i = 0; i < LEN; i++) {
dv.setUint16(0, i % UINT16, true);
}
});
timer('setUint16 - BE', function() {
for (i = 0; i < LEN; i++) {
dv.setUint16(0, i % UINT16);
}
});
timer('setUint32 - LE', function() {
for (i = 0; i < LEN; i++) {
dv.setUint32(0, i % UINT32, true);
}
});
timer('setUint32 - BE', function() {
for (i = 0; i < LEN; i++) {
dv.setUint32(0, i % UINT32);
}
});
timer('setInt8', function() {
for (i = 0; i < LEN; i++) {
dv.setInt8(0, i % INT8);
}
});
timer('setInt16 - LE', function() {
for (i = 0; i < LEN; i++) {
dv.setInt16(0, i % INT16, true);
}
});
timer('setInt16 - BE', function() {
for (i = 0; i < LEN; i++) {
dv.setInt16(0, i % INT16);
}
});
timer('setInt32 - LE', function() {
for (i = 0; i < LEN; i++) {
dv.setInt32(0, i % INT32, true);
}
});
timer('setInt32 - BE', function() {
for (i = 0; i < LEN; i++) {
dv.setInt32(0, i % INT32);
}
});
timer('setFloat32 - LE', function() {
for (i = 0; i < LEN; i++) {
dv.setFloat32(0, i * 0.1, true);
}
});
timer('setFloat32 - BE', function() {
for (i = 0; i < LEN; i++) {
dv.setFloat32(0, i * 0.1);
}
});
timer('setFloat64 - LE', function() {
for (i = 0; i < LEN; i++) {
dv.setFloat64(0, i * 0.1, true);
}
});
timer('setFloat64 - BE', function() {
for (i = 0; i < LEN; i++) {
dv.setFloat64(0, i * 0.1);
}
});

116
lib/buffer.js

@ -829,46 +829,35 @@ Buffer.prototype.readInt32BE = function(offset, noAssert) {
return readInt32(this, offset, true, noAssert);
};
function readFloat(buffer, offset, isBigEndian, noAssert) {
if (!noAssert) {
assert.ok(typeof (isBigEndian) === 'boolean',
'missing or invalid endian');
assert.ok(offset + 3 < buffer.length,
'Trying to read beyond buffer length');
}
return require('buffer_ieee754').readIEEE754(buffer, offset, isBigEndian,
23, 4);
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');
}
Buffer.prototype.readFloatLE = function(offset, noAssert) {
return readFloat(this, offset, false, noAssert);
if (!noAssert)
checkOffset(offset, 4, this.length);
return this.parent.readFloatLE(this.offset + offset, !!noAssert);
};
Buffer.prototype.readFloatBE = function(offset, noAssert) {
return readFloat(this, offset, true, noAssert);
if (!noAssert)
checkOffset(offset, 4, this.length);
return this.parent.readFloatBE(this.offset + offset, !!noAssert);
};
function readDouble(buffer, offset, isBigEndian, noAssert) {
if (!noAssert) {
assert.ok(typeof (isBigEndian) === 'boolean',
'missing or invalid endian');
assert.ok(offset + 7 < buffer.length,
'Trying to read beyond buffer length');
}
return require('buffer_ieee754').readIEEE754(buffer, offset, isBigEndian,
52, 8);
}
Buffer.prototype.readDoubleLE = function(offset, noAssert) {
return readDouble(this, offset, false, noAssert);
if (!noAssert)
checkOffset(offset, 8, this.length);
return this.parent.readDoubleLE(this.offset + offset, !!noAssert);
};
Buffer.prototype.readDoubleBE = function(offset, noAssert) {
return readDouble(this, offset, true, noAssert);
if (!noAssert)
checkOffset(offset, 8, this.length);
return this.parent.readDoubleBE(this.offset + offset, !!noAssert);
};
@ -1036,19 +1025,6 @@ function verifsint(value, max, min) {
assert.ok(Math.floor(value) === value, 'value has a fractional component');
}
function verifyIEEE754(value, max, min) {
assert.ok(typeof (value) == 'number',
'cannot write a non-number as a number');
if (isNaN(value) || value === Infinity || value === -Infinity) {
return;
}
assert.ok(value <= max, 'value larger than maximum allowed value');
assert.ok(value >= min, 'value smaller than minimum allowed value');
}
Buffer.prototype.writeInt8 = function(value, offset, noAssert) {
var buffer = this;
@ -1103,60 +1079,26 @@ Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
writeInt32(this, value, offset, true, noAssert);
};
function writeFloat(buffer, value, offset, isBigEndian, noAssert) {
if (!noAssert) {
assert.ok(value !== undefined && value !== null,
'missing value');
assert.ok(typeof (isBigEndian) === 'boolean',
'missing or invalid endian');
assert.ok(offset !== undefined && offset !== null,
'missing offset');
assert.ok(offset + 3 < buffer.length,
'Trying to write beyond buffer length');
verifyIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38);
}
require('buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian,
23, 4);
}
Buffer.prototype.writeFloatLE = function(value, offset, noAssert) {
writeFloat(this, value, offset, false, noAssert);
if (!noAssert)
checkOffset(offset, 4, this.length);
this.parent.writeFloatLE(value, this.offset + offset, !!noAssert);
};
Buffer.prototype.writeFloatBE = function(value, offset, noAssert) {
writeFloat(this, value, offset, true, noAssert);
if (!noAssert)
checkOffset(offset, 4, this.length);
this.parent.writeFloatBE(value, this.offset + offset, !!noAssert);
};
function writeDouble(buffer, value, offset, isBigEndian, noAssert) {
if (!noAssert) {
assert.ok(value !== undefined && value !== null,
'missing value');
assert.ok(typeof (isBigEndian) === 'boolean',
'missing or invalid endian');
assert.ok(offset !== undefined && offset !== null,
'missing offset');
assert.ok(offset + 7 < buffer.length,
'Trying to write beyond buffer length');
verifyIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308);
}
require('buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian,
52, 8);
}
Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) {
writeDouble(this, value, offset, false, noAssert);
if (!noAssert)
checkOffset(offset, 8, this.length);
this.parent.writeDoubleLE(value, this.offset + offset, !!noAssert);
};
Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) {
writeDouble(this, value, offset, true, noAssert);
if (!noAssert)
checkOffset(offset, 8, this.length);
this.parent.writeDoubleBE(value, this.offset + offset, !!noAssert);
};

116
lib/buffer_ieee754.js

@ -1,116 +0,0 @@
// Copyright (c) 2008, Fair Oaks Labs, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of Fair Oaks Labs, Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//
// Modifications to writeIEEE754 to support negative zeroes made by Brian White
exports.readIEEE754 = function(buffer, offset, isBE, mLen, nBytes) {
var e, m,
eLen = nBytes * 8 - mLen - 1,
eMax = (1 << eLen) - 1,
eBias = eMax >> 1,
nBits = -7,
i = isBE ? 0 : (nBytes - 1),
d = isBE ? 1 : -1,
s = buffer[offset + i];
i += d;
e = s & ((1 << (-nBits)) - 1);
s >>= (-nBits);
nBits += eLen;
for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);
m = e & ((1 << (-nBits)) - 1);
e >>= (-nBits);
nBits += mLen;
for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);
if (e === 0) {
e = 1 - eBias;
} else if (e === eMax) {
return m ? NaN : ((s ? -1 : 1) * Infinity);
} else {
m = m + Math.pow(2, mLen);
e = e - eBias;
}
return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
};
exports.writeIEEE754 = function(buffer, value, offset, isBE, mLen, nBytes) {
var e, m, c,
eLen = nBytes * 8 - mLen - 1,
eMax = (1 << eLen) - 1,
eBias = eMax >> 1,
rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
i = isBE ? (nBytes - 1) : 0,
d = isBE ? -1 : 1,
s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
value = Math.abs(value);
if (isNaN(value) || value === Infinity) {
m = isNaN(value) ? 1 : 0;
e = eMax;
} else {
e = Math.floor(Math.log(value) / Math.LN2);
if (value * (c = Math.pow(2, -e)) < 1) {
e--;
c *= 2;
}
if (e + eBias >= 1) {
value += rt / c;
} else {
value += rt * Math.pow(2, 1 - eBias);
}
if (value * c >= 2) {
e++;
c /= 2;
}
if (e + eBias >= eMax) {
m = 0;
e = eMax;
} else if (e + eBias >= 1) {
m = (value * c - 1) * Math.pow(2, mLen);
e = e + eBias;
} else {
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
e = 0;
}
}
for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);
e = (e << mLen) | m;
eLen += mLen;
for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);
buffer[offset + i - d] |= s * 128;
};

1
node.gyp

@ -22,7 +22,6 @@
'lib/_linklist.js',
'lib/assert.js',
'lib/buffer.js',
'lib/buffer_ieee754.js',
'lib/child_process.js',
'lib/console.js',
'lib/constants.js',

141
src/node_buffer.cc

@ -28,6 +28,8 @@
#include <assert.h>
#include <string.h> // memcpy
#include <float.h> // float limits
#include <math.h> // infinity
#define MIN(a,b) ((a) < (b) ? (a) : (b))
@ -671,6 +673,137 @@ Handle<Value> Buffer::BinaryWrite(const Arguments &args) {
}
static bool is_big_endian() {
const union { uint8_t u8[2]; uint16_t u16; } u = {{0, 1}};
return u.u16 == 1 ? true : false;
}
static void swizzle(char* buf, size_t len) {
char t;
for (size_t i = 0; i < len / 2; ++i) {
t = buf[i];
buf[i] = buf[len - i - 1];
buf[len - i - 1] = t;
}
}
inline bool OutOfRangeCheck(float val, double val_tmp) {
if ((val_tmp > 0 && (val_tmp > FLT_MAX || val_tmp < FLT_MIN)
&& val_tmp != INFINITY) ||
((val_tmp < 0 && (val_tmp < -FLT_MAX || val_tmp > -FLT_MIN)
&& val_tmp != -INFINITY)))
return true;
return false;
}
template <typename T, bool ENDIANNESS>
Handle<Value> ReadFloatGeneric(const Arguments& args) {
double offset_tmp = args[0]->NumberValue();
int64_t offset = static_cast<int64_t>(offset_tmp);
bool doAssert = !args[1]->BooleanValue();
if (doAssert) {
if (offset_tmp != offset || offset < 0)
return ThrowTypeError("offset is not uint");
size_t len = static_cast<size_t>(
args.This()->GetIndexedPropertiesExternalArrayDataLength());
if (offset + sizeof(T) > len)
return ThrowRangeError("Trying to read beyond buffer length");
}
T val;
char* data = static_cast<char*>(
args.This()->GetIndexedPropertiesExternalArrayData());
char* ptr = data + offset;
memcpy(&val, ptr, sizeof(T));
if (ENDIANNESS != is_big_endian())
swizzle(reinterpret_cast<char*>(&val), sizeof(T));
// TODO: when Number::New is updated to accept an Isolate, make the change
return Number::New(val);
}
Handle<Value> Buffer::ReadFloatLE(const Arguments& args) {
return ReadFloatGeneric<float, false>(args);
}
Handle<Value> Buffer::ReadFloatBE(const Arguments& args) {
return ReadFloatGeneric<float, true>(args);
}
Handle<Value> Buffer::ReadDoubleLE(const Arguments& args) {
return ReadFloatGeneric<double, false>(args);
}
Handle<Value> Buffer::ReadDoubleBE(const Arguments& args) {
return ReadFloatGeneric<double, true>(args);
}
template <typename T, bool ENDIANNESS>
Handle<Value> WriteFloatGeneric(const Arguments& args) {
bool doAssert = !args[2]->BooleanValue();
if (doAssert) {
if (!args[0]->IsNumber())
return ThrowTypeError("value not a number");
}
double val_tmp = args[0]->NumberValue();
T val = static_cast<T>(val_tmp);
double offset_tmp = args[1]->NumberValue();
int64_t offset = static_cast<int64_t>(offset_tmp);
char* data = static_cast<char*>(
args.This()->GetIndexedPropertiesExternalArrayData());
char* ptr = data + offset;
if (doAssert) {
if (offset_tmp != offset || offset < 0)
return ThrowTypeError("offset is not uint");
if (sizeof(T) == 4 && OutOfRangeCheck(val, val_tmp))
return ThrowRangeError("value is out of type range");
size_t len = static_cast<size_t>(
args.This()->GetIndexedPropertiesExternalArrayDataLength());
if (offset + sizeof(T) > len)
return ThrowRangeError("Trying to write beyond buffer length");
}
memcpy(ptr, &val, sizeof(T));
if (ENDIANNESS != is_big_endian())
swizzle(ptr, sizeof(T));
return Undefined(node_isolate);
}
Handle<Value> Buffer::WriteFloatLE(const Arguments& args) {
return WriteFloatGeneric<float, false>(args);
}
Handle<Value> Buffer::WriteFloatBE(const Arguments& args) {
return WriteFloatGeneric<float, true>(args);
}
Handle<Value> Buffer::WriteDoubleLE(const Arguments& args) {
return WriteFloatGeneric<double, false>(args);
}
Handle<Value> Buffer::WriteDoubleBE(const Arguments& args) {
return WriteFloatGeneric<double, true>(args);
}
// var nbytes = Buffer.byteLength("string", "utf8")
Handle<Value> Buffer::ByteLength(const Arguments &args) {
HandleScope scope;
@ -814,6 +947,14 @@ void Buffer::Initialize(Handle<Object> target) {
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, "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);

8
src/node_buffer.h

@ -123,6 +123,14 @@ class NODE_EXTERN Buffer: public ObjectWrap {
static v8::Handle<v8::Value> AsciiWrite(const v8::Arguments &args);
static v8::Handle<v8::Value> Utf8Write(const v8::Arguments &args);
static v8::Handle<v8::Value> Ucs2Write(const v8::Arguments &args);
static v8::Handle<v8::Value> ReadFloatLE(const v8::Arguments &args);
static v8::Handle<v8::Value> ReadFloatBE(const v8::Arguments &args);
static v8::Handle<v8::Value> ReadDoubleLE(const v8::Arguments &args);
static v8::Handle<v8::Value> ReadDoubleBE(const v8::Arguments &args);
static v8::Handle<v8::Value> WriteFloatLE(const v8::Arguments &args);
static v8::Handle<v8::Value> WriteFloatBE(const v8::Arguments &args);
static v8::Handle<v8::Value> WriteDoubleLE(const v8::Arguments &args);
static v8::Handle<v8::Value> WriteDoubleBE(const v8::Arguments &args);
static v8::Handle<v8::Value> ByteLength(const v8::Arguments &args);
static v8::Handle<v8::Value> MakeFastBuffer(const v8::Arguments &args);
static v8::Handle<v8::Value> Fill(const v8::Arguments &args);

44
test/simple/test-buffer.js

@ -812,3 +812,47 @@ assert.throws(function() {
assert.throws(function() {
new Buffer(0xFFFFFFFFF);
}, TypeError);
// 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/);
assert.throws(function() {
var buf = new Buffer(8);
buf.writeFloatLE(0.0, 0xffffffff);
}, /Trying to access beyond buffer length/);
assert.throws(function() {
var buf = new SlowBuffer(8);
buf.readFloatLE(0xffffffff);
}, /Trying to read beyond buffer length/);
assert.throws(function() {
var buf = new SlowBuffer(8);
buf.writeFloatLE(0.0, 0xffffffff);
}, /Trying to write beyond buffer length/);
// ensure negative values can't get past offset
assert.throws(function() {
var buf = new Buffer(8);
buf.readFloatLE(-1);
}, /offset is not uint/);
assert.throws(function() {
var buf = new Buffer(8);
buf.writeFloatLE(0.0, -1);
}, /offset is not uint/);
assert.throws(function() {
var buf = new SlowBuffer(8);
buf.readFloatLE(-1);
}, /offset is not uint/);
assert.throws(function() {
var buf = new SlowBuffer(8);
buf.writeFloatLE(0.0, -1);
}, /offset is not uint/);

8
test/simple/test-writedouble.js

@ -169,20 +169,20 @@ function test(clazz) {
buffer.writeDoubleBE(NaN, 0);
buffer.writeDoubleLE(NaN, 8);
ASSERT.equal(0x7F, buffer[0]);
ASSERT.equal(0xF0, buffer[1]);
ASSERT.equal(0xF8, buffer[1]);
ASSERT.equal(0x00, buffer[2]);
ASSERT.equal(0x00, buffer[3]);
ASSERT.equal(0x00, buffer[4]);
ASSERT.equal(0x00, buffer[5]);
ASSERT.equal(0x00, buffer[6]);
ASSERT.equal(0x01, buffer[7]);
ASSERT.equal(0x01, buffer[8]);
ASSERT.equal(0x00, buffer[7]);
ASSERT.equal(0x00, buffer[8]);
ASSERT.equal(0x00, buffer[9]);
ASSERT.equal(0x00, buffer[10]);
ASSERT.equal(0x00, buffer[11]);
ASSERT.equal(0x00, buffer[12]);
ASSERT.equal(0x00, buffer[13]);
ASSERT.equal(0xF0, buffer[14]);
ASSERT.equal(0xF8, buffer[14]);
ASSERT.equal(0x7F, buffer[15]);
ASSERT.ok(isNaN(buffer.readDoubleBE(0)));
ASSERT.ok(isNaN(buffer.readDoubleLE(8)));

30
test/simple/test-writefloat.js

@ -40,17 +40,6 @@ function test(clazz) {
ASSERT.equal(0x80, buffer[6]);
ASSERT.equal(0x3f, buffer[7]);
buffer.writeFloatBE(1.793662034335766e-43, 0);
buffer.writeFloatLE(1.793662034335766e-43, 4);
ASSERT.equal(0x00, buffer[0]);
ASSERT.equal(0x00, buffer[1]);
ASSERT.equal(0x00, buffer[2]);
ASSERT.equal(0x80, buffer[3]);
ASSERT.equal(0x80, buffer[4]);
ASSERT.equal(0x00, buffer[5]);
ASSERT.equal(0x00, buffer[6]);
ASSERT.equal(0x00, buffer[7]);
buffer.writeFloatBE(1 / 3, 0);
buffer.writeFloatLE(1 / 3, 4);
ASSERT.equal(0x3e, buffer[0]);
@ -73,6 +62,17 @@ function test(clazz) {
ASSERT.equal(0x7f, buffer[6]);
ASSERT.equal(0x7f, buffer[7]);
buffer.writeFloatLE(1.1754943508222875e-38, 0);
buffer.writeFloatBE(1.1754943508222875e-38, 4);
ASSERT.equal(0x00, buffer[0]);
ASSERT.equal(0x00, buffer[1]);
ASSERT.equal(0x80, buffer[2]);
ASSERT.equal(0x00, buffer[3]);
ASSERT.equal(0x00, buffer[4]);
ASSERT.equal(0x80, buffer[5]);
ASSERT.equal(0x00, buffer[6]);
ASSERT.equal(0x00, buffer[7]);
buffer.writeFloatBE(0 * -1, 0);
buffer.writeFloatLE(0 * -1, 4);
ASSERT.equal(0x80, buffer[0]);
@ -113,12 +113,12 @@ function test(clazz) {
buffer.writeFloatBE(NaN, 0);
buffer.writeFloatLE(NaN, 4);
ASSERT.equal(0x7F, buffer[0]);
ASSERT.equal(0x80, buffer[1]);
ASSERT.equal(0xc0, buffer[1]);
ASSERT.equal(0x00, buffer[2]);
ASSERT.equal(0x01, buffer[3]);
ASSERT.equal(0x01, buffer[4]);
ASSERT.equal(0x00, buffer[3]);
ASSERT.equal(0x00, buffer[4]);
ASSERT.equal(0x00, buffer[5]);
ASSERT.equal(0x80, buffer[6]);
ASSERT.equal(0xc0, buffer[6]);
ASSERT.equal(0x7F, buffer[7]);
ASSERT.ok(isNaN(buffer.readFloatBE(0)));
ASSERT.ok(isNaN(buffer.readFloatLE(4)));

Loading…
Cancel
Save