UNPKG

@webreflection/coincident

Version:

An Atomics based Proxy to simplify, and synchronize, Worker related tasks

163 lines (157 loc) 4.65 kB
import createEncoder from '../utils/encoder.js'; import types from './types.js'; import { minByteLength } from '../utils.js'; import { toConstructorName, toSymbolName, toType } from '../utils/shared.js'; const { isArray } = Array; const { isView } = ArrayBuffer; const { stringify } = JSON; const toCache = (arr, cache, value) => { const index = cache.get(value); if (typeof index === 'number') { arr.push(types.ref, index); return true; } cache.set(value, arr.length); return false; }; const flatten = (arr, cache, value, type = toType(value)) => { switch (type) { case 'null': { arr.push(types.null); break; } case 'object': { if (toCache(arr, cache, value)) break; switch (true) { case isArray(value): { const length = value.length; arr.push(types.array, length); for (let i = 0; i < length; i++) flatten(arr, cache, value[i]); break; } case isView(value): { const { BYTES_PER_ELEMENT, buffer, byteOffset, length } = value; arr.push( types.view, toConstructorName(value.constructor), byteOffset, length !== ((buffer.byteLength - byteOffset) / BYTES_PER_ELEMENT) ? length : 0 ); if (toCache(arr, cache, buffer)) break; value = buffer; } case value instanceof ArrayBuffer: { const byteLength = value.byteLength; const maxByteLength = value.resizable ? value.maxByteLength : 0; arr.push(types.buffer, byteLength, maxByteLength); if (byteLength) { const ui8a = new Uint8Array(value, 0, byteLength); for (let i = 0; i < ui8a.length; i += minByteLength) arr.push.apply(arr, ui8a.subarray(i, i + minByteLength)); } break; } case value instanceof Date: { arr.push(types.date, +value); break; } case value instanceof Map: { const length = arr.push(types.map, 0); let i = 0; for (const [k, v] of value) { const tk = toType(k); const tv = toType(v); if (tk && tv) { flatten(arr, cache, k, tk); flatten(arr, cache, v, tv); i++; } } arr[length - 1] = i; break; } case value instanceof Set: { const length = arr.push(types.set, 0); let i = 0; for (const v of value) { const t = toType(v); if (t) { flatten(arr, cache, v, t); i++; } } arr[length - 1] = i; break; } case value instanceof RegExp: { arr.push(types.regexp, value.source, value.flags); break; } case value instanceof Error: { arr.push(types.error, value.name, value.message, value.stack, value.cause); break; } default: { if ('toJSON' in value) { const other = value.toJSON(); if (other === value) { arr.push(types.object, 0); break; } value = other; if (toCache(arr, cache, value)) break; } const length = arr.push(types.object, 0); let i = 0; for (const k in value) { const v = value[k]; const t = toType(v); if (t) { flatten(arr, cache, k, 'string'); flatten(arr, cache, v, t); i++; } } arr[length - 1] = i; break; } } break; } case 'boolean': { arr.push(value ? types.true : types.false); break; } case 'string': { if (toCache(arr, cache, value)) break; arr.push(types.string, value); break; } case 'bigint': { arr.push(types[type], value.toString()); break; } case 'symbol': { const name = toSymbolName(value); if (name) { arr.push(types[type], name); break; } } case '': { arr.push(types.undefined); break; } default: { arr.push(types[type], value); break; } } }; export const encode = value => { const arr = []; flatten(arr, new Map, value); return stringify(arr); }; export const encoder = createEncoder(encode);