buffer: mv floating point read/write checks to JS
authorTrevor Norris <trev.norris@gmail.com>
Wed, 1 Oct 2014 22:42:02 +0000 (15:42 -0700)
committerTrevor Norris <trev.norris@gmail.com>
Wed, 1 Oct 2014 22:59:55 +0000 (15:59 -0700)
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>
lib/buffer.js
src/node_buffer.cc

index 7fd2975..80f6a42 100644 (file)
@@ -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;
+};
index 8be551d..3f8de59 100644 (file)
@@ -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);
 }