binary-structures-values 0.1.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/dist/cjs/bit-stream-reader.d.ts +33 -0
- package/dist/cjs/bit-stream-reader.js +340 -0
- package/dist/cjs/bit-stream-writer.d.ts +36 -0
- package/dist/cjs/bit-stream-writer.js +336 -0
- package/dist/cjs/compiler-old.d.ts +9 -0
- package/dist/cjs/compiler-old.js +268 -0
- package/dist/cjs/data-stream-reader.d.ts +23 -0
- package/dist/cjs/data-stream-reader.js +132 -0
- package/dist/cjs/data-stream-writer.d.ts +22 -0
- package/dist/cjs/data-stream-writer.js +179 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.js +21 -0
- package/dist/cjs/schema-structure.d.ts +54 -0
- package/dist/cjs/schema-structure.js +273 -0
- package/dist/esm/bit-stream-reader.d.ts +33 -0
- package/dist/esm/bit-stream-reader.js +336 -0
- package/dist/esm/bit-stream-writer.d.ts +36 -0
- package/dist/esm/bit-stream-writer.js +335 -0
- package/dist/esm/compiler-old.d.ts +9 -0
- package/dist/esm/compiler-old.js +264 -0
- package/dist/esm/data-stream-reader.d.ts +23 -0
- package/dist/esm/data-stream-reader.js +128 -0
- package/dist/esm/data-stream-writer.d.ts +22 -0
- package/dist/esm/data-stream-writer.js +175 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/schema-structure.d.ts +54 -0
- package/dist/esm/schema-structure.js +267 -0
- package/package.json +38 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeSchemaString = exports.encodeSchema = exports.createSchema = void 0;
|
|
4
|
+
const createSchema = (schema) => {
|
|
5
|
+
return schema;
|
|
6
|
+
};
|
|
7
|
+
exports.createSchema = createSchema;
|
|
8
|
+
// OPTIMIZATION 4: Improved Base62 with fast path
|
|
9
|
+
const BASE62_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
10
|
+
const encodeBase62 = (n) => {
|
|
11
|
+
if (n < 0)
|
|
12
|
+
throw new Error('Cannot encode negative numbers');
|
|
13
|
+
if (n < 62)
|
|
14
|
+
return BASE62_CHARS[n]; // Fast path for single digit
|
|
15
|
+
const digits = [];
|
|
16
|
+
while (n > 0) {
|
|
17
|
+
digits.push(BASE62_CHARS[n % 62]);
|
|
18
|
+
n = Math.floor(n / 62);
|
|
19
|
+
}
|
|
20
|
+
return digits.reverse().join('');
|
|
21
|
+
};
|
|
22
|
+
const decodeBase62 = (str) => {
|
|
23
|
+
let result = 0;
|
|
24
|
+
for (let i = 0; i < str.length; i++) {
|
|
25
|
+
const char = str[i];
|
|
26
|
+
const value = BASE62_CHARS.indexOf(char);
|
|
27
|
+
if (value === -1)
|
|
28
|
+
throw new Error(`Invalid base62 character: ${char}`);
|
|
29
|
+
result = result * 62 + value;
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
};
|
|
33
|
+
// OPTIMIZATION 2: Map-based lookups for O(1) complexity
|
|
34
|
+
const encodeSingleType = (type, key, keyMap, typeMap) => {
|
|
35
|
+
// Use Map for O(1) lookup instead of indexOf O(n)
|
|
36
|
+
let typeIndex = typeMap.get(type.type);
|
|
37
|
+
if (typeIndex === undefined) {
|
|
38
|
+
typeIndex = typeMap.size;
|
|
39
|
+
typeMap.set(type.type, typeIndex);
|
|
40
|
+
}
|
|
41
|
+
const typeIndexEncoded = encodeBase62(typeIndex);
|
|
42
|
+
// if (type.type === 'null') return typeIndexEncoded
|
|
43
|
+
if (type.type === 'object') {
|
|
44
|
+
const [keys, types] = encodeObjectSchema(type.items, keyMap, typeMap);
|
|
45
|
+
let typeStr = `{${types}}`;
|
|
46
|
+
if ('optional' in type && type.optional) {
|
|
47
|
+
typeStr += `?`;
|
|
48
|
+
}
|
|
49
|
+
return [`{${keys}}`, typeStr];
|
|
50
|
+
}
|
|
51
|
+
if (type.type === 'array') {
|
|
52
|
+
const [keys, types] = encodeDataType(type.items, key, keyMap, typeMap);
|
|
53
|
+
let typeStr = `[${types}]`;
|
|
54
|
+
if ('optional' in type && type.optional) {
|
|
55
|
+
typeStr += `?`;
|
|
56
|
+
}
|
|
57
|
+
return [`[${keys}]`, typeStr];
|
|
58
|
+
}
|
|
59
|
+
if (!('optional' in type))
|
|
60
|
+
return typeIndexEncoded;
|
|
61
|
+
return `${typeIndexEncoded}${type.optional ? `?${type.default !== undefined ? `${JSON.stringify(type.default)}` : ''}` : ''}`;
|
|
62
|
+
};
|
|
63
|
+
const encodeDataType = (datatype, key, keyMap, typeMap) => {
|
|
64
|
+
let keys = [];
|
|
65
|
+
let types = [];
|
|
66
|
+
if ("types" in datatype) {
|
|
67
|
+
const midTypes = [];
|
|
68
|
+
const midKeys = [];
|
|
69
|
+
let isFirst = true;
|
|
70
|
+
for (let valueType of datatype.types) {
|
|
71
|
+
if (typeof valueType === 'string')
|
|
72
|
+
valueType = { type: valueType };
|
|
73
|
+
if ("optional" in valueType && valueType.optional && !isFirst)
|
|
74
|
+
throw new Error(`Only first type can be optional for multi-type`);
|
|
75
|
+
isFirst = false;
|
|
76
|
+
const returnValue = encodeSingleType(valueType, key, keyMap, typeMap);
|
|
77
|
+
if (typeof returnValue === 'string') {
|
|
78
|
+
midTypes.push(returnValue);
|
|
79
|
+
midKeys.push(key);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const [vKey, vType] = returnValue;
|
|
83
|
+
midKeys.push(`${key}${vKey}`);
|
|
84
|
+
midTypes.push(`${vType}`);
|
|
85
|
+
}
|
|
86
|
+
types.push(midTypes.join('|'));
|
|
87
|
+
keys.push(midKeys.join('|'));
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const returnValue = encodeSingleType(datatype, key, keyMap, typeMap);
|
|
91
|
+
if (typeof returnValue === 'string') {
|
|
92
|
+
keys.push(key);
|
|
93
|
+
types.push(encodeSingleType(datatype, key, keyMap, typeMap));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const [vKey, vType] = returnValue;
|
|
97
|
+
keys.push(`${key}${vKey}`);
|
|
98
|
+
types.push(`${vType}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return [keys, types];
|
|
102
|
+
};
|
|
103
|
+
// OPTIMIZATION 2 & 5: Map-based lookups + pre-sorted keys
|
|
104
|
+
const encodeObjectSchema = (schema, keyMap, typeMap) => {
|
|
105
|
+
let keys = [];
|
|
106
|
+
let types = [];
|
|
107
|
+
// OPTIMIZATION 5: Pre-sort keys once (avoid Object.entries tuple allocation)
|
|
108
|
+
const sortedKeys = Object.keys(schema).sort();
|
|
109
|
+
for (const key of sortedKeys) {
|
|
110
|
+
const value = schema[key];
|
|
111
|
+
// OPTIMIZATION 2: Use Map for O(1) lookup instead of indexOf O(n)
|
|
112
|
+
let keyIndex = keyMap.get(key);
|
|
113
|
+
if (keyIndex === undefined) {
|
|
114
|
+
keyIndex = keyMap.size;
|
|
115
|
+
keyMap.set(key, keyIndex);
|
|
116
|
+
}
|
|
117
|
+
const keyIndexEncoded = encodeBase62(keyIndex);
|
|
118
|
+
const [k, t] = encodeDataType(value, keyIndexEncoded, keyMap, typeMap);
|
|
119
|
+
keys.push(...k);
|
|
120
|
+
types.push(...t);
|
|
121
|
+
}
|
|
122
|
+
return [keys.join(','), types.join(',')];
|
|
123
|
+
};
|
|
124
|
+
const encodeSchema = (schema) => {
|
|
125
|
+
const keyMap = new Map();
|
|
126
|
+
const typeMap = new Map();
|
|
127
|
+
const [keys, types] = encodeObjectSchema(schema, keyMap, typeMap);
|
|
128
|
+
const keyList = Array.from(keyMap.entries()).sort((a, b) => a[1] - b[1]).map(([k]) => k);
|
|
129
|
+
const typeList = Array.from(typeMap.entries()).sort((a, b) => a[1] - b[1]).map(([k]) => k);
|
|
130
|
+
return `${keyList.join(',')}\n${typeList.join(',')}\n${keys}\n${types}`;
|
|
131
|
+
};
|
|
132
|
+
exports.encodeSchema = encodeSchema;
|
|
133
|
+
const decodeObjectSchemaFromStrings = (keyList, typeList, keysEncoded, typesEncoded) => {
|
|
134
|
+
const keyTokens = splitTopLevel(keysEncoded, ',');
|
|
135
|
+
const typeTokens = splitTopLevel(typesEncoded, ',');
|
|
136
|
+
if (keyTokens.length !== typeTokens.length) {
|
|
137
|
+
throw new Error('Invalid schema string: keys/types length mismatch');
|
|
138
|
+
}
|
|
139
|
+
const out = {};
|
|
140
|
+
for (let i = 0; i < keyTokens.length; i++) {
|
|
141
|
+
const keyToken = keyTokens[i];
|
|
142
|
+
const typeToken = typeTokens[i];
|
|
143
|
+
const [keyIndex] = parseLeadingBase62(keyToken);
|
|
144
|
+
const keyName = keyList[keyIndex];
|
|
145
|
+
if (keyName === undefined)
|
|
146
|
+
throw new Error(`Invalid schema string: unknown key index ${keyIndex}`);
|
|
147
|
+
out[keyName] = decodeDataTypeFromTokens(keyList, typeList, keyToken, typeToken);
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
};
|
|
151
|
+
const splitTopLevel = (input, separator) => {
|
|
152
|
+
if (!input)
|
|
153
|
+
return [];
|
|
154
|
+
const parts = [];
|
|
155
|
+
let start = 0;
|
|
156
|
+
let braceDepth = 0;
|
|
157
|
+
let bracketDepth = 0;
|
|
158
|
+
for (let i = 0; i < input.length; i++) {
|
|
159
|
+
const ch = input[i];
|
|
160
|
+
if (ch === '{')
|
|
161
|
+
braceDepth++;
|
|
162
|
+
else if (ch === '}')
|
|
163
|
+
braceDepth--;
|
|
164
|
+
else if (ch === '[')
|
|
165
|
+
bracketDepth++;
|
|
166
|
+
else if (ch === ']')
|
|
167
|
+
bracketDepth--;
|
|
168
|
+
if (braceDepth === 0 && bracketDepth === 0 && ch === separator) {
|
|
169
|
+
parts.push(input.slice(start, i));
|
|
170
|
+
start = i + 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
parts.push(input.slice(start));
|
|
174
|
+
return parts;
|
|
175
|
+
};
|
|
176
|
+
const parseLeadingBase62 = (input) => {
|
|
177
|
+
let i = 0;
|
|
178
|
+
while (i < input.length && BASE62_CHARS.indexOf(input[i]) !== -1) {
|
|
179
|
+
i++;
|
|
180
|
+
}
|
|
181
|
+
if (i === 0)
|
|
182
|
+
throw new Error(`Invalid schema token: expected leading index, got: ${input}`);
|
|
183
|
+
const encoded = input.slice(0, i);
|
|
184
|
+
return [decodeBase62(encoded), i];
|
|
185
|
+
};
|
|
186
|
+
const stripWrapper = (input, open, close) => {
|
|
187
|
+
if (!input.startsWith(open) || !input.endsWith(close)) {
|
|
188
|
+
throw new Error(`Invalid schema token: expected ${open}...${close}, got: ${input}`);
|
|
189
|
+
}
|
|
190
|
+
return input.slice(1, -1);
|
|
191
|
+
};
|
|
192
|
+
const decodeDataTypeFromTokens = (keyList, typeList, keyToken, typeToken) => {
|
|
193
|
+
const typeParts = splitTopLevel(typeToken, '|');
|
|
194
|
+
if (typeParts.length > 1) {
|
|
195
|
+
const keyParts = splitTopLevel(keyToken, '|');
|
|
196
|
+
if (keyParts.length !== typeParts.length) {
|
|
197
|
+
throw new Error('Invalid schema string: multi-type keys/types length mismatch');
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
types: typeParts.map((t, idx) => decodeSingleTypeFromTokens(keyList, typeList, keyParts[idx], t))
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return decodeSingleTypeFromTokens(keyList, typeList, keyToken, typeToken);
|
|
204
|
+
};
|
|
205
|
+
const decodeSingleTypeFromTokens = (keyList, typeList, keyToken, typeToken) => {
|
|
206
|
+
if (typeToken.startsWith('{')) {
|
|
207
|
+
const braceEnd = typeToken.lastIndexOf('}');
|
|
208
|
+
if (braceEnd === -1)
|
|
209
|
+
throw new Error(`Invalid schema token: expected } in ${typeToken}`);
|
|
210
|
+
const [keyIndex, keyIndexLen] = parseLeadingBase62(keyToken);
|
|
211
|
+
const innerKeys = stripWrapper(keyToken.slice(keyIndexLen), '{', '}');
|
|
212
|
+
const innerTypes = typeToken.slice(1, braceEnd);
|
|
213
|
+
let out = {
|
|
214
|
+
type: 'object',
|
|
215
|
+
items: decodeObjectSchemaFromStrings(keyList, typeList, innerKeys, innerTypes)
|
|
216
|
+
};
|
|
217
|
+
const optionalPart = typeToken.slice(braceEnd + 1);
|
|
218
|
+
if (optionalPart.startsWith('?')) {
|
|
219
|
+
out.optional = true;
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
if (typeToken.startsWith('[')) {
|
|
224
|
+
const bracketEnd = typeToken.lastIndexOf(']');
|
|
225
|
+
if (bracketEnd === -1)
|
|
226
|
+
throw new Error(`Invalid schema token: expected ] in ${typeToken}`);
|
|
227
|
+
const [keyIndex, keyIndexLen] = parseLeadingBase62(keyToken);
|
|
228
|
+
const keySuffix = keyToken.slice(keyIndexLen);
|
|
229
|
+
const innerKeys = stripWrapper(keySuffix, '[', ']');
|
|
230
|
+
const innerTypes = typeToken.slice(1, bracketEnd);
|
|
231
|
+
const innerKeyTokens = splitTopLevel(innerKeys, ',');
|
|
232
|
+
const innerTypeTokens = splitTopLevel(innerTypes, ',');
|
|
233
|
+
if (innerKeyTokens.length !== 1 || innerTypeTokens.length !== 1) {
|
|
234
|
+
throw new Error('Invalid schema string: array item encoding malformed');
|
|
235
|
+
}
|
|
236
|
+
let out = {
|
|
237
|
+
type: 'array',
|
|
238
|
+
items: decodeDataTypeFromTokens(keyList, typeList, innerKeyTokens[0], innerTypeTokens[0])
|
|
239
|
+
};
|
|
240
|
+
const optionalPart = typeToken.slice(bracketEnd + 1);
|
|
241
|
+
if (optionalPart.startsWith('?')) {
|
|
242
|
+
out.optional = true;
|
|
243
|
+
}
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
const [typeIndexRaw, optionalRaw] = typeToken.split('?', 2);
|
|
247
|
+
const typeIndex = decodeBase62(typeIndexRaw);
|
|
248
|
+
const resolvedType = typeList[typeIndex];
|
|
249
|
+
if (!resolvedType)
|
|
250
|
+
throw new Error(`Invalid schema string: unknown type index ${typeIndex}`);
|
|
251
|
+
if (optionalRaw === undefined)
|
|
252
|
+
return { type: resolvedType };
|
|
253
|
+
const out = { type: resolvedType, optional: true };
|
|
254
|
+
if (optionalRaw.length)
|
|
255
|
+
out.default = JSON.parse(optionalRaw);
|
|
256
|
+
return out;
|
|
257
|
+
};
|
|
258
|
+
const decodeSchemaString = (schemaString) => {
|
|
259
|
+
const lines = schemaString.split(/\r?\n/);
|
|
260
|
+
if (lines.length < 4 || lines.length > 5)
|
|
261
|
+
throw new Error('Invalid schema string: expected 4 or 5 lines');
|
|
262
|
+
let metadata = '';
|
|
263
|
+
if (lines.length === 5) {
|
|
264
|
+
metadata = lines.shift();
|
|
265
|
+
}
|
|
266
|
+
const keyList = lines[0].length ? lines[0].split(',') : [];
|
|
267
|
+
const typeList = lines[1].length ? lines[1].split(',') : [];
|
|
268
|
+
const keysEncoded = lines[2] ?? '';
|
|
269
|
+
const typesEncoded = lines[3] ?? '';
|
|
270
|
+
const schema = decodeObjectSchemaFromStrings(keyList, typeList, keysEncoded, typesEncoded);
|
|
271
|
+
return [schema, metadata];
|
|
272
|
+
};
|
|
273
|
+
exports.decodeSchemaString = decodeSchemaString;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare class BitStreamReader {
|
|
2
|
+
private byteArray;
|
|
3
|
+
protected bitPosition: number;
|
|
4
|
+
private currentDataSize;
|
|
5
|
+
private totalAvailableBits;
|
|
6
|
+
private isComplete;
|
|
7
|
+
private _shared_array_buffer;
|
|
8
|
+
private _shared_float_64_array;
|
|
9
|
+
private _shared_float_32_array;
|
|
10
|
+
private _shared_uint8_array;
|
|
11
|
+
private _shared_data_view;
|
|
12
|
+
constructor(initialData?: Uint8Array | Buffer<ArrayBuffer>, isComplete?: boolean);
|
|
13
|
+
private resizeIfNeeded;
|
|
14
|
+
markComplete(): void;
|
|
15
|
+
private recalculateTotalBits;
|
|
16
|
+
reset(): void;
|
|
17
|
+
addData(data: Uint8Array): void;
|
|
18
|
+
readBit(): boolean;
|
|
19
|
+
readBits(count: number): boolean[];
|
|
20
|
+
readByte(): number;
|
|
21
|
+
private readInt;
|
|
22
|
+
readInt8(): number;
|
|
23
|
+
readInt16(): number;
|
|
24
|
+
readInt32(): number;
|
|
25
|
+
readBigInt(): bigint;
|
|
26
|
+
readFloat32(): number;
|
|
27
|
+
readFloat64(): number;
|
|
28
|
+
readString(): string;
|
|
29
|
+
canReadBits(size: number, throwIfNotEnough?: boolean): boolean;
|
|
30
|
+
canReadString(throwIfNotEnough?: boolean): boolean;
|
|
31
|
+
getAvailableBits(): number;
|
|
32
|
+
isDataComplete(): boolean;
|
|
33
|
+
}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
export class BitStreamReader {
|
|
2
|
+
bitPosition = 0;
|
|
3
|
+
currentDataSize = 0;
|
|
4
|
+
totalAvailableBits = 0;
|
|
5
|
+
isComplete = false;
|
|
6
|
+
_shared_array_buffer = new ArrayBuffer(8);
|
|
7
|
+
_shared_float_64_array = new Float64Array(this._shared_array_buffer);
|
|
8
|
+
_shared_float_32_array = new Float32Array(this._shared_array_buffer);
|
|
9
|
+
_shared_uint8_array = new Uint8Array(this._shared_array_buffer);
|
|
10
|
+
_shared_data_view = new DataView(this._shared_array_buffer);
|
|
11
|
+
constructor(initialData, isComplete = false) {
|
|
12
|
+
this.byteArray = new Uint8Array(0);
|
|
13
|
+
this.isComplete = isComplete;
|
|
14
|
+
if (initialData)
|
|
15
|
+
this.addData(initialData);
|
|
16
|
+
}
|
|
17
|
+
resizeIfNeeded(needed) {
|
|
18
|
+
if (this.currentDataSize + needed > this.byteArray.length) {
|
|
19
|
+
const newLength = Math.max(this.byteArray.length << 1, this.currentDataSize + needed);
|
|
20
|
+
const newArray = new Uint8Array(newLength);
|
|
21
|
+
newArray.set(this.byteArray.subarray(0, this.currentDataSize));
|
|
22
|
+
this.byteArray = newArray;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
markComplete() {
|
|
26
|
+
this.isComplete = true;
|
|
27
|
+
this.recalculateTotalBits();
|
|
28
|
+
}
|
|
29
|
+
recalculateTotalBits() {
|
|
30
|
+
if (this.currentDataSize === 0) {
|
|
31
|
+
this.totalAvailableBits = 0;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (this.isComplete) {
|
|
35
|
+
const paddingByte = this.byteArray[this.currentDataSize - 1];
|
|
36
|
+
this.totalAvailableBits = ((this.currentDataSize - 1) << 3) - paddingByte;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const safeBytesCount = Math.max(0, this.currentDataSize - 2);
|
|
40
|
+
this.totalAvailableBits = safeBytesCount << 3;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
reset() {
|
|
44
|
+
this.bitPosition = 0;
|
|
45
|
+
}
|
|
46
|
+
addData(data) {
|
|
47
|
+
this.resizeIfNeeded(data.length);
|
|
48
|
+
this.byteArray.set(data, this.currentDataSize);
|
|
49
|
+
this.currentDataSize += data.length;
|
|
50
|
+
this.recalculateTotalBits();
|
|
51
|
+
}
|
|
52
|
+
readBit() {
|
|
53
|
+
if (!this.isComplete) {
|
|
54
|
+
if (this.bitPosition >= this.totalAvailableBits) {
|
|
55
|
+
throw new Error(`Cannot read 1 bits at position ${this.bitPosition}, not enough bits available`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const byteIndex = this.bitPosition >>> 3;
|
|
59
|
+
const bitIndex = 7 - (this.bitPosition & 7);
|
|
60
|
+
const bit = (this.byteArray[byteIndex] >> bitIndex) & 1;
|
|
61
|
+
this.bitPosition++;
|
|
62
|
+
return bit === 1;
|
|
63
|
+
}
|
|
64
|
+
readBits(count) {
|
|
65
|
+
if (!this.isComplete) {
|
|
66
|
+
if (this.bitPosition + count > this.totalAvailableBits) {
|
|
67
|
+
throw new Error(`Cannot read ${count} bits at position ${this.bitPosition}, not enough bits available`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const bits = new Array(count);
|
|
71
|
+
const data = this.byteArray;
|
|
72
|
+
let pos = this.bitPosition;
|
|
73
|
+
for (let i = 0; i < count; i++) {
|
|
74
|
+
const byteIndex = pos >>> 3;
|
|
75
|
+
const bitIndex = 7 - (pos & 7);
|
|
76
|
+
bits[i] = ((data[byteIndex] >> bitIndex) & 1) === 1;
|
|
77
|
+
pos++;
|
|
78
|
+
}
|
|
79
|
+
this.bitPosition = pos;
|
|
80
|
+
return bits;
|
|
81
|
+
}
|
|
82
|
+
readByte() {
|
|
83
|
+
if (!this.isComplete) {
|
|
84
|
+
if (this.bitPosition + 8 > this.totalAvailableBits) {
|
|
85
|
+
throw new Error(`Cannot read 8 bits at position ${this.bitPosition}, not enough bits available`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const data = this.byteArray;
|
|
89
|
+
let pos = this.bitPosition;
|
|
90
|
+
let value = 0;
|
|
91
|
+
for (let i = 7; i >= 0; i--) {
|
|
92
|
+
const byteIndex = pos >>> 3;
|
|
93
|
+
const bitIndex = 7 - (pos & 7);
|
|
94
|
+
const bit = (data[byteIndex] >> bitIndex) & 1;
|
|
95
|
+
if (bit)
|
|
96
|
+
value |= (1 << i);
|
|
97
|
+
pos++;
|
|
98
|
+
}
|
|
99
|
+
this.bitPosition = pos;
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
readInt(bytes) {
|
|
103
|
+
const totalBits = bytes << 3;
|
|
104
|
+
if (!this.isComplete) {
|
|
105
|
+
if (this.bitPosition + totalBits > this.totalAvailableBits) {
|
|
106
|
+
throw new Error(`Cannot read ${totalBits} bits at position ${this.bitPosition}, not enough bits available`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const data = this.byteArray;
|
|
110
|
+
let pos = this.bitPosition;
|
|
111
|
+
const bitOffset = pos & 7;
|
|
112
|
+
const byteIndex = pos >> 3;
|
|
113
|
+
let value = 0n;
|
|
114
|
+
if (bitOffset === 0) {
|
|
115
|
+
// Byte-aligned: fastest path
|
|
116
|
+
for (let i = 0; i < bytes; i++) {
|
|
117
|
+
value = (value << 8n) | BigInt(data[byteIndex + i]);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Bit-unaligned: optimized bit extraction
|
|
122
|
+
const shift = 8 - bitOffset;
|
|
123
|
+
for (let i = 0; i < bytes; i++) {
|
|
124
|
+
const byte = (data[byteIndex + i] << bitOffset) | (data[byteIndex + i + 1] >> shift);
|
|
125
|
+
value = (value << 8n) | BigInt(byte & 0xFF);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
this.bitPosition = pos + totalBits;
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
// Specialized versions for common sizes (even faster)
|
|
132
|
+
readInt8() {
|
|
133
|
+
if (!this.isComplete) {
|
|
134
|
+
if (this.bitPosition + 8 > this.totalAvailableBits) {
|
|
135
|
+
throw new Error(`Cannot read 8 bits at position ${this.bitPosition}, not enough bits available`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const data = this.byteArray;
|
|
139
|
+
let pos = this.bitPosition;
|
|
140
|
+
const bitOffset = pos & 7;
|
|
141
|
+
const byteIndex = pos >> 3;
|
|
142
|
+
let value;
|
|
143
|
+
if (bitOffset === 0) {
|
|
144
|
+
value = data[byteIndex];
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
const shift = 8 - bitOffset;
|
|
148
|
+
value = ((data[byteIndex] << bitOffset) | (data[byteIndex + 1] >> shift)) & 0xFF;
|
|
149
|
+
}
|
|
150
|
+
this.bitPosition = pos + 8;
|
|
151
|
+
return (value << 24) >> 24;
|
|
152
|
+
}
|
|
153
|
+
readInt16() {
|
|
154
|
+
if (!this.isComplete) {
|
|
155
|
+
if (this.bitPosition + 16 > this.totalAvailableBits) {
|
|
156
|
+
throw new Error(`Cannot read 16 bits at position ${this.bitPosition}, not enough bits available`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const data = this.byteArray;
|
|
160
|
+
let pos = this.bitPosition;
|
|
161
|
+
const bitOffset = pos & 7;
|
|
162
|
+
const byteIndex = pos >> 3;
|
|
163
|
+
let value;
|
|
164
|
+
if (bitOffset === 0) {
|
|
165
|
+
value = (data[byteIndex] << 8) | data[byteIndex + 1];
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const shift = 8 - bitOffset;
|
|
169
|
+
const byte0 = ((data[byteIndex] << bitOffset) | (data[byteIndex + 1] >> shift)) & 0xFF;
|
|
170
|
+
const byte1 = ((data[byteIndex + 1] << bitOffset) | (data[byteIndex + 2] >> shift)) & 0xFF;
|
|
171
|
+
value = (byte0 << 8) | byte1;
|
|
172
|
+
}
|
|
173
|
+
this.bitPosition = pos + 16;
|
|
174
|
+
return (value << 16) >> 16;
|
|
175
|
+
}
|
|
176
|
+
readInt32() {
|
|
177
|
+
if (!this.isComplete) {
|
|
178
|
+
if (this.bitPosition + 32 > this.totalAvailableBits) {
|
|
179
|
+
throw new Error(`Cannot read 32 bits at position ${this.bitPosition}, not enough bits available`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const data = this.byteArray;
|
|
183
|
+
let pos = this.bitPosition;
|
|
184
|
+
const bitOffset = pos & 7;
|
|
185
|
+
const byteIndex = pos >> 3;
|
|
186
|
+
let value;
|
|
187
|
+
if (bitOffset === 0) {
|
|
188
|
+
value = (data[byteIndex] << 24) | (data[byteIndex + 1] << 16) |
|
|
189
|
+
(data[byteIndex + 2] << 8) | data[byteIndex + 3];
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
const shift = 8 - bitOffset;
|
|
193
|
+
const byte0 = ((data[byteIndex] << bitOffset) | (data[byteIndex + 1] >> shift)) & 0xFF;
|
|
194
|
+
const byte1 = ((data[byteIndex + 1] << bitOffset) | (data[byteIndex + 2] >> shift)) & 0xFF;
|
|
195
|
+
const byte2 = ((data[byteIndex + 2] << bitOffset) | (data[byteIndex + 3] >> shift)) & 0xFF;
|
|
196
|
+
const byte3 = ((data[byteIndex + 3] << bitOffset) | (data[byteIndex + 4] >> shift)) & 0xFF;
|
|
197
|
+
value = (byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3;
|
|
198
|
+
}
|
|
199
|
+
this.bitPosition = pos + 32;
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
readBigInt() {
|
|
203
|
+
let result = 0n;
|
|
204
|
+
let shift = 0n;
|
|
205
|
+
let currentBitPos = this.bitPosition;
|
|
206
|
+
let byte;
|
|
207
|
+
do {
|
|
208
|
+
const byteIndex = currentBitPos >> 3;
|
|
209
|
+
const bitOffset = currentBitPos & 7;
|
|
210
|
+
if (bitOffset === 0) {
|
|
211
|
+
byte = this.byteArray[byteIndex];
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
const shift_amount = 8 - bitOffset;
|
|
215
|
+
const mask = 0xFF >> bitOffset;
|
|
216
|
+
const part1 = (this.byteArray[byteIndex] & mask) << bitOffset;
|
|
217
|
+
const part2 = this.byteArray[byteIndex + 1] >> shift_amount;
|
|
218
|
+
byte = part1 | part2;
|
|
219
|
+
}
|
|
220
|
+
result |= BigInt(byte & 0x7F) << shift;
|
|
221
|
+
shift += 7n;
|
|
222
|
+
currentBitPos += 8;
|
|
223
|
+
} while ((byte & 0x80) !== 0);
|
|
224
|
+
this.bitPosition = currentBitPos;
|
|
225
|
+
return (result & 1n) === 0n ? result >> 1n : -((result + 1n) >> 1n);
|
|
226
|
+
}
|
|
227
|
+
readFloat32() {
|
|
228
|
+
const data = this.byteArray;
|
|
229
|
+
let pos = this.bitPosition;
|
|
230
|
+
const bitOffset = pos & 7;
|
|
231
|
+
const byteIndex = pos >> 3;
|
|
232
|
+
if (bitOffset === 0) {
|
|
233
|
+
// Byte-aligned: fastest path
|
|
234
|
+
this._shared_uint8_array[0] = data[byteIndex];
|
|
235
|
+
this._shared_uint8_array[1] = data[byteIndex + 1];
|
|
236
|
+
this._shared_uint8_array[2] = data[byteIndex + 2];
|
|
237
|
+
this._shared_uint8_array[3] = data[byteIndex + 3];
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
// Bit-unaligned: optimized bit extraction
|
|
241
|
+
const shift = 8 - bitOffset;
|
|
242
|
+
this._shared_uint8_array[0] = (data[byteIndex] << bitOffset) | (data[byteIndex + 1] >> shift);
|
|
243
|
+
this._shared_uint8_array[1] = (data[byteIndex + 1] << bitOffset) | (data[byteIndex + 2] >> shift);
|
|
244
|
+
this._shared_uint8_array[2] = (data[byteIndex + 2] << bitOffset) | (data[byteIndex + 3] >> shift);
|
|
245
|
+
this._shared_uint8_array[3] = (data[byteIndex + 3] << bitOffset) | (data[byteIndex + 4] >> shift);
|
|
246
|
+
}
|
|
247
|
+
this.bitPosition = pos + 32;
|
|
248
|
+
return this._shared_float_32_array[0];
|
|
249
|
+
}
|
|
250
|
+
// readFloat32(): number {
|
|
251
|
+
// const uint32 = Number(this.readInt(4));
|
|
252
|
+
// const buffer = new ArrayBuffer(4);
|
|
253
|
+
// const view = new DataView(buffer);
|
|
254
|
+
// view.setUint32(0, uint32, true);
|
|
255
|
+
// return view.getFloat32(0, true);
|
|
256
|
+
// }
|
|
257
|
+
readFloat64() {
|
|
258
|
+
const data = this.byteArray;
|
|
259
|
+
let pos = this.bitPosition;
|
|
260
|
+
const bitOffset = pos & 7;
|
|
261
|
+
const byteIndex = pos >> 3;
|
|
262
|
+
if (bitOffset === 0) {
|
|
263
|
+
// Byte-aligned: fastest path
|
|
264
|
+
this._shared_uint8_array[0] = data[byteIndex];
|
|
265
|
+
this._shared_uint8_array[1] = data[byteIndex + 1];
|
|
266
|
+
this._shared_uint8_array[2] = data[byteIndex + 2];
|
|
267
|
+
this._shared_uint8_array[3] = data[byteIndex + 3];
|
|
268
|
+
this._shared_uint8_array[4] = data[byteIndex + 4];
|
|
269
|
+
this._shared_uint8_array[5] = data[byteIndex + 5];
|
|
270
|
+
this._shared_uint8_array[6] = data[byteIndex + 6];
|
|
271
|
+
this._shared_uint8_array[7] = data[byteIndex + 7];
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
// Bit-unaligned: optimized bit extraction
|
|
275
|
+
const shift = 8 - bitOffset;
|
|
276
|
+
this._shared_uint8_array[0] = (data[byteIndex] << bitOffset) | (data[byteIndex + 1] >> shift);
|
|
277
|
+
this._shared_uint8_array[1] = (data[byteIndex + 1] << bitOffset) | (data[byteIndex + 2] >> shift);
|
|
278
|
+
this._shared_uint8_array[2] = (data[byteIndex + 2] << bitOffset) | (data[byteIndex + 3] >> shift);
|
|
279
|
+
this._shared_uint8_array[3] = (data[byteIndex + 3] << bitOffset) | (data[byteIndex + 4] >> shift);
|
|
280
|
+
this._shared_uint8_array[4] = (data[byteIndex + 4] << bitOffset) | (data[byteIndex + 5] >> shift);
|
|
281
|
+
this._shared_uint8_array[5] = (data[byteIndex + 5] << bitOffset) | (data[byteIndex + 6] >> shift);
|
|
282
|
+
this._shared_uint8_array[6] = (data[byteIndex + 6] << bitOffset) | (data[byteIndex + 7] >> shift);
|
|
283
|
+
this._shared_uint8_array[7] = (data[byteIndex + 7] << bitOffset) | (data[byteIndex + 8] >> shift);
|
|
284
|
+
}
|
|
285
|
+
this.bitPosition = pos + 64;
|
|
286
|
+
return this._shared_float_64_array[0];
|
|
287
|
+
}
|
|
288
|
+
readString() {
|
|
289
|
+
const bytes = [];
|
|
290
|
+
while (true) {
|
|
291
|
+
const byte = this.readByte();
|
|
292
|
+
if (byte === 0)
|
|
293
|
+
break;
|
|
294
|
+
bytes.push(byte);
|
|
295
|
+
}
|
|
296
|
+
return new TextDecoder().decode(new Uint8Array(bytes));
|
|
297
|
+
}
|
|
298
|
+
canReadBits(size, throwIfNotEnough = false) {
|
|
299
|
+
const enough = (this.bitPosition + size) <= this.totalAvailableBits;
|
|
300
|
+
if (!enough && throwIfNotEnough) {
|
|
301
|
+
throw new Error(`Cannot read ${size} bits at position ${this.bitPosition}, not enough bits available`);
|
|
302
|
+
}
|
|
303
|
+
return enough;
|
|
304
|
+
}
|
|
305
|
+
canReadString(throwIfNotEnough = false) {
|
|
306
|
+
let tempPos = this.bitPosition;
|
|
307
|
+
while (true) {
|
|
308
|
+
if (tempPos + 8 > this.totalAvailableBits) {
|
|
309
|
+
if (throwIfNotEnough) {
|
|
310
|
+
throw new Error(`Cannot read string at position ${this.bitPosition}, not enough bits available`);
|
|
311
|
+
}
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
let isZero = true;
|
|
315
|
+
for (let i = 0; i < 8; i++) {
|
|
316
|
+
const byteIndex = tempPos >>> 3;
|
|
317
|
+
const bitIndex = 7 - (tempPos & 7);
|
|
318
|
+
const bit = (this.byteArray[byteIndex] >> bitIndex) & 1;
|
|
319
|
+
if (bit === 1) {
|
|
320
|
+
isZero = false;
|
|
321
|
+
tempPos += 8 - i;
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
tempPos++;
|
|
325
|
+
}
|
|
326
|
+
if (isZero)
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
getAvailableBits() {
|
|
331
|
+
return this.totalAvailableBits - this.bitPosition;
|
|
332
|
+
}
|
|
333
|
+
isDataComplete() {
|
|
334
|
+
return this.isComplete;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare class BitStreamWriter {
|
|
2
|
+
private deletePreviousDataOnGetFixedBytes;
|
|
3
|
+
private bufferByte;
|
|
4
|
+
private bufferSize;
|
|
5
|
+
private byteArray;
|
|
6
|
+
private bitPosition;
|
|
7
|
+
private nextFixedBytePosition;
|
|
8
|
+
private guaranteedCapacity;
|
|
9
|
+
private static textEncoder;
|
|
10
|
+
private _shared_array_buffer;
|
|
11
|
+
private _shared_float_64_array;
|
|
12
|
+
private _shared_float_32_array;
|
|
13
|
+
private _shared_uint8_array;
|
|
14
|
+
private _shared_data_view;
|
|
15
|
+
constructor(deletePreviousDataOnGetFixedBytes?: boolean);
|
|
16
|
+
clear(): void;
|
|
17
|
+
ensureCapacity(newBits: number): void;
|
|
18
|
+
writeBit(bit: 0 | 1): void;
|
|
19
|
+
writeBits(bits: (0 | 1)[]): void;
|
|
20
|
+
private writeIntBytes;
|
|
21
|
+
writeInt8(value: number): void;
|
|
22
|
+
writeInt16(value: number): void;
|
|
23
|
+
writeInt32(value: number): void;
|
|
24
|
+
writeBigInt(value: bigint): void;
|
|
25
|
+
dumpBytes(label: string, bytes: Uint8Array): void;
|
|
26
|
+
writeFloat32(value: number): void;
|
|
27
|
+
writeFloat64(value: number): void;
|
|
28
|
+
writeString(value: string, end?: number): void;
|
|
29
|
+
removeBits(count: number): void;
|
|
30
|
+
getBytes(): Uint8Array<ArrayBuffer>;
|
|
31
|
+
getNextFixedBytes(): Uint8Array<ArrayBufferLike>;
|
|
32
|
+
getRemainingBytesAfterFixed(): Uint8Array<ArrayBuffer>;
|
|
33
|
+
getNextFixedBuffer(): Buffer<ArrayBuffer>;
|
|
34
|
+
getRemainingBufferAfterFixed(): Buffer<ArrayBuffer>;
|
|
35
|
+
getBuffer(): Buffer<ArrayBuffer>;
|
|
36
|
+
}
|