UNPKG

@cloudpss/ubjson

Version:

Opinionated UBJSON encoder/decoder for CloudPSS.

99 lines (94 loc) 4.33 kB
import { Observable, type OperatorFunction } from 'rxjs'; import { toUint8Array } from '../helper/utils.js'; import { read } from '../helper/decode-ae.js'; import type { DecodeOptions } from '../options.js'; import { UnexpectedEofError } from '../helper/errors.js'; const EMPTY_BUFFER = new Uint8Array(0); const EMPTY_VIEW = new DataView(EMPTY_BUFFER.buffer); /** 流式解码 UBJSON */ export function decode(options?: DecodeOptions): OperatorFunction<BufferSource, unknown> { return (observable) => new Observable<unknown>((subscriber) => { const data = EMPTY_BUFFER; const cursor = { view: EMPTY_VIEW, data, capacity: 0, size: 0, offset: 0, options, }; /** reader 返回的还需接收的字节数 */ let required = 1; let reader = read(cursor); return observable.subscribe({ next(value) { const chunk = toUint8Array(value); const chunkSize = chunk.byteLength; if (cursor.capacity - cursor.size < chunkSize) { // 当前缓冲区不足,需要扩容 const newSize = Math.max( // 不缩小缓冲区 cursor.capacity, // 扩大缓冲区到足够容纳 2 倍当前数据 chunkSize * 2 + cursor.size - cursor.offset, ); if (newSize > cursor.capacity) { // 需要增大缓冲区 const newData = new Uint8Array(newSize); newData.set(cursor.data.subarray(cursor.offset, cursor.size), 0); newData.set(chunk, cursor.size - cursor.offset); cursor.data = newData; cursor.view = new DataView(newData.buffer, newData.byteOffset, newData.byteLength); cursor.capacity = newSize; } else { // 无需增大缓冲区,直接移动数据 cursor.data.copyWithin(0, cursor.offset, cursor.size); cursor.data.set(chunk, cursor.size - cursor.offset); } cursor.size = cursor.size - cursor.offset + chunkSize; cursor.offset = 0; } else { // 当前缓冲区足够,直接写入 cursor.data.set(chunk, cursor.size); cursor.size += chunkSize; } required -= chunkSize; // 未读够数据,继续等待 if (required > 0) return; try { for (;;) { const result = reader.next(); if (result.done) { // 读取完成,新建 reader 读取下一个值 subscriber.next(result.value); reader = read(cursor); if (cursor.offset === cursor.size) { break; } } else { required = result.value; break; } } } catch (ex) { subscriber.error(ex); } }, error(err) { subscriber.error(err); cursor.data = EMPTY_BUFFER; cursor.view = EMPTY_VIEW; }, complete() { if (cursor.size > cursor.offset) { subscriber.error(new UnexpectedEofError()); } else { subscriber.complete(); } cursor.data = EMPTY_BUFFER; cursor.view = EMPTY_VIEW; }, }); }); }