Files

359 lines
7.6 KiB
TypeScript

const TE = new TextEncoder();
const TD = new TextDecoder('utf-8', { fatal: false, ignoreBOM: true });
export class Writer {
buf: Uint8Array;
view: DataView;
pos: number;
constructor(initial = 1024) {
this.buf = new Uint8Array(initial);
this.view = new DataView(this.buf.buffer);
this.pos = 0;
}
reset(): void {
this.pos = 0;
}
bytes(): Uint8Array {
return this.buf.subarray(0, this.pos);
}
bytesCopy(): Uint8Array {
return this.buf.slice(0, this.pos);
}
ensure(n: number): void {
if (this.pos + n > this.buf.byteLength) this.grow(n);
}
private grow(n: number): void {
let next = this.buf.byteLength * 2;
const need = this.pos + n;
while (next < need) next *= 2;
const nb = new Uint8Array(next);
nb.set(this.buf);
this.buf = nb;
this.view = new DataView(nb.buffer);
}
u8(v: number): void {
if (this.pos + 1 > this.buf.byteLength) this.grow(1);
this.buf[this.pos++] = v;
}
u16(v: number): void {
if (this.pos + 2 > this.buf.byteLength) this.grow(2);
this.view.setUint16(this.pos, v, true);
this.pos += 2;
}
i16(v: number): void {
if (this.pos + 2 > this.buf.byteLength) this.grow(2);
this.view.setInt16(this.pos, v, true);
this.pos += 2;
}
u32(v: number): void {
if (this.pos + 4 > this.buf.byteLength) this.grow(4);
this.view.setUint32(this.pos, v, true);
this.pos += 4;
}
i32(v: number): void {
if (this.pos + 4 > this.buf.byteLength) this.grow(4);
this.view.setInt32(this.pos, v, true);
this.pos += 4;
}
f32(v: number): void {
if (this.pos + 4 > this.buf.byteLength) this.grow(4);
this.view.setFloat32(this.pos, v, true);
this.pos += 4;
}
f64(v: number): void {
if (this.pos + 8 > this.buf.byteLength) this.grow(8);
this.view.setFloat64(this.pos, v, true);
this.pos += 8;
}
bool(v: boolean): void {
if (this.pos + 1 > this.buf.byteLength) this.grow(1);
this.buf[this.pos++] = v ? 1 : 0;
}
varu32(v: number): void {
if (this.pos + 5 > this.buf.byteLength) this.grow(5);
while (v >= 0x80) {
this.buf[this.pos++] = (v & 0x7f) | 0x80;
v >>>= 7;
}
this.buf[this.pos++] = v;
}
vari32(v: number): void {
const z = ((v << 1) ^ (v >> 31)) >>> 0;
this.varu32(z);
}
varu53(v: number): void {
if (this.pos + 10 > this.buf.byteLength) this.grow(10);
while (v >= 0x80) {
this.buf[this.pos++] = (v & 0x7f) | 0x80;
v = Math.floor(v / 128);
}
this.buf[this.pos++] = v;
}
vari53(v: number): void {
const z = v >= 0 ? BigInt(v) * 2n : -BigInt(v) * 2n - 1n;
this.varbu(z);
}
varbu(v: bigint): void {
if (this.pos + 10 > this.buf.byteLength) this.grow(10);
while (v >= 0x80n) {
this.buf[this.pos++] = Number(v & 0x7fn) | 0x80;
v >>= 7n;
}
this.buf[this.pos++] = Number(v);
}
varbi(v: bigint): void {
const z = v >= 0n ? v << 1n : (-v << 1n) - 1n;
this.varbu(z);
}
raw(src: Uint8Array): void {
if (this.pos + src.length > this.buf.byteLength) this.grow(src.length);
this.buf.set(src, this.pos);
this.pos += src.length;
}
bytesPrefixed(src: Uint8Array): void {
this.varu53(src.length);
this.raw(src);
}
str(s: string): void {
const len = s.length;
if (len === 0) {
this.u8(0);
return;
}
if (len < 64) {
if (this.pos + len + 5 > this.buf.byteLength) this.grow(len + 5);
let allAscii = true;
for (let i = 0; i < len; i++) {
if (s.charCodeAt(i) > 127) {
allAscii = false;
break;
}
}
if (allAscii) {
let p = this.pos;
let lv = len;
while (lv >= 0x80) {
this.buf[p++] = (lv & 0x7f) | 0x80;
lv >>>= 7;
}
this.buf[p++] = lv;
for (let i = 0; i < len; i++) this.buf[p++] = s.charCodeAt(i);
this.pos = p;
return;
}
}
const maxBytes = len * 3;
if (this.pos + maxBytes + 5 > this.buf.byteLength) this.grow(maxBytes + 5);
const dst = this.buf.subarray(this.pos + 5);
const { written } = TE.encodeInto(s, dst);
const w = written ?? 0;
let lenBytes = 1;
let lv = w;
while (lv >= 0x80) {
lenBytes++;
lv >>>= 7;
}
if (lenBytes < 5 && w > 0) {
this.buf.copyWithin(this.pos + lenBytes, this.pos + 5, this.pos + 5 + w);
}
let p = this.pos;
lv = w;
while (lv >= 0x80) {
this.buf[p++] = (lv & 0x7f) | 0x80;
lv >>>= 7;
}
this.buf[p++] = lv;
this.pos = p + w;
}
}
export class Reader {
buf: Uint8Array;
view: DataView;
pos: number;
end: number;
constructor(buf: Uint8Array) {
this.buf = buf;
this.view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
this.pos = 0;
this.end = buf.byteLength;
}
reset(buf: Uint8Array): void {
this.buf = buf;
this.view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
this.pos = 0;
this.end = buf.byteLength;
}
remaining(): number {
return this.end - this.pos;
}
u8(): number {
return this.buf[this.pos++]!;
}
u16(): number {
const v = this.view.getUint16(this.pos, true);
this.pos += 2;
return v;
}
i16(): number {
const v = this.view.getInt16(this.pos, true);
this.pos += 2;
return v;
}
u32(): number {
const v = this.view.getUint32(this.pos, true);
this.pos += 4;
return v;
}
i32(): number {
const v = this.view.getInt32(this.pos, true);
this.pos += 4;
return v;
}
f32(): number {
const v = this.view.getFloat32(this.pos, true);
this.pos += 4;
return v;
}
f64(): number {
const v = this.view.getFloat64(this.pos, true);
this.pos += 8;
return v;
}
bool(): boolean {
return this.buf[this.pos++] !== 0;
}
varu32(): number {
let v = 0;
let shift = 0;
let byte = 0;
do {
byte = this.buf[this.pos++]!;
v |= (byte & 0x7f) << shift;
shift += 7;
} while (byte & 0x80);
return v >>> 0;
}
vari32(): number {
const z = this.varu32();
return (z >>> 1) ^ -(z & 1);
}
varu53(): number {
let v = 0;
let mult = 1;
let byte = 0;
do {
byte = this.buf[this.pos++]!;
v += (byte & 0x7f) * mult;
mult *= 128;
} while (byte & 0x80);
return v;
}
vari53(): number {
const z = this.varbu();
const v = (z & 1n) === 0n ? z >> 1n : -((z >> 1n) + 1n);
return Number(v);
}
varbu(): bigint {
let v = 0n;
let shift = 0n;
let byte = 0;
do {
byte = this.buf[this.pos++]!;
v |= BigInt(byte & 0x7f) << shift;
shift += 7n;
} while (byte & 0x80);
return v;
}
varbi(): bigint {
const z = this.varbu();
return (z & 1n) === 0n ? z >> 1n : -((z >> 1n) + 1n);
}
bytes(n: number): Uint8Array {
const slice = this.buf.subarray(this.pos, this.pos + n);
this.pos += n;
return slice;
}
bytesPrefixed(): Uint8Array {
const n = this.varu53();
return this.bytes(n);
}
str(): string {
const len = this.varu53();
if (len === 0) return '';
const start = this.pos;
const end = start + len;
const buf = this.buf;
if (len < 32) {
let allAscii = true;
for (let i = start; i < end; i++) {
if (buf[i]! > 127) {
allAscii = false;
break;
}
}
if (allAscii) {
let s = '';
for (let i = start; i < end; i++) {
s += String.fromCharCode(buf[i]!);
}
this.pos = end;
return s;
}
}
const slice = buf.subarray(start, end);
this.pos = end;
return TD.decode(slice);
}
}