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) { function checkInt(buffer, value, offset, ext, max, min) {
if (!(buffer instanceof Buffer)) if (!(buffer instanceof Buffer))
throw new TypeError('buffer must be a Buffer instance'); throw new TypeError('buffer must be a Buffer instance');
@ -705,3 +737,51 @@ Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
this[offset + 3] = value; this[offset + 3] = value;
return offset + 4; 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> template <typename T, enum Endianness endianness>
void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) { void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args.GetIsolate()); ARGS_THIS(args[0].As<Object>());
bool doAssert = !args[1]->BooleanValue();
size_t offset;
CHECK_NOT_OOB(ParseArrayIndex(args[0], 0, &offset)); uint32_t offset = args[1]->Uint32Value();
CHECK_LE(offset + sizeof(T), obj_length);
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");
}
union NoAlias { union NoAlias {
T val; T val;
@ -480,8 +473,7 @@ void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) {
}; };
union NoAlias na; union NoAlias na;
const void* data = args.This()->GetIndexedPropertiesExternalArrayData(); const char* ptr = static_cast<const char*>(obj_data) + offset;
const char* ptr = static_cast<const char*>(data) + offset;
memcpy(na.bytes, ptr, sizeof(na.bytes)); memcpy(na.bytes, ptr, sizeof(na.bytes));
if (endianness != GetEndianness()) if (endianness != GetEndianness())
Swizzle(na.bytes, sizeof(na.bytes)); Swizzle(na.bytes, sizeof(na.bytes));
@ -512,24 +504,11 @@ void ReadDoubleBE(const FunctionCallbackInfo<Value>& args) {
template <typename T, enum Endianness endianness> template <typename T, enum Endianness endianness>
uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) { uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args.GetIsolate()); ARGS_THIS(args[0].As<Object>())
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;
}
if (doAssert) { T val = args[1]->NumberValue();
size_t len = Length(args.This()); uint32_t offset = args[2]->Uint32Value();
if (offset + sizeof(T) > len || offset + sizeof(T) < offset) { CHECK_LE(offset + sizeof(T), obj_length);
env->ThrowRangeError("Trying to write beyond buffer length");
return 0;
}
}
union NoAlias { union NoAlias {
T val; T val;
@ -537,8 +516,7 @@ uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
}; };
union NoAlias na = { val }; union NoAlias na = { val };
void* data = args.This()->GetIndexedPropertiesExternalArrayData(); char* ptr = static_cast<char*>(obj_data) + offset;
char* ptr = static_cast<char*>(data) + offset;
if (endianness != GetEndianness()) if (endianness != GetEndianness())
Swizzle(na.bytes, sizeof(na.bytes)); Swizzle(na.bytes, sizeof(na.bytes));
memcpy(ptr, 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, "ucs2Write", Ucs2Write);
NODE_SET_METHOD(proto, "utf8Write", Utf8Write); 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); NODE_SET_METHOD(proto, "copy", Copy);
// for backwards compatibility // for backwards compatibility
@ -668,6 +636,16 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
NODE_SET_METHOD(internal, "byteLength", ByteLength); NODE_SET_METHOD(internal, "byteLength", ByteLength);
NODE_SET_METHOD(internal, "compare", Compare); NODE_SET_METHOD(internal, "compare", Compare);
NODE_SET_METHOD(internal, "fill", Fill); 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