You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# TypedArray, ArrayBuffer, and SharedArrayBuffer Concatenation
2
2
3
-
ECMAScript Proposal for TypedArray concatentation
3
+
ECMAScript Proposal for TypedArray, ArrayBuffer, and SharedArrayBuffer concatenation
4
4
5
5
This proposal is currently [stage 1](https://github.com/tc39/proposals/blob/master/README.md) of the [process](https://tc39.github.io/process-document/).
6
6
7
7
## Problem
8
8
9
-
ECMAScript should provide a native method for concatenating TypedArrays that enables implementations to optimize through strategies that can avoid the current requirement of eagerly allocating and copying data into new buffers
9
+
ECMAScript should provide native methods for concatenating TypedArrays and ArrayBuffers that enable implementations to optimize through strategies that can avoid the current requirement of eagerly allocating and copying data into new buffers.
10
10
11
-
It is common for applications on the web (both browser and server side) to need to concatenate two or more TypedArray instances as part of a data pipeline. Unfortunately, the mechanisms available for concatenation are difficult to optimize for performance. All require additional allocations and copying at inopportune times in the application.
11
+
It is common for applications on the web (both browser and server side) to need to concatenate two or more TypedArray or ArrayBuffer instances as part of a data pipeline. Unfortunately, the mechanisms available for concatenation are difficult to optimize for performance. All require additional allocations and copying at inopportune times in the application.
12
12
13
13
A common example is a `WritableStream` instance that collects writes up to a defined threshold before passing those on in a single coalesced chunk. Server-side applications have typically relied on Node.js' `Buffer.concat` API, while browser applications have relied on either browser-compatible polyfills of `Buffer` or `TypedArray.prototype.set`.
14
14
@@ -18,9 +18,8 @@ let size = 0;
18
18
newWritableStream({
19
19
write(chunk) {
20
20
buffers.push(chunk);
21
-
size +=chunks.length;
22
-
if (buffer.byteLength>=4096) {
23
-
// Not yet the actual proposed syntax... we have to determine that still
21
+
size +=chunk.length;
22
+
if (size >=4096) {
24
23
flushBuffer(concat(buffers, size));
25
24
buffers = [];
26
25
size =0;
@@ -35,49 +34,197 @@ function concat(buffers, size) {
35
34
dest.set(buffer, offset);
36
35
offset +=buffer.length;
37
36
}
37
+
return dest;
38
38
}
39
39
```
40
40
41
-
```js
42
-
constbuffer1=Buffer.from('hello');
43
-
constbuffer2=Buffer.from('world');
44
-
constbuffer3=Buffer.concat([buffer1, buffer2]);
45
-
```
46
-
47
-
While these approaches work, they end up being difficult to optimize because they require potential expensive allocations and data copying at inopportune times while processing the information. The `TypedArray.prototype.set` method does provide an approach for concatenation that is workable, but the way the algorithm is defined, there is no allowance given for implementation-defined optimization.
41
+
While these approaches work, they end up being difficult to optimize because they require potentially expensive allocations and data copying at inopportune times while processing the information. The `TypedArray.prototype.set` method does provide an approach for concatenation that is workable, but the way the algorithm is defined, there is no allowance given for implementation-defined optimization.
48
42
49
43
## Proposal
50
44
51
-
This proposal seeks to improve the current state by providing a mechanism that provides an optimizable concatenation path for TypedArrays within the language.
45
+
This proposal provides three complementary static methods for concatenation:
46
+
47
+
1.**`%TypedArray%.concat(items [, length])`** — element-oriented concatenation of same-type TypedArrays
48
+
2.**`ArrayBuffer.concat(items [, options])`** — byte-oriented concatenation returning an ArrayBuffer
49
+
3.**`SharedArrayBuffer.concat(items [, options])`** — byte-oriented concatenation returning a SharedArrayBuffer
50
+
51
+
All three methods afford implementations the ability to determine the most optimal approach, and optimal timing, for performing the allocations and copies, but no specific optimization is required.
52
+
53
+
`%TypedArray%.concat` accepts only TypedArrays of the same type as the constructor (e.g., all `Uint8Array` for `Uint8Array.concat`), though those TypedArrays may be backed by either an ArrayBuffer or a SharedArrayBuffer. `ArrayBuffer.concat` and `SharedArrayBuffer.concat` accept any mix of ArrayBuffer, SharedArrayBuffer, TypedArray, and DataView inputs — the return type is determined by which method is called, not by the input types.
52
54
53
-
As a stage 1 proposal, the exact mechanism has yet to be defined but the goal would be to achieve a model very similar to Node.js' `Buffer.concat`, where multiple input `TypedArray`s can be given and the implementation can determine the most optimum approach to concatenating those into a single returned `TypedArray` of the same type.
55
+
### `%TypedArray%.concat(items [, length])`
56
+
57
+
Concatenates multiple TypedArrays of the same type into a new TypedArray.
A key goal, if a reasonable approach to do so is found, would be to afford implementations the ability to determine the most optimal approach, and optimal timing, for performing the allocations and copies, but no specific optimization would be required.
67
+
-`items` — an iterable of TypedArray instances, all of the same type as the constructor.
68
+
-`length` (optional) — a non-negative integer specifying the element length of the result. If less than the total, the result is truncated. If greater, the result is zero-filled. Defaults to the sum of all input lengths.
63
69
64
-
### Differences from `set`
70
+
All items must be TypedArrays of the same type as the constructor (e.g., all `Uint8Array` for `Uint8Array.concat`). A `TypeError` is thrown if any item is a different type. Items may be backed by either an ArrayBuffer or a SharedArrayBuffer.
65
71
66
-
Per the current definition of `TypedArray.prototype.set` in the language specification, the user code is responsible for allocating the destination `TypedArray` in advance along with calculating and updating the offset at which each copied segment should go. Allocations can be expensive and the book keeping can be cumbersome, particularly when the are multiple input `TypedArrays`. The `set` algorithm is also written such that each element of the copied `TypedArray` is copied to the destination one element at a time, with no affordance given to allow the implementation to determine an alternative, more optimal copy strategy.
72
+
A `TypeError` is thrown if any item is a detached TypedArray. A `RangeError`is thrown if the total element count exceeds 2<sup>53</sup> - 1.
Concatenates the byte contents of multiple ArrayBuffers, SharedArrayBuffers, TypedArrays, or DataViews into a new ArrayBuffer.
143
+
144
+
```js
145
+
constab1=newArrayBuffer(4);
146
+
constab2=newArrayBuffer(4);
147
+
constab3=ArrayBuffer.concat([ab1, ab2]);
148
+
// ab3.byteLength === 8
149
+
```
150
+
151
+
-`items` — an iterable of ArrayBuffer, SharedArrayBuffer, TypedArray, or DataView instances. For TypedArray and DataView inputs, only the viewed portion of the underlying buffer is included.
152
+
-`options` (optional) — an object with the following properties:
153
+
-`length` — a non-negative integer specifying the byte length of the result. If less than the total input bytes, the result is truncated. If greater, the result is zero-filled. Defaults to the sum of all input byte lengths.
154
+
-`resizable` — a boolean. If `true`, the result is a resizable ArrayBuffer where `length` specifies the maximum byte length (`maxByteLength`). The actual `byteLength` is the lesser of the total input bytes and `length`. Defaults to `false`.
155
+
-`immutable` — a boolean. If `true`, the result is an immutable ArrayBuffer whose contents cannot be changed, resized, or detached. Defaults to `false`. *This option depends on the [Immutable ArrayBuffer proposal](https://github.com/tc39/proposal-immutable-arraybuffer).*
156
+
157
+
The `resizable` and `immutable` options are mutually exclusive. A `TypeError` is thrown if both are `true`.
158
+
159
+
A `TypeError` is thrown for detached buffers or out-of-bounds DataViews. A `RangeError` is thrown if the total byte count exceeds 2<sup>53</sup> - 1.
160
+
161
+
```js
162
+
// Mix of ArrayBuffer, TypedArray, and DataView inputs
Concatenates the byte contents of multiple ArrayBuffers, SharedArrayBuffers, TypedArrays, or DataViews into a new SharedArrayBuffer.
189
+
190
+
```js
191
+
constsab1=newSharedArrayBuffer(4);
192
+
constsab2=newSharedArrayBuffer(4);
193
+
constsab3=SharedArrayBuffer.concat([sab1, sab2]);
194
+
// sab3.byteLength === 8
195
+
```
196
+
197
+
-`items` — an iterable of ArrayBuffer, SharedArrayBuffer, TypedArray, or DataView instances. For TypedArray and DataView inputs, only the viewed portion of the underlying buffer is included.
198
+
-`options` (optional) — an object with the following properties:
199
+
-`length` — a non-negative integer specifying the byte length of the result. If less than the total input bytes, the result is truncated. If greater, the result is zero-filled. Defaults to the sum of all input byte lengths.
200
+
-`growable` — a boolean. If `true`, the result is a growable SharedArrayBuffer where `length` specifies the maximum byte length (`maxByteLength`). The actual `byteLength` is the lesser of the total input bytes and `length`. Defaults to `false`.
201
+
202
+
Note: The `immutable` option is not available for SharedArrayBuffers.
203
+
204
+
A `TypeError` is thrown for detached buffers or out-of-bounds DataViews. A `RangeError` is thrown if the total byte count exceeds 2<sup>53</sup> - 1.
205
+
206
+
```js
207
+
// Mix of SharedArrayBuffer, TypedArray, and DataView inputs
// growable.maxByteLength === 32 (can grow up to 32)
218
+
```
219
+
220
+
### Differences from `set`
221
+
222
+
Per the current definition of `TypedArray.prototype.set` in the language specification, the user code is responsible for allocating the destination `TypedArray` in advance along with calculating and updating the offset at which each copied segment should go. Allocations can be expensive and the book keeping can be cumbersome, particularly when there are multiple input `TypedArrays`. The `set` algorithm is also written such that each element of the copied `TypedArray` is copied to the destination one element at a time, with no affordance given to allow the implementation to determine an alternative, more optimal copy strategy.
223
+
224
+
### Why three methods?
225
+
226
+
`%TypedArray%.concat` operates at the TypedArray level — it is element-oriented, requires same-type inputs, and returns a TypedArray. This is the right level of abstraction when working with typed data (e.g., concatenating `Uint8Array` chunks in a stream).
227
+
228
+
`ArrayBuffer.concat` and `SharedArrayBuffer.concat` operate at the buffer level — they are byte-oriented, accept heterogeneous inputs (ArrayBuffer/SharedArrayBuffer, TypedArray, DataView), and return the appropriate buffer type. This is the right level of abstraction for controlling buffer properties like resizability/growability and immutability, which are concerns of the buffer, not the TypedArray.
229
+
230
+
`ArrayBuffer.concat` and `SharedArrayBuffer.concat` are separate methods because the return type differs and the available options differ (`immutable` is only available for ArrayBuffer, `growable` is only available for SharedArrayBuffer). This mirrors the existing separation between the `ArrayBuffer` and `SharedArrayBuffer` constructors in the language.
0 commit comments