@itwin/core-common
Version:
iTwin.js components common to frontend and backend
158 lines • 5.42 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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