@cloudpss/ubjson
Version:
Opinionated UBJSON encoder/decoder for CloudPSS.
99 lines (94 loc) • 4.33 kB
text/typescript
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;
},
});
});
}