@@ -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);
}
});

Large diffs are not rendered by default.

This file was deleted.

@@ -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',
@@ -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);

@@ -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);
@@ -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/);
@@ -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)));
@@ -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]);
Copy link
Author

@trevnorris trevnorris Jan 7, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above two values are greater than FLT_MAX, so removed the tests.

@@ -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);
Copy link
Author

@trevnorris trevnorris Jan 7, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a check for FLT_MIN values as well.

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]);
Copy link
Member

@bnoordhuis bnoordhuis Jan 4, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do the tests need updating?

Copy link
Author

@trevnorris trevnorris Jan 4, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, I'm not sure. They fail otherwise. But the updated result matches d8's implementation more closely. Run the following with d8:

var bf = new ArrayBuffer(4);
var uV = new Uint8Array(bf);
var fV = new Float32Array(bf);

fV[0] = NaN;

print(uV[0].toString(16) + ' ' +
      uV[1].toString(16) + ' ' +
      uV[2].toString(16) + ' ' +
      uV[3].toString(16));

// output: 0 0 c0 7f

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are different types of NaN. Apparently you are writing a quiet NaN and not a signaling NaN as we used to do.

(FYI, a quiet NaN has the highest bit of the mantissa set, whereas a signaling NaN is represented by having the highest bit of the mantissa cleared, but with at least one other bit of the mantissa set.)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO a quiet NaN is more appropriate btw.

Copy link
Member

@bnoordhuis bnoordhuis Jan 5, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO a quiet NaN is more appropriate btw.

Yes, agreed.

ASSERT.equal(0x7F, buffer[7]);
ASSERT.ok(isNaN(buffer.readFloatBE(0)));
ASSERT.ok(isNaN(buffer.readFloatLE(4)));