From fcf0e8ebdf5aac09f0588f7d460da24b09f33734 Mon Sep 17 00:00:00 2001
From: Matt Loring <mattloring@google.com>
Date: Tue, 10 Nov 2015 18:25:01 -0800
Subject: [PATCH] buffer: move checkFloat from lib into src

The type and range checks performed by this function can be done more
efficiently in native code.

PR-URL: https://github.com/nodejs/node/pull/3763
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
---
 lib/buffer.js      | 28 ++++++++++++----------------
 src/node_buffer.cc | 43 ++++++++++++++++++++++++++++++-------------
 2 files changed, 42 insertions(+), 29 deletions(-)

diff --git a/lib/buffer.js b/lib/buffer.js
index 53553253209923..210fdf4f058ff2 100644
--- a/lib/buffer.js
+++ b/lib/buffer.js
@@ -1028,20 +1028,13 @@ Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
 };
 
 
-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);
-  binding.writeFloatLE(this, val, offset);
+    binding.writeFloatLE(this, val, offset);
+  else
+    binding.writeFloatLE(this, val, offset, true);
   return offset + 4;
 };
 
@@ -1050,8 +1043,9 @@ Buffer.prototype.writeFloatBE = function writeFloatBE(val, offset, noAssert) {
   val = +val;
   offset = offset >>> 0;
   if (!noAssert)
-    checkFloat(this, val, offset, 4);
-  binding.writeFloatBE(this, val, offset);
+    binding.writeFloatBE(this, val, offset);
+  else
+    binding.writeFloatBE(this, val, offset, true);
   return offset + 4;
 };
 
@@ -1060,8 +1054,9 @@ Buffer.prototype.writeDoubleLE = function writeDoubleLE(val, offset, noAssert) {
   val = +val;
   offset = offset >>> 0;
   if (!noAssert)
-    checkFloat(this, val, offset, 8);
-  binding.writeDoubleLE(this, val, offset);
+    binding.writeDoubleLE(this, val, offset);
+  else
+    binding.writeDoubleLE(this, val, offset, true);
   return offset + 8;
 };
 
@@ -1070,7 +1065,8 @@ Buffer.prototype.writeDoubleBE = function writeDoubleBE(val, offset, noAssert) {
   val = +val;
   offset = offset >>> 0;
   if (!noAssert)
-    checkFloat(this, val, offset, 8);
-  binding.writeDoubleBE(this, val, offset);
+    binding.writeDoubleBE(this, val, offset);
+  else
+    binding.writeDoubleBE(this, val, offset, true);
   return offset + 8;
 };
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
index b508c48c4742e4..8b850700792a5b 100644
--- a/src/node_buffer.cc
+++ b/src/node_buffer.cc
@@ -730,15 +730,37 @@ void ReadDoubleBE(const FunctionCallbackInfo<Value>& args) {
 
 
 template <typename T, enum Endianness endianness>
-uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
-  SPREAD_ARG(args[0], ts_obj);
+void WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+
+  bool should_assert = args.Length() < 4;
+
+  if (should_assert) {
+    THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
+  }
+
+  Local<Uint8Array> ts_obj = args[0].As<Uint8Array>();
+  ArrayBuffer::Contents ts_obj_c = ts_obj->Buffer()->GetContents();
+  const size_t ts_obj_offset = ts_obj->ByteOffset();
+  const size_t ts_obj_length = ts_obj->ByteLength();
+  char* const ts_obj_data =
+      static_cast<char*>(ts_obj_c.Data()) + ts_obj_offset;
+  if (ts_obj_length > 0)
+    CHECK_NE(ts_obj_data, nullptr);
+
+  T val = args[1]->NumberValue(env->context()).FromMaybe(0);
+  size_t offset = args[2]->IntegerValue(env->context()).FromMaybe(0);
 
-  T val = args[1]->NumberValue();
-  uint32_t offset = args[2]->Uint32Value();
   size_t memcpy_num = sizeof(T);
   if (offset + sizeof(T) > ts_obj_length)
     memcpy_num = ts_obj_length - offset;
 
+  if (should_assert) {
+    CHECK_NOT_OOB(offset + memcpy_num >= memcpy_num);
+    CHECK_NOT_OOB(offset + memcpy_num <= ts_obj_length);
+  }
+  CHECK_LE(offset + memcpy_num, ts_obj_length);
+
   union NoAlias {
     T val;
     char bytes[sizeof(T)];
@@ -749,31 +771,26 @@ uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
   if (endianness != GetEndianness())
     Swizzle(na.bytes, sizeof(na.bytes));
   memcpy(ptr, na.bytes, memcpy_num);
-  return offset + memcpy_num;
 }
 
 
 void WriteFloatLE(const FunctionCallbackInfo<Value>& args) {
-  THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
-  args.GetReturnValue().Set(WriteFloatGeneric<float, kLittleEndian>(args));
+  WriteFloatGeneric<float, kLittleEndian>(args);
 }
 
 
 void WriteFloatBE(const FunctionCallbackInfo<Value>& args) {
-  THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
-  args.GetReturnValue().Set(WriteFloatGeneric<float, kBigEndian>(args));
+  WriteFloatGeneric<float, kBigEndian>(args);
 }
 
 
 void WriteDoubleLE(const FunctionCallbackInfo<Value>& args) {
-  THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
-  args.GetReturnValue().Set(WriteFloatGeneric<double, kLittleEndian>(args));
+  WriteFloatGeneric<double, kLittleEndian>(args);
 }
 
 
 void WriteDoubleBE(const FunctionCallbackInfo<Value>& args) {
-  THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
-  args.GetReturnValue().Set(WriteFloatGeneric<double, kBigEndian>(args));
+  WriteFloatGeneric<double, kBigEndian>(args);
 }