typia
Version:
Superfast runtime validators with only one line
189 lines (146 loc) • 4.87 kB
text/typescript
import { ProtobufWire } from "../programmers/helpers/ProtobufWire";
import { Singleton } from "../utils/Singleton";
/// @reference https://github.com/piotr-oles/as-proto/blob/main/packages/as-proto/assembly/internal/FixedReader.ts
export class _ProtobufReader {
/** Read buffer */
private buf: Uint8Array;
/** Read buffer pointer. */
private ptr: number;
/** DataView for buffer. */
private view: DataView;
public constructor(buf: Uint8Array) {
this.buf = buf;
this.ptr = 0;
this.view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
}
public index(): number {
return this.ptr;
}
public size(): number {
return this.buf.length;
}
public uint32(): number {
return this.varint32();
}
public int32(): number {
return this.varint32();
}
public sint32(): number {
const value: number = this.varint32();
return (value >>> 1) ^ -(value & 1);
}
public uint64(): bigint {
return this.varint64();
}
public int64(): bigint {
return this.varint64();
}
public sint64(): bigint {
const value = this.varint64();
return (value >> BigInt(0x01)) ^ -(value & BigInt(0x01));
}
public bool(): boolean {
return this.varint32() !== 0;
}
public float(): number {
const value: number = this.view.getFloat32(this.ptr, true);
this.ptr += 4;
return value;
}
public double(): number {
const value: number = this.view.getFloat64(this.ptr, true);
this.ptr += 8;
return value;
}
public bytes(): Uint8Array {
const length: number = this.uint32();
const from: number = this.ptr;
this.ptr += length;
return this.buf.subarray(from, from + length);
}
public string(): string {
return utf8.get().decode(this.bytes());
}
public skip(length: number): void {
if (length === 0) while (this.u8() & 0x80);
else {
if (this.index() + length > this.size())
throw new Error("Error on typia.protobuf.decode(): buffer overflow.");
this.ptr += length;
}
}
public skipType(wireType: ProtobufWire): void {
switch (wireType) {
case ProtobufWire.VARIANT:
this.skip(0);
break;
case ProtobufWire.I64:
this.skip(8);
break;
case ProtobufWire.LEN:
this.skip(this.uint32());
break;
case ProtobufWire.START_GROUP:
while ((wireType = this.uint32() & 0x07) !== ProtobufWire.END_GROUP)
this.skipType(wireType);
break;
case ProtobufWire.I32:
this.skip(4);
break;
default:
throw new Error(`Invalid wire type ${wireType} at offset ${this.ptr}.`);
}
}
private varint32(): number {
let loaded: number;
let value: number;
value = (loaded = this.u8()) & 0x7f;
if (loaded < 0x80) return value;
value |= ((loaded = this.u8()) & 0x7f) << 7;
if (loaded < 0x80) return value;
value |= ((loaded = this.u8()) & 0x7f) << 14;
if (loaded < 0x80) return value;
value |= ((loaded = this.u8()) & 0x7f) << 21;
if (loaded < 0x80) return value;
value |= ((loaded = this.u8()) & 0xf) << 28;
if (loaded < 0x80) return value;
// increment position until there is no continuation bit or until we read 10 bytes
if (this.u8() < 0x80) return value;
if (this.u8() < 0x80) return value;
if (this.u8() < 0x80) return value;
if (this.u8() < 0x80) return value;
if (this.u8() < 0x80) return value;
return value;
}
private varint64(): bigint {
let loaded: bigint;
let value: bigint;
value = (loaded = this.u8n()) & BigInt(0x7f);
if (loaded < BigInt(0x80)) return value;
value |= ((loaded = this.u8n()) & BigInt(0x7f)) << BigInt(7);
if (loaded < BigInt(0x80)) return value;
value |= ((loaded = this.u8n()) & BigInt(0x7f)) << BigInt(14);
if (loaded < BigInt(0x80)) return value;
value |= ((loaded = this.u8n()) & BigInt(0x7f)) << BigInt(21);
if (loaded < BigInt(0x80)) return value;
value |= ((loaded = this.u8n()) & BigInt(0x7f)) << BigInt(28);
if (loaded < BigInt(0x80)) return value;
value |= ((loaded = this.u8n()) & BigInt(0x7f)) << BigInt(35);
if (loaded < BigInt(0x80)) return value;
value |= ((loaded = this.u8n()) & BigInt(0x7f)) << BigInt(42);
if (loaded < BigInt(0x80)) return value;
value |= ((loaded = this.u8n()) & BigInt(0x7f)) << BigInt(49);
if (loaded < BigInt(0x80)) return value;
value |= ((loaded = this.u8n()) & BigInt(0x7f)) << BigInt(56);
if (loaded < BigInt(0x80)) return value;
value |= (this.u8n() & BigInt(0x01)) << BigInt(63);
return BigInt.asIntN(64, value);
}
private u8(): number {
return this.view.getUint8(this.ptr++);
}
private u8n(): bigint {
return BigInt(this.u8());
}
}
const utf8 = /** @__PURE__ */ new Singleton(() => new TextDecoder("utf-8"));