@sachitv/avro-typescript 0.4.1 → 0.5.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.
- package/esm/avro_reader_sync.d.ts +48 -0
- package/esm/avro_reader_sync.js +79 -0
- package/esm/avro_writer_sync.d.ts +28 -0
- package/esm/avro_writer_sync.js +63 -0
- package/esm/internal/collections/circular_buffer.d.ts +4 -3
- package/esm/internal/collections/circular_buffer.js +8 -6
- package/esm/mod.d.ts +17 -0
- package/esm/mod.js +11 -0
- package/esm/schemas/base_type.d.ts +8 -0
- package/esm/schemas/base_type.js +21 -0
- package/esm/schemas/complex/array_type.d.ts +31 -10
- package/esm/schemas/complex/array_type.js +129 -39
- package/esm/schemas/complex/enum_type.d.ts +24 -7
- package/esm/schemas/complex/enum_type.js +61 -15
- package/esm/schemas/complex/fixed_type.d.ts +27 -12
- package/esm/schemas/complex/fixed_type.js +40 -27
- package/esm/schemas/complex/map_type.d.ts +31 -10
- package/esm/schemas/complex/map_type.js +88 -47
- package/esm/schemas/complex/named_type.d.ts +1 -1
- package/esm/schemas/complex/named_type.js +2 -2
- package/esm/schemas/complex/record_field.d.ts +62 -0
- package/esm/schemas/complex/record_field.js +112 -0
- package/esm/schemas/complex/record_resolver.d.ts +45 -0
- package/esm/schemas/complex/record_resolver.js +92 -0
- package/esm/schemas/complex/record_type.d.ts +45 -70
- package/esm/schemas/complex/record_type.js +158 -213
- package/esm/schemas/complex/record_writer_cache.d.ts +44 -0
- package/esm/schemas/complex/record_writer_cache.js +141 -0
- package/esm/schemas/complex/record_writer_strategy.d.ts +123 -0
- package/esm/schemas/complex/record_writer_strategy.js +309 -0
- package/esm/schemas/complex/union_type.d.ts +23 -11
- package/esm/schemas/complex/union_type.js +77 -29
- package/esm/schemas/logical/decimal_logical_type.d.ts +1 -1
- package/esm/schemas/logical/decimal_logical_type.js +2 -2
- package/esm/schemas/logical/duration_logical_type.d.ts +1 -1
- package/esm/schemas/logical/duration_logical_type.js +2 -2
- package/esm/schemas/logical/logical_type.d.ts +27 -9
- package/esm/schemas/logical/logical_type.js +50 -17
- package/esm/schemas/logical/temporal_logical_types.d.ts +9 -9
- package/esm/schemas/logical/temporal_logical_types.js +18 -18
- package/esm/schemas/logical/uuid_logical_type.d.ts +1 -1
- package/esm/schemas/logical/uuid_logical_type.js +2 -2
- package/esm/schemas/primitive/boolean_type.d.ts +15 -4
- package/esm/schemas/primitive/boolean_type.js +22 -7
- package/esm/schemas/primitive/bytes_type.d.ts +16 -5
- package/esm/schemas/primitive/bytes_type.js +31 -16
- package/esm/schemas/primitive/double_type.d.ts +13 -2
- package/esm/schemas/primitive/double_type.js +34 -5
- package/esm/schemas/primitive/fixed_size_base_type.d.ts +11 -7
- package/esm/schemas/primitive/fixed_size_base_type.js +14 -14
- package/esm/schemas/primitive/float_type.d.ts +11 -2
- package/esm/schemas/primitive/float_type.js +26 -5
- package/esm/schemas/primitive/int_type.d.ts +17 -6
- package/esm/schemas/primitive/int_type.js +26 -17
- package/esm/schemas/primitive/long_type.d.ts +16 -5
- package/esm/schemas/primitive/long_type.js +30 -16
- package/esm/schemas/primitive/null_type.d.ts +15 -4
- package/esm/schemas/primitive/null_type.js +20 -7
- package/esm/schemas/primitive/primitive_type.d.ts +2 -0
- package/esm/schemas/primitive/primitive_type.js +4 -0
- package/esm/schemas/primitive/string_type.d.ts +17 -6
- package/esm/schemas/primitive/string_type.js +33 -18
- package/esm/schemas/resolver.d.ts +6 -0
- package/esm/schemas/type.d.ts +64 -4
- package/esm/schemas/type.js +97 -0
- package/esm/serialization/avro_file_parser_sync.d.ts +34 -0
- package/esm/serialization/avro_file_parser_sync.js +160 -0
- package/esm/serialization/avro_file_writer_sync.d.ts +47 -0
- package/esm/serialization/avro_file_writer_sync.js +211 -0
- package/esm/serialization/buffers/blob_readable_buffer.d.ts +4 -3
- package/esm/serialization/buffers/blob_readable_buffer.js +20 -6
- package/esm/serialization/buffers/buffer.d.ts +6 -1
- package/esm/serialization/buffers/buffer.js +3 -0
- package/esm/serialization/buffers/buffer_error.d.ts +28 -0
- package/esm/serialization/buffers/buffer_error.js +70 -0
- package/esm/serialization/buffers/buffer_sync.d.ts +51 -0
- package/esm/serialization/buffers/buffer_sync.js +4 -0
- package/esm/serialization/buffers/in_memory_buffer.d.ts +1 -0
- package/esm/serialization/buffers/in_memory_buffer.js +7 -5
- package/esm/serialization/buffers/in_memory_buffer_sync.d.ts +133 -0
- package/esm/serialization/buffers/in_memory_buffer_sync.js +259 -0
- package/esm/serialization/counting_writable_tap.d.ts +45 -0
- package/esm/serialization/counting_writable_tap.js +90 -0
- package/esm/serialization/counting_writable_tap_sync.d.ts +46 -0
- package/esm/serialization/counting_writable_tap_sync.js +87 -0
- package/esm/serialization/decoders/decoder_null_sync.d.ts +12 -0
- package/esm/serialization/decoders/decoder_null_sync.js +13 -0
- package/esm/serialization/decoders/decoder_sync.d.ts +15 -0
- package/esm/serialization/decoders/decoder_sync.js +1 -0
- package/esm/serialization/encoders/encoder_null_sync.d.ts +12 -0
- package/esm/serialization/encoders/encoder_null_sync.js +13 -0
- package/esm/serialization/encoders/encoder_sync.d.ts +15 -0
- package/esm/serialization/encoders/encoder_sync.js +1 -0
- package/esm/serialization/streams/fixed_size_stream_readable_buffer_adapter.d.ts +3 -2
- package/esm/serialization/streams/fixed_size_stream_readable_buffer_adapter.js +19 -9
- package/esm/serialization/streams/fixed_size_stream_reader_sync.d.ts +25 -0
- package/esm/serialization/streams/fixed_size_stream_reader_sync.js +63 -0
- package/esm/serialization/streams/fixed_size_stream_writer_sync.d.ts +45 -0
- package/esm/serialization/streams/fixed_size_stream_writer_sync.js +98 -0
- package/esm/serialization/streams/forward_only_stream_readable_buffer_adapter.d.ts +5 -3
- package/esm/serialization/streams/forward_only_stream_readable_buffer_adapter.js +18 -8
- package/esm/serialization/streams/stream_readable_buffer_adapter.d.ts +3 -2
- package/esm/serialization/streams/stream_readable_buffer_adapter.js +19 -6
- package/esm/serialization/streams/stream_readable_buffer_adapter_sync.d.ts +36 -0
- package/esm/serialization/streams/stream_readable_buffer_adapter_sync.js +93 -0
- package/esm/serialization/streams/stream_writable_buffer_adapter_sync.d.ts +45 -0
- package/esm/serialization/streams/stream_writable_buffer_adapter_sync.js +78 -0
- package/esm/serialization/streams/streams_sync.d.ts +36 -0
- package/esm/serialization/streams/streams_sync.js +4 -0
- package/esm/serialization/tap.d.ts +15 -32
- package/esm/serialization/tap.js +45 -134
- package/esm/serialization/tap_sync.d.ts +240 -0
- package/esm/serialization/tap_sync.js +545 -0
- package/esm/serialization/text_encoding.d.ts +16 -0
- package/esm/serialization/text_encoding.js +48 -1
- package/esm/type/create_type.d.ts +20 -0
- package/esm/type/create_type.js +49 -28
- package/package.json +2 -2
- package/esm/serialization/manipulate_bytes.d.ts +0 -6
- package/esm/serialization/manipulate_bytes.js +0 -13
- package/esm/serialization/read_uint_le.d.ts +0 -4
- package/esm/serialization/read_uint_le.js +0 -14
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import { bigIntToSafeNumber } from "./conversion.js";
|
|
2
|
+
import { compareUint8Arrays } from "./compare_bytes.js";
|
|
3
|
+
import { decode, encoder } from "./text_encoding.js";
|
|
4
|
+
import { TapBase } from "./tap.js";
|
|
5
|
+
import { ReadBufferError } from "./buffers/buffer_error.js";
|
|
6
|
+
import { SyncInMemoryReadableBuffer, SyncInMemoryWritableBuffer, } from "./buffers/in_memory_buffer_sync.js";
|
|
7
|
+
/**
|
|
8
|
+
* Type guard to check if an object implements ISyncReadable.
|
|
9
|
+
*/
|
|
10
|
+
function isISyncReadable(obj) {
|
|
11
|
+
return (typeof obj === "object" &&
|
|
12
|
+
obj !== null &&
|
|
13
|
+
typeof obj.read === "function" &&
|
|
14
|
+
typeof obj.canReadMore === "function");
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Type guard to check if an object implements ISyncWritable.
|
|
18
|
+
*/
|
|
19
|
+
function isISyncWritable(obj) {
|
|
20
|
+
return (typeof obj === "object" &&
|
|
21
|
+
obj !== null &&
|
|
22
|
+
typeof obj.appendBytes === "function" &&
|
|
23
|
+
typeof obj.appendBytesFrom === "function" &&
|
|
24
|
+
typeof obj.isValid === "function" &&
|
|
25
|
+
typeof obj.canAppendMore === "function");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Synchronous readable tap over an in-memory buffer.
|
|
29
|
+
* Mirrors ReadableTap but without Promises.
|
|
30
|
+
*/
|
|
31
|
+
export class SyncReadableTap extends TapBase {
|
|
32
|
+
/**
|
|
33
|
+
* Creates a new SyncReadableTap.
|
|
34
|
+
* @param buf The buffer to read from, either an ArrayBuffer or ISyncReadable.
|
|
35
|
+
* @param pos The initial position in the buffer (default 0).
|
|
36
|
+
*/
|
|
37
|
+
constructor(buf, pos = 0) {
|
|
38
|
+
super(pos);
|
|
39
|
+
Object.defineProperty(this, "buffer", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
configurable: true,
|
|
42
|
+
writable: true,
|
|
43
|
+
value: void 0
|
|
44
|
+
});
|
|
45
|
+
if (buf instanceof ArrayBuffer) {
|
|
46
|
+
this.buffer = new SyncInMemoryReadableBuffer(buf);
|
|
47
|
+
}
|
|
48
|
+
else if (isISyncReadable(buf)) {
|
|
49
|
+
this.buffer = buf;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
throw new TypeError("SyncReadableTap requires an ArrayBuffer or ISyncReadable buffer.");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Returns whether the cursor is positioned within the buffer bounds. */
|
|
56
|
+
isValid() {
|
|
57
|
+
try {
|
|
58
|
+
this.buffer.read(this.pos, 0);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (err instanceof ReadBufferError) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Returns whether more data can be read from the current position. */
|
|
69
|
+
canReadMore() {
|
|
70
|
+
return this.buffer.canReadMore(this.pos);
|
|
71
|
+
}
|
|
72
|
+
/** Returns the buffer contents from the start up to the current cursor. */
|
|
73
|
+
getValue() {
|
|
74
|
+
if (this.pos < 0) {
|
|
75
|
+
throw new ReadBufferError("Tap position is negative.", this.pos, 0, 0);
|
|
76
|
+
}
|
|
77
|
+
return this.buffer.read(0, this.pos);
|
|
78
|
+
}
|
|
79
|
+
/** Retrieves the byte at the specified position in the buffer. */
|
|
80
|
+
getByteAt(position) {
|
|
81
|
+
const result = this.buffer.read(position, 1);
|
|
82
|
+
return result[0];
|
|
83
|
+
}
|
|
84
|
+
/** Reads the next byte as a boolean value and advances the cursor. */
|
|
85
|
+
readBoolean() {
|
|
86
|
+
const value = this.getByteAt(this.pos);
|
|
87
|
+
this.pos += 1;
|
|
88
|
+
return !!value;
|
|
89
|
+
}
|
|
90
|
+
/** Skips a boolean value by advancing the cursor by one byte. */
|
|
91
|
+
skipBoolean() {
|
|
92
|
+
this.pos += 1;
|
|
93
|
+
}
|
|
94
|
+
/** Reads a variable-length zig-zag encoded 32-bit signed integer. */
|
|
95
|
+
readInt() {
|
|
96
|
+
return bigIntToSafeNumber(this.readLong(), "readInt value");
|
|
97
|
+
}
|
|
98
|
+
/** Reads a variable-length zig-zag encoded 64-bit signed integer as bigint. */
|
|
99
|
+
readLong() {
|
|
100
|
+
let pos = this.pos;
|
|
101
|
+
let shift = 0n;
|
|
102
|
+
let result = 0n;
|
|
103
|
+
let byte;
|
|
104
|
+
do {
|
|
105
|
+
byte = this.getByteAt(pos++);
|
|
106
|
+
result |= BigInt(byte & 0x7f) << shift;
|
|
107
|
+
shift += 7n;
|
|
108
|
+
} while ((byte & 0x80) !== 0 && shift < 70n);
|
|
109
|
+
while ((byte & 0x80) !== 0) {
|
|
110
|
+
byte = this.getByteAt(pos++);
|
|
111
|
+
result |= BigInt(byte & 0x7f) << shift;
|
|
112
|
+
shift += 7n;
|
|
113
|
+
}
|
|
114
|
+
this.pos = pos;
|
|
115
|
+
return (result >> 1n) ^ -(result & 1n);
|
|
116
|
+
}
|
|
117
|
+
/** Skips a zig-zag encoded 32-bit integer by delegating to skipLong. */
|
|
118
|
+
skipInt() {
|
|
119
|
+
this.skipLong();
|
|
120
|
+
}
|
|
121
|
+
/** Skips a zig-zag encoded 64-bit integer, advancing past continuation bytes. */
|
|
122
|
+
skipLong() {
|
|
123
|
+
let pos = this.pos;
|
|
124
|
+
while (this.getByteAt(pos++) & 0x80) {
|
|
125
|
+
/* no-op */
|
|
126
|
+
}
|
|
127
|
+
this.pos = pos;
|
|
128
|
+
}
|
|
129
|
+
/** Reads a 32-bit little-endian floating point number. */
|
|
130
|
+
readFloat() {
|
|
131
|
+
const pos = this.pos;
|
|
132
|
+
this.pos += 4;
|
|
133
|
+
const bytes = this.buffer.read(pos, 4);
|
|
134
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
135
|
+
return view.getFloat32(0, true);
|
|
136
|
+
}
|
|
137
|
+
/** Skips a 32-bit floating point value by advancing four bytes. */
|
|
138
|
+
skipFloat() {
|
|
139
|
+
this.pos += 4;
|
|
140
|
+
}
|
|
141
|
+
/** Reads a 64-bit little-endian floating point number. */
|
|
142
|
+
readDouble() {
|
|
143
|
+
const pos = this.pos;
|
|
144
|
+
this.pos += 8;
|
|
145
|
+
const bytes = this.buffer.read(pos, 8);
|
|
146
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
147
|
+
return view.getFloat64(0, true);
|
|
148
|
+
}
|
|
149
|
+
/** Skips a 64-bit floating point value by advancing eight bytes. */
|
|
150
|
+
skipDouble() {
|
|
151
|
+
this.pos += 8;
|
|
152
|
+
}
|
|
153
|
+
/** Reads a fixed-length byte sequence. */
|
|
154
|
+
readFixed(len) {
|
|
155
|
+
const pos = this.pos;
|
|
156
|
+
this.pos += len;
|
|
157
|
+
return this.buffer.read(pos, len);
|
|
158
|
+
}
|
|
159
|
+
/** Skips a fixed-length byte sequence. */
|
|
160
|
+
skipFixed(len) {
|
|
161
|
+
this.pos += len;
|
|
162
|
+
}
|
|
163
|
+
/** Reads a length-prefixed byte sequence. */
|
|
164
|
+
readBytes() {
|
|
165
|
+
const length = bigIntToSafeNumber(this.readLong(), "readBytes length");
|
|
166
|
+
return this.readFixed(length);
|
|
167
|
+
}
|
|
168
|
+
/** Skips a length-prefixed byte sequence. */
|
|
169
|
+
skipBytes() {
|
|
170
|
+
const len = bigIntToSafeNumber(this.readLong(), "skipBytes length");
|
|
171
|
+
this.pos += len;
|
|
172
|
+
}
|
|
173
|
+
/** Reads a length-prefixed UTF-8 string. */
|
|
174
|
+
readString() {
|
|
175
|
+
const len = bigIntToSafeNumber(this.readLong(), "readString length");
|
|
176
|
+
const bytes = this.readFixed(len);
|
|
177
|
+
return decode(bytes);
|
|
178
|
+
}
|
|
179
|
+
/** Skips a length-prefixed UTF-8 string. */
|
|
180
|
+
skipString() {
|
|
181
|
+
const len = bigIntToSafeNumber(this.readLong(), "skipString length");
|
|
182
|
+
this.pos += len;
|
|
183
|
+
}
|
|
184
|
+
/** Compares boolean values from this tap and another. */
|
|
185
|
+
matchBoolean(tap) {
|
|
186
|
+
const v1 = this.readBoolean();
|
|
187
|
+
const v2 = tap.readBoolean();
|
|
188
|
+
return Number(v1) - Number(v2);
|
|
189
|
+
}
|
|
190
|
+
/** Compares 32-bit integer values from this tap and another. */
|
|
191
|
+
matchInt(tap) {
|
|
192
|
+
return this.matchLong(tap);
|
|
193
|
+
}
|
|
194
|
+
/** Compares 64-bit integer values from this tap and another. */
|
|
195
|
+
matchLong(tap) {
|
|
196
|
+
const n1 = this.readLong();
|
|
197
|
+
const n2 = tap.readLong();
|
|
198
|
+
if (n1 === n2) {
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
201
|
+
else if (n1 < n2) {
|
|
202
|
+
return -1;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
return 1;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** Compares 32-bit float values from this tap and another. */
|
|
209
|
+
matchFloat(tap) {
|
|
210
|
+
const n1 = this.readFloat();
|
|
211
|
+
const n2 = tap.readFloat();
|
|
212
|
+
if (n1 === n2) {
|
|
213
|
+
return 0;
|
|
214
|
+
}
|
|
215
|
+
else if (n1 < n2) {
|
|
216
|
+
return -1;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
return 1;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/** Compares 64-bit double values from this tap and another. */
|
|
223
|
+
matchDouble(tap) {
|
|
224
|
+
const n1 = this.readDouble();
|
|
225
|
+
const n2 = tap.readDouble();
|
|
226
|
+
if (n1 === n2) {
|
|
227
|
+
return 0;
|
|
228
|
+
}
|
|
229
|
+
else if (n1 < n2) {
|
|
230
|
+
return -1;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
return 1;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/** Compares fixed-length byte sequences from this tap and another. */
|
|
237
|
+
matchFixed(tap, len) {
|
|
238
|
+
const f1 = this.readFixed(len);
|
|
239
|
+
const f2 = tap.readFixed(len);
|
|
240
|
+
return compareUint8Arrays(f1, f2);
|
|
241
|
+
}
|
|
242
|
+
/** Compares length-prefixed byte sequences from this tap and another. */
|
|
243
|
+
matchBytes(tap) {
|
|
244
|
+
return this.matchString(tap);
|
|
245
|
+
}
|
|
246
|
+
/** Compares length-prefixed strings from this tap and another. */
|
|
247
|
+
matchString(tap) {
|
|
248
|
+
const l1 = bigIntToSafeNumber(this.readLong(), "matchString length this");
|
|
249
|
+
const b1 = this.readFixed(l1);
|
|
250
|
+
const l2 = bigIntToSafeNumber(tap.readLong(), "matchString length tap");
|
|
251
|
+
const b2 = tap.readFixed(l2);
|
|
252
|
+
return compareUint8Arrays(b1, b2);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Synchronous writable tap over an in-memory buffer (Uint8Array / ArrayBuffer).
|
|
257
|
+
* Mirrors WritableTap but without Promises.
|
|
258
|
+
*/
|
|
259
|
+
/**
|
|
260
|
+
* High-performance synchronous writable tap for Avro serialization.
|
|
261
|
+
*
|
|
262
|
+
* Key optimizations:
|
|
263
|
+
* - Buffer pool pattern: Pre-allocated static buffers eliminate GC pressure
|
|
264
|
+
* - Varint encoding: Direct buffer writing avoids intermediate allocations
|
|
265
|
+
* - ASCII string optimization: Fast path for ASCII strings, encodeInto() for Unicode
|
|
266
|
+
* - Static primitive buffers: Reuse buffers for booleans, floats, and doubles
|
|
267
|
+
*/
|
|
268
|
+
export class SyncWritableTap extends TapBase {
|
|
269
|
+
/**
|
|
270
|
+
* Creates a new SyncWritableTap.
|
|
271
|
+
* @param buf The buffer to write to, either an ArrayBuffer or ISyncWritable.
|
|
272
|
+
* @param pos The initial position in the buffer (default 0).
|
|
273
|
+
*/
|
|
274
|
+
constructor(buf, pos = 0) {
|
|
275
|
+
super(pos);
|
|
276
|
+
Object.defineProperty(this, "buffer", {
|
|
277
|
+
enumerable: true,
|
|
278
|
+
configurable: true,
|
|
279
|
+
writable: true,
|
|
280
|
+
value: void 0
|
|
281
|
+
});
|
|
282
|
+
if (buf instanceof ArrayBuffer) {
|
|
283
|
+
this.buffer = new SyncInMemoryWritableBuffer(buf, pos);
|
|
284
|
+
}
|
|
285
|
+
else if (isISyncWritable(buf)) {
|
|
286
|
+
this.buffer = buf;
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
throw new TypeError("SyncWritableTap requires an ArrayBuffer or ISyncWritable buffer.");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/** Returns whether the tap is valid for writing. */
|
|
293
|
+
isValid() {
|
|
294
|
+
return this.buffer.isValid();
|
|
295
|
+
}
|
|
296
|
+
/** Appends raw bytes to the buffer and advances the cursor. */
|
|
297
|
+
appendRawBytes(bytes, offset = 0, length = bytes.length - offset) {
|
|
298
|
+
if (length <= 0)
|
|
299
|
+
return;
|
|
300
|
+
this.buffer.appendBytesFrom(bytes, offset, length);
|
|
301
|
+
this.pos += length;
|
|
302
|
+
}
|
|
303
|
+
/** Writes a boolean value as a single byte. */
|
|
304
|
+
writeBoolean(value) {
|
|
305
|
+
this.appendRawBytes(value ? SyncWritableTap.trueByte : SyncWritableTap.falseByte);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Writes a 32-bit integer using zigzag + varint encoding.
|
|
309
|
+
* Uses 32-bit math to avoid BigInt casts for performance.
|
|
310
|
+
*/
|
|
311
|
+
writeInt(value) {
|
|
312
|
+
if (typeof value !== "number" ||
|
|
313
|
+
!Number.isInteger(value) ||
|
|
314
|
+
value < SyncWritableTap.INT_MIN ||
|
|
315
|
+
value > SyncWritableTap.INT_MAX) {
|
|
316
|
+
throw new RangeError(`Value ${value} out of range for Avro int (${SyncWritableTap.INT_MIN}..${SyncWritableTap.INT_MAX})`);
|
|
317
|
+
}
|
|
318
|
+
this.writeVarint32ZigZag(value);
|
|
319
|
+
}
|
|
320
|
+
/** Encodes a 32-bit integer using zigzag + varint encoding. */
|
|
321
|
+
writeVarint32ZigZag(value) {
|
|
322
|
+
// Zigzag encode to an unsigned 32-bit integer:
|
|
323
|
+
// (n << 1) ^ (n >> 31)
|
|
324
|
+
let n = ((value << 1) ^ (value >> 31)) >>> 0;
|
|
325
|
+
let i = 0;
|
|
326
|
+
const buf = SyncWritableTap.varintBufferInt;
|
|
327
|
+
while (n > 0x7f) {
|
|
328
|
+
buf[i++] = (n & 0x7f) | 0x80;
|
|
329
|
+
n >>>= 7;
|
|
330
|
+
}
|
|
331
|
+
buf[i++] = n;
|
|
332
|
+
this.appendRawBytes(buf, 0, i);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Writes a 64-bit integer using zigzag + varint encoding.
|
|
336
|
+
*
|
|
337
|
+
* Optimizations:
|
|
338
|
+
* - Buffer pool: Uses pre-allocated varintBufferLong to avoid allocations
|
|
339
|
+
* - Direct buffer writing: Encodes into a shared buffer then appends a slice
|
|
340
|
+
*
|
|
341
|
+
* Performance impact: ~3x faster than original array-based approach
|
|
342
|
+
*/
|
|
343
|
+
writeLong(value) {
|
|
344
|
+
let n = value;
|
|
345
|
+
if (n < 0n) {
|
|
346
|
+
n = ((-n) << 1n) - 1n;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
n <<= 1n;
|
|
350
|
+
}
|
|
351
|
+
// Fast varint encoding using pre-allocated buffer
|
|
352
|
+
let i = 0;
|
|
353
|
+
const buf = SyncWritableTap.varintBufferLong;
|
|
354
|
+
while (n >= 0x80n) {
|
|
355
|
+
buf[i++] = Number(n & 0x7fn) | 0x80;
|
|
356
|
+
n >>= 7n;
|
|
357
|
+
}
|
|
358
|
+
buf[i++] = Number(n);
|
|
359
|
+
this.appendRawBytes(buf, 0, i);
|
|
360
|
+
}
|
|
361
|
+
/** Writes a 32-bit little-endian floating point number. */
|
|
362
|
+
writeFloat(value) {
|
|
363
|
+
SyncWritableTap.floatView.setFloat32(0, value, true);
|
|
364
|
+
this.appendRawBytes(SyncWritableTap.float32Bytes);
|
|
365
|
+
}
|
|
366
|
+
/** Writes a 64-bit little-endian floating point number. */
|
|
367
|
+
writeDouble(value) {
|
|
368
|
+
SyncWritableTap.floatView.setFloat64(0, value, true);
|
|
369
|
+
this.appendRawBytes(SyncWritableTap.float64Bytes);
|
|
370
|
+
}
|
|
371
|
+
/** Writes a fixed-length byte sequence. */
|
|
372
|
+
writeFixed(buf) {
|
|
373
|
+
if (buf.length === 0)
|
|
374
|
+
return;
|
|
375
|
+
this.appendRawBytes(buf, 0, buf.length);
|
|
376
|
+
}
|
|
377
|
+
/** Writes a length-prefixed byte sequence. */
|
|
378
|
+
writeBytes(buf) {
|
|
379
|
+
const len = buf.length;
|
|
380
|
+
this.writeLong(BigInt(len));
|
|
381
|
+
this.writeFixed(buf);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Writes a UTF-8 encoded string with length prefix.
|
|
385
|
+
*
|
|
386
|
+
* Optimizations:
|
|
387
|
+
* - ASCII fast path: Direct charCodeAt() loop for ASCII strings (most common)
|
|
388
|
+
* - Unicode optimization: Uses TextEncoder.encodeInto() for zero-copy encoding
|
|
389
|
+
* - Buffer pool: Reuses stringBuffer to avoid allocations
|
|
390
|
+
* - Size estimation: Conservative UTF-8 size prediction (2x chars)
|
|
391
|
+
*
|
|
392
|
+
* Performance: ASCII strings ~800ns, Unicode strings ~1µs with encodeInto()
|
|
393
|
+
* Rationale: ASCII dominates real-world usage, Unicode needs zero-copy encoding
|
|
394
|
+
*/
|
|
395
|
+
writeString(str) {
|
|
396
|
+
const strLen = str.length;
|
|
397
|
+
// Hybrid approach: ASCII fast path + encodeInto() for Unicode
|
|
398
|
+
if (strLen <= 1024) {
|
|
399
|
+
// Check if string is ASCII (single pass detection)
|
|
400
|
+
const buf = SyncWritableTap.stringBuffer;
|
|
401
|
+
let i = 0;
|
|
402
|
+
for (; i < strLen; i++) {
|
|
403
|
+
const code = str.charCodeAt(i);
|
|
404
|
+
if (code > 127) {
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
buf[i] = code;
|
|
408
|
+
}
|
|
409
|
+
if (i === strLen) {
|
|
410
|
+
// Fast ASCII path: direct charCodeAt() encoding
|
|
411
|
+
// Avoids TextEncoder overhead for common ASCII strings
|
|
412
|
+
// In this path, encoded length == strLen, so it can fit in an int.
|
|
413
|
+
this.writeInt(strLen);
|
|
414
|
+
if (strLen > 0) {
|
|
415
|
+
this.appendRawBytes(buf, 0, strLen);
|
|
416
|
+
}
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
// Fallback for large or complex strings
|
|
421
|
+
const estimatedSize = strLen * 3 + 4;
|
|
422
|
+
let encodedBuffer = SyncWritableTap.ensureEncodedStringBuffer(estimatedSize);
|
|
423
|
+
let result = encoder.encodeInto(str, encodedBuffer);
|
|
424
|
+
if (result.read !== strLen) {
|
|
425
|
+
encodedBuffer = SyncWritableTap.ensureEncodedStringBuffer(strLen * 4 + 8);
|
|
426
|
+
result = encoder.encodeInto(str, encodedBuffer);
|
|
427
|
+
if (result.read !== strLen) {
|
|
428
|
+
throw new Error("TextEncoder.encodeInto failed to consume the entire string.");
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (result.written > SyncWritableTap.INT_MAX) {
|
|
432
|
+
this.writeLong(BigInt(result.written));
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
this.writeInt(result.written);
|
|
436
|
+
}
|
|
437
|
+
this.appendRawBytes(encodedBuffer, 0, result.written);
|
|
438
|
+
}
|
|
439
|
+
/** Writes raw binary bytes without a length prefix. */
|
|
440
|
+
writeBinary(str, len) {
|
|
441
|
+
if (len <= 0)
|
|
442
|
+
return;
|
|
443
|
+
const bytes = SyncWritableTap.ensureBinaryBuffer(len);
|
|
444
|
+
for (let i = 0; i < len; i++) {
|
|
445
|
+
bytes[i] = str.charCodeAt(i) & 0xff;
|
|
446
|
+
}
|
|
447
|
+
this.appendRawBytes(bytes, 0, len);
|
|
448
|
+
}
|
|
449
|
+
/** Ensures the encoded string buffer is at least minSize bytes. */
|
|
450
|
+
static ensureEncodedStringBuffer(minSize) {
|
|
451
|
+
if (SyncWritableTap.encodedStringBuffer.length < minSize) {
|
|
452
|
+
SyncWritableTap.encodedStringBuffer = new Uint8Array(minSize);
|
|
453
|
+
}
|
|
454
|
+
return SyncWritableTap.encodedStringBuffer;
|
|
455
|
+
}
|
|
456
|
+
/** Ensures the binary buffer is at least size bytes. */
|
|
457
|
+
static ensureBinaryBuffer(size) {
|
|
458
|
+
if (SyncWritableTap.binaryBuffer.length < size) {
|
|
459
|
+
SyncWritableTap.binaryBuffer = new Uint8Array(size);
|
|
460
|
+
}
|
|
461
|
+
return SyncWritableTap.binaryBuffer;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
Object.defineProperty(SyncWritableTap, "INT_MIN", {
|
|
465
|
+
enumerable: true,
|
|
466
|
+
configurable: true,
|
|
467
|
+
writable: true,
|
|
468
|
+
value: -2147483648
|
|
469
|
+
});
|
|
470
|
+
Object.defineProperty(SyncWritableTap, "INT_MAX", {
|
|
471
|
+
enumerable: true,
|
|
472
|
+
configurable: true,
|
|
473
|
+
writable: true,
|
|
474
|
+
value: 2147483647
|
|
475
|
+
});
|
|
476
|
+
// Buffer pool optimization: Pre-allocated static buffers to avoid per-operation allocations
|
|
477
|
+
// Reduces GC pressure and improves performance for hot serialization paths
|
|
478
|
+
Object.defineProperty(SyncWritableTap, "floatBuffer", {
|
|
479
|
+
enumerable: true,
|
|
480
|
+
configurable: true,
|
|
481
|
+
writable: true,
|
|
482
|
+
value: new ArrayBuffer(8)
|
|
483
|
+
});
|
|
484
|
+
Object.defineProperty(SyncWritableTap, "floatView", {
|
|
485
|
+
enumerable: true,
|
|
486
|
+
configurable: true,
|
|
487
|
+
writable: true,
|
|
488
|
+
value: new DataView(SyncWritableTap.floatBuffer)
|
|
489
|
+
});
|
|
490
|
+
Object.defineProperty(SyncWritableTap, "float32Bytes", {
|
|
491
|
+
enumerable: true,
|
|
492
|
+
configurable: true,
|
|
493
|
+
writable: true,
|
|
494
|
+
value: new Uint8Array(SyncWritableTap.floatBuffer, 0, 4)
|
|
495
|
+
});
|
|
496
|
+
Object.defineProperty(SyncWritableTap, "float64Bytes", {
|
|
497
|
+
enumerable: true,
|
|
498
|
+
configurable: true,
|
|
499
|
+
writable: true,
|
|
500
|
+
value: new Uint8Array(SyncWritableTap.floatBuffer, 0, 8)
|
|
501
|
+
});
|
|
502
|
+
Object.defineProperty(SyncWritableTap, "trueByte", {
|
|
503
|
+
enumerable: true,
|
|
504
|
+
configurable: true,
|
|
505
|
+
writable: true,
|
|
506
|
+
value: new Uint8Array([1])
|
|
507
|
+
});
|
|
508
|
+
Object.defineProperty(SyncWritableTap, "falseByte", {
|
|
509
|
+
enumerable: true,
|
|
510
|
+
configurable: true,
|
|
511
|
+
writable: true,
|
|
512
|
+
value: new Uint8Array([0])
|
|
513
|
+
});
|
|
514
|
+
Object.defineProperty(SyncWritableTap, "varintBufferLong", {
|
|
515
|
+
enumerable: true,
|
|
516
|
+
configurable: true,
|
|
517
|
+
writable: true,
|
|
518
|
+
value: new Uint8Array(11)
|
|
519
|
+
}); // Max 11 bytes for 64-bit long (2^70 needs 11 bytes)
|
|
520
|
+
Object.defineProperty(SyncWritableTap, "varintBufferInt", {
|
|
521
|
+
enumerable: true,
|
|
522
|
+
configurable: true,
|
|
523
|
+
writable: true,
|
|
524
|
+
value: new Uint8Array(5)
|
|
525
|
+
}); // Max 5 bytes for zigzag-encoded int32
|
|
526
|
+
// String buffer pool: Pre-allocated buffer for ASCII string encoding
|
|
527
|
+
// Avoids allocations for common ASCII strings up to 1024 characters
|
|
528
|
+
Object.defineProperty(SyncWritableTap, "stringBuffer", {
|
|
529
|
+
enumerable: true,
|
|
530
|
+
configurable: true,
|
|
531
|
+
writable: true,
|
|
532
|
+
value: new Uint8Array(1024)
|
|
533
|
+
});
|
|
534
|
+
Object.defineProperty(SyncWritableTap, "encodedStringBuffer", {
|
|
535
|
+
enumerable: true,
|
|
536
|
+
configurable: true,
|
|
537
|
+
writable: true,
|
|
538
|
+
value: new Uint8Array(0)
|
|
539
|
+
});
|
|
540
|
+
Object.defineProperty(SyncWritableTap, "binaryBuffer", {
|
|
541
|
+
enumerable: true,
|
|
542
|
+
configurable: true,
|
|
543
|
+
writable: true,
|
|
544
|
+
value: new Uint8Array(0)
|
|
545
|
+
});
|
|
@@ -1,9 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text encoding utilities with performance optimizations.
|
|
3
|
+
*
|
|
4
|
+
* Optimizations:
|
|
5
|
+
* - Export encoder for direct encodeInto() access (browser equivalent of utf8Write)
|
|
6
|
+
* - encodeInto() provides zero-copy UTF-8 encoding for modern browsers
|
|
7
|
+
* - Used in writeString() for Unicode string optimization
|
|
8
|
+
*/
|
|
9
|
+
export declare const encoder: TextEncoder;
|
|
1
10
|
/**
|
|
2
11
|
* Encodes a string into a Uint8Array using UTF-8 encoding.
|
|
3
12
|
* @param input The string to encode.
|
|
4
13
|
* @returns A Uint8Array representing the encoded string.
|
|
5
14
|
*/
|
|
6
15
|
export declare const encode: (input: string) => Uint8Array;
|
|
16
|
+
/**
|
|
17
|
+
* Returns the number of bytes needed to encode a string as UTF-8.
|
|
18
|
+
*
|
|
19
|
+
* This is equivalent to `encode(input).length` but avoids allocating a
|
|
20
|
+
* `Uint8Array`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function utf8ByteLength(input: string): number;
|
|
7
23
|
/**
|
|
8
24
|
* Decodes a Uint8Array into a string using UTF-8 encoding.
|
|
9
25
|
* @param bytes The Uint8Array to decode.
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Text encoding utilities with performance optimizations.
|
|
3
|
+
*
|
|
4
|
+
* Optimizations:
|
|
5
|
+
* - Export encoder for direct encodeInto() access (browser equivalent of utf8Write)
|
|
6
|
+
* - encodeInto() provides zero-copy UTF-8 encoding for modern browsers
|
|
7
|
+
* - Used in writeString() for Unicode string optimization
|
|
8
|
+
*/
|
|
9
|
+
export const encoder = new TextEncoder();
|
|
2
10
|
const decoder = new TextDecoder();
|
|
3
11
|
/**
|
|
4
12
|
* Encodes a string into a Uint8Array using UTF-8 encoding.
|
|
@@ -6,6 +14,45 @@ const decoder = new TextDecoder();
|
|
|
6
14
|
* @returns A Uint8Array representing the encoded string.
|
|
7
15
|
*/
|
|
8
16
|
export const encode = (input) => encoder.encode(input);
|
|
17
|
+
/**
|
|
18
|
+
* Returns the number of bytes needed to encode a string as UTF-8.
|
|
19
|
+
*
|
|
20
|
+
* This is equivalent to `encode(input).length` but avoids allocating a
|
|
21
|
+
* `Uint8Array`.
|
|
22
|
+
*/
|
|
23
|
+
export function utf8ByteLength(input) {
|
|
24
|
+
let length = 0;
|
|
25
|
+
for (let index = 0; index < input.length; index++) {
|
|
26
|
+
const codeUnit = input.charCodeAt(index);
|
|
27
|
+
if (codeUnit < 0x80) {
|
|
28
|
+
length += 1;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (codeUnit < 0x800) {
|
|
32
|
+
length += 2;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) {
|
|
36
|
+
const nextCodeUnit = input.charCodeAt(index + 1);
|
|
37
|
+
if (nextCodeUnit >= 0xdc00 && nextCodeUnit <= 0xdfff) {
|
|
38
|
+
// Valid surrogate pair => code point >= 0x10000.
|
|
39
|
+
length += 4;
|
|
40
|
+
index++;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
// Unpaired high surrogate; TextEncoder encodes this as U+FFFD.
|
|
44
|
+
length += 3;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (codeUnit >= 0xdc00 && codeUnit <= 0xdfff) {
|
|
48
|
+
// Unpaired low surrogate; TextEncoder encodes this as U+FFFD.
|
|
49
|
+
length += 3;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
length += 3;
|
|
53
|
+
}
|
|
54
|
+
return length;
|
|
55
|
+
}
|
|
9
56
|
/**
|
|
10
57
|
* Decodes a Uint8Array into a string using UTF-8 encoding.
|
|
11
58
|
* @param bytes The Uint8Array to decode.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type RecordWriterStrategy } from "../schemas/complex/record_type.js";
|
|
1
2
|
import { Type } from "../schemas/type.js";
|
|
2
3
|
/**
|
|
3
4
|
* Represents an Avro schema definition.
|
|
@@ -47,6 +48,25 @@ export interface CreateTypeOptions {
|
|
|
47
48
|
* Registry of shared named types.
|
|
48
49
|
*/
|
|
49
50
|
registry?: Map<string, Type>;
|
|
51
|
+
/**
|
|
52
|
+
* Whether to validate values during serialization writes.
|
|
53
|
+
*
|
|
54
|
+
* When set to `false`, types may skip runtime type/range checks on hot write
|
|
55
|
+
* paths for performance. This is unsafe for untrusted input and should only
|
|
56
|
+
* be used when values are already known to match the schema.
|
|
57
|
+
*/
|
|
58
|
+
validate?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Optional writer strategy for record types.
|
|
61
|
+
*
|
|
62
|
+
* Controls how record field writers are compiled:
|
|
63
|
+
* - `CompiledWriterStrategy` (default): Inlines primitive tap methods for performance.
|
|
64
|
+
* - `InterpretedWriterStrategy`: Delegates to type.write() for simplicity.
|
|
65
|
+
*
|
|
66
|
+
* Custom strategies can be provided for instrumentation, debugging, or
|
|
67
|
+
* alternative compilation approaches.
|
|
68
|
+
*/
|
|
69
|
+
writerStrategy?: RecordWriterStrategy;
|
|
50
70
|
}
|
|
51
71
|
/**
|
|
52
72
|
* Constructs an Avro {@link Type} from a schema definition.
|