From 632ff325ed2e8f9edd47d6369fefffb85afa1003 Mon Sep 17 00:00:00 2001 From: Mr Martian Date: Tue, 21 Jan 2025 08:22:02 -0500 Subject: [PATCH] support only reading class --- README.md | 10 +++ bun.lockb | Bin 88013 -> 88381 bytes package.json | 14 +-- src/index.ts | 215 ++++++++++++++++---------------------------- tests/index.test.ts | 8 +- 5 files changed, 98 insertions(+), 149 deletions(-) diff --git a/README.md b/README.md index 8014444..27da5c9 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,16 @@ pbf.writeVarintField(1, 1); const result = pbf.commit(); ``` +If you want to reduce build size and know you're only reading data, not writing to it, use the `PbfReader` class: + +```ts +import { readFileSync } from 'fs'; +import { PbfReader } from 'pbf-ts'; + +const pbf = new PbfReader(readFileSync(path)); +// ... +``` + More complex example: ```ts diff --git a/bun.lockb b/bun.lockb index 3bbd07a830fb70b3eba47503e4afe3abdb0a878b..87213877427f792b0bd3f82cc8d31c1aa84fd1a3 100755 GIT binary patch delta 10770 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 diff --git a/package.json b/package.json index 9c577a2..20fa67d 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pbf-ts", - "version": "1.0.3", + "version": "1.1.0", "description": "A low-level, fast, ultra-lightweight typescript library for decoding and encoding protocol buffers.", "type": "module", "keywords": [ @@ -56,18 +56,18 @@ "homepage": "https://github.com/Open-S2/s2-tools#readme", "devDependencies": { "@skypack/package-check": "^0.2.2", - "@types/bun": "^1.1.16", - "@types/node": "^22.10.5", + "@types/bun": "^1.1.18", + "@types/node": "^22.10.7", "coveralls": "^3.1.1", "eslint": "^9.18.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-jsdoc": "^50.6.1", - "eslint-plugin-prettier": "^5.2.1", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-jsdoc": "^50.6.2", + "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-tsdoc": "^0.4.0", "prettier": "^3.4.2", "typedoc": "^0.27.6", "typedoc-plugin-coverage": "^3.4.1", "typescript": "^5.7.3", - "typescript-eslint": "^8.19.1" + "typescript-eslint": "^8.21.0" } } diff --git a/src/index.ts b/src/index.ts index 98f9e75..4afd481 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,11 @@ const SHIFT_LEFT_32 = 4294967296; // (1 << 16) * (1 << 16); const SHIFT_RIGHT_32 = 2.3283064365386963e-10; // 1 / SHIFT_LEFT_32; -// Threshold chosen based on both benchmarking and knowledge about browser string -// data structures (which currently switch structure types at 12 bytes or more) -const TEXT_DECODER_MIN_LENGTH = 12; /** * User defined function to read in fields from a Pbf instance into input. * @template U - the input type */ -export type ReadFieldFunction = (tag: number, input: U, pbf: Pbf) => void; +export type ReadFieldFunction = (tag: number, input: U, pbf: Pbf & PbfReader) => void; /** * A tag is a pair of a number and a type. @@ -20,31 +17,8 @@ export interface Tag { type: number; } -/** - * # Protobuffer - * - * ## Description - * Create a new PBF instance and either read or write to it. - * Follows the early Protobuf spec supporting various types of encoding - * including messages (which are usually representative of class objects). - * - * ## Usage - * - * ### Reading: - * ```ts - * const data = fs.readFileSync(path); - * const pbf = new Pbf(data); - * ``` - * - * ### Writing: - * ```ts - * const pbf = new Pbf(); - * pbf.writeVarintField(1, 1); - * // ... - * const result = pbf.commit(); - * ``` - */ -export class Pbf { +/** Base class for the Protobuf spec */ +export class BasePbf { buf: Uint8Array; dataView: DataView; pos: number; @@ -78,9 +52,26 @@ export class Pbf { this.type = 0; this.length = 0; } +} - // === READING ================================================================= - +/** + * # Protobuffer Reader + * + * ## Description + * Create a new PBF instance read to it. + * Follows the early Protobuf spec supporting various types of encoding + * including messages (which are usually representative of class objects). + * + * ## Usage + * + * ### Reading: + * ```ts + * const data = fs.readFileSync(path); + * const pbf = new Pbf(data); + * // start reading here + * ``` + */ +export class PbfReader extends BasePbf { /** * Reads a tag from the buffer, pulls out the tag and type and returns it. * @returns - {tag: number, type: number} @@ -128,7 +119,7 @@ export class Pbf { const startPos = this.pos; this.type = val & 0x7; - readField(tag, input, this); + readField(tag, input, this as unknown as Pbf); if (this.pos === startPos) this.skip(val); } @@ -298,12 +289,7 @@ export class Pbf { const pos = this.pos; this.pos = end; - if (end - pos >= TEXT_DECODER_MIN_LENGTH) { - // longer strings are fast with the built-in browser TextDecoder API - return this.textDecoder.decode(this.buf.subarray(pos, end)); - } - - return readUtf8(this.buf, pos, end); + return this.textDecoder.decode(this.buf.subarray(pos, end)); } /** @@ -318,15 +304,13 @@ export class Pbf { return buffer; } - // verbose for performance reasons; doesn't affect gzipped size - /** * @param arr - the array to write to * @param isSigned - true if the numbers are signed * @returns - the `arr` input with the decoded numbers is also returned */ readPackedVarint(arr: number[] = [], isSigned = false): number[] { - if (this.type !== Pbf.Bytes) { + if (this.type !== BasePbf.Bytes) { arr.push(this.readVarint(isSigned)); } else { const end = readPackedEnd(this); @@ -340,7 +324,7 @@ export class Pbf { * @returns - the `arr` input with the decoded numbers is also returned */ readPackedSVarint(arr: number[] = []): number[] { - if (this.type !== Pbf.Bytes) { + if (this.type !== BasePbf.Bytes) { arr.push(this.readSVarint()); } else { const end = readPackedEnd(this); @@ -354,7 +338,7 @@ export class Pbf { * @returns - the `arr` input with the decoded boolean values is also returned */ readPackedBoolean(arr: boolean[] = []): boolean[] { - if (this.type !== Pbf.Bytes) { + if (this.type !== BasePbf.Bytes) { arr.push(this.readBoolean()); } else { const end = readPackedEnd(this); @@ -368,7 +352,7 @@ export class Pbf { * @returns - the `arr` input with the decoded floats is also returned */ readPackedFloat(arr: number[] = []): number[] { - if (this.type !== Pbf.Bytes) { + if (this.type !== BasePbf.Bytes) { arr.push(this.readFloat()); } else { const end = readPackedEnd(this); @@ -382,7 +366,7 @@ export class Pbf { * @returns - the `arr` input with the decoded doubles is also returned */ readPackedDouble(arr: number[] = []): number[] { - if (this.type !== Pbf.Bytes) { + if (this.type !== BasePbf.Bytes) { arr.push(this.readDouble()); } else { const end = readPackedEnd(this); @@ -396,7 +380,7 @@ export class Pbf { * @returns - the `arr` input with the decoded unsigned integers is also returned */ readPackedFixed32(arr: number[] = []): number[] { - if (this.type !== Pbf.Bytes) { + if (this.type !== BasePbf.Bytes) { arr.push(this.readFixed32()); } else { const end = readPackedEnd(this); @@ -410,7 +394,7 @@ export class Pbf { * @returns - the `arr` input with the decoded signed integers is also returned */ readPackedSFixed32(arr: number[] = []): number[] { - if (this.type !== Pbf.Bytes) { + if (this.type !== BasePbf.Bytes) { arr.push(this.readSFixed32()); } else { const end = readPackedEnd(this); @@ -424,7 +408,7 @@ export class Pbf { * @returns - the `arr` input with the decoded unsigned 64-bit integers is also returned */ readPackedFixed64(arr: number[] = []): number[] { - if (this.type !== Pbf.Bytes) { + if (this.type !== BasePbf.Bytes) { arr.push(this.readFixed64()); } else { const end = readPackedEnd(this); @@ -438,7 +422,7 @@ export class Pbf { * @returns - the `arr` input with the decoded signed 64-bit integers is also returned */ readPackedSFixed64(arr: number[] = []): number[] { - if (this.type !== Pbf.Bytes) { + if (this.type !== BasePbf.Bytes) { arr.push(this.readSFixed64()); } else { const end = readPackedEnd(this); @@ -453,18 +437,42 @@ export class Pbf { */ skip(val: number): void { const type = val & 0x7; - if (type === Pbf.Varint) { + if (type === BasePbf.Varint) { while (this.buf[this.pos++] > 0x7f) { continue; } - } else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos; - else if (type === Pbf.Fixed32) this.pos += 4; - else if (type === Pbf.Fixed64) this.pos += 8; + } else if (type === BasePbf.Bytes) this.pos = this.readVarint() + this.pos; + else if (type === BasePbf.Fixed32) this.pos += 4; + else if (type === BasePbf.Fixed64) this.pos += 8; else throw new Error('Unimplemented type: ' + String(type)); } +} - // === WRITING ================================================================= - +/** + * # Protobuffer Reader and Writer + * + * ## Description + * Create a new PBF instance and either read or write to it. + * Follows the early Protobuf spec supporting various types of encoding + * including messages (which are usually representative of class objects). + * + * ## Usage + * + * ### Reading: + * ```ts + * const data = fs.readFileSync(path); + * const pbf = new Pbf(data); + * ``` + * + * ### Writing: + * ```ts + * const pbf = new Pbf(); + * pbf.writeVarintField(1, 1); + * // ... + * const result = pbf.commit(); + * ``` + */ +export class Pbf extends PbfReader { /** * Write a tag and its associated type * @param tag - the tag to write @@ -676,7 +684,7 @@ export class Pbf { * @param obj - the object to pass to the user defined function */ writeMessage(tag: number, fn: (obj: T, pbf: Pbf) => void, obj: T): void { - this.writeTag(tag, Pbf.Bytes); + this.writeTag(tag, BasePbf.Bytes); this.writeRawMessage(fn, obj); } @@ -769,7 +777,7 @@ export class Pbf { * @param buffer - the buffer of bytes to write. */ writeBytesField(tag: number, buffer: Buffer | Uint8Array | ArrayBuffer): void { - this.writeTag(tag, Pbf.Bytes); + this.writeTag(tag, BasePbf.Bytes); this.writeBytes(buffer); } @@ -780,7 +788,7 @@ export class Pbf { * @param val - the unsigned 32-bit integer to write. */ writeFixed32Field(tag: number, val: number): void { - this.writeTag(tag, Pbf.Fixed32); + this.writeTag(tag, BasePbf.Fixed32); this.writeFixed32(val); } @@ -791,7 +799,7 @@ export class Pbf { * @param val - the signed 32-bit integer to write. */ writeSFixed32Field(tag: number, val: number): void { - this.writeTag(tag, Pbf.Fixed32); + this.writeTag(tag, BasePbf.Fixed32); this.writeSFixed32(val); } @@ -802,7 +810,7 @@ export class Pbf { * @param val - the unsigned 64-bit integer to write. */ writeFixed64Field(tag: number, val: number): void { - this.writeTag(tag, Pbf.Fixed64); + this.writeTag(tag, BasePbf.Fixed64); this.writeFixed64(val); } @@ -813,7 +821,7 @@ export class Pbf { * @param val - the signed 64-bit integer to write. */ writeSFixed64Field(tag: number, val: number): void { - this.writeTag(tag, Pbf.Fixed64); + this.writeTag(tag, BasePbf.Fixed64); this.writeSFixed64(val); } @@ -824,7 +832,7 @@ export class Pbf { * @param val - the unsigned number to write. */ writeVarintField(tag: number, val: number): void { - this.writeTag(tag, Pbf.Varint); + this.writeTag(tag, BasePbf.Varint); this.writeVarint(val); } @@ -835,7 +843,7 @@ export class Pbf { * @param val - the signed number to write. */ writeSVarintField(tag: number, val: number): void { - this.writeTag(tag, Pbf.Varint); + this.writeTag(tag, BasePbf.Varint); this.writeSVarint(val); } @@ -846,7 +854,7 @@ export class Pbf { * @param str - the string to write. */ writeStringField(tag: number, str: string): void { - this.writeTag(tag, Pbf.Bytes); + this.writeTag(tag, BasePbf.Bytes); this.writeString(str); } @@ -857,7 +865,7 @@ export class Pbf { * @param val - the float to write. */ writeFloatField(tag: number, val: number): void { - this.writeTag(tag, Pbf.Fixed32); + this.writeTag(tag, BasePbf.Fixed32); this.writeFloat(val); } @@ -868,7 +876,7 @@ export class Pbf { * @param val - the double to write. */ writeDoubleField(tag: number, val: number): void { - this.writeTag(tag, Pbf.Fixed64); + this.writeTag(tag, BasePbf.Fixed64); this.writeDouble(val); } @@ -890,7 +898,7 @@ export class Pbf { * @param p - the protobuf * @returns - the decoded remainder */ -function readVarintRemainder(l: number, s: boolean, p: Pbf): number { +function readVarintRemainder(l: number, s: boolean, p: Pbf | PbfReader): number { const buf = p.buf; let h; let b; @@ -922,8 +930,8 @@ function readVarintRemainder(l: number, s: boolean, p: Pbf): number { * @param pbf - the protobuf * @returns - the end of the packed array */ -function readPackedEnd(pbf: Pbf): number { - return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1; +function readPackedEnd(pbf: Pbf | PbfReader): number { + return pbf.type === BasePbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1; } /** @@ -1109,75 +1117,6 @@ function writePackedSFixed64(arr: number[], pbf: Pbf): void { // Buffer code below from https://github.com/feross/buffer, MIT-licensed -/** - * Read UTF-8 string from buffer at "pos" till "end" - * @param buf - the buffer of bytes - * @param pos - the position in the buffer to read from - * @param end - the position in the buffer to stop at - * @returns - the utf-8 string - */ -function readUtf8(buf: Uint8Array, pos: number, end: number): string { - let str = ''; - let i = pos; - - while (i < end) { - const b0 = buf[i]; - let c: number | null = null; // codepoint - let bytesPerSequence = b0 > 0xef ? 4 : b0 > 0xdf ? 3 : b0 > 0xbf ? 2 : 1; - - if (i + bytesPerSequence > end) break; - - let b1: number, b2: number, b3: number; - - if (bytesPerSequence === 1) { - if (b0 < 0x80) { - c = b0; - } - } else if (bytesPerSequence === 2) { - b1 = buf[i + 1]; - if ((b1 & 0xc0) === 0x80) { - c = ((b0 & 0x1f) << 0x6) | (b1 & 0x3f); - if (c <= 0x7f) { - c = null; - } - } - } else if (bytesPerSequence === 3) { - b1 = buf[i + 1]; - b2 = buf[i + 2]; - if ((b1 & 0xc0) === 0x80 && (b2 & 0xc0) === 0x80) { - c = ((b0 & 0xf) << 0xc) | ((b1 & 0x3f) << 0x6) | (b2 & 0x3f); - if (c <= 0x7ff || (c >= 0xd800 && c <= 0xdfff)) { - c = null; - } - } - } else if (bytesPerSequence === 4) { - b1 = buf[i + 1]; - b2 = buf[i + 2]; - b3 = buf[i + 3]; - if ((b1 & 0xc0) === 0x80 && (b2 & 0xc0) === 0x80 && (b3 & 0xc0) === 0x80) { - c = ((b0 & 0xf) << 0x12) | ((b1 & 0x3f) << 0xc) | ((b2 & 0x3f) << 0x6) | (b3 & 0x3f); - if (c <= 0xffff || c >= 0x110000) { - c = null; - } - } - } - - if (c === null) { - c = 0xfffd; - bytesPerSequence = 1; - } else if (c > 0xffff) { - c -= 0x10000; - str += String.fromCharCode(((c >>> 10) & 0x3ff) | 0xd800); - c = 0xdc00 | (c & 0x3ff); - } - - str += String.fromCharCode(c); - i += bytesPerSequence; - } - - return str; -} - /** * Write a utf8 string to the buffer * @param buf - the buffer of bytes diff --git a/tests/index.test.ts b/tests/index.test.ts index e55a4fc..eae0251 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -1,4 +1,4 @@ -import { Pbf as Protobuf } from '../src'; +import { PbfReader, Pbf as Protobuf } from '../src'; import { expect, test } from 'bun:test'; test('constructor', () => { @@ -454,7 +454,7 @@ test('writeMessage & (readMessage/readFields)', () => { * @param pbf - the Protobuf object to read from * @param end - the position to stop at */ - constructor(pbf: Protobuf, end = 0) { + constructor(pbf: Protobuf | PbfReader, end = 0) { pbf.readFields(Test.read, this, end); } /** @@ -500,12 +500,12 @@ test('writeMessage & (readMessage/readFields)', () => { const data = pbf.commit(); expect(data).toEqual(new Uint8Array([42, 9, 8, 1, 21, 205, 204, 12, 64, 24, 5])); - const pbf2 = new Protobuf(data); + const pbf2 = new PbfReader(data); expect(pbf2.readTag()).toEqual({ tag: 5, type: Protobuf.Bytes }); const t2 = new Test(pbf2, pbf2.readVarint() + pbf2.pos); expect(t2).toEqual({ a: 1, b: 2.200000047683716, c: -3 } as Test); - const pbf3 = new Protobuf(data); + const pbf3 = new PbfReader(data); const t3 = Test.newTestDefault(); expect(pbf3.readTag()).toEqual({ tag: 5, type: Protobuf.Bytes }); pbf3.readMessage(Test.read, t3);