55/* c8 ignore start */
66
77const {
8- ArrayBufferPrototypeGetByteLength,
9- ArrayBufferPrototypeTransfer,
108 ArrayIsArray,
119 ArrayPrototypePush,
1210 BigInt,
13- DataViewPrototypeGetBuffer,
1411 DataViewPrototypeGetByteLength,
15- DataViewPrototypeGetByteOffset,
1612 FunctionPrototypeBind,
1713 Number,
1814 ObjectDefineProperties,
@@ -26,10 +22,7 @@ const {
2622 SymbolAsyncIterator,
2723 SymbolDispose,
2824 SymbolIterator,
29- TypedArrayPrototypeGetBuffer,
3025 TypedArrayPrototypeGetByteLength,
31- TypedArrayPrototypeGetByteOffset,
32- TypedArrayPrototypeSlice,
3326 Uint8Array,
3427} = primordials ;
3528
@@ -1073,53 +1066,24 @@ function invokeOnerror(fn, error) {
10731066
10741067function validateBody ( body ) {
10751068 if ( body === undefined ) return body ;
1076- // Transfer ArrayBuffers...
1077- if ( isArrayBuffer ( body ) ) {
1078- return ArrayBufferPrototypeTransfer ( body ) ;
1079- }
1080- // With a SharedArrayBuffer, we always copy. We cannot transfer
1081- // and it's likely unsafe to use the underlying buffer directly.
1082- if ( isSharedArrayBuffer ( body ) ) {
1083- return TypedArrayPrototypeSlice ( new Uint8Array ( body ) ) ;
1084- }
1085- if ( isArrayBufferView ( body ) ) {
1086- let size , offset , buffer ;
1087- if ( isDataView ( body ) ) {
1088- size = DataViewPrototypeGetByteLength ( body ) ;
1089- offset = DataViewPrototypeGetByteOffset ( body ) ;
1090- buffer = DataViewPrototypeGetBuffer ( body ) ;
1091- } else {
1092- size = TypedArrayPrototypeGetByteLength ( body ) ;
1093- offset = TypedArrayPrototypeGetByteOffset ( body ) ;
1094- buffer = TypedArrayPrototypeGetBuffer ( body ) ;
1095- }
1096- // We have to be careful in this case. If the ArrayBufferView is a
1097- // subset of the underlying ArrayBuffer, transferring the entire
1098- // ArrayBuffer could be incorrect if other views are also using it.
1099- // So if offset > 0 or size != buffer.byteLength, we'll copy the
1100- // subset into a new ArrayBuffer instead of transferring.
1101- if ( isSharedArrayBuffer ( buffer ) ||
1102- offset !== 0 ||
1103- size !== ArrayBufferPrototypeGetByteLength ( buffer ) ) {
1104- return TypedArrayPrototypeSlice (
1105- new Uint8Array ( buffer , offset , size ) ) ;
1106- }
1107- // It's still possible that the ArrayBuffer is being used elsewhere,
1108- // but we really have no way of knowing. We'll just have to trust
1109- // the caller in this case.
1110- return new Uint8Array (
1111- ArrayBufferPrototypeTransfer ( buffer ) , offset , size ) ;
1069+ // ArrayBuffers, SharedArrayBuffers, and ArrayBufferViews are passed
1070+ // through to the C++ layer which copies the bytes into its own
1071+ // BackingStore. Callers can therefore safely reuse or mutate their
1072+ // input buffers after the call returns. Callers that want to ensure
1073+ // their buffer cannot be mutated after handing it off (for example,
1074+ // when sharing the source with another async consumer) can call
1075+ // ArrayBuffer.prototype.transfer() themselves before passing the
1076+ // buffer.
1077+ if ( isArrayBuffer ( body ) ||
1078+ isSharedArrayBuffer ( body ) ||
1079+ isArrayBufferView ( body ) ) {
1080+ return body ;
11121081 }
11131082 if ( isBlob ( body ) ) return body [ kBlobHandle ] ;
11141083
11151084 // Strings are encoded as UTF-8.
11161085 if ( typeof body === 'string' ) {
1117- body = Buffer . from ( body , 'utf8' ) ;
1118- // Fall through -- Buffer is an ArrayBufferView, handled above.
1119- return TypedArrayPrototypeSlice ( new Uint8Array (
1120- TypedArrayPrototypeGetBuffer ( body ) ,
1121- TypedArrayPrototypeGetByteOffset ( body ) ,
1122- TypedArrayPrototypeGetByteLength ( body ) ) ) ;
1086+ return Buffer . from ( body , 'utf8' ) ;
11231087 }
11241088
11251089 // FileHandle -- lock it and pass the C++ handle to GetDataQueueFromSource
@@ -1911,11 +1875,12 @@ class QuicStream {
19111875 }
19121876 // When destroying with an error, ensure the peer learns about
19131877 // it via RESET_STREAM. The writer.fail path inside [kFinishClose]
1914- // emits RESET_STREAM only when a writer was previously created;
1915- // streams that destroyed without ever accessing stream.writer
1916- // (e.g. used setBody or never wrote at all) previously left their
1917- // write side dangling on the wire. The condition gates the
1918- // emission to error-path destroys with a still-open writable side.
1878+ // emits RESET_STREAM only when a writer has been created;
1879+ // streams that destroy without ever accessing stream.writer
1880+ // (e.g. used setBody or never wrote at all) need an explicit
1881+ // RESET_STREAM here so the write side does not dangle on the
1882+ // wire. The condition gates the emission to error-path destroys
1883+ // with a still-open writable side.
19191884 // Direction model for the writable side:
19201885 // * bidi: always has a writable side.
19211886 // * uni + #reader === undefined: locally-initiated, write-only.
@@ -3165,8 +3130,12 @@ class QuicSession {
31653130 *
31663131 * If a string is given it will be encoded using the specified encoding.
31673132 *
3168- * If an ArrayBufferView is given, the underlying ArrayBuffer will be
3169- * transferred if possible, otherwise the data will be copied.
3133+ * If an ArrayBufferView is given, the bytes are copied into an internal
3134+ * buffer; the caller's source buffer is unchanged and may be reused
3135+ * immediately. Callers that want to ensure their source cannot be
3136+ * mutated after the call (for example, when handing the buffer off to
3137+ * another async consumer) can call ArrayBuffer.prototype.transfer()
3138+ * themselves before passing it.
31703139 *
31713140 * If a Promise is given, it will be awaited before sending. If the
31723141 * session closes while awaiting, 0n is returned silently.
@@ -3179,7 +3148,6 @@ class QuicSession {
31793148 if ( this . #isClosedOrClosing) {
31803149 throw new ERR_INVALID_STATE ( 'Session is closed' ) ;
31813150 }
3182- let offset , length , buffer ;
31833151
31843152 const maxDatagramSize = this . #state. maxDatagramSize ;
31853153
@@ -3196,45 +3164,21 @@ class QuicSession {
31963164 }
31973165
31983166 if ( typeof datagram === 'string' ) {
3199- // Buffer.from may return a pooled buffer that can't be transferred.
3200- // Slice to get an independent copy.
32013167 datagram = new Uint8Array ( Buffer . from ( datagram , encoding ) ) ;
3202- length = TypedArrayPrototypeGetByteLength ( datagram ) ;
3203- if ( length === 0 ) return kNilDatagramId ;
3204- } else {
3205- if ( ! isArrayBufferView ( datagram ) ) {
3206- throw new ERR_INVALID_ARG_TYPE ( 'datagram' ,
3207- [ 'ArrayBufferView' , 'string' ] ,
3208- datagram ) ;
3209- }
3210- if ( isDataView ( datagram ) ) {
3211- offset = DataViewPrototypeGetByteOffset ( datagram ) ;
3212- length = DataViewPrototypeGetByteLength ( datagram ) ;
3213- buffer = DataViewPrototypeGetBuffer ( datagram ) ;
3214- } else {
3215- offset = TypedArrayPrototypeGetByteOffset ( datagram ) ;
3216- length = TypedArrayPrototypeGetByteLength ( datagram ) ;
3217- buffer = TypedArrayPrototypeGetBuffer ( datagram ) ;
3218- }
3219-
3220- // If the view has zero length (e.g. detached buffer), there's
3221- // nothing to send.
3222- if ( length === 0 ) return kNilDatagramId ;
3223-
3224- if ( isSharedArrayBuffer ( buffer ) ||
3225- offset !== 0 ||
3226- length !== ArrayBufferPrototypeGetByteLength ( buffer ) ) {
3227- // Copy if the buffer is not transferable (SharedArrayBuffer)
3228- // or if the view is over a subset of the buffer (e.g. a
3229- // Node.js Buffer from the pool).
3230- datagram = TypedArrayPrototypeSlice (
3231- new Uint8Array ( buffer ) , offset , offset + length ) ;
3232- } else {
3233- datagram = new Uint8Array (
3234- ArrayBufferPrototypeTransfer ( buffer ) , offset , length ) ;
3235- }
3168+ } else if ( ! isArrayBufferView ( datagram ) ) {
3169+ throw new ERR_INVALID_ARG_TYPE ( 'datagram' ,
3170+ [ 'ArrayBufferView' , 'string' ] ,
3171+ datagram ) ;
32363172 }
32373173
3174+ const length = isDataView ( datagram ) ?
3175+ DataViewPrototypeGetByteLength ( datagram ) :
3176+ TypedArrayPrototypeGetByteLength ( datagram ) ;
3177+
3178+ // If the view has zero length (e.g. detached buffer), there's
3179+ // nothing to send.
3180+ if ( length === 0 ) return kNilDatagramId ;
3181+
32383182 // The peer max datagram size is less than the datagram we want to send,
32393183 // so... don't send it.
32403184 if ( length > maxDatagramSize ) return kNilDatagramId ;
0 commit comments