UNPKG

@itwin/core-common

Version:

iTwin.js components common to frontend and backend

158 lines 5.42 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module IpcSocket */ import { IpcWebSocketMessage } from "./IpcWebSocket"; function isBuffer(val) { return val && typeof (val.constructor) !== "undefined" && typeof (val.constructor.isBuffer) === "function" && val.constructor.isBuffer(val); } let parts = []; /** @internal */ export class IpcWebSocketTransport { _partial; _received = []; _outstanding = 0; unwrap(data) { return (typeof (Blob) !== "undefined" && data instanceof Blob) ? data.arrayBuffer() : data; } async notifyIncoming(data, connection) { if (this._partial) { this._received.push(data); --this._outstanding; if (this._outstanding === 0) { const partial = this._partial; const received = this._received; this._partial = undefined; this._received = []; await Promise.all(received.map(async (v, i, a) => a[i] = await this.unwrap(v))); parts = received; const message = JSON.parse(partial, reviver); parts.length = 0; return InSentOrder.deliver(message, connection); } else { return IpcWebSocketMessage.internal(); } } else { const [serialized, followers] = JSON.parse(data); if (followers) { this._partial = serialized; this._outstanding = followers; return IpcWebSocketMessage.internal(); } else { const message = JSON.parse(serialized, reviver); return InSentOrder.deliver(message, connection); } } } serialize(data) { parts.length = 0; const objects = JSON.stringify(data, replacer); const value = [JSON.stringify([objects, parts.length]), ...parts]; parts.length = 0; return value; } notifyClose(connection) { InSentOrder.close(connection); } } const types = [Uint8Array, Int8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, DataView]; function identify(value) { return isBuffer(value) ? 0 : types.indexOf(value.constructor); } function lookup(value) { return types[value.type]; } function replacer(_key, value) { const asBinary = replaceBinary(value); if (asBinary) { return asBinary; } return value; } function reviver(_key, value) { if (typeof (value) === "object" && value !== null && value.hasOwnProperty("ipc") && value.ipc === "binary") { return reviveBinary(value); } return value; } function replaceBinary(value) { if (ArrayBuffer.isView(value) || isBuffer(value)) { const index = parts.push(value) - 1; const type = identify(value); return { ipc: "binary", type, index }; } else { return undefined; } } function reviveBinary(value) { const constructor = lookup(value); const part = parts[value.index]; return new constructor(part); } function makePromise() { let resolve = () => { }; let reject = () => { }; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; } /* Reconstructing the sequence in which messages were sent is necessary since the binary data for a message has to be awaited in IpcWebSocketTransport.unwrap. */ class InSentOrder { static _connections = new Map(); static async deliver(message, connection) { let context = this._connections.get(connection); if (!context) { context = { queue: [], last: -1 }; this._connections.set(connection, context); } const entry = new InSentOrder(message); context.queue.push(entry); context.queue.sort((a, b) => a.sequence - b.sequence); while (context.queue.length !== 0) { const next = context.queue[0]; const duplicate = next.sequence <= context.last; const match = next.sequence === (context.last + 1); if (duplicate) { next.duplicate = true; } else if (match) { ++context.last; } if (duplicate || match) { context.queue.shift(); next.release(); } else { break; } } return entry.message; } static close(connection) { this._connections.delete(connection); } release = () => { }; sequence; duplicate = false; message; constructor(message) { this.sequence = message.sequence; const { promise, resolve } = makePromise(); this.message = promise; this.release = () => { const value = this.duplicate ? IpcWebSocketMessage.duplicate() : message; resolve(value); }; } } //# sourceMappingURL=IpcWebSocketTransport.js.map