diff options
Diffstat (limited to 'src/mono/wasm/runtime/web-socket.ts')
-rw-r--r-- | src/mono/wasm/runtime/web-socket.ts | 484 |
1 files changed, 190 insertions, 294 deletions
diff --git a/src/mono/wasm/runtime/web-socket.ts b/src/mono/wasm/runtime/web-socket.ts index caaa9bd3930..feeebf0f449 100644 --- a/src/mono/wasm/runtime/web-socket.ts +++ b/src/mono/wasm/runtime/web-socket.ts @@ -1,19 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { mono_wasm_new_root, mono_wasm_new_external_root } from "./roots"; -import { setI32 } from "./memory"; import { prevent_timer_throttling } from "./scheduling"; import { Queue } from "./queue"; -import { PromiseControl, _create_cancelable_promise } from "./cancelable-promise"; -import { mono_array_root_to_js_array, _wrap_delegate_root_as_function } from "./cs-to-js"; -import { mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle } from "./gc-handles"; -import { _wrap_js_thenable_as_task_root } from "./js-to-cs"; -import { wrap_error_root } from "./method-calls"; -import { conv_string_root } from "./strings"; -import { JSHandle, MonoArray, MonoObject, MonoString, MonoObjectRef } from "./types"; -import { Module } from "./imports"; -import { Int32Ptr, VoidPtr } from "./types/emscripten"; +import { PromiseControl, create_cancelable_promise } from "./cancelable-promise"; +import { mono_assert } from "./types"; +import { ArraySegment, IDisposable } from "./marshal"; const wasm_ws_pending_send_buffer = Symbol.for("wasm ws_pending_send_buffer"); const wasm_ws_pending_send_buffer_offset = Symbol.for("wasm ws_pending_send_buffer_offset"); @@ -30,302 +22,210 @@ let _text_encoder_utf8: TextEncoder | undefined = undefined; const ws_send_buffer_blocking_threshold = 65536; const emptyBuffer = new Uint8Array(); -export function mono_wasm_web_socket_open_ref(uri_address: MonoObjectRef, subProtocols: MonoObjectRef, on_close: MonoObjectRef, web_socket_js_handle: Int32Ptr, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - const result_root = mono_wasm_new_external_root<MonoObject>(result_address); - const uri_root = mono_wasm_new_external_root<MonoString>(uri_address); - const sub_root = mono_wasm_new_external_root<MonoArray>(subProtocols); - const on_close_root = mono_wasm_new_root(); - on_close_root.copy_from_address(on_close); - try { - const js_uri = conv_string_root(uri_root); - if (!js_uri) { - wrap_error_root(is_exception, "ERR12: Invalid uri '" + uri_root.value + "'", result_root); - return; - } +export function ws_wasm_create(uri: string, sub_protocols: string[] | null, onClosed: (code: number, reason: string) => void): WebSocketExtension { + mono_assert(uri && typeof uri === "string", () => `ERR12: Invalid uri ${typeof uri}`); + + const ws = new globalThis.WebSocket(uri, sub_protocols || undefined) as WebSocketExtension; + const { promise_control: open_promise_control } = create_cancelable_promise(); + + ws[wasm_ws_pending_receive_event_queue] = new Queue(); + ws[wasm_ws_pending_receive_promise_queue] = new Queue(); + ws[wasm_ws_pending_open_promise] = open_promise_control; + ws[wasm_ws_pending_send_promises] = []; + ws[wasm_ws_pending_close_promises] = []; + ws.binaryType = "arraybuffer"; + const local_on_open = () => { + if (ws[wasm_ws_is_aborted]) return; + open_promise_control.resolve(ws); + prevent_timer_throttling(); + }; + const local_on_message = (ev: MessageEvent) => { + if (ws[wasm_ws_is_aborted]) return; + _mono_wasm_web_socket_on_message(ws, ev); + prevent_timer_throttling(); + }; + const local_on_close = (ev: CloseEvent) => { + ws.removeEventListener("message", local_on_message); + if (ws[wasm_ws_is_aborted]) return; + if (onClosed) onClosed(ev.code, ev.reason); + + // this reject would not do anything if there was already "open" before it. + open_promise_control.reject(ev.reason); - const js_subs = mono_array_root_to_js_array(sub_root); - - const js_on_close = _wrap_delegate_root_as_function(on_close_root)!; - - const ws = new globalThis.WebSocket(js_uri, <any>js_subs) as WebSocketExtension; - const { promise, promise_control: open_promise_control } = _create_cancelable_promise(); - - ws[wasm_ws_pending_receive_event_queue] = new Queue(); - ws[wasm_ws_pending_receive_promise_queue] = new Queue(); - ws[wasm_ws_pending_open_promise] = open_promise_control; - ws[wasm_ws_pending_send_promises] = []; - ws[wasm_ws_pending_close_promises] = []; - ws.binaryType = "arraybuffer"; - const local_on_open = () => { - if (ws[wasm_ws_is_aborted]) return; - open_promise_control.resolve(null); - prevent_timer_throttling(); - }; - const local_on_message = (ev: MessageEvent) => { - if (ws[wasm_ws_is_aborted]) return; - _mono_wasm_web_socket_on_message(ws, ev); - prevent_timer_throttling(); - }; - const local_on_close = (ev: CloseEvent) => { - ws.removeEventListener("message", local_on_message); - if (ws[wasm_ws_is_aborted]) return; - js_on_close(ev.code, ev.reason); - - // this reject would not do anything if there was already "open" before it. - open_promise_control.reject(ev.reason); - - for (const close_promise_control of ws[wasm_ws_pending_close_promises]) { - close_promise_control.resolve(); - } + for (const close_promise_control of ws[wasm_ws_pending_close_promises]) { + close_promise_control.resolve(); + } - // send close to any pending receivers, to wake them - const receive_promise_queue = ws[wasm_ws_pending_receive_promise_queue]; - receive_promise_queue.drain((receive_promise_control) => { - const response_ptr = receive_promise_control.response_ptr; - setI32(<any>response_ptr + 0, 0);// count - setI32(<any>response_ptr + 4, 2);// type:close - setI32(<any>response_ptr + 8, 1);// end_of_message: true - receive_promise_control.resolve(null); - }); - }; - const local_on_error = (ev: any) => { - open_promise_control.reject(ev.message); - }; - ws.addEventListener("message", local_on_message); - ws.addEventListener("open", local_on_open, { once: true }); - ws.addEventListener("close", local_on_close, { once: true }); - ws.addEventListener("error", local_on_error, { once: true }); - const ws_js_handle = mono_wasm_get_js_handle(ws); - Module.setValue(web_socket_js_handle, <any>ws_js_handle, "i32"); - - const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root); - // task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC - Module.setValue(thenable_js_handle, <any>then_js_handle, "i32"); - } - catch (ex) { - wrap_error_root(is_exception, ex, result_root); - } - finally { - result_root.release(); - uri_root.release(); - sub_root.release(); - on_close_root.release(); - } + // send close to any pending receivers, to wake them + const receive_promise_queue = ws[wasm_ws_pending_receive_promise_queue]; + receive_promise_queue.drain((receive_promise_control) => { + + const response = new Int32Array([ + 0,// count + 2, // type:close + 1]);// end_of_message: true + receive_promise_control.responseView.set(response); + receive_promise_control.resolve(null); + }); + }; + const local_on_error = (ev: any) => { + open_promise_control.reject(ev.message || "WebSocket error"); + }; + ws.addEventListener("message", local_on_message); + ws.addEventListener("open", local_on_open, { once: true }); + ws.addEventListener("close", local_on_close, { once: true }); + ws.addEventListener("error", local_on_error, { once: true }); + + return ws; } -export function mono_wasm_web_socket_send(webSocket_js_handle: JSHandle, buffer_ptr: VoidPtr, offset: number, length: number, message_type: number, end_of_message: boolean, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - const result_root = mono_wasm_new_external_root<MonoObject>(result_address); - try { - const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle); - if (!ws) - throw new Error("ERR17: Invalid JS object handle " + webSocket_js_handle); +export function ws_wasm_open(ws: WebSocketExtension): Promise<void> | null { + mono_assert(!!ws, "ERR17: expected ws instance"); + const open_promise_control = ws[wasm_ws_pending_open_promise]; + return open_promise_control.promise; +} - if (ws.readyState != WebSocket.OPEN) { - throw new Error("InvalidState: The WebSocket is not connected."); - } +export function ws_wasm_send(ws: WebSocketExtension, bufferView: ArraySegment, message_type: number, end_of_message: boolean): Promise<void> | null { + mono_assert(!!ws, "ERR17: expected ws instance"); - const whole_buffer = _mono_wasm_web_socket_send_buffering(ws, buffer_ptr, offset, length, message_type, end_of_message); + const whole_buffer = _mono_wasm_web_socket_send_buffering(ws, bufferView, message_type, end_of_message); - if (!end_of_message || !whole_buffer) { - result_root.clear(); // we are done buffering synchronously, no promise - return; - } - _mono_wasm_web_socket_send_and_wait(ws, whole_buffer, thenable_js_handle, result_address); - } - catch (ex) { - wrap_error_root(is_exception, ex, result_root); - } - finally { - result_root.release(); + if (!end_of_message || !whole_buffer) { + return null; } + + return _mono_wasm_web_socket_send_and_wait(ws, whole_buffer, bufferView); } -export function mono_wasm_web_socket_receive(webSocket_js_handle: JSHandle, buffer_ptr: VoidPtr, offset: number, length: number, response_ptr: VoidPtr, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - const result_root = mono_wasm_new_external_root<MonoObject>(result_address); +export function ws_wasm_receive(ws: WebSocketExtension, bufferView: ArraySegment, responseView: ArraySegment): Promise<void> | null { + mono_assert(!!ws, "ERR18: expected ws instance"); - try { - const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle); - if (!ws) - throw new Error("ERR18: Invalid JS object handle " + webSocket_js_handle); - const receive_event_queue = ws[wasm_ws_pending_receive_event_queue]; - const receive_promise_queue = ws[wasm_ws_pending_receive_promise_queue]; + const receive_event_queue = ws[wasm_ws_pending_receive_event_queue]; + const receive_promise_queue = ws[wasm_ws_pending_receive_promise_queue]; - const readyState = ws.readyState; - if (readyState != WebSocket.OPEN && readyState != WebSocket.CLOSING) { - throw new Error("InvalidState: The WebSocket is not connected."); - } + const readyState = ws.readyState; + if (readyState != WebSocket.OPEN && readyState != WebSocket.CLOSING) { + throw new Error("InvalidState: The WebSocket is not connected."); + } - if (receive_event_queue.getLength()) { - if (receive_promise_queue.getLength() != 0) { - throw new Error("ERR20: Invalid WS state");// assert - } - // finish synchronously - _mono_wasm_web_socket_receive_buffering(receive_event_queue, buffer_ptr, offset, length, response_ptr); + if (receive_event_queue.getLength()) { + mono_assert(receive_promise_queue.getLength() == 0, "ERR20: Invalid WS state"); - Module.setValue(thenable_js_handle, 0, "i32"); - result_root.clear(); - return; - } - const { promise, promise_control } = _create_cancelable_promise(undefined, undefined); - const receive_promise_control = promise_control as ReceivePromiseControl; - receive_promise_control.buffer_ptr = buffer_ptr; - receive_promise_control.buffer_offset = offset; - receive_promise_control.buffer_length = length; - receive_promise_control.response_ptr = response_ptr; - receive_promise_queue.enqueue(receive_promise_control); - - const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root); - // task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC - Module.setValue(thenable_js_handle, <any>then_js_handle, "i32"); - } - catch (ex) { - wrap_error_root(is_exception, ex, result_root); - } - finally { - result_root.release(); + // finish synchronously + _mono_wasm_web_socket_receive_buffering(receive_event_queue, bufferView, responseView); + + return null; } -} + const { promise, promise_control } = create_cancelable_promise(undefined, undefined); + const receive_promise_control = promise_control as ReceivePromiseControl; + receive_promise_control.bufferView = bufferView; + receive_promise_control.responseView = responseView; + receive_promise_queue.enqueue(receive_promise_control); -export function mono_wasm_web_socket_close_ref(webSocket_js_handle: JSHandle, code: number, reason: MonoObjectRef, wait_for_close_received: boolean, thenable_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - const result_root = mono_wasm_new_external_root<MonoObject>(result_address); - const reason_root = mono_wasm_new_external_root<MonoString>(reason); - try { - const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle); - if (!ws) - throw new Error("ERR19: Invalid JS object handle " + webSocket_js_handle); + return promise; +} - if (ws.readyState == WebSocket.CLOSED) { - result_root.clear(); - return; - } +export function ws_wasm_close(ws: WebSocketExtension, code: number, reason: string | null, wait_for_close_received: boolean): Promise<void> | null { + mono_assert(!!ws, "ERR19: expected ws instance"); - const js_reason = conv_string_root(reason_root); - if (wait_for_close_received) { - const { promise, promise_control } = _create_cancelable_promise(); - ws[wasm_ws_pending_close_promises].push(promise_control); + if (ws.readyState == WebSocket.CLOSED) { + return null; + } - if (typeof (js_reason) === "string") { - ws.close(code, js_reason); - } else { - ws.close(code); - } + if (wait_for_close_received) { + const { promise, promise_control } = create_cancelable_promise(); + ws[wasm_ws_pending_close_promises].push(promise_control); - const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root); - // task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC - Module.setValue(thenable_js_handle, <any>then_js_handle, "i32"); - } - else { - if (!mono_wasm_web_socket_close_warning) { - mono_wasm_web_socket_close_warning = true; - console.warn("WARNING: Web browsers do not support closing the output side of a WebSocket. CloseOutputAsync has closed the socket and discarded any incoming messages."); - } - if (typeof (js_reason) === "string") { - ws.close(code, js_reason); - } else { - ws.close(code); - } - Module.setValue(thenable_js_handle, 0, "i32"); - result_root.clear(); + if (typeof reason === "string") { + ws.close(code, reason); + } else { + ws.close(code); } + return promise; } - catch (ex) { - wrap_error_root(is_exception, ex, result_root); - } - finally { - result_root.release(); - reason_root.release(); - } -} - -export function mono_wasm_web_socket_abort(webSocket_js_handle: JSHandle, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - const result_root = mono_wasm_new_external_root<MonoObject>(result_address); - try { - const ws = mono_wasm_get_jsobj_from_js_handle(webSocket_js_handle) as WebSocketExtension; - if (!ws) - throw new Error("ERR18: Invalid JS object handle " + webSocket_js_handle); - - ws[wasm_ws_is_aborted] = true; - const open_promise_control = ws[wasm_ws_pending_open_promise]; - if (open_promise_control) { - open_promise_control.reject("OperationCanceledException"); - } - for (const close_promise_control of ws[wasm_ws_pending_close_promises]) { - close_promise_control.reject("OperationCanceledException"); + else { + if (!mono_wasm_web_socket_close_warning) { + mono_wasm_web_socket_close_warning = true; + console.warn("WARNING: Web browsers do not support closing the output side of a WebSocket. CloseOutputAsync has closed the socket and discarded any incoming messages."); } - for (const send_promise_control of ws[wasm_ws_pending_send_promises]) { - send_promise_control.reject("OperationCanceledException"); + if (typeof reason === "string") { + ws.close(code, reason); + } else { + ws.close(code); } + return null; + } +} - ws[wasm_ws_pending_receive_promise_queue].drain(receive_promise_control => { - receive_promise_control.reject("OperationCanceledException"); - }); - - // this is different from Managed implementation - ws.close(1000, "Connection was aborted."); +export function ws_wasm_abort(ws: WebSocketExtension): void { + mono_assert(!!ws, "ERR18: expected ws instance"); - result_root.clear(); + ws[wasm_ws_is_aborted] = true; + const open_promise_control = ws[wasm_ws_pending_open_promise]; + if (open_promise_control) { + open_promise_control.reject("OperationCanceledException"); } - catch (ex) { - wrap_error_root(is_exception, ex, result_root); + for (const close_promise_control of ws[wasm_ws_pending_close_promises]) { + close_promise_control.reject("OperationCanceledException"); } - finally { - result_root.release(); + for (const send_promise_control of ws[wasm_ws_pending_send_promises]) { + send_promise_control.reject("OperationCanceledException"); } + + ws[wasm_ws_pending_receive_promise_queue].drain(receive_promise_control => { + receive_promise_control.reject("OperationCanceledException"); + }); + + // this is different from Managed implementation + ws.close(1000, "Connection was aborted."); } -function _mono_wasm_web_socket_send_and_wait(ws: WebSocketExtension, buffer: Uint8Array | string, thenable_js_handle: Int32Ptr, result_address: MonoObjectRef): void { - const result_root = mono_wasm_new_external_root<MonoObject>(result_address); - try { - // send and return promise - ws.send(buffer); - ws[wasm_ws_pending_send_buffer] = null; - - // if the remaining send buffer is small, we don't block so that the throughput doesn't suffer. - // Otherwise we block so that we apply some backpresure to the application sending large data. - // this is different from Managed implementation - if (ws.bufferedAmount < ws_send_buffer_blocking_threshold) { - result_root.clear(); - return; - } +function _mono_wasm_web_socket_send_and_wait(ws: WebSocketExtension, buffer: Uint8Array | string, managedBuffer: IDisposable): Promise<void> | null { + // send and return promise + ws.send(buffer); + managedBuffer.dispose(); + ws[wasm_ws_pending_send_buffer] = null; + + // if the remaining send buffer is small, we don't block so that the throughput doesn't suffer. + // Otherwise we block so that we apply some backpresure to the application sending large data. + // this is different from Managed implementation + if (ws.bufferedAmount < ws_send_buffer_blocking_threshold) { + return null; + } - // block the promise/task until the browser passed the buffer to OS - const { promise, promise_control } = _create_cancelable_promise(); - const pending = ws[wasm_ws_pending_send_promises]; - pending.push(promise_control); + // block the promise/task until the browser passed the buffer to OS + const { promise, promise_control } = create_cancelable_promise(); + const pending = ws[wasm_ws_pending_send_promises]; + pending.push(promise_control); - let nextDelay = 1; - const polling_check = () => { - // was it all sent yet ? - if (ws.bufferedAmount === 0) { - promise_control.resolve(null); - } - else if (ws.readyState != WebSocket.OPEN) { - // only reject if the data were not sent - // bufferedAmount does not reset to zero once the connection closes - promise_control.reject("InvalidState: The WebSocket is not connected."); - } - else if (!promise_control.isDone) { - globalThis.setTimeout(polling_check, nextDelay); - // exponentially longer delays, up to 1000ms - nextDelay = Math.min(nextDelay * 1.5, 1000); - return; - } - // remove from pending - const index = pending.indexOf(promise_control); - if (index > -1) { - pending.splice(index, 1); - } - }; + let nextDelay = 1; + const polling_check = () => { + // was it all sent yet ? + if (ws.bufferedAmount === 0) { + promise_control.resolve(null); + } + else if (ws.readyState != WebSocket.OPEN) { + // only reject if the data were not sent + // bufferedAmount does not reset to zero once the connection closes + promise_control.reject("InvalidState: The WebSocket is not connected."); + } + else if (!promise_control.isDone) { + globalThis.setTimeout(polling_check, nextDelay); + // exponentially longer delays, up to 1000ms + nextDelay = Math.min(nextDelay * 1.5, 1000); + return; + } + // remove from pending + const index = pending.indexOf(promise_control); + if (index > -1) { + pending.splice(index, 1); + } + }; - globalThis.setTimeout(polling_check, 0); + globalThis.setTimeout(polling_check, 0); - const { then_js_handle } = _wrap_js_thenable_as_task_root(promise, result_root); - // task_ptr above is not rooted, we need to return it to mono without any intermediate mono call which could cause GC - Module.setValue(thenable_js_handle, <any>then_js_handle, "i32"); - } finally { - result_root.release(); - } + return promise; } function _mono_wasm_web_socket_on_message(ws: WebSocketExtension, event: MessageEvent) { @@ -361,36 +261,37 @@ function _mono_wasm_web_socket_on_message(ws: WebSocketExtension, event: Message while (promise_queue.getLength() && event_queue.getLength()) { const promise_control = promise_queue.dequeue()!; _mono_wasm_web_socket_receive_buffering(event_queue, - promise_control.buffer_ptr, promise_control.buffer_offset, promise_control.buffer_length, - promise_control.response_ptr); + promise_control.bufferView, promise_control.responseView); promise_control.resolve(null); } prevent_timer_throttling(); } -function _mono_wasm_web_socket_receive_buffering(event_queue: Queue<any>, buffer_ptr: VoidPtr, buffer_offset: number, buffer_length: number, response_ptr: VoidPtr) { +function _mono_wasm_web_socket_receive_buffering(event_queue: Queue<any>, bufferView: ArraySegment, responseView: ArraySegment) { const event = event_queue.peek(); - const count = Math.min(buffer_length, event.data.length - event.offset); + const count = Math.min(bufferView.length, event.data.length - event.offset); if (count > 0) { - const targetView = Module.HEAPU8.subarray(<any>buffer_ptr + buffer_offset, <any>buffer_ptr + buffer_offset + buffer_length); const sourceView = event.data.subarray(event.offset, event.offset + count); - targetView.set(sourceView, 0); + bufferView.set(sourceView, 0); event.offset += count; } const end_of_message = event.data.length === event.offset ? 1 : 0; if (end_of_message) { event_queue.dequeue(); } - setI32(<any>response_ptr + 0, count); - setI32(<any>response_ptr + 4, event.type); - setI32(<any>response_ptr + 8, end_of_message); + + const response = new Int32Array([count, event.type, end_of_message]); + responseView.set(response); + + bufferView.dispose(); + responseView.dispose(); } -function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, buffer_ptr: VoidPtr, buffer_offset: number, length: number, message_type: number, end_of_message: boolean): Uint8Array | string | null { +function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, bufferView: ArraySegment, message_type: number, end_of_message: boolean): Uint8Array | string | null { let buffer = ws[wasm_ws_pending_send_buffer]; let offset = 0; - const message_ptr = <any>buffer_ptr + buffer_offset; + const length = bufferView.length; if (buffer) { offset = ws[wasm_ws_pending_send_buffer_offset]; @@ -398,15 +299,14 @@ function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, buffer_ptr message_type = ws[wasm_ws_pending_send_buffer_type]; // if not empty message, append to existing buffer if (length !== 0) { - const view = Module.HEAPU8.subarray(message_ptr, message_ptr + length); if (offset + length > buffer.length) { const newbuffer = new Uint8Array((offset + length + 50) * 1.5); // exponential growth newbuffer.set(buffer, 0);// copy previous buffer - newbuffer.set(view, offset);// append copy at the end + bufferView.copyTo(newbuffer.subarray(offset));// append copy at the end ws[wasm_ws_pending_send_buffer] = buffer = newbuffer; } else { - buffer.set(view, offset);// append copy at the end + bufferView.copyTo(buffer.subarray(offset));// append copy at the end } offset += length; ws[wasm_ws_pending_send_buffer_offset] = offset; @@ -415,8 +315,7 @@ function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, buffer_ptr else if (!end_of_message) { // create new buffer if (length !== 0) { - const view = Module.HEAPU8.subarray(message_ptr, message_ptr + length); - buffer = new Uint8Array(view); // copy + buffer = <Uint8Array>bufferView.slice(); // copy offset = length; ws[wasm_ws_pending_send_buffer_offset] = offset; ws[wasm_ws_pending_send_buffer] = buffer; @@ -424,10 +323,9 @@ function _mono_wasm_web_socket_send_buffering(ws: WebSocketExtension, buffer_ptr ws[wasm_ws_pending_send_buffer_type] = message_type; } else { - // use the buffer only localy if (length !== 0) { - const memoryView = Module.HEAPU8.subarray(message_ptr, message_ptr + length); - buffer = memoryView; // send will make a copy + // we could use the unsafe view, because it will be immediately used in ws.send() + buffer = <Uint8Array>bufferView._unsafe_create_view(); offset = length; } } @@ -469,10 +367,8 @@ type WebSocketExtension = WebSocket & { } type ReceivePromiseControl = PromiseControl & { - response_ptr: VoidPtr - buffer_ptr: VoidPtr - buffer_offset: number - buffer_length: number + bufferView: ArraySegment, + responseView: ArraySegment } type Message = { |