@elaraai/east 0.0.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/LICENSE.md +682 -0
  2. package/README.md +276 -0
  3. package/dist/src/analyze.d.ts +95 -0
  4. package/dist/src/analyze.d.ts.map +1 -0
  5. package/dist/src/analyze.js +1110 -0
  6. package/dist/src/analyze.js.map +1 -0
  7. package/dist/src/ast.d.ts +263 -0
  8. package/dist/src/ast.d.ts.map +1 -0
  9. package/dist/src/ast.js +151 -0
  10. package/dist/src/ast.js.map +1 -0
  11. package/dist/src/ast_to_ir.d.ts +24 -0
  12. package/dist/src/ast_to_ir.d.ts.map +1 -0
  13. package/dist/src/ast_to_ir.js +834 -0
  14. package/dist/src/ast_to_ir.js.map +1 -0
  15. package/dist/src/builtins.d.ts +18 -0
  16. package/dist/src/builtins.d.ts.map +1 -0
  17. package/dist/src/builtins.js +1105 -0
  18. package/dist/src/builtins.js.map +1 -0
  19. package/dist/src/comparison.d.ts +28 -0
  20. package/dist/src/comparison.d.ts.map +1 -0
  21. package/dist/src/comparison.js +1017 -0
  22. package/dist/src/comparison.js.map +1 -0
  23. package/dist/src/compile.d.ts +22 -0
  24. package/dist/src/compile.d.ts.map +1 -0
  25. package/dist/src/compile.js +3260 -0
  26. package/dist/src/compile.js.map +1 -0
  27. package/dist/src/containers/ref.d.ts +106 -0
  28. package/dist/src/containers/ref.d.ts.map +1 -0
  29. package/dist/src/containers/ref.js +100 -0
  30. package/dist/src/containers/ref.js.map +1 -0
  31. package/dist/src/containers/sortedmap.d.ts +165 -0
  32. package/dist/src/containers/sortedmap.d.ts.map +1 -0
  33. package/dist/src/containers/sortedmap.js +237 -0
  34. package/dist/src/containers/sortedmap.js.map +1 -0
  35. package/dist/src/containers/sortedset.d.ts +185 -0
  36. package/dist/src/containers/sortedset.d.ts.map +1 -0
  37. package/dist/src/containers/sortedset.js +312 -0
  38. package/dist/src/containers/sortedset.js.map +1 -0
  39. package/dist/src/containers/variant.d.ts +131 -0
  40. package/dist/src/containers/variant.d.ts.map +1 -0
  41. package/dist/src/containers/variant.js +68 -0
  42. package/dist/src/containers/variant.js.map +1 -0
  43. package/dist/src/datetime_format/parse.d.ts +50 -0
  44. package/dist/src/datetime_format/parse.d.ts.map +1 -0
  45. package/dist/src/datetime_format/parse.js +908 -0
  46. package/dist/src/datetime_format/parse.js.map +1 -0
  47. package/dist/src/datetime_format/print.d.ts +35 -0
  48. package/dist/src/datetime_format/print.d.ts.map +1 -0
  49. package/dist/src/datetime_format/print.js +157 -0
  50. package/dist/src/datetime_format/print.js.map +1 -0
  51. package/dist/src/datetime_format/tokenize.d.ts +76 -0
  52. package/dist/src/datetime_format/tokenize.d.ts.map +1 -0
  53. package/dist/src/datetime_format/tokenize.js +271 -0
  54. package/dist/src/datetime_format/tokenize.js.map +1 -0
  55. package/dist/src/datetime_format/types.d.ts +99 -0
  56. package/dist/src/datetime_format/types.d.ts.map +1 -0
  57. package/dist/src/datetime_format/types.js +103 -0
  58. package/dist/src/datetime_format/types.js.map +1 -0
  59. package/dist/src/datetime_format/validate.d.ts +51 -0
  60. package/dist/src/datetime_format/validate.d.ts.map +1 -0
  61. package/dist/src/datetime_format/validate.js +208 -0
  62. package/dist/src/datetime_format/validate.js.map +1 -0
  63. package/dist/src/default.d.ts +21 -0
  64. package/dist/src/default.d.ts.map +1 -0
  65. package/dist/src/default.js +82 -0
  66. package/dist/src/default.js.map +1 -0
  67. package/dist/src/eastir.d.ts +33 -0
  68. package/dist/src/eastir.d.ts.map +1 -0
  69. package/dist/src/eastir.js +92 -0
  70. package/dist/src/eastir.js.map +1 -0
  71. package/dist/src/error.d.ts +13 -0
  72. package/dist/src/error.d.ts.map +1 -0
  73. package/dist/src/error.js +8 -0
  74. package/dist/src/error.js.map +1 -0
  75. package/dist/src/expr/array.d.ts +1711 -0
  76. package/dist/src/expr/array.d.ts.map +1 -0
  77. package/dist/src/expr/array.js +1805 -0
  78. package/dist/src/expr/array.js.map +1 -0
  79. package/dist/src/expr/ast.d.ts +17 -0
  80. package/dist/src/expr/ast.d.ts.map +1 -0
  81. package/dist/src/expr/ast.js +302 -0
  82. package/dist/src/expr/ast.js.map +1 -0
  83. package/dist/src/expr/blob.d.ts +141 -0
  84. package/dist/src/expr/blob.d.ts.map +1 -0
  85. package/dist/src/expr/blob.js +198 -0
  86. package/dist/src/expr/blob.js.map +1 -0
  87. package/dist/src/expr/block.d.ts +201 -0
  88. package/dist/src/expr/block.d.ts.map +1 -0
  89. package/dist/src/expr/block.js +1505 -0
  90. package/dist/src/expr/block.js.map +1 -0
  91. package/dist/src/expr/boolean.d.ts +207 -0
  92. package/dist/src/expr/boolean.d.ts.map +1 -0
  93. package/dist/src/expr/boolean.js +261 -0
  94. package/dist/src/expr/boolean.js.map +1 -0
  95. package/dist/src/expr/datetime.d.ts +544 -0
  96. package/dist/src/expr/datetime.d.ts.map +1 -0
  97. package/dist/src/expr/datetime.js +980 -0
  98. package/dist/src/expr/datetime.js.map +1 -0
  99. package/dist/src/expr/dict.d.ts +1242 -0
  100. package/dist/src/expr/dict.d.ts.map +1 -0
  101. package/dist/src/expr/dict.js +1492 -0
  102. package/dist/src/expr/dict.js.map +1 -0
  103. package/dist/src/expr/expr.d.ts +95 -0
  104. package/dist/src/expr/expr.d.ts.map +1 -0
  105. package/dist/src/expr/expr.js +171 -0
  106. package/dist/src/expr/expr.js.map +1 -0
  107. package/dist/src/expr/float.d.ts +357 -0
  108. package/dist/src/expr/float.d.ts.map +1 -0
  109. package/dist/src/expr/float.js +637 -0
  110. package/dist/src/expr/float.js.map +1 -0
  111. package/dist/src/expr/function.d.ts +46 -0
  112. package/dist/src/expr/function.d.ts.map +1 -0
  113. package/dist/src/expr/function.js +58 -0
  114. package/dist/src/expr/function.js.map +1 -0
  115. package/dist/src/expr/index.d.ts +450 -0
  116. package/dist/src/expr/index.d.ts.map +1 -0
  117. package/dist/src/expr/index.js +423 -0
  118. package/dist/src/expr/index.js.map +1 -0
  119. package/dist/src/expr/integer.d.ts +256 -0
  120. package/dist/src/expr/integer.d.ts.map +1 -0
  121. package/dist/src/expr/integer.js +311 -0
  122. package/dist/src/expr/integer.js.map +1 -0
  123. package/dist/src/expr/libs/array.d.ts +106 -0
  124. package/dist/src/expr/libs/array.d.ts.map +1 -0
  125. package/dist/src/expr/libs/array.js +140 -0
  126. package/dist/src/expr/libs/array.js.map +1 -0
  127. package/dist/src/expr/libs/blob.d.ts +42 -0
  128. package/dist/src/expr/libs/blob.d.ts.map +1 -0
  129. package/dist/src/expr/libs/blob.js +70 -0
  130. package/dist/src/expr/libs/blob.js.map +1 -0
  131. package/dist/src/expr/libs/datetime.d.ts +479 -0
  132. package/dist/src/expr/libs/datetime.d.ts.map +1 -0
  133. package/dist/src/expr/libs/datetime.js +624 -0
  134. package/dist/src/expr/libs/datetime.js.map +1 -0
  135. package/dist/src/expr/libs/dict.d.ts +66 -0
  136. package/dist/src/expr/libs/dict.d.ts.map +1 -0
  137. package/dist/src/expr/libs/dict.js +77 -0
  138. package/dist/src/expr/libs/dict.js.map +1 -0
  139. package/dist/src/expr/libs/float.d.ts +299 -0
  140. package/dist/src/expr/libs/float.d.ts.map +1 -0
  141. package/dist/src/expr/libs/float.js +564 -0
  142. package/dist/src/expr/libs/float.js.map +1 -0
  143. package/dist/src/expr/libs/integer.d.ts +228 -0
  144. package/dist/src/expr/libs/integer.d.ts.map +1 -0
  145. package/dist/src/expr/libs/integer.js +398 -0
  146. package/dist/src/expr/libs/integer.js.map +1 -0
  147. package/dist/src/expr/libs/set.d.ts +59 -0
  148. package/dist/src/expr/libs/set.d.ts.map +1 -0
  149. package/dist/src/expr/libs/set.js +69 -0
  150. package/dist/src/expr/libs/set.js.map +1 -0
  151. package/dist/src/expr/libs/string.d.ts +71 -0
  152. package/dist/src/expr/libs/string.d.ts.map +1 -0
  153. package/dist/src/expr/libs/string.js +75 -0
  154. package/dist/src/expr/libs/string.js.map +1 -0
  155. package/dist/src/expr/never.d.ts +15 -0
  156. package/dist/src/expr/never.d.ts.map +1 -0
  157. package/dist/src/expr/never.js +12 -0
  158. package/dist/src/expr/never.js.map +1 -0
  159. package/dist/src/expr/null.d.ts +15 -0
  160. package/dist/src/expr/null.d.ts.map +1 -0
  161. package/dist/src/expr/null.js +12 -0
  162. package/dist/src/expr/null.js.map +1 -0
  163. package/dist/src/expr/ref.d.ts +103 -0
  164. package/dist/src/expr/ref.d.ts.map +1 -0
  165. package/dist/src/expr/ref.js +131 -0
  166. package/dist/src/expr/ref.js.map +1 -0
  167. package/dist/src/expr/regex_validation.d.ts +25 -0
  168. package/dist/src/expr/regex_validation.d.ts.map +1 -0
  169. package/dist/src/expr/regex_validation.js +130 -0
  170. package/dist/src/expr/regex_validation.js.map +1 -0
  171. package/dist/src/expr/set.d.ts +1071 -0
  172. package/dist/src/expr/set.d.ts.map +1 -0
  173. package/dist/src/expr/set.js +1137 -0
  174. package/dist/src/expr/set.js.map +1 -0
  175. package/dist/src/expr/string.d.ts +414 -0
  176. package/dist/src/expr/string.d.ts.map +1 -0
  177. package/dist/src/expr/string.js +683 -0
  178. package/dist/src/expr/string.js.map +1 -0
  179. package/dist/src/expr/struct.d.ts +48 -0
  180. package/dist/src/expr/struct.d.ts.map +1 -0
  181. package/dist/src/expr/struct.js +65 -0
  182. package/dist/src/expr/struct.js.map +1 -0
  183. package/dist/src/expr/types.d.ts +68 -0
  184. package/dist/src/expr/types.d.ts.map +1 -0
  185. package/dist/src/expr/types.js +6 -0
  186. package/dist/src/expr/types.js.map +1 -0
  187. package/dist/src/expr/variant.d.ts +137 -0
  188. package/dist/src/expr/variant.d.ts.map +1 -0
  189. package/dist/src/expr/variant.js +105 -0
  190. package/dist/src/expr/variant.js.map +1 -0
  191. package/dist/src/fuzz.d.ts +80 -0
  192. package/dist/src/fuzz.d.ts.map +1 -0
  193. package/dist/src/fuzz.js +300 -0
  194. package/dist/src/fuzz.js.map +1 -0
  195. package/dist/src/index.d.ts +21 -0
  196. package/dist/src/index.d.ts.map +1 -0
  197. package/dist/src/index.js +21 -0
  198. package/dist/src/index.js.map +1 -0
  199. package/dist/src/internal.d.ts +36 -0
  200. package/dist/src/internal.d.ts.map +1 -0
  201. package/dist/src/internal.js +11 -0
  202. package/dist/src/internal.js.map +1 -0
  203. package/dist/src/ir.d.ts +1571 -0
  204. package/dist/src/ir.d.ts.map +1 -0
  205. package/dist/src/ir.js +56 -0
  206. package/dist/src/ir.js.map +1 -0
  207. package/dist/src/location.d.ts +48 -0
  208. package/dist/src/location.d.ts.map +1 -0
  209. package/dist/src/location.js +62 -0
  210. package/dist/src/location.js.map +1 -0
  211. package/dist/src/platform.d.ts +21 -0
  212. package/dist/src/platform.d.ts.map +1 -0
  213. package/dist/src/platform.js +8 -0
  214. package/dist/src/platform.js.map +1 -0
  215. package/dist/src/serialization/beast.d.ts +39 -0
  216. package/dist/src/serialization/beast.d.ts.map +1 -0
  217. package/dist/src/serialization/beast.js +555 -0
  218. package/dist/src/serialization/beast.js.map +1 -0
  219. package/dist/src/serialization/beast2-stream.d.ts +38 -0
  220. package/dist/src/serialization/beast2-stream.d.ts.map +1 -0
  221. package/dist/src/serialization/beast2-stream.js +665 -0
  222. package/dist/src/serialization/beast2-stream.js.map +1 -0
  223. package/dist/src/serialization/beast2.d.ts +41 -0
  224. package/dist/src/serialization/beast2.d.ts.map +1 -0
  225. package/dist/src/serialization/beast2.js +489 -0
  226. package/dist/src/serialization/beast2.js.map +1 -0
  227. package/dist/src/serialization/binary-utils.d.ts +151 -0
  228. package/dist/src/serialization/binary-utils.d.ts.map +1 -0
  229. package/dist/src/serialization/binary-utils.js +929 -0
  230. package/dist/src/serialization/binary-utils.js.map +1 -0
  231. package/dist/src/serialization/east.d.ts +84 -0
  232. package/dist/src/serialization/east.d.ts.map +1 -0
  233. package/dist/src/serialization/east.js +1802 -0
  234. package/dist/src/serialization/east.js.map +1 -0
  235. package/dist/src/serialization/index.d.ts +11 -0
  236. package/dist/src/serialization/index.d.ts.map +1 -0
  237. package/dist/src/serialization/index.js +12 -0
  238. package/dist/src/serialization/index.js.map +1 -0
  239. package/dist/src/serialization/json.d.ts +36 -0
  240. package/dist/src/serialization/json.d.ts.map +1 -0
  241. package/dist/src/serialization/json.js +849 -0
  242. package/dist/src/serialization/json.js.map +1 -0
  243. package/dist/src/type_of_type.d.ts +115 -0
  244. package/dist/src/type_of_type.d.ts.map +1 -0
  245. package/dist/src/type_of_type.js +362 -0
  246. package/dist/src/type_of_type.js.map +1 -0
  247. package/dist/src/types.d.ts +648 -0
  248. package/dist/src/types.d.ts.map +1 -0
  249. package/dist/src/types.js +1631 -0
  250. package/dist/src/types.js.map +1 -0
  251. package/package.json +87 -0
@@ -0,0 +1,929 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ // =============================================================================
6
+ // BufferWriter - Managed Uint8Array with auto-growth
7
+ // =============================================================================
8
+ export class BufferWriter {
9
+ buffer;
10
+ view;
11
+ offset = 0;
12
+ constructor(initialCapacity = 16384) {
13
+ this.buffer = new Uint8Array(initialCapacity);
14
+ this.view = new DataView(this.buffer.buffer);
15
+ }
16
+ ensureCapacity(needed) {
17
+ const required = this.offset + needed;
18
+ if (required <= this.buffer.length) {
19
+ return; // sufficient capacity
20
+ }
21
+ // Exponential growth: min 2x, max +1GB per resize
22
+ const doubled = this.buffer.length * 2;
23
+ const maxGrowth = this.buffer.length + 1024 * 1024 * 1024;
24
+ const newSize = Math.max(Math.min(doubled, maxGrowth), required);
25
+ const newBuffer = new Uint8Array(newSize);
26
+ newBuffer.set(this.buffer);
27
+ this.buffer = newBuffer;
28
+ this.view = new DataView(newBuffer.buffer);
29
+ }
30
+ writeUint8(value) {
31
+ this.ensureCapacity(1);
32
+ this.view.setUint8(this.offset, value);
33
+ this.offset += 1;
34
+ }
35
+ writeUint16LE(value) {
36
+ this.ensureCapacity(2);
37
+ this.view.setUint16(this.offset, value, true);
38
+ this.offset += 2;
39
+ }
40
+ writeUint16BE(value) {
41
+ this.ensureCapacity(2);
42
+ this.view.setUint16(this.offset, value, false);
43
+ this.offset += 2;
44
+ }
45
+ writeBytes(bytes) {
46
+ this.ensureCapacity(bytes.length);
47
+ this.buffer.set(bytes, this.offset);
48
+ this.offset += bytes.length;
49
+ }
50
+ // ===========================================================================
51
+ // Varint encoding (LEB128 - used by beast2)
52
+ // ===========================================================================
53
+ writeVarint(value) {
54
+ // Unsigned LEB128 encoding for sizes/lengths/tags
55
+ if (value < 0) {
56
+ throw new Error(`writeVarint requires non-negative value, got ${value}`);
57
+ }
58
+ if (!Number.isSafeInteger(value)) {
59
+ throw new Error(`writeVarint requires safe integer, got ${value}`);
60
+ }
61
+ this.ensureCapacity(10); // max 10 bytes for 64-bit value
62
+ // Use BigInt to avoid precision loss with large numbers
63
+ let n = BigInt(value);
64
+ while (n >= 0x80n) {
65
+ this.view.setUint8(this.offset, Number(n & 0x7fn) | 0x80);
66
+ this.offset += 1;
67
+ n >>= 7n;
68
+ }
69
+ this.view.setUint8(this.offset, Number(n));
70
+ this.offset += 1;
71
+ }
72
+ writeZigzag(value) {
73
+ // Zigzag encoding: (n << 1) ^ (n >> 63)
74
+ const zigzagged = (value << 1n) ^ (value >> 63n);
75
+ // Convert to unsigned LEB128
76
+ this.ensureCapacity(10); // max 10 bytes for 64-bit value
77
+ let n = zigzagged;
78
+ while (n >= 0x80n) {
79
+ this.view.setUint8(this.offset, Number(n & 0x7fn) | 0x80);
80
+ this.offset += 1;
81
+ n >>= 7n;
82
+ }
83
+ this.view.setUint8(this.offset, Number(n));
84
+ this.offset += 1;
85
+ }
86
+ // ===========================================================================
87
+ // Integer encoding - beast v1 (big-endian with sign-bit flip for ordering)
88
+ // ===========================================================================
89
+ writeInt64Twiddled(value) {
90
+ this.ensureCapacity(8);
91
+ // Flip sign bit for memcmp ordering (total ordering for all int64 values)
92
+ // Positive values become > 0x8000_0000_0000_0000
93
+ // Negative values become < 0x8000_0000_0000_0000
94
+ // Note: Using -9223372036854775808n (MIN_INT64 = -2^63) which is 0x8000_0000_0000_0000 as signed
95
+ const twiddled = value >= 0n
96
+ ? value + -9223372036854775808n // Add MIN_INT64 to flip sign bit
97
+ : value - -9223372036854775808n; // Subtract MIN_INT64 (same as add MAX_INT64+1)
98
+ this.view.setBigInt64(this.offset, twiddled, false); // big-endian
99
+ this.offset += 8;
100
+ }
101
+ // ===========================================================================
102
+ // Float encoding - beast2 (little-endian, canonical NaN)
103
+ // ===========================================================================
104
+ writeFloat64LE(value) {
105
+ this.ensureCapacity(8);
106
+ // NaN normalization: always write canonical quiet NaN
107
+ // 0x7FF8000000000000 is the bit pattern (same for big and little-endian)
108
+ // The littleEndian flag controls byte order in memory
109
+ if (Number.isNaN(value)) {
110
+ this.view.setBigUint64(this.offset, 0x7ff8000000000000n, true); // little-endian byte order (Avro-compatible)
111
+ }
112
+ else {
113
+ this.view.setFloat64(this.offset, value, true); // little-endian (Avro-compatible)
114
+ }
115
+ this.offset += 8;
116
+ }
117
+ // ===========================================================================
118
+ // Float encoding - beast v1 (big-endian with bit-twiddling for total ordering)
119
+ // ===========================================================================
120
+ // Preallocated buffers for float bit-twiddling (reused across calls)
121
+ static _floatBuffer = new Float64Array(1);
122
+ static _uintBuffer = new BigUint64Array(BufferWriter._floatBuffer.buffer);
123
+ writeFloat64Twiddled(value) {
124
+ this.ensureCapacity(8);
125
+ // Convert to bit pattern using preallocated buffers
126
+ BufferWriter._floatBuffer[0] = value;
127
+ let uint = BufferWriter._uintBuffer[0]; // Non-null assertion: array access always succeeds for typed arrays
128
+ // Bit-twiddling for total ordering (including NaNs, infinities, signed zeros)
129
+ // This ensures memcmp(encoded_a, encoded_b) matches float comparison
130
+ if (uint < 0x8000000000000000n) {
131
+ // Positive float (sign bit = 0) - flip sign bit
132
+ // Maps: 0.0 -> 0x8000..., +inf -> 0xFFF0..., NaN -> 0xFFF8...
133
+ uint = uint ^ 0x8000000000000000n;
134
+ }
135
+ else {
136
+ // Negative float (sign bit = 1) - flip all bits
137
+ // Maps: -0.0 -> 0x7FFF..., -inf -> 0x000F..., -smallest -> 0x7FFF...
138
+ uint = ~uint;
139
+ }
140
+ this.view.setBigUint64(this.offset, uint, false); // big-endian
141
+ this.offset += 8;
142
+ }
143
+ // ===========================================================================
144
+ // String encoding - beast2 (varint-length prefixed)
145
+ // ===========================================================================
146
+ writeStringUtf8Varint(str) {
147
+ // Encode UTF-8 first to know the length
148
+ const maxUtf8Length = str.length * 3; // worst case: 3 bytes per UTF-16 code unit
149
+ this.ensureCapacity(10 + maxUtf8Length); // varint length + string bytes
150
+ // Encode string into a temporary position (after max varint size)
151
+ const tempStringStart = this.offset + 10; // Reserve max varint bytes
152
+ const stringEnd = utf8EncodeInto(str, this.buffer, tempStringStart);
153
+ const utf8ByteLength = stringEnd - tempStringStart;
154
+ // Now write the actual varint length
155
+ this.writeVarint(utf8ByteLength);
156
+ const varintEnd = this.offset;
157
+ // Shift string data to immediately after the varint (if needed)
158
+ if (varintEnd !== tempStringStart) {
159
+ this.buffer.copyWithin(varintEnd, tempStringStart, stringEnd);
160
+ }
161
+ // Update offset to end of string
162
+ this.offset = varintEnd + utf8ByteLength;
163
+ }
164
+ // ===========================================================================
165
+ // String encoding - beast v1 (null-terminated)
166
+ // ===========================================================================
167
+ writeStringUtf8Null(str) {
168
+ const maxUtf8Length = str.length * 3; // worst case: 3 bytes per UTF-16 code unit
169
+ this.ensureCapacity(maxUtf8Length + 1); // +1 for null byte
170
+ this.offset = utf8EncodeInto(str, this.buffer, this.offset);
171
+ this.view.setUint8(this.offset, 0); // null terminator
172
+ this.offset += 1;
173
+ }
174
+ get size() {
175
+ return this.offset;
176
+ }
177
+ get currentOffset() {
178
+ return this.offset;
179
+ }
180
+ /**
181
+ * Extract the current buffer contents and reset for reuse.
182
+ * Used by streaming encoders to generate chunks.
183
+ */
184
+ pop() {
185
+ const chunk = this.buffer.subarray(0, this.offset);
186
+ this.offset = 0;
187
+ return chunk;
188
+ }
189
+ toUint8Array() {
190
+ return this.buffer.subarray(0, this.offset);
191
+ }
192
+ }
193
+ // =============================================================================
194
+ // Stateless read functions
195
+ // =============================================================================
196
+ // ===========================================================================
197
+ // Varint (beast2)
198
+ // ===========================================================================
199
+ export function readVarint(buffer, offset) {
200
+ // Unsigned LEB128 → number for sizes/lengths/tags
201
+ // Use BigInt to avoid precision loss, then convert to number
202
+ let result = 0n;
203
+ let shift = 0n;
204
+ while (true) {
205
+ if (offset >= buffer.length) {
206
+ throw new Error(`Buffer underflow reading varint at offset ${offset}`);
207
+ }
208
+ const byte = buffer[offset++];
209
+ result |= BigInt(byte & 0x7F) << shift;
210
+ if ((byte & 0x80) === 0) {
211
+ break; // last byte
212
+ }
213
+ shift += 7n;
214
+ if (shift >= 64n) {
215
+ throw new Error(`Varint too long at offset ${offset - 1}`);
216
+ }
217
+ }
218
+ const numResult = Number(result);
219
+ if (!Number.isSafeInteger(numResult)) {
220
+ throw new Error(`Varint value ${result} exceeds MAX_SAFE_INTEGER`);
221
+ }
222
+ return [numResult, offset];
223
+ }
224
+ export function readZigzag(buffer, offset) {
225
+ // Zigzag varint → bigint for signed IntegerType values
226
+ let result = 0n;
227
+ let shift = 0n;
228
+ while (true) {
229
+ if (offset >= buffer.length) {
230
+ throw new Error(`Buffer underflow reading zigzag at offset ${offset}`);
231
+ }
232
+ const byte = buffer[offset++];
233
+ result |= BigInt(byte & 0x7F) << shift;
234
+ if ((byte & 0x80) === 0) {
235
+ break; // last byte
236
+ }
237
+ shift += 7n;
238
+ if (shift >= 64n) {
239
+ throw new Error(`Zigzag varint too long at offset ${offset - 1}`);
240
+ }
241
+ }
242
+ // Decode zigzag: (n >> 1) ^ -(n & 1)
243
+ const decoded = (result >> 1n) ^ -(result & 1n);
244
+ return [decoded, offset];
245
+ }
246
+ // ===========================================================================
247
+ // Integer - beast v1 (big-endian twiddled)
248
+ // ===========================================================================
249
+ export function readInt64Twiddled(view, offset) {
250
+ if (offset + 8 > view.byteLength) {
251
+ throw new Error(`Buffer underflow reading int64 at offset ${offset}`);
252
+ }
253
+ const twiddled = view.getBigInt64(offset, false); // big-endian
254
+ // Undo sign-bit flip (same operation as encoding - XOR is self-inverse)
255
+ // Note: Using -9223372036854775808n (MIN_INT64 = -2^63)
256
+ const value = twiddled >= 0n
257
+ ? twiddled + -9223372036854775808n
258
+ : twiddled - -9223372036854775808n;
259
+ return [value, offset + 8];
260
+ }
261
+ // ===========================================================================
262
+ // Float - beast2 (little-endian with NaN validation)
263
+ // ===========================================================================
264
+ export function readFloat64LE(view, offset) {
265
+ if (offset + 8 > view.byteLength) {
266
+ throw new Error(`Buffer underflow reading float64 at offset ${offset}`);
267
+ }
268
+ const bits = view.getBigUint64(offset, true); // little-endian byte order (Avro-compatible)
269
+ // Check for NaN (exponent = all 1s, mantissa != 0)
270
+ // Bit pattern is the same regardless of endianness
271
+ if ((bits & 0x7ff0000000000000n) === 0x7ff0000000000000n &&
272
+ (bits & 0x000fffffffffffffn) !== 0n) {
273
+ // It's a NaN - must be canonical quiet NaN (positive or negative)
274
+ if (bits !== 0x7ff8000000000000n && bits !== 0xfff8000000000000n) {
275
+ throw new Error(`Non-canonical NaN at offset ${offset}: 0x${bits.toString(16)}`);
276
+ }
277
+ }
278
+ return view.getFloat64(offset, true); // little-endian byte order (Avro-compatible)
279
+ }
280
+ // ===========================================================================
281
+ // Float - beast v1 (big-endian twiddled)
282
+ // ===========================================================================
283
+ // Preallocated buffers for float bit-twiddling (reused across calls)
284
+ const _floatReadUintBuffer = new BigUint64Array(1);
285
+ const _floatReadBuffer = new Float64Array(_floatReadUintBuffer.buffer);
286
+ export function readFloat64Twiddled(view, offset) {
287
+ if (offset + 8 > view.byteLength) {
288
+ throw new Error(`Buffer underflow reading float64 at offset ${offset}`);
289
+ }
290
+ let uint = view.getBigUint64(offset, false); // big-endian
291
+ // Undo bit-twiddling for total ordering
292
+ if (uint >= 0x8000000000000000n) {
293
+ // Positive float - flip sign bit
294
+ uint = uint ^ 0x8000000000000000n;
295
+ }
296
+ else {
297
+ // Negative float - flip all bits
298
+ uint = ~uint;
299
+ }
300
+ _floatReadUintBuffer[0] = uint;
301
+ return _floatReadBuffer[0]; // Non-null assertion: array access always succeeds for typed arrays
302
+ }
303
+ // ===========================================================================
304
+ // String - beast2 (varint-length)
305
+ // ===========================================================================
306
+ export function readStringUtf8Varint(buffer, offset) {
307
+ // Read varint length
308
+ const [length, newOffset] = readVarint(buffer, offset);
309
+ if (newOffset + length > buffer.length) {
310
+ throw new Error(`Buffer underflow reading string at offset ${offset}, length ${length}`);
311
+ }
312
+ // Decode UTF-8
313
+ const str = utf8Decode(buffer, newOffset, length);
314
+ return [str, newOffset + length];
315
+ }
316
+ // ===========================================================================
317
+ // String - beast v1 (null-terminated)
318
+ // ===========================================================================
319
+ export function readStringUtf8Null(buffer, offset) {
320
+ // Single-pass: decode UTF-8 and find null terminator simultaneously (like ELARACore)
321
+ const codePoints = [];
322
+ let i = offset;
323
+ while (i < buffer.length) {
324
+ const byte1 = buffer[i++];
325
+ if (byte1 === 0) {
326
+ // Null terminator found
327
+ return [String.fromCharCode.apply(String, codePoints), i];
328
+ }
329
+ if ((byte1 & 0x80) === 0) {
330
+ // 1-byte (ASCII)
331
+ codePoints.push(byte1);
332
+ }
333
+ else if ((byte1 & 0xE0) === 0xC0) {
334
+ // 2-byte
335
+ const byte2 = buffer[i++] & 0x3F;
336
+ codePoints.push(((byte1 & 0x1F) << 6) | byte2);
337
+ }
338
+ else if ((byte1 & 0xF0) === 0xE0) {
339
+ // 3-byte
340
+ const byte2 = buffer[i++] & 0x3F;
341
+ const byte3 = buffer[i++] & 0x3F;
342
+ codePoints.push(((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3);
343
+ }
344
+ else if ((byte1 & 0xF8) === 0xF0) {
345
+ // 4-byte
346
+ const byte2 = buffer[i++] & 0x3F;
347
+ const byte3 = buffer[i++] & 0x3F;
348
+ const byte4 = buffer[i++] & 0x3F;
349
+ let codePoint = ((byte1 & 0x07) << 18) | (byte2 << 12) | (byte3 << 6) | byte4;
350
+ // Encode as surrogate pair if needed
351
+ if (codePoint > 0xFFFF) {
352
+ codePoint -= 0x10000;
353
+ codePoints.push(((codePoint >>> 10) & 0x3FF) | 0xD800);
354
+ codePoint = 0xDC00 | (codePoint & 0x3FF);
355
+ }
356
+ codePoints.push(codePoint);
357
+ }
358
+ else {
359
+ // Invalid UTF-8, skip byte
360
+ codePoints.push(byte1);
361
+ }
362
+ }
363
+ throw new Error(`No null terminator found for string at offset ${offset}`);
364
+ }
365
+ // =============================================================================
366
+ // UTF-8 encoding/decoding (shared by both formats)
367
+ // =============================================================================
368
+ // Adapted from old implementation, optimized for small strings
369
+ export function utf8EncodeInto(str, buffer, offset) {
370
+ let pos = 0;
371
+ const len = str.length;
372
+ let at = offset;
373
+ while (pos < len) {
374
+ let value = str.charCodeAt(pos++);
375
+ // Handle surrogate pairs
376
+ if (value >= 0xD800 && value <= 0xDBFF) {
377
+ // high surrogate
378
+ if (pos < len) {
379
+ const extra = str.charCodeAt(pos);
380
+ if ((extra & 0xFC00) === 0xDC00) {
381
+ ++pos;
382
+ value = ((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000;
383
+ }
384
+ }
385
+ if (value >= 0xD800 && value <= 0xDBFF) {
386
+ continue; // drop lone surrogate
387
+ }
388
+ }
389
+ if ((value & 0xFFFFFF80) === 0) {
390
+ // 1-byte (ASCII)
391
+ buffer[at++] = value;
392
+ }
393
+ else if ((value & 0xFFFFF800) === 0) {
394
+ // 2-byte
395
+ buffer[at++] = ((value >>> 6) & 0x1F) | 0xC0;
396
+ buffer[at++] = (value & 0x3F) | 0x80;
397
+ }
398
+ else if ((value & 0xFFFF0000) === 0) {
399
+ // 3-byte
400
+ buffer[at++] = ((value >>> 12) & 0x0F) | 0xE0;
401
+ buffer[at++] = ((value >>> 6) & 0x3F) | 0x80;
402
+ buffer[at++] = (value & 0x3F) | 0x80;
403
+ }
404
+ else if ((value & 0xFFE00000) === 0) {
405
+ // 4-byte
406
+ buffer[at++] = ((value >>> 18) & 0x07) | 0xF0;
407
+ buffer[at++] = ((value >>> 12) & 0x3F) | 0x80;
408
+ buffer[at++] = ((value >>> 6) & 0x3F) | 0x80;
409
+ buffer[at++] = (value & 0x3F) | 0x80;
410
+ }
411
+ else {
412
+ continue; // out of range
413
+ }
414
+ }
415
+ return at;
416
+ }
417
+ export function utf8Decode(buffer, offset, length) {
418
+ const end = offset + length;
419
+ const codePoints = [];
420
+ let i = offset;
421
+ while (i < end) {
422
+ const byte1 = buffer[i++];
423
+ if ((byte1 & 0x80) === 0) {
424
+ // 1-byte (ASCII)
425
+ codePoints.push(byte1);
426
+ }
427
+ else if ((byte1 & 0xE0) === 0xC0) {
428
+ // 2-byte
429
+ const byte2 = buffer[i++] & 0x3F;
430
+ codePoints.push(((byte1 & 0x1F) << 6) | byte2);
431
+ }
432
+ else if ((byte1 & 0xF0) === 0xE0) {
433
+ // 3-byte
434
+ const byte2 = buffer[i++] & 0x3F;
435
+ const byte3 = buffer[i++] & 0x3F;
436
+ codePoints.push(((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3);
437
+ }
438
+ else if ((byte1 & 0xF8) === 0xF0) {
439
+ // 4-byte
440
+ const byte2 = buffer[i++] & 0x3F;
441
+ const byte3 = buffer[i++] & 0x3F;
442
+ const byte4 = buffer[i++] & 0x3F;
443
+ let codePoint = ((byte1 & 0x07) << 18) | (byte2 << 12) | (byte3 << 6) | byte4;
444
+ // Encode as surrogate pair if needed
445
+ if (codePoint > 0xFFFF) {
446
+ codePoint -= 0x10000;
447
+ codePoints.push(((codePoint >>> 10) & 0x3FF) | 0xD800);
448
+ codePoint = 0xDC00 | (codePoint & 0x3FF);
449
+ }
450
+ codePoints.push(codePoint);
451
+ }
452
+ else {
453
+ // Invalid UTF-8, skip byte
454
+ codePoints.push(byte1);
455
+ }
456
+ }
457
+ return String.fromCharCode(...codePoints);
458
+ }
459
+ /**
460
+ * StreamBufferReader - Helper for reading bytes from a Web Streams ReadableStream
461
+ * with lookahead support for parsing.
462
+ *
463
+ * Maintains an internal buffer of unconsumed bytes pulled from the underlying stream.
464
+ */
465
+ export class StreamBufferReader {
466
+ reader;
467
+ buffer;
468
+ bufferOffset;
469
+ done;
470
+ totalBytesConsumed;
471
+ // Preallocated buffers for efficient primitive decoding (reused across calls)
472
+ tempBuffer8 = new Uint8Array(8);
473
+ tempView8 = new DataView(this.tempBuffer8.buffer);
474
+ floatUintBuffer = new BigUint64Array(1);
475
+ floatBuffer = new Float64Array(this.floatUintBuffer.buffer);
476
+ constructor(stream) {
477
+ this.reader = stream.getReader();
478
+ this.buffer = new Uint8Array(0);
479
+ this.bufferOffset = 0;
480
+ this.done = false;
481
+ this.totalBytesConsumed = 0;
482
+ }
483
+ /**
484
+ * Get the current position in the stream (total bytes read so far).
485
+ */
486
+ get position() {
487
+ return this.totalBytesConsumed + this.bufferOffset;
488
+ }
489
+ /**
490
+ * Ensure we have at least `count` bytes available in the buffer.
491
+ * Pulls from underlying stream as needed.
492
+ */
493
+ async ensureBytes(count) {
494
+ // Keep pulling chunks until we have enough bytes
495
+ while (true) {
496
+ const available = this.buffer.length - this.bufferOffset;
497
+ if (available >= count) {
498
+ return; // Already have enough
499
+ }
500
+ if (this.done) {
501
+ throw new Error(`Unexpected end of stream: needed ${count} bytes, only ${available} available`);
502
+ }
503
+ // Need more bytes - pull from stream
504
+ const { value, done } = await this.reader.read();
505
+ if (done) {
506
+ this.done = true;
507
+ if (this.buffer.length - this.bufferOffset < count) {
508
+ throw new Error(`Unexpected end of stream: needed ${count} bytes, only ${this.buffer.length - this.bufferOffset} available`);
509
+ }
510
+ return;
511
+ }
512
+ if (!value || value.length === 0) {
513
+ // Empty chunk, try again (loop will continue)
514
+ continue;
515
+ }
516
+ // Append new chunk to buffer
517
+ const unconsumed = this.buffer.subarray(this.bufferOffset);
518
+ this.totalBytesConsumed += this.bufferOffset;
519
+ const newBuffer = new Uint8Array(unconsumed.length + value.length);
520
+ newBuffer.set(unconsumed, 0);
521
+ newBuffer.set(value, unconsumed.length);
522
+ this.buffer = newBuffer;
523
+ this.bufferOffset = 0;
524
+ // Loop continues to check if we have enough bytes now
525
+ }
526
+ }
527
+ /**
528
+ * Peek at the next byte without consuming it.
529
+ * Returns null if stream is done.
530
+ * Optimized to avoid promises unless necessary.
531
+ */
532
+ peekByte() {
533
+ if (this.buffer.length - this.bufferOffset >= 1) {
534
+ return this.buffer[this.bufferOffset];
535
+ }
536
+ return this.peekByteAsync();
537
+ }
538
+ async peekByteAsync() {
539
+ try {
540
+ await this.ensureBytes(1);
541
+ return this.buffer[this.bufferOffset];
542
+ }
543
+ catch {
544
+ return null; // End of stream
545
+ }
546
+ }
547
+ /**
548
+ * Read and consume the next byte.
549
+ * Returns synchronously if data is buffered, otherwise returns a Promise.
550
+ * Throws if stream is done.
551
+ * Optimized to avoid promises unless necessary.
552
+ */
553
+ readByte() {
554
+ // Fast path: if we have buffered data, return it synchronously
555
+ const available = this.buffer.length - this.bufferOffset;
556
+ if (available >= 1) {
557
+ const byte = this.buffer[this.bufferOffset];
558
+ this.bufferOffset++;
559
+ return byte;
560
+ }
561
+ // Slow path: need to fetch from stream
562
+ return this.readByteAsync();
563
+ }
564
+ async readByteAsync() {
565
+ await this.ensureBytes(1);
566
+ const byte = this.buffer[this.bufferOffset];
567
+ this.bufferOffset++;
568
+ return byte;
569
+ }
570
+ /**
571
+ * Read exactly `buffer.length` bytes into the provided buffer.
572
+ * Throws if not enough bytes available.
573
+ * Optimized to avoid promises unless necessary.
574
+ */
575
+ readBytes(buffer) {
576
+ // Fast path: if we have enough buffered data, read synchronously
577
+ if (this.buffer.length - this.bufferOffset >= buffer.length) {
578
+ buffer.set(this.buffer.subarray(this.bufferOffset, this.bufferOffset + buffer.length));
579
+ this.bufferOffset += buffer.length;
580
+ return;
581
+ }
582
+ // Slow path: need to fetch from stream
583
+ return this.readBytesAsync(buffer);
584
+ }
585
+ async readBytesAsync(buffer) {
586
+ await this.ensureBytes(buffer.length);
587
+ buffer.set(this.buffer.subarray(this.bufferOffset, this.bufferOffset + buffer.length));
588
+ this.bufferOffset += buffer.length;
589
+ }
590
+ /**
591
+ * Read bytes until the terminator byte is found (exclusive).
592
+ * Returns the bytes read (not including terminator).
593
+ * The terminator byte is consumed.
594
+ * Optimized to avoid promises unless necessary.
595
+ */
596
+ readUntilByte(terminator) {
597
+ // Fast path: if we have enough buffered data, read synchronously
598
+ for (let i = this.bufferOffset; i < this.buffer.length; i++) {
599
+ if (this.buffer[i] === terminator) {
600
+ const result = this.buffer.subarray(this.bufferOffset, i);
601
+ this.bufferOffset = i + 1; // consume terminator
602
+ return result;
603
+ }
604
+ }
605
+ // Slow path: need to fetch from stream
606
+ return this.readUntilByteAsync(terminator);
607
+ }
608
+ async readUntilByteAsync(terminator) {
609
+ const result = [];
610
+ while (true) {
611
+ const byte = await this.readByte();
612
+ if (byte === terminator) {
613
+ break;
614
+ }
615
+ result.push(byte);
616
+ }
617
+ return new Uint8Array(result);
618
+ }
619
+ /**
620
+ * Release the underlying reader.
621
+ * Should be called when done reading to release resources.
622
+ */
623
+ async release() {
624
+ await this.reader.releaseLock();
625
+ }
626
+ // =============================================================================
627
+ // Specialized read methods for Beast v1 format
628
+ // =============================================================================
629
+ /**
630
+ * Read a twiddled big-endian int64 (Beast v1 format).
631
+ * Uses sign-bit flip for total ordering.
632
+ * Optimized to avoid promises unless necessary.
633
+ */
634
+ readInt64Twiddled() {
635
+ // Fast path: if we have enough buffered data, read synchronously
636
+ if (this.buffer.length - this.bufferOffset >= 8) {
637
+ this.tempBuffer8.set(this.buffer.subarray(this.bufferOffset, this.bufferOffset + 8));
638
+ const twiddled = this.tempView8.getBigInt64(0, false); // big-endian
639
+ this.bufferOffset += 8;
640
+ // Undo sign-bit flip (same operation as encoding - XOR is self-inverse)
641
+ const value = twiddled >= 0n
642
+ ? twiddled + -9223372036854775808n
643
+ : twiddled - -9223372036854775808n;
644
+ return value;
645
+ }
646
+ // Slow path: need to fetch from stream
647
+ return this.readInt64TwiddledAsync();
648
+ }
649
+ async readInt64TwiddledAsync() {
650
+ await this.readBytes(this.tempBuffer8);
651
+ const twiddled = this.tempView8.getBigInt64(0, false); // big-endian
652
+ // Undo sign-bit flip (same operation as encoding - XOR is self-inverse)
653
+ const value = twiddled >= 0n
654
+ ? twiddled + -9223372036854775808n
655
+ : twiddled - -9223372036854775808n;
656
+ return value;
657
+ }
658
+ /**
659
+ * Read a twiddled big-endian float64 (Beast v1 format).
660
+ * Uses bit-twiddling for total ordering.
661
+ */
662
+ readFloat64Twiddled() {
663
+ // Fast path: if we have enough buffered data, read synchronously
664
+ if (this.buffer.length - this.bufferOffset >= 8) {
665
+ this.tempBuffer8.set(this.buffer.subarray(this.bufferOffset, this.bufferOffset + 8));
666
+ this.bufferOffset += 8;
667
+ let uint = this.tempView8.getBigUint64(0, false); // big-endian
668
+ // Undo bit-twiddling for total ordering
669
+ if (uint >= 0x8000000000000000n) {
670
+ // Positive float - flip sign bit
671
+ uint = uint ^ 0x8000000000000000n;
672
+ }
673
+ else {
674
+ // Negative float - flip all bits
675
+ uint = ~uint;
676
+ }
677
+ // Convert to float using preallocated buffers
678
+ this.floatUintBuffer[0] = uint;
679
+ return this.floatBuffer[0];
680
+ }
681
+ // Slow path: need to fetch from stream
682
+ return this.readFloat64TwiddledAsync();
683
+ }
684
+ async readFloat64TwiddledAsync() {
685
+ await this.readBytes(this.tempBuffer8);
686
+ let uint = this.tempView8.getBigUint64(0, false); // big-endian
687
+ // Undo bit-twiddling for total ordering
688
+ if (uint >= 0x8000000000000000n) {
689
+ // Positive float - flip sign bit
690
+ uint = uint ^ 0x8000000000000000n;
691
+ }
692
+ else {
693
+ // Negative float - flip all bits
694
+ uint = ~uint;
695
+ }
696
+ // Convert to float using preallocated buffers
697
+ this.floatUintBuffer[0] = uint;
698
+ return this.floatBuffer[0];
699
+ }
700
+ /**
701
+ * Read a null-terminated UTF-8 string (Beast v1 format).
702
+ * Decodes UTF-8 while searching for null terminator in single pass.
703
+ * Optimized to avoid promises unless necessary.
704
+ */
705
+ readStringUtf8Null() {
706
+ // Fast path: if we have enough buffered data, read synchronously
707
+ for (let i = this.bufferOffset; i < this.buffer.length; i++) {
708
+ if (this.buffer[i] === 0) {
709
+ // Found null terminator in buffer
710
+ const str = utf8Decode(this.buffer, this.bufferOffset, i - this.bufferOffset);
711
+ this.bufferOffset = i + 1; // consume null terminator
712
+ return str;
713
+ }
714
+ }
715
+ // Slow path: need to fetch from stream
716
+ return this.readStringUtf8NullAsync();
717
+ }
718
+ async readStringUtf8NullAsync() {
719
+ const codePoints = [];
720
+ while (true) {
721
+ const byte1 = await this.readByte();
722
+ if (byte1 === 0) {
723
+ // Null terminator found
724
+ return String.fromCharCode.apply(String, codePoints);
725
+ }
726
+ if ((byte1 & 0x80) === 0) {
727
+ // 1-byte (ASCII)
728
+ codePoints.push(byte1);
729
+ }
730
+ else if ((byte1 & 0xE0) === 0xC0) {
731
+ // 2-byte
732
+ const byte2 = (await this.readByte()) & 0x3F;
733
+ codePoints.push(((byte1 & 0x1F) << 6) | byte2);
734
+ }
735
+ else if ((byte1 & 0xF0) === 0xE0) {
736
+ // 3-byte
737
+ const byte2 = (await this.readByte()) & 0x3F;
738
+ const byte3 = (await this.readByte()) & 0x3F;
739
+ codePoints.push(((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3);
740
+ }
741
+ else if ((byte1 & 0xF8) === 0xF0) {
742
+ // 4-byte
743
+ const byte2 = (await this.readByte()) & 0x3F;
744
+ const byte3 = (await this.readByte()) & 0x3F;
745
+ const byte4 = (await this.readByte()) & 0x3F;
746
+ let codePoint = ((byte1 & 0x07) << 18) | (byte2 << 12) | (byte3 << 6) | byte4;
747
+ // Encode as surrogate pair if needed
748
+ if (codePoint > 0xFFFF) {
749
+ codePoint -= 0x10000;
750
+ codePoints.push(((codePoint >>> 10) & 0x3FF) | 0xD800);
751
+ codePoint = 0xDC00 | (codePoint & 0x3FF);
752
+ }
753
+ codePoints.push(codePoint);
754
+ }
755
+ else {
756
+ // Invalid UTF-8, skip byte
757
+ codePoints.push(byte1);
758
+ }
759
+ }
760
+ }
761
+ // =============================================================================
762
+ // Specialized read methods for Beast v2 format
763
+ // =============================================================================
764
+ /**
765
+ * Read an unsigned varint (LEB128) - Beast v2 format.
766
+ * Returns a number (safe for lengths/tags up to 2^53-1).
767
+ * Optimized to avoid promises unless necessary.
768
+ */
769
+ readVarint() {
770
+ // Fast path: if we have enough buffered data, read synchronously
771
+ if (this.buffer.length - this.bufferOffset >= 10) {
772
+ const [value, newOffset] = readVarint(this.buffer, this.bufferOffset);
773
+ this.bufferOffset = newOffset;
774
+ return value;
775
+ }
776
+ // Slow path: need to fetch from stream
777
+ return this.readVarintAsync();
778
+ }
779
+ async readVarintAsync() {
780
+ let result = 0;
781
+ let shift = 0;
782
+ while (true) {
783
+ const byte = await this.readByte();
784
+ // Use number operations for values that fit in safe integer range
785
+ if (shift < 49) { // 7 * 7 = 49 bits safely fits in number
786
+ result |= (byte & 0x7F) << shift;
787
+ }
788
+ else {
789
+ // Overflow to BigInt path for large values (rare)
790
+ let bigResult = BigInt(result);
791
+ bigResult |= BigInt(byte & 0x7F) << BigInt(shift);
792
+ if ((byte & 0x80) === 0) {
793
+ return Number(bigResult);
794
+ }
795
+ shift += 7;
796
+ while (true) {
797
+ const nextByte = await this.readByte();
798
+ shift += 7;
799
+ if (shift >= 64) {
800
+ throw new Error(`Varint too long`);
801
+ }
802
+ bigResult |= BigInt(nextByte & 0x7F) << BigInt(shift - 7);
803
+ if ((nextByte & 0x80) === 0) {
804
+ return Number(bigResult);
805
+ }
806
+ }
807
+ }
808
+ if ((byte & 0x80) === 0) {
809
+ break; // last byte
810
+ }
811
+ shift += 7;
812
+ }
813
+ return result;
814
+ }
815
+ /**
816
+ * Read a zigzag-encoded signed integer (varint) - Beast v2 format.
817
+ * Returns a bigint.
818
+ * Optimized to avoid promises unless necessary.
819
+ */
820
+ readZigzag() {
821
+ // Fast path: if we have enough buffered data, read synchronously
822
+ if (this.buffer.length - this.bufferOffset >= 10) {
823
+ const [value, newOffset] = readZigzag(this.buffer, this.bufferOffset);
824
+ this.bufferOffset = newOffset;
825
+ return value;
826
+ }
827
+ // Slow path: need to fetch from stream
828
+ return this.readZigzagAsync();
829
+ }
830
+ async readZigzagAsync() {
831
+ let result = 0n;
832
+ let shift = 0n;
833
+ while (true) {
834
+ const byte = await this.readByte();
835
+ result |= BigInt(byte & 0x7F) << shift;
836
+ if ((byte & 0x80) === 0) {
837
+ break; // last byte
838
+ }
839
+ shift += 7n;
840
+ if (shift >= 64n) {
841
+ throw new Error(`Zigzag varint too long`);
842
+ }
843
+ }
844
+ // Decode zigzag: (n >> 1) ^ -(n & 1)
845
+ const decoded = (result >> 1n) ^ -(result & 1n);
846
+ return decoded;
847
+ }
848
+ /**
849
+ * Read a little-endian float64 - Beast v2 format.
850
+ * Validates NaN values are canonical.
851
+ * Optimized to avoid promises unless necessary.
852
+ */
853
+ readFloat64LE() {
854
+ // Fast path: if we have enough buffered data, read synchronously
855
+ if (this.buffer.length - this.bufferOffset >= 8) {
856
+ this.tempBuffer8.set(this.buffer.subarray(this.bufferOffset, this.bufferOffset + 8));
857
+ this.bufferOffset += 8;
858
+ const bits = this.tempView8.getBigUint64(0, true); // little-endian
859
+ // Check for NaN (exponent = all 1s, mantissa != 0)
860
+ if ((bits & 0x7ff0000000000000n) === 0x7ff0000000000000n &&
861
+ (bits & 0x000fffffffffffffn) !== 0n) {
862
+ // It's a NaN - must be canonical quiet NaN (positive or negative)
863
+ if (bits !== 0x7ff8000000000000n && bits !== 0xfff8000000000000n) {
864
+ throw new Error(`Non-canonical NaN`);
865
+ }
866
+ }
867
+ return this.tempView8.getFloat64(0, true); // little-endian
868
+ }
869
+ // Slow path: need to fetch from stream
870
+ return this.readFloat64LEAsync();
871
+ }
872
+ async readFloat64LEAsync() {
873
+ await this.readBytes(this.tempBuffer8);
874
+ const bits = this.tempView8.getBigUint64(0, true); // little-endian
875
+ // Check for NaN (exponent = all 1s, mantissa != 0)
876
+ if ((bits & 0x7ff0000000000000n) === 0x7ff0000000000000n &&
877
+ (bits & 0x000fffffffffffffn) !== 0n) {
878
+ // It's a NaN - must be canonical quiet NaN (positive or negative)
879
+ if (bits !== 0x7ff8000000000000n && bits !== 0xfff8000000000000n) {
880
+ throw new Error(`Non-canonical NaN`);
881
+ }
882
+ }
883
+ return this.tempView8.getFloat64(0, true); // little-endian
884
+ }
885
+ /**
886
+ * Read a varint-length-prefixed UTF-8 string - Beast v2 format.
887
+ */
888
+ readStringUtf8Varint() {
889
+ if (this.buffer.length - this.bufferOffset >= 10) {
890
+ // Fast path: if we have enough buffered data, read synchronously
891
+ const [length, newOffset] = readVarint(this.buffer, this.bufferOffset);
892
+ this.bufferOffset = newOffset;
893
+ if (this.buffer.length - this.bufferOffset >= length) {
894
+ const str = utf8Decode(this.buffer, this.bufferOffset, length);
895
+ this.bufferOffset += length;
896
+ return str;
897
+ }
898
+ else {
899
+ return this.readStringUtf8SizedAsync(length);
900
+ }
901
+ }
902
+ // Slow path: need to fetch from stream
903
+ return this.readStringUtf8VarintAsync();
904
+ }
905
+ async readStringUtf8VarintAsync() {
906
+ const length = await this.readVarint();
907
+ if (length === 0) {
908
+ return "";
909
+ }
910
+ // Read the string bytes
911
+ const bytes = new Uint8Array(length);
912
+ await this.readBytes(bytes);
913
+ // Decode UTF-8
914
+ const decoder = new TextDecoder('utf-8', { fatal: true });
915
+ return decoder.decode(bytes);
916
+ }
917
+ async readStringUtf8SizedAsync(length) {
918
+ if (length === 0) {
919
+ return "";
920
+ }
921
+ // Read the string bytes
922
+ const bytes = new Uint8Array(length);
923
+ await this.readBytes(bytes);
924
+ // Decode UTF-8
925
+ const decoder = new TextDecoder('utf-8', { fatal: true });
926
+ return decoder.decode(bytes);
927
+ }
928
+ }
929
+ //# sourceMappingURL=binary-utils.js.map