Browse Source

buffer: mv floating point read/write checks to JS

Performance improvement by moving checks for floating point operations
to JS and doing the operation on a protected internal function that
assumes all arguments are correct. Still abort if the operation
overflows memory. This can only be caused if the Buffer's length
property isn't the same as the actual internal length.

Signed-off-by: Trevor Norris <trev.norris@gmail.com>
v0.11.15-release
Trevor Norris 10 years ago
parent
commit
e9ca7b9d8d
  1. 80
      lib/buffer.js
  2. 60
      src/node_buffer.cc

80
lib/buffer.js

@ -581,6 +581,38 @@ Buffer.prototype.readInt32BE = function(offset, noAssert) {
};
Buffer.prototype.readFloatLE = function readFloatLE(offset, noAssert) {
offset = offset >>> 0;
if (!noAssert)
checkOffset(offset, 4, this.length);
return internal.readFloatLE(this, offset);
};
Buffer.prototype.readFloatBE = function readFloatBE(offset, noAssert) {
offset = offset >>> 0;
if (!noAssert)
checkOffset(offset, 4, this.length);
return internal.readFloatBE(this, offset);
};
Buffer.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) {
offset = offset >>> 0;
if (!noAssert)
checkOffset(offset, 8, this.length);
return internal.readDoubleLE(this, offset);
};
Buffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) {
offset = offset >>> 0;
if (!noAssert)
checkOffset(offset, 8, this.length);
return internal.readDoubleBE(this, offset);
};
function checkInt(buffer, value, offset, ext, max, min) {
if (!(buffer instanceof Buffer))
throw new TypeError('buffer must be a Buffer instance');
@ -705,3 +737,51 @@ Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
this[offset + 3] = value;
return offset + 4;
};
function checkFloat(buffer, value, offset, ext) {
if (!(buffer instanceof Buffer))
throw new TypeError('buffer must be a Buffer instance');
if (offset + ext > buffer.length)
throw new RangeError('index out of range');
}
Buffer.prototype.writeFloatLE = function writeFloatLE(val, offset, noAssert) {
val = +val;
offset = offset >>> 0;
if (!noAssert)
checkFloat(this, val, offset, 4);
internal.writeFloatLE(this, val, offset);
return offset + 4;
};
Buffer.prototype.writeFloatBE = function writeFloatBE(val, offset, noAssert) {
val = +val;
offset = offset >>> 0;
if (!noAssert)
checkFloat(this, val, offset, 4);
internal.writeFloatBE(this, val, offset);
return offset + 4;
};
Buffer.prototype.writeDoubleLE = function writeDoubleLE(val, offset, noAssert) {
val = +val;
offset = offset >>> 0;
if (!noAssert)
checkFloat(this, val, offset, 8);
internal.writeDoubleLE(this, val, offset);
return offset + 8;
};
Buffer.prototype.writeDoubleBE = function writeDoubleBE(val, offset, noAssert) {
val = +val;
offset = offset >>> 0;
if (!noAssert)
checkFloat(this, val, offset, 8);
internal.writeDoubleBE(this, val, offset);
return offset + 8;
};

60
src/node_buffer.cc

@ -462,17 +462,10 @@ static inline void Swizzle(char* start, unsigned int len) {
template <typename T, enum Endianness endianness>
void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args.GetIsolate());
bool doAssert = !args[1]->BooleanValue();
size_t offset;
ARGS_THIS(args[0].As<Object>());
CHECK_NOT_OOB(ParseArrayIndex(args[0], 0, &offset));
if (doAssert) {
size_t len = Length(args.This());
if (offset + sizeof(T) > len || offset + sizeof(T) < offset)
return env->ThrowRangeError("Trying to read beyond buffer length");
}
uint32_t offset = args[1]->Uint32Value();
CHECK_LE(offset + sizeof(T), obj_length);
union NoAlias {
T val;
@ -480,8 +473,7 @@ void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) {
};
union NoAlias na;
const void* data = args.This()->GetIndexedPropertiesExternalArrayData();
const char* ptr = static_cast<const char*>(data) + offset;
const char* ptr = static_cast<const char*>(obj_data) + offset;
memcpy(na.bytes, ptr, sizeof(na.bytes));
if (endianness != GetEndianness())
Swizzle(na.bytes, sizeof(na.bytes));
@ -512,24 +504,11 @@ void ReadDoubleBE(const FunctionCallbackInfo<Value>& args) {
template <typename T, enum Endianness endianness>
uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args.GetIsolate());
bool doAssert = !args[2]->BooleanValue();
T val = static_cast<T>(args[0]->NumberValue());
size_t offset;
if (!ParseArrayIndex(args[1], 0, &offset)) {
env->ThrowRangeError("out of range index");
return 0;
}
ARGS_THIS(args[0].As<Object>())
if (doAssert) {
size_t len = Length(args.This());
if (offset + sizeof(T) > len || offset + sizeof(T) < offset) {
env->ThrowRangeError("Trying to write beyond buffer length");
return 0;
}
}
T val = args[1]->NumberValue();
uint32_t offset = args[2]->Uint32Value();
CHECK_LE(offset + sizeof(T), obj_length);
union NoAlias {
T val;
@ -537,8 +516,7 @@ uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
};
union NoAlias na = { val };
void* data = args.This()->GetIndexedPropertiesExternalArrayData();
char* ptr = static_cast<char*>(data) + offset;
char* ptr = static_cast<char*>(obj_data) + offset;
if (endianness != GetEndianness())
Swizzle(na.bytes, sizeof(na.bytes));
memcpy(ptr, na.bytes, sizeof(na.bytes));
@ -643,16 +621,6 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
NODE_SET_METHOD(proto, "ucs2Write", Ucs2Write);
NODE_SET_METHOD(proto, "utf8Write", Utf8Write);
NODE_SET_METHOD(proto, "readDoubleBE", ReadDoubleBE);
NODE_SET_METHOD(proto, "readDoubleLE", ReadDoubleLE);
NODE_SET_METHOD(proto, "readFloatBE", ReadFloatBE);
NODE_SET_METHOD(proto, "readFloatLE", ReadFloatLE);
NODE_SET_METHOD(proto, "writeDoubleBE", WriteDoubleBE);
NODE_SET_METHOD(proto, "writeDoubleLE", WriteDoubleLE);
NODE_SET_METHOD(proto, "writeFloatBE", WriteFloatBE);
NODE_SET_METHOD(proto, "writeFloatLE", WriteFloatLE);
NODE_SET_METHOD(proto, "copy", Copy);
// for backwards compatibility
@ -668,6 +636,16 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
NODE_SET_METHOD(internal, "byteLength", ByteLength);
NODE_SET_METHOD(internal, "compare", Compare);
NODE_SET_METHOD(internal, "fill", Fill);
NODE_SET_METHOD(internal, "readDoubleBE", ReadDoubleBE);
NODE_SET_METHOD(internal, "readDoubleLE", ReadDoubleLE);
NODE_SET_METHOD(internal, "readFloatBE", ReadFloatBE);
NODE_SET_METHOD(internal, "readFloatLE", ReadFloatLE);
NODE_SET_METHOD(internal, "writeDoubleBE", WriteDoubleBE);
NODE_SET_METHOD(internal, "writeDoubleLE", WriteDoubleLE);
NODE_SET_METHOD(internal, "writeFloatBE", WriteFloatBE);
NODE_SET_METHOD(internal, "writeFloatLE", WriteFloatLE);
}

Loading…
Cancel
Save