File size: 4,531 Bytes
f6266b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
 * Protobuf wire format codec β€” zero-dependency, schema-less.
 *
 * Wire types:
 *   0 = Varint    (int32, uint64, bool, enum)
 *   1 = Fixed64   (double, fixed64)
 *   2 = LenDelim  (string, bytes, embedded messages)
 *   5 = Fixed32   (float, fixed32)
 */

// ─── Varint ────────────────────────────────────────────────

export function encodeVarint(value) {
  const bytes = [];
  let v = Number(value);
  if (v < 0) {
    const big = BigInt(v) & 0xFFFFFFFFFFFFFFFFn;
    let b = big;
    for (let i = 0; i < 10; i++) {
      bytes.push(Number(b & 0x7Fn) | (i < 9 ? 0x80 : 0));
      b >>= 7n;
    }
    return Buffer.from(bytes);
  }
  do {
    let byte = v & 0x7F;
    v >>>= 7;
    if (v > 0) byte |= 0x80;
    bytes.push(byte);
  } while (v > 0);
  return Buffer.from(bytes);
}

export function decodeVarint(buf, offset = 0) {
  let result = 0, shift = 0, pos = offset;
  while (pos < buf.length) {
    const byte = buf[pos++];
    result |= (byte & 0x7F) << shift;
    if (!(byte & 0x80)) break;
    shift += 7;
    if (shift >= 64) throw new Error('Varint overflow');
  }
  return { value: result >>> 0, length: pos - offset };
}

// ─── Field-level writers (standalone functions) ────────────

function makeTag(field, wireType) {
  return encodeVarint((field << 3) | wireType);
}

/** Write a varint field (wire type 0). */
export function writeVarintField(field, value) {
  return Buffer.concat([makeTag(field, 0), encodeVarint(value)]);
}

/** Write a length-delimited string field (wire type 2). */
export function writeStringField(field, str) {
  if (!str && str !== '') return Buffer.alloc(0);
  const data = Buffer.from(str, 'utf-8');
  return Buffer.concat([makeTag(field, 2), encodeVarint(data.length), data]);
}

/** Write a length-delimited bytes field (wire type 2). */
export function writeBytesField(field, data) {
  const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
  return Buffer.concat([makeTag(field, 2), encodeVarint(buf.length), buf]);
}

/** Write an embedded message field (wire type 2). */
export function writeMessageField(field, msgBuf) {
  if (!msgBuf || msgBuf.length === 0) return Buffer.alloc(0);
  return Buffer.concat([makeTag(field, 2), encodeVarint(msgBuf.length), msgBuf]);
}

/** Write a fixed64 field (wire type 1). */
export function writeFixed64Field(field, buf8) {
  return Buffer.concat([makeTag(field, 1), buf8]);
}

/** Write a bool field (wire type 0), only if true. */
export function writeBoolField(field, value) {
  if (!value) return Buffer.alloc(0);
  return writeVarintField(field, 1);
}

// ─── Parser ────────────────────────────────────────────────

/**
 * Parse a protobuf buffer into an array of { field, wireType, value }.
 * For varint (0): value is a Number.
 * For lendelim (2): value is a Buffer (caller decides string vs message).
 * For fixed64 (1): value is an 8-byte Buffer.
 * For fixed32 (5): value is a 4-byte Buffer.
 */
export function parseFields(buf) {
  const fields = [];
  let pos = 0;
  while (pos < buf.length) {
    const tag = decodeVarint(buf, pos);
    pos += tag.length;
    const fieldNum = tag.value >>> 3;
    const wireType = tag.value & 0x07;

    let value;
    switch (wireType) {
      case 0: { // varint
        const v = decodeVarint(buf, pos);
        pos += v.length;
        value = v.value;
        break;
      }
      case 1: { // fixed64
        value = buf.subarray(pos, pos + 8);
        pos += 8;
        break;
      }
      case 2: { // length-delimited
        const len = decodeVarint(buf, pos);
        pos += len.length;
        value = buf.subarray(pos, pos + len.value);
        pos += len.value;
        break;
      }
      case 5: { // fixed32
        value = buf.subarray(pos, pos + 4);
        pos += 4;
        break;
      }
      default:
        throw new Error(`Unknown wire type ${wireType} at offset ${pos}`);
    }
    fields.push({ field: fieldNum, wireType, value });
  }
  return fields;
}

/** Get first field matching number and optional wire type. */
export function getField(fields, num, wireType) {
  return fields.find(f => f.field === num && (wireType === undefined || f.wireType === wireType)) || null;
}

/** Get all fields matching number. */
export function getAllFields(fields, num) {
  return fields.filter(f => f.field === num);
}