Stage 1 Draft / February 12, 2026

TypedArray, ArrayBuffer, and SharedArrayBuffer Concatenation

1 TypedArray Objects

1.1 The %TypedArray% Intrinsic Object

1.1.1 Abstract Operations for TypedArray Objects

1.1.1.1 ValidateIntegralNumber ( value, default )

The abstract operation ValidateIntegralNumber takes arguments value (an ECMAScript language value) and default (an integer) and returns either a normal completion containing an integer or a throw completion. It validates that value is either undefined or an integral Number, returning the corresponding mathematical integer. If value is undefined, the default is returned. It performs the following steps when called:

  1. If value is undefined, return default.
  2. If value is not a Number, throw a TypeError exception.
  3. If value is NaN, throw a RangeError exception.
  4. If truncate((value)) is not (value), throw a RangeError exception.
  5. Return (value).

1.1.2 Properties of the %TypedArray% Intrinsic Object

1.1.2.1 %TypedArray%.concat ( items [ , length ] )

This method concatenates the elements of multiple TypedArrays of the same type into a new TypedArray. It performs the following steps when called:

  1. Let constructor be the this value.
  2. If IsConstructor(constructor) is false, throw a TypeError exception.
  3. If constructor does not have a [[TypedArrayName]] internal slot, throw a TypeError exception.
  4. Let arrayList be ? IteratorToList(? GetIteratorFromMethod(items, ? GetMethod(items, %Symbol.iterator%))).
  5. Let newLength be empty.
  6. If length is present and length is not undefined, then
    1. Set newLength to ? ValidateIntegralNumber(length, 0).
    2. If newLength < 0, throw a RangeError exception.
    3. If newLength > 253 - 1, throw a RangeError exception.
  7. Let totalLength be 0.
  8. Let constructorName be constructor.[[TypedArrayName]].
  9. For each element item of arrayList, do
    1. Let taRecord be ? ValidateTypedArray(item, seq-cst).
    2. If item.[[TypedArrayName]] is not constructorName, throw a TypeError exception.
    3. Let itemLength be TypedArrayLength(taRecord).
    4. Set totalLength to totalLength + itemLength.
    5. If totalLength > 253 - 1, throw a RangeError exception.
  10. If newLength is empty, then
    1. Set newLength to totalLength.
  11. Let defaultProto be the String value of the Prototype column of the row in Table 70 for constructorName.
  12. Let result be ? AllocateTypedArray(constructorName, constructor, defaultProto).
  13. Perform ? AllocateTypedArrayBuffer(result, newLength).
  14. Let elementSize be TypedArrayElementSize(result).
  15. Let writeOffset be 0.
  16. For each element item of arrayList, do
    1. Let remaining be newLength - writeOffset.
    2. If remaining > 0, then
      1. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(item, seq-cst).
      2. Let itemLength be TypedArrayLength(taRecord).
      3. Let count be the result of clamping itemLength between 0 and remaining.
      4. Let srcByteOffset be item.[[ByteOffset]].
      5. Let srcByteLength be count × elementSize.
      6. Perform CopyDataBlockBytes(result.[[ViewedArrayBuffer]].[[ArrayBufferData]], writeOffset × elementSize, item.[[ViewedArrayBuffer]].[[ArrayBufferData]], srcByteOffset, srcByteLength).
      7. Set writeOffset to writeOffset + count.
  17. Return result.
Editor's Note

The algorithm from 15 through 16.b.vii is specified with explicit allocation and byte-copying steps for clarity. Because all items share the same element type and the copy operates on the underlying data blocks, the result of the concatenation is not observable to user code during construction. Implementations may therefore use any technique — such as bulk memory copy, deferred allocation, or platform-specific optimizations — provided the observable result is equivalent.

2 Abstract Operations for Buffer Concatenation

2.1 GetConcatenationSources ( arrayList )

The abstract operation GetConcatenationSources takes argument arrayList (a List of ECMAScript language values) and returns either a normal completion containing a Record with fields [[Sources]] (a List of Records) and [[TotalByteLength]] (a non-negative integer), or a throw completion. It validates each element of arrayList as an ArrayBuffer, SharedArrayBuffer, TypedArray, or DataView, and returns a Record containing a List of source Records (each with [[Buffer]], [[ByteOffset]], and [[ByteLength]] fields) and the total byte length of all sources. It performs the following steps when called:

  1. Let totalByteLength be 0.
  2. Let sources be a new empty List.
  3. For each element item of arrayList, do
    1. If item has a [[TypedArrayName]] internal slot, then
      1. Let taRecord be ? ValidateTypedArray(item, seq-cst).
      2. Let byteLen be TypedArrayByteLength(taRecord).
      3. Append the Record { [[Buffer]]: item.[[ViewedArrayBuffer]], [[ByteOffset]]: item.[[ByteOffset]], [[ByteLength]]: byteLen } to sources.
      4. Set totalByteLength to totalByteLength + byteLen.
      5. If totalByteLength > 253 - 1, throw a RangeError exception.
    2. Else if item has a [[DataView]] internal slot, then
      1. Let dvRecord be MakeDataViewWithBufferWitnessRecord(item, seq-cst).
      2. If IsViewOutOfBounds(dvRecord) is true, throw a TypeError exception.
      3. Let byteLen be GetViewByteLength(dvRecord).
      4. Append the Record { [[Buffer]]: item.[[ViewedArrayBuffer]], [[ByteOffset]]: item.[[ByteOffset]], [[ByteLength]]: byteLen } to sources.
      5. Set totalByteLength to totalByteLength + byteLen.
      6. If totalByteLength > 253 - 1, throw a RangeError exception.
    3. Else if item has an [[ArrayBufferData]] internal slot, then
      1. If IsDetachedBuffer(item) is true, throw a TypeError exception.
      2. Let byteLen be ArrayBufferByteLength(item, seq-cst).
      3. Append the Record { [[Buffer]]: item, [[ByteOffset]]: 0, [[ByteLength]]: byteLen } to sources.
      4. Set totalByteLength to totalByteLength + byteLen.
      5. If totalByteLength > 253 - 1, throw a RangeError exception.
    4. Else,
      1. Throw a TypeError exception.
  4. Return the Record { [[Sources]]: sources, [[TotalByteLength]]: totalByteLength }.
Note

For TypedArray and DataView inputs, only the viewed portion of the underlying buffer is included. The byte lengths are snapshotted with seq-cst ordering at the time each item is validated.

2.2 CopySourcesToBuffer ( result, sources )

The abstract operation CopySourcesToBuffer takes arguments result (an ArrayBuffer or SharedArrayBuffer) and sources (a List of Records with fields [[Buffer]], [[ByteOffset]], and [[ByteLength]]) and returns unused. It copies byte data from sources into result in order, stopping when result is full. It performs the following steps when called:

  1. Let writeOffset be 0.
  2. For each element source of sources, do
    1. Let remaining be result.[[ArrayBufferByteLength]] - writeOffset.
    2. If remaining > 0, then
      1. Let sourceBuffer be source.[[Buffer]].
      2. Let sourceByteOffset be source.[[ByteOffset]].
      3. Let sourceByteLength be source.[[ByteLength]].
      4. Let count be the result of clamping sourceByteLength between 0 and remaining.
      5. Perform CopyDataBlockBytes(result.[[ArrayBufferData]], writeOffset, sourceBuffer.[[ArrayBufferData]], sourceByteOffset, count).
      6. Set writeOffset to writeOffset + count.
  3. Return unused.
Editor's Note

The algorithm from 1 through 2.b.vi is specified with explicit byte-copying steps for clarity. Because the copy operates on the underlying data blocks, the result of the concatenation is not observable to user code during construction. Implementations may therefore use any technique — such as bulk memory copy, deferred allocation, or platform-specific optimizations — provided the observable result is equivalent.

3 ArrayBuffer Objects

3.1 MakeArrayBufferImmutable ( arrayBuffer )

The abstract operation MakeArrayBufferImmutable takes argument arrayBuffer (an ArrayBuffer) and returns unused. It transitions arrayBuffer to an immutable state whose contents cannot be changed, resized, or detached. It performs the following steps when called:

  1. Assert: IsDetachedBuffer(arrayBuffer) is false.
  2. NOTE: The concrete steps for this abstract operation will be provided by the Immutable ArrayBuffer proposal.
  3. Return unused.
Editor's Note

This is a placeholder abstract operation. The actual mechanism for transitioning an ArrayBuffer to an immutable state will be defined by the Immutable ArrayBuffer proposal (currently at stage 2.7).

3.2 Properties of the ArrayBuffer Constructor

3.2.1 ArrayBuffer.concat ( items [ , options ] )

This method concatenates the byte contents of multiple ArrayBuffers, SharedArrayBuffers, TypedArrays, or DataViews into a new ArrayBuffer. It performs the following steps when called:

  1. Let arrayList be ? IteratorToList(? GetIteratorFromMethod(items, ? GetMethod(items, %Symbol.iterator%))).
  2. If options is undefined, then
    1. Let optionsObj be OrdinaryObjectCreate(null).
  3. Else if options is an Object, then
    1. Let optionsObj be options.
  4. Else,
    1. Throw a TypeError exception.
  5. Let lengthOption be ? Get(optionsObj, "length").
  6. Let newByteLength be empty.
  7. If lengthOption is not undefined, then
    1. Set newByteLength to ? ValidateIntegralNumber(lengthOption, 0).
    2. If newByteLength < 0, throw a RangeError exception.
    3. If newByteLength > 253 - 1, throw a RangeError exception.
  8. Let resizable be ToBoolean(? Get(optionsObj, "resizable")).
  9. Let immutable be ToBoolean(? Get(optionsObj, "immutable")).
  10. If resizable is true and immutable is true, throw a TypeError exception.
  11. Let concatenation be ? GetConcatenationSources(arrayList).
  12. Let sources be concatenation.[[Sources]].
  13. Let totalByteLength be concatenation.[[TotalByteLength]].
  14. If newByteLength is empty, then
    1. Set newByteLength to totalByteLength.
  15. If resizable is true, then
    1. Let maxByteLength be newByteLength.
    2. Let byteLength be the result of clamping totalByteLength between 0 and maxByteLength.
    3. Let result be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength, maxByteLength).
  16. Else,
    1. Let result be ? AllocateArrayBuffer(%ArrayBuffer%, newByteLength).
  17. Perform CopySourcesToBuffer(result, sources).
  18. If immutable is true, then
    1. Perform MakeArrayBufferImmutable(result).
  19. Return result.
Editor's Note

The immutable option depends on the Immutable ArrayBuffer proposal (currently at stage 2.7). MakeArrayBufferImmutable is a placeholder for the abstract operation that will be defined by that proposal.

Note

The result is always a (non-shared) ArrayBuffer regardless of the input types.

If the optional options object is provided, the "length" property controls the byte length of the result. If "length" is less than the total byte length of the inputs, the result is truncated. If "length" is greater, the result is zero-filled beyond the copied bytes.

If the "resizable" option is true, the result is a resizable ArrayBuffer where "length" specifies the maximum byte length. The actual byte length is the lesser of the total input bytes and "length".

If the "immutable" option is true, the result is an immutable ArrayBuffer whose contents cannot be changed, resized, or detached. The "resizable" and "immutable" options are mutually exclusive.

4 SharedArrayBuffer Objects

4.1 Properties of the SharedArrayBuffer Constructor

4.1.1 SharedArrayBuffer.concat ( items [ , options ] )

This method concatenates the byte contents of multiple ArrayBuffers, SharedArrayBuffers, TypedArrays, or DataViews into a new SharedArrayBuffer. It performs the following steps when called:

  1. Let arrayList be ? IteratorToList(? GetIteratorFromMethod(items, ? GetMethod(items, %Symbol.iterator%))).
  2. If options is undefined, then
    1. Let optionsObj be OrdinaryObjectCreate(null).
  3. Else if options is an Object, then
    1. Let optionsObj be options.
  4. Else,
    1. Throw a TypeError exception.
  5. Let lengthOption be ? Get(optionsObj, "length").
  6. Let newByteLength be empty.
  7. If lengthOption is not undefined, then
    1. Set newByteLength to ? ValidateIntegralNumber(lengthOption, 0).
    2. If newByteLength < 0, throw a RangeError exception.
    3. If newByteLength > 253 - 1, throw a RangeError exception.
  8. Let growable be ToBoolean(? Get(optionsObj, "growable")).
  9. Let concatenation be ? GetConcatenationSources(arrayList).
  10. Let sources be concatenation.[[Sources]].
  11. Let totalByteLength be concatenation.[[TotalByteLength]].
  12. If newByteLength is empty, then
    1. Set newByteLength to totalByteLength.
  13. If growable is true, then
    1. Let maxByteLength be newByteLength.
    2. Let byteLength be the result of clamping totalByteLength between 0 and maxByteLength.
    3. Let result be ? AllocateSharedArrayBuffer(%SharedArrayBuffer%, byteLength, maxByteLength).
  14. Else,
    1. Let result be ? AllocateSharedArrayBuffer(%SharedArrayBuffer%, newByteLength).
  15. Perform CopySourcesToBuffer(result, sources).
  16. Return result.
Note

The result is always a SharedArrayBuffer regardless of the input types.

If the optional options object is provided, the "length" property controls the byte length of the result. If "length" is less than the total byte length of the inputs, the result is truncated. If "length" is greater, the result is zero-filled beyond the copied bytes.

If the "growable" option is true, the result is a growable SharedArrayBuffer where "length" specifies the maximum byte length. The actual byte length is the lesser of the total input bytes and "length".

Copyright & Software License

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.