buffer: allow ArrayBuffer as Buffer argument
authorTrevor Norris <trev.norris@gmail.com>
Wed, 17 Jun 2015 16:43:13 +0000 (10:43 -0600)
committerRod Vagg <rod@vagg.org>
Tue, 4 Aug 2015 18:56:13 +0000 (11:56 -0700)
Buffer now uses the ArrayBuffer as the backing store if passed to the
constructor.

Fixes: https://github.com/nodejs/io.js/issues/106
PR-URL: https://github.com/nodejs/io.js/pull/2002
Reviewed-By: Domenic Denicola <d@domenic.me>
lib/internal/buffer_new.js
src/node_buffer.cc
test/parallel/test-buffer-arraybuffer.js [new file with mode: 0644]

index 3b3a200..8a30fc5 100644 (file)
@@ -104,7 +104,10 @@ function fromObject(obj) {
     return b;
   }
 
-  // TODO(trevnorris): This will fail for an actual ArrayBuffer.
+  if (obj instanceof ArrayBuffer) {
+    return binding.createFromArrayBuffer(obj);
+  }
+
   if (obj.buffer instanceof ArrayBuffer || obj.length) {
     var length;
     if (typeof obj.length !== 'number' || obj.length !== obj.length)
index 96cd61f..dbd1489 100644 (file)
@@ -528,6 +528,20 @@ void CreateFromString(const FunctionCallbackInfo<Value>& args) {
 }
 
 
+void CreateFromArrayBuffer(const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  if (!args[0]->IsArrayBuffer())
+    return env->ThrowTypeError("argument is not an ArrayBuffer");
+  Local<ArrayBuffer> ab = args[0].As<ArrayBuffer>();
+  Local<Uint8Array> ui = Uint8Array::New(ab, 0, ab->ByteLength());
+  Maybe<bool> mb =
+      ui->SetPrototype(env->context(), env->buffer_prototype_object());
+  if (!mb.FromMaybe(false))
+    return env->ThrowError("Unable to set Object prototype");
+  args.GetReturnValue().Set(ui);
+}
+
+
 void Slice(const FunctionCallbackInfo<Value>& args) {
   CHECK(args[0]->IsUint8Array());
   CHECK(args[1]->IsNumber());
@@ -1171,6 +1185,7 @@ void Initialize(Handle<Object> target,
   env->SetMethod(target, "setupBufferJS", SetupBufferJS);
   env->SetMethod(target, "create", Create);
   env->SetMethod(target, "createFromString", CreateFromString);
+  env->SetMethod(target, "createFromArrayBuffer", CreateFromArrayBuffer);
 
   env->SetMethod(target, "slice", Slice);
   env->SetMethod(target, "byteLengthUtf8", ByteLengthUtf8);
diff --git a/test/parallel/test-buffer-arraybuffer.js b/test/parallel/test-buffer-arraybuffer.js
new file mode 100644 (file)
index 0000000..c13d0ba
--- /dev/null
@@ -0,0 +1,46 @@
+'use strict';
+
+const common = require('../common');
+const assert = require('assert');
+
+const Buffer = require('buffer').Buffer;
+const LENGTH = 16;
+
+const ab = new ArrayBuffer(LENGTH);
+const dv = new DataView(ab);
+const ui = new Uint8Array(ab);
+const buf = new Buffer(ab);
+
+
+assert.ok(buf instanceof Buffer);
+// For backwards compatibility of old .parent property test that if buf is not
+// a slice then .parent should be undefined.
+assert.equal(buf.parent, undefined);
+assert.equal(buf.buffer, ab);
+assert.equal(buf.length, ab.byteLength);
+
+
+buf.fill(0xC);
+for (let i = 0; i < LENGTH; i++) {
+  assert.equal(ui[i], 0xC);
+  ui[i] = 0xF;
+  assert.equal(buf[i], 0xF);
+}
+
+buf.writeUInt32LE(0xF00, 0);
+buf.writeUInt32BE(0xB47, 4);
+buf.writeDoubleLE(3.1415, 8);
+
+assert.equal(dv.getUint32(0, true), 0xF00);
+assert.equal(dv.getUint32(4), 0xB47);
+assert.equal(dv.getFloat64(8, true), 3.1415);
+
+
+// Now test protecting users from doing stupid things
+
+assert.throws(function() {
+  function AB() { }
+  AB.__proto__ = ArrayBuffer;
+  AB.prototype.__proto__ = ArrayBuffer.prototype;
+  new Buffer(new AB());
+}, TypeError);