From 356a0f684bc98cbce3051e4a18d8acdd428661c9 Mon Sep 17 00:00:00 2001
From: LiviaMedeiros <livia@cirno.name>
Date: Sat, 16 Jul 2022 00:32:30 +0800
Subject: [PATCH 1/3] buffer: add from(ArrayBufferView) support

---
 doc/api/buffer.md                  | 12 ++++--
 lib/buffer.js                      | 12 +++++-
 test/parallel/test-buffer-alloc.js | 66 ++++++++++++++++++++++++++----
 3 files changed, 77 insertions(+), 13 deletions(-)

diff --git a/doc/api/buffer.md b/doc/api/buffer.md
index 5757e93e314c78..7cbcc34ff47b08 100644
--- a/doc/api/buffer.md
+++ b/doc/api/buffer.md
@@ -1207,10 +1207,14 @@ console.log(buf);
 
 <!-- YAML
 added: v5.10.0
+changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/43863
+    description: The `buffer` parameter can now be an any `ArrayBufferView`.
 -->
 
-* `buffer` {Buffer|Uint8Array} An existing `Buffer` or [`Uint8Array`][] from
-  which to copy data.
+* `buffer` {Buffer|ArrayBufferView} An existing `Buffer`, [`TypedArray`][],
+  or [`DataView`][] from which to copy data.
 
 Copies the passed `buffer` data onto a new `Buffer` instance.
 
@@ -1242,8 +1246,8 @@ console.log(buf2.toString());
 // Prints: buffer
 ```
 
-A `TypeError` will be thrown if `buffer` is not a `Buffer` or another type
-appropriate for `Buffer.from()` variants.
+A `TypeError` will be thrown if `buffer` is not an `ArrayBufferView`
+or another type appropriate for `Buffer.from()` variants.
 
 ### Static method: `Buffer.from(object[, offsetOrEncoding[, length]])`
 
diff --git a/lib/buffer.js b/lib/buffer.js
index d5fde6debc2f74..d61c4f967f02aa 100644
--- a/lib/buffer.js
+++ b/lib/buffer.js
@@ -83,7 +83,8 @@ const {
 const {
   isAnyArrayBuffer,
   isArrayBufferView,
-  isUint8Array
+  isTypedArray,
+  isUint8Array,
 } = require('internal/util/types');
 const {
   inspect: utilInspect
@@ -306,6 +307,15 @@ Buffer.from = function from(value, encodingOrOffset, length) {
     if (isAnyArrayBuffer(value))
       return fromArrayBuffer(value, encodingOrOffset, length);
 
+    if (isArrayBufferView(value) && !isUint8Array(value)) {
+      if (!isTypedArray(value)) {
+        // DataView is not a TypedArray
+        return fromArrayBuffer(value.buffer.slice(value.byteOffset,
+                                                  value.byteOffset + value.byteLength));
+      }
+      return fromArrayBuffer(value.slice().buffer);
+    }
+
     const valueOf = value.valueOf && value.valueOf();
     if (valueOf != null &&
         valueOf !== value &&
diff --git a/test/parallel/test-buffer-alloc.js b/test/parallel/test-buffer-alloc.js
index d2085d11802aee..ffe7bbf48427d0 100644
--- a/test/parallel/test-buffer-alloc.js
+++ b/test/parallel/test-buffer-alloc.js
@@ -59,22 +59,72 @@ assert.strictEqual(d.length, 0);
 }
 
 // Test creating a Buffer from a Uint32Array
-// Note: it is implicitly interpreted as Array of integers modulo 256
 {
   const ui32 = new Uint32Array(4).fill(42);
   const e = Buffer.from(ui32);
-  for (const [index, value] of e.entries()) {
-    assert.strictEqual(value, ui32[index]);
-  }
+  assert.deepStrictEqual(e, Buffer.from([
+    42, 0, 0, 0,
+    42, 0, 0, 0,
+    42, 0, 0, 0,
+    42, 0, 0, 0,
+  ]));
 }
 // Test creating a Buffer from a Uint32Array (old constructor)
-// Note: it is implicitly interpreted as Array of integers modulo 256
 {
   const ui32 = new Uint32Array(4).fill(42);
   const e = Buffer(ui32);
-  for (const [key, value] of e.entries()) {
-    assert.strictEqual(value, ui32[key]);
-  }
+  assert.deepStrictEqual(e, Buffer.from([
+    42, 0, 0, 0,
+    42, 0, 0, 0,
+    42, 0, 0, 0,
+    42, 0, 0, 0,
+  ]));
+}
+
+// Test creating a Buffer from a BigUint64Array
+{
+  const bui64 = new BigUint64Array(4).fill(42n);
+  const e = Buffer.from(bui64);
+  assert.deepStrictEqual(e, Buffer.from([
+    42, 0, 0, 0, 0, 0, 0, 0,
+    42, 0, 0, 0, 0, 0, 0, 0,
+    42, 0, 0, 0, 0, 0, 0, 0,
+    42, 0, 0, 0, 0, 0, 0, 0,
+  ]));
+}
+// Test creating a Buffer from a BigUint64Array (old constructor)
+{
+  const bui64 = new BigUint64Array(4).fill(42n);
+  const e = Buffer(bui64);
+  assert.deepStrictEqual(e, Buffer.from([
+    42, 0, 0, 0, 0, 0, 0, 0,
+    42, 0, 0, 0, 0, 0, 0, 0,
+    42, 0, 0, 0, 0, 0, 0, 0,
+    42, 0, 0, 0, 0, 0, 0, 0,
+  ]));
+}
+
+// Test creating a Buffer from a Float64Array
+{
+  const f64 = new Float64Array(4).fill(42);
+  const e = Buffer.from(f64);
+  assert.deepStrictEqual(e, Buffer.from([
+    0, 0, 0, 0, 0, 0, 69, 64,
+    0, 0, 0, 0, 0, 0, 69, 64,
+    0, 0, 0, 0, 0, 0, 69, 64,
+    0, 0, 0, 0, 0, 0, 69, 64,
+  ]));
+}
+// Test creating a Buffer from a Float64Array (old constructor)
+{
+  const f64 = new Float64Array(4).fill(42);
+  const e = Buffer(f64);
+  assert.deepStrictEqual(e, Buffer.from([
+    0, 0, 0, 0, 0, 0, 69, 64,
+    0, 0, 0, 0, 0, 0, 69, 64,
+    0, 0, 0, 0, 0, 0, 69, 64,
+    0, 0, 0, 0, 0, 0, 69, 64,
+  ]));
 }
 
 // Test invalid encoding for Buffer.toString

From 4ac40d60442f171b8110c7653f0bcca13f7b69b1 Mon Sep 17 00:00:00 2001
From: LiviaMedeiros <livia@cirno.name>
Date: Sun, 17 Jul 2022 17:11:24 +0800
Subject: [PATCH 2/3] squash: test when endianness is big

---
 test/parallel/test-buffer-alloc.js | 45 ++++++++++++++++++++++++++----
 1 file changed, 39 insertions(+), 6 deletions(-)

diff --git a/test/parallel/test-buffer-alloc.js b/test/parallel/test-buffer-alloc.js
index ffe7bbf48427d0..747a312988eede 100644
--- a/test/parallel/test-buffer-alloc.js
+++ b/test/parallel/test-buffer-alloc.js
@@ -3,9 +3,12 @@ const common = require('../common');
 
 const assert = require('assert');
 const vm = require('vm');
+const os = require('os');
 
 const SlowBuffer = require('buffer').SlowBuffer;
 
+const isBigEnd = os.endianness() === 'BE';
+
 // Verify the maximum Uint8Array size. There is no concrete limit by spec. The
 // internal limits should be updated if this fails.
 assert.throws(
@@ -62,7 +65,12 @@ assert.strictEqual(d.length, 0);
 {
   const ui32 = new Uint32Array(4).fill(42);
   const e = Buffer.from(ui32);
-  assert.deepStrictEqual(e, Buffer.from([
+  assert.deepStrictEqual(e, Buffer.from(isBigEnd ? [
+    0, 0, 0, 42,
+    0, 0, 0, 42,
+    0, 0, 0, 42,
+    0, 0, 0, 42,
+  ] : [
     42, 0, 0, 0,
     42, 0, 0, 0,
     42, 0, 0, 0,
@@ -73,7 +81,12 @@ assert.strictEqual(d.length, 0);
 {
   const ui32 = new Uint32Array(4).fill(42);
   const e = Buffer(ui32);
-  assert.deepStrictEqual(e, Buffer.from([
+  assert.deepStrictEqual(e, Buffer.from(isBigEnd ? [
+    0, 0, 0, 42,
+    0, 0, 0, 42,
+    0, 0, 0, 42,
+    0, 0, 0, 42,
+  ] : [
     42, 0, 0, 0,
     42, 0, 0, 0,
     42, 0, 0, 0,
@@ -85,7 +98,12 @@ assert.strictEqual(d.length, 0);
 {
   const bui64 = new BigUint64Array(4).fill(42n);
   const e = Buffer.from(bui64);
-  assert.deepStrictEqual(e, Buffer.from([
+  assert.deepStrictEqual(e, Buffer.from(isBigEnd ? [
+    0, 0, 0, 0, 0, 0, 0, 42,
+    0, 0, 0, 0, 0, 0, 0, 42,
+    0, 0, 0, 0, 0, 0, 0, 42,
+    0, 0, 0, 0, 0, 0, 0, 42,
+  ] : [
     42, 0, 0, 0, 0, 0, 0, 0,
     42, 0, 0, 0, 0, 0, 0, 0,
     42, 0, 0, 0, 0, 0, 0, 0,
@@ -96,7 +114,12 @@ assert.strictEqual(d.length, 0);
 {
   const bui64 = new BigUint64Array(4).fill(42n);
   const e = Buffer(bui64);
-  assert.deepStrictEqual(e, Buffer.from([
+  assert.deepStrictEqual(e, Buffer.from(isBigEnd ? [
+    0, 0, 0, 0, 0, 0, 0, 42,
+    0, 0, 0, 0, 0, 0, 0, 42,
+    0, 0, 0, 0, 0, 0, 0, 42,
+    0, 0, 0, 0, 0, 0, 0, 42,
+  ] : [
     42, 0, 0, 0, 0, 0, 0, 0,
     42, 0, 0, 0, 0, 0, 0, 0,
     42, 0, 0, 0, 0, 0, 0, 0,
@@ -108,7 +131,12 @@ assert.strictEqual(d.length, 0);
 {
   const f64 = new Float64Array(4).fill(42);
   const e = Buffer.from(f64);
-  assert.deepStrictEqual(e, Buffer.from([
+  assert.deepStrictEqual(e, Buffer.from(isBigEnd ? [
+    64, 69, 0, 0, 0, 0, 0, 0,
+    64, 69, 0, 0, 0, 0, 0, 0,
+    64, 69, 0, 0, 0, 0, 0, 0,
+    64, 69, 0, 0, 0, 0, 0, 0,
+  ] : [
     0, 0, 0, 0, 0, 0, 69, 64,
     0, 0, 0, 0, 0, 0, 69, 64,
     0, 0, 0, 0, 0, 0, 69, 64,
@@ -119,7 +147,12 @@ assert.strictEqual(d.length, 0);
 {
   const f64 = new Float64Array(4).fill(42);
   const e = Buffer(f64);
-  assert.deepStrictEqual(e, Buffer.from([
+  assert.deepStrictEqual(e, Buffer.from(isBigEnd ? [
+    64, 69, 0, 0, 0, 0, 0, 0,
+    64, 69, 0, 0, 0, 0, 0, 0,
+    64, 69, 0, 0, 0, 0, 0, 0,
+    64, 69, 0, 0, 0, 0, 0, 0,
+  ] : [
     0, 0, 0, 0, 0, 0, 69, 64,
     0, 0, 0, 0, 0, 0, 69, 64,
     0, 0, 0, 0, 0, 0, 69, 64,

From ed85f0a35e539fe96af6c78f7e8f4a872c0a94f3 Mon Sep 17 00:00:00 2001
From: LiviaMedeiros <livia@cirno.name>
Date: Mon, 18 Jul 2022 21:03:57 +0800
Subject: [PATCH 3/3] squash: add test

---
 .../test-buffer-from-arraybufferview.js       | 72 +++++++++++++++++++
 1 file changed, 72 insertions(+)
 create mode 100644 test/parallel/test-buffer-from-arraybufferview.js

diff --git a/test/parallel/test-buffer-from-arraybufferview.js b/test/parallel/test-buffer-from-arraybufferview.js
new file mode 100644
index 00000000000000..303c9ddcfc892a
--- /dev/null
+++ b/test/parallel/test-buffer-from-arraybufferview.js
@@ -0,0 +1,72 @@
+'use strict';
+const common = require('../common');
+
+// Test Buffer.from(ArrayBufferView)
+
+const assert = require('assert');
+
+// The buffer contains numbers from 27 * 0 to 27 * 127, modulo 256
+const full = Uint8Array.from({ length: 128 }, (_, i) => 27 * i);
+
+{
+  const expected = Buffer.from(full.buffer);
+
+  assert.deepStrictEqual(
+    Buffer.from(Buffer.from(full.buffer)),
+    expected);
+
+  for (const abv of common.getArrayBufferViews(full)) {
+    assert.deepStrictEqual(
+      Buffer.from(abv),
+      expected);
+  }
+}
+
+{
+  const expected = Buffer.from(full.buffer.slice(48, 96));
+
+  assert.deepStrictEqual(
+    Buffer.from(Buffer.from(full.buffer, 48, 48)),
+    expected);
+
+  assert.deepStrictEqual(
+    Buffer.from(new Int8Array(full.buffer, 48, 48)),
+    expected);
+  assert.deepStrictEqual(
+    Buffer.from(new Uint8Array(full.buffer, 48, 48)),
+    expected);
+  assert.deepStrictEqual(
+    Buffer.from(new Uint8ClampedArray(full.buffer, 48, 48)),
+    expected);
+
+  assert.deepStrictEqual(
+    Buffer.from(new Int16Array(full.buffer, 48, 24)),
+    expected);
+  assert.deepStrictEqual(
+    Buffer.from(new Uint16Array(full.buffer, 48, 24)),
+    expected);
+
+  assert.deepStrictEqual(
+    Buffer.from(new Int32Array(full.buffer, 48, 12)),
+    expected);
+  assert.deepStrictEqual(
+    Buffer.from(new Uint32Array(full.buffer, 48, 12)),
+    expected);
+  assert.deepStrictEqual(
+    Buffer.from(new Float32Array(full.buffer, 48, 12)),
+    expected);
+
+  assert.deepStrictEqual(
+    Buffer.from(new Float64Array(full.buffer, 48, 6)),
+    expected);
+  assert.deepStrictEqual(
+    Buffer.from(new BigInt64Array(full.buffer, 48, 6)),
+    expected);
+  assert.deepStrictEqual(
+    Buffer.from(new BigUint64Array(full.buffer, 48, 6)),
+    expected);
+
+  assert.deepStrictEqual(
+    Buffer.from(new DataView(full.buffer, 48, 48)),
+    expected);
+}