@maplibre/mlt 1.1.2 → 1.1.5
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/decoding/decodingTestUtils.d.ts +75 -0
- package/dist/decoding/decodingTestUtils.js +285 -0
- package/dist/decoding/decodingTestUtils.js.map +1 -0
- package/dist/decoding/decodingUtils.d.ts +5 -8
- package/dist/decoding/decodingUtils.js +22 -49
- package/dist/decoding/decodingUtils.js.map +1 -1
- package/dist/decoding/decodingUtils.spec.js +85 -69
- package/dist/decoding/decodingUtils.spec.js.map +1 -1
- package/dist/decoding/fsstDecoder.spec.js +52 -35
- package/dist/decoding/fsstDecoder.spec.js.map +1 -1
- package/dist/decoding/geometryDecoder.js +41 -30
- package/dist/decoding/geometryDecoder.js.map +1 -1
- package/dist/decoding/integerDecodingUtils.d.ts +18 -31
- package/dist/decoding/integerDecodingUtils.js +134 -299
- package/dist/decoding/integerDecodingUtils.js.map +1 -1
- package/dist/decoding/integerDecodingUtils.spec.js +254 -148
- package/dist/decoding/integerDecodingUtils.spec.js.map +1 -1
- package/dist/decoding/integerStreamDecoder.d.ts +4 -6
- package/dist/decoding/integerStreamDecoder.js +104 -122
- package/dist/decoding/integerStreamDecoder.js.map +1 -1
- package/dist/decoding/integerStreamDecoder.spec.js +370 -131
- package/dist/decoding/integerStreamDecoder.spec.js.map +1 -1
- package/dist/decoding/propertyDecoder.js +14 -24
- package/dist/decoding/propertyDecoder.js.map +1 -1
- package/dist/decoding/propertyDecoder.spec.js +409 -606
- package/dist/decoding/propertyDecoder.spec.js.map +1 -1
- package/dist/decoding/stringDecoder.js +10 -10
- package/dist/decoding/stringDecoder.js.map +1 -1
- package/dist/decoding/stringDecoder.spec.js +352 -320
- package/dist/decoding/stringDecoder.spec.js.map +1 -1
- package/dist/decoding/unpackNullableUtils.d.ts +25 -0
- package/dist/decoding/unpackNullableUtils.js +51 -0
- package/dist/decoding/unpackNullableUtils.js.map +1 -0
- package/dist/decoding/unpackNullableUtils.spec.js +71 -0
- package/dist/decoding/unpackNullableUtils.spec.js.map +1 -0
- package/dist/encoding/embeddedTilesetMetadataEncoder.d.ts +16 -0
- package/dist/encoding/embeddedTilesetMetadataEncoder.js +40 -0
- package/dist/encoding/embeddedTilesetMetadataEncoder.js.map +1 -0
- package/dist/encoding/encodingUtils.d.ts +7 -0
- package/dist/encoding/encodingUtils.js +107 -0
- package/dist/encoding/encodingUtils.js.map +1 -0
- package/dist/encoding/fsstEncoder.d.ts +21 -0
- package/dist/encoding/fsstEncoder.js +78 -0
- package/dist/encoding/fsstEncoder.js.map +1 -0
- package/dist/encoding/integerEncodingUtils.d.ts +68 -0
- package/dist/encoding/integerEncodingUtils.js +655 -0
- package/dist/encoding/integerEncodingUtils.js.map +1 -0
- package/dist/encoding/integerStreamEncoder.d.ts +27 -0
- package/dist/encoding/integerStreamEncoder.js +139 -0
- package/dist/encoding/integerStreamEncoder.js.map +1 -0
- package/dist/encoding/packNullableUtils.d.ts +4 -0
- package/dist/encoding/packNullableUtils.js +55 -0
- package/dist/encoding/packNullableUtils.js.map +1 -0
- package/dist/encoding/propertyEncoder.d.ts +78 -0
- package/dist/encoding/propertyEncoder.js +335 -0
- package/dist/encoding/propertyEncoder.js.map +1 -0
- package/dist/encoding/stringEncoder.d.ts +12 -0
- package/dist/encoding/stringEncoder.js +182 -0
- package/dist/encoding/stringEncoder.js.map +1 -0
- package/dist/encoding/zOrderCurveEncoder.d.ts +1 -0
- package/dist/encoding/zOrderCurveEncoder.js +10 -0
- package/dist/encoding/zOrderCurveEncoder.js.map +1 -0
- package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.d.ts +5 -1
- package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.js +29 -41
- package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.js.map +1 -1
- package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.spec.d.ts +1 -0
- package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.spec.js +142 -0
- package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.spec.js.map +1 -0
- package/dist/mltDecoder.js +2 -3
- package/dist/mltDecoder.js.map +1 -1
- package/dist/vector/dictionary/stringDictionaryVector.d.ts +1 -1
- package/dist/vector/dictionary/stringDictionaryVector.js.map +1 -1
- package/dist/vector/flat/stringFlatVector.d.ts +1 -1
- package/dist/vector/flat/stringFlatVector.js.map +1 -1
- package/dist/vector/fsst-dictionary/stringFsstDictionaryVector.d.ts +1 -1
- package/dist/vector/fsst-dictionary/stringFsstDictionaryVector.js.map +1 -1
- package/dist/vector/fsst-dictionary/stringFsstDictionaryVector.spec.js +2 -2
- package/dist/vector/fsst-dictionary/stringFsstDictionaryVector.spec.js.map +1 -1
- package/dist/vector/geometry/constGpuVector.d.ts +2 -2
- package/dist/vector/geometry/constGpuVector.js.map +1 -1
- package/dist/vector/geometry/flatGpuVector.d.ts +2 -2
- package/dist/vector/geometry/flatGpuVector.js.map +1 -1
- package/dist/vector/geometry/gpuVector.d.ts +2 -2
- package/dist/vector/geometry/gpuVector.js.map +1 -1
- package/dist/vector/geometry/topologyVector.d.ts +4 -4
- package/dist/vector/geometry/topologyVector.js +0 -1
- package/dist/vector/geometry/topologyVector.js.map +1 -1
- package/dist/vector/geometry/zOrderCurve.spec.js +17 -11
- package/dist/vector/geometry/zOrderCurve.spec.js.map +1 -1
- package/dist/vector/variableSizeVector.d.ts +2 -2
- package/dist/vector/variableSizeVector.js +0 -1
- package/dist/vector/variableSizeVector.js.map +1 -1
- package/package.json +6 -8
- package/dist/decoding/geometryDecoder.spec.js +0 -5
- package/dist/decoding/geometryDecoder.spec.js.map +0 -1
- /package/dist/decoding/{geometryDecoder.spec.d.ts → unpackNullableUtils.spec.d.ts} +0 -0
|
@@ -1,354 +1,386 @@
|
|
|
1
|
-
import { describe, it, expect
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import IntWrapper from "./intWrapper";
|
|
3
|
+
import { decodeString, decodeSharedDictionary } from "./stringDecoder";
|
|
4
|
+
import { encodePlainStrings, encodeDictionaryStrings } from "../encoding/stringEncoder";
|
|
5
|
+
import { concatenateBuffers, createColumnMetadataForStruct, createStream, encodeFsstStrings, encodeSharedDictionary, encodeStructField, } from "./decodingTestUtils";
|
|
6
|
+
import { StringFlatVector } from "../vector/flat/stringFlatVector";
|
|
7
|
+
import { StringDictionaryVector } from "../vector/dictionary/stringDictionaryVector";
|
|
8
|
+
import { StringFsstDictionaryVector } from "../vector/fsst-dictionary/stringFsstDictionaryVector";
|
|
9
|
+
import { ScalarType } from "../metadata/tileset/tilesetMetadata";
|
|
5
10
|
import { PhysicalStreamType } from "../metadata/tile/physicalStreamType";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
function createMockChildField(name = "fieldName", type = "scalarField", physicalType = ScalarType.STRING) {
|
|
22
|
-
return {
|
|
23
|
-
name,
|
|
24
|
-
type,
|
|
25
|
-
scalarField: { physicalType },
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
function createMockColumn(name = "testColumn", children = []) {
|
|
29
|
-
return {
|
|
30
|
-
name,
|
|
31
|
-
complexType: {
|
|
32
|
-
children: children.length > 0 ? children : [createMockChildField()],
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
function setupOffsetMock(initialValue = 0) {
|
|
37
|
-
let offsetValue = initialValue;
|
|
38
|
-
const mockOffset = {
|
|
39
|
-
get: vi.fn(() => offsetValue),
|
|
40
|
-
add: vi.fn((amount) => {
|
|
41
|
-
offsetValue += amount;
|
|
42
|
-
}),
|
|
43
|
-
};
|
|
44
|
-
return mockOffset;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Setup StreamMetadataDecoder mock with a pool of metadata.
|
|
48
|
-
* Cycles through the pool if more calls are made than metadata provided.
|
|
49
|
-
*/
|
|
50
|
-
function setupStreamMetadataDecodeMock(metadata) {
|
|
51
|
-
let callCount = 0;
|
|
52
|
-
vi.spyOn(StreamMetadataDecoder, "decodeStreamMetadata").mockImplementation(() => {
|
|
53
|
-
const result = metadata[callCount % metadata.length];
|
|
54
|
-
callCount++;
|
|
55
|
-
return result;
|
|
11
|
+
import { LengthType } from "../metadata/tile/lengthType";
|
|
12
|
+
import { LogicalStreamType } from "../metadata/tile/logicalStreamType";
|
|
13
|
+
describe("decodeString - Plain String Decoder", () => {
|
|
14
|
+
it("should decode plain strings with simple ASCII values", () => {
|
|
15
|
+
const expectedStrings = ["hello", "world", "test"];
|
|
16
|
+
const encodedStrings = encodePlainStrings(expectedStrings);
|
|
17
|
+
const offset = new IntWrapper(0);
|
|
18
|
+
const result = decodeString("testColumn", encodedStrings, offset, 2);
|
|
19
|
+
expect(result).toBeInstanceOf(StringFlatVector);
|
|
20
|
+
const resultVec = result;
|
|
21
|
+
for (let i = 0; i < expectedStrings.length; i++) {
|
|
22
|
+
expect(resultVec.getValue(i)).toBe(expectedStrings[i]);
|
|
23
|
+
}
|
|
56
24
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
offset
|
|
61
|
-
|
|
25
|
+
it("should decode plain strings with varying lengths", () => {
|
|
26
|
+
const expectedStrings = ["a", "abc", "hello world"];
|
|
27
|
+
const encodedStrings = encodePlainStrings(expectedStrings);
|
|
28
|
+
const offset = new IntWrapper(0);
|
|
29
|
+
const result = decodeString("testColumn", encodedStrings, offset, 2);
|
|
30
|
+
const resultVec = result;
|
|
31
|
+
for (let i = 0; i < expectedStrings.length; i++) {
|
|
32
|
+
expect(resultVec.getValue(i)).toBe(expectedStrings[i]);
|
|
33
|
+
}
|
|
62
34
|
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
35
|
+
it("should decode plain strings with empty strings", () => {
|
|
36
|
+
const expectedStrings = ["", "encodedStrings", "", "more"];
|
|
37
|
+
const encodedStrings = encodePlainStrings(expectedStrings);
|
|
38
|
+
const offset = new IntWrapper(0);
|
|
39
|
+
const result = decodeString("testColumn", encodedStrings, offset, 2);
|
|
40
|
+
expect(result).toBeInstanceOf(StringFlatVector);
|
|
41
|
+
const resultVec = result;
|
|
42
|
+
for (let i = 0; i < expectedStrings.length; i++) {
|
|
43
|
+
expect(resultVec.getValue(i)).toBe(expectedStrings[i]);
|
|
44
|
+
}
|
|
71
45
|
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
46
|
+
it("should decode mixed null and empty strings", () => {
|
|
47
|
+
const expectedStrings = [null, "", "encodedStrings", null, ""];
|
|
48
|
+
const encodedStrings = encodePlainStrings(expectedStrings);
|
|
49
|
+
const offset = new IntWrapper(0);
|
|
50
|
+
const result = decodeString("testColumn", encodedStrings, offset, 3);
|
|
51
|
+
expect(result).not.toBeNull();
|
|
52
|
+
for (let i = 0; i < expectedStrings.length; i++) {
|
|
53
|
+
expect(result.getValue(i)).toBe(expectedStrings[i]);
|
|
54
|
+
}
|
|
77
55
|
});
|
|
78
|
-
it
|
|
79
|
-
const
|
|
80
|
-
|
|
56
|
+
it("should decode mixed ASCII and UTF-8 strings", () => {
|
|
57
|
+
const expectedStrings = ["hello", "Привет", "world", "日本"];
|
|
58
|
+
const encodedStrings = encodePlainStrings(expectedStrings);
|
|
59
|
+
const offset = new IntWrapper(0);
|
|
60
|
+
const result = decodeString("testColumn", encodedStrings, offset, 2);
|
|
61
|
+
expect(result).toBeInstanceOf(StringFlatVector);
|
|
62
|
+
const resultVec = result;
|
|
63
|
+
for (let i = 0; i < expectedStrings.length; i++) {
|
|
64
|
+
expect(resultVec.getValue(i)).toBe(expectedStrings[i]);
|
|
65
|
+
}
|
|
81
66
|
});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
67
|
+
});
|
|
68
|
+
describe("decodeString - Dictionary String Decoder", () => {
|
|
69
|
+
it("should decode dictionary-compressed strings with repeated values", () => {
|
|
70
|
+
const expectedStrings = ["cat", "dog", "cat", "cat", "dog"];
|
|
71
|
+
const encodedStrings = encodeDictionaryStrings(expectedStrings);
|
|
72
|
+
const offset = new IntWrapper(0);
|
|
73
|
+
const result = decodeString("testColumn", encodedStrings, offset, 3);
|
|
74
|
+
expect(result).toBeInstanceOf(StringDictionaryVector);
|
|
75
|
+
const resultVec = result;
|
|
76
|
+
for (let i = 0; i < expectedStrings.length; i++) {
|
|
77
|
+
expect(resultVec.getValue(i)).toBe(expectedStrings[i]);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
it("should decode dictionary with single repeated string", () => {
|
|
81
|
+
const expectedStrings = ["same", "same", "same"];
|
|
82
|
+
const encodedStrings = encodeDictionaryStrings(expectedStrings);
|
|
83
|
+
const offset = new IntWrapper(0);
|
|
84
|
+
const result = decodeString("testColumn", encodedStrings, offset, 3);
|
|
85
|
+
expect(result).toBeInstanceOf(StringDictionaryVector);
|
|
86
|
+
for (let i = 0; i < expectedStrings.length; i++) {
|
|
87
|
+
expect(result.getValue(i)).toBe(expectedStrings[i]);
|
|
88
|
+
}
|
|
89
89
|
});
|
|
90
|
-
it
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
90
|
+
it("should decode dictionary with UTF-8 strings", () => {
|
|
91
|
+
const expectedStrings = ["café", "日本", "café", "日本"];
|
|
92
|
+
const encodedStrings = encodeDictionaryStrings(expectedStrings);
|
|
93
|
+
const offset = new IntWrapper(0);
|
|
94
|
+
const result = decodeString("testColumn", encodedStrings, offset, 3);
|
|
95
|
+
expect(result).toBeInstanceOf(StringDictionaryVector);
|
|
96
|
+
for (let i = 0; i < expectedStrings.length; i++) {
|
|
97
|
+
expect(result.getValue(i)).toBe(expectedStrings[i]);
|
|
98
|
+
}
|
|
98
99
|
});
|
|
99
|
-
it
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
expect(result.name).toBe("test");
|
|
100
|
+
it("should decode dictionary with all unique strings", () => {
|
|
101
|
+
const expectedStrings = ["unique1", "unique2", "unique3", "unique4"];
|
|
102
|
+
const encodedStrings = encodeDictionaryStrings(expectedStrings);
|
|
103
|
+
const offset = new IntWrapper(0);
|
|
104
|
+
const result = decodeString("testColumn", encodedStrings, offset, 3);
|
|
105
|
+
expect(result).toBeInstanceOf(StringDictionaryVector);
|
|
106
|
+
for (let i = 0; i < expectedStrings.length; i++) {
|
|
107
|
+
expect(result.getValue(i)).toBe(expectedStrings[i]);
|
|
108
|
+
}
|
|
109
109
|
});
|
|
110
|
-
it
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
expect(result
|
|
110
|
+
it("should decode nullable dictionary strings", () => {
|
|
111
|
+
const expectedStrings = [null, "", "encodedStrings", "", null];
|
|
112
|
+
const encodedStrings = encodeDictionaryStrings(expectedStrings);
|
|
113
|
+
const offset = new IntWrapper(0);
|
|
114
|
+
const result = decodeString("testColumn", encodedStrings, offset, 4);
|
|
115
|
+
expect(result).toBeInstanceOf(StringDictionaryVector);
|
|
116
|
+
for (let i = 0; i < expectedStrings.length; i++) {
|
|
117
|
+
expect(result.getValue(i)).toBe(expectedStrings[i]);
|
|
118
|
+
}
|
|
116
119
|
});
|
|
117
120
|
});
|
|
118
|
-
describe("
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
describe("decodeString - FSST Dictionary Decoder (Basic Coverage)", () => {
|
|
122
|
+
it("should decode FSST-compressed strings with simple symbol table", () => {
|
|
123
|
+
const encodedStrings = encodeFsstStrings();
|
|
124
|
+
const offset = new IntWrapper(0);
|
|
125
|
+
const result = decodeString("testColumn", encodedStrings, offset, 6);
|
|
126
|
+
expect(result).toBeInstanceOf(StringFsstDictionaryVector);
|
|
127
|
+
const resultVec = result;
|
|
128
|
+
const expectedValues = ["cat", "dog", "cat"];
|
|
129
|
+
for (let i = 0; i < expectedValues.length; i++) {
|
|
130
|
+
expect(resultVec.getValue(i)).toBe(expectedValues[i]);
|
|
131
|
+
}
|
|
129
132
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
expect(StreamMetadataDecoder.decodeStreamMetadata).toHaveBeenCalledWith(mockData, mockOffset);
|
|
142
|
-
expect(IntegerStreamDecoder.decodeLengthStreamToOffsetBuffer).toHaveBeenCalled();
|
|
143
|
-
expect(result).toBeDefined();
|
|
144
|
-
expect(Array.isArray(result)).toBe(true);
|
|
145
|
-
});
|
|
146
|
-
it("should decode LENGTH stream for symbol offset buffer", () => {
|
|
147
|
-
const lengthLogicalType = { lengthType: LengthType.SYMBOL };
|
|
148
|
-
const streamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 20);
|
|
149
|
-
const dataLogicalType = { dictionaryType: DictionaryType.SHARED };
|
|
150
|
-
const dataStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dataLogicalType, 50);
|
|
151
|
-
const expectedOffsetBuffer = new Int32Array([0, 3, 6, 9]);
|
|
152
|
-
setupStreamMetadataDecodeMock([streamMetadata, dataStreamMetadata]);
|
|
153
|
-
setupLengthStreamDecodeMock(expectedOffsetBuffer, streamMetadata);
|
|
154
|
-
setupVarintDecodeMock(0);
|
|
155
|
-
const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, numFeatures);
|
|
156
|
-
expect(result).toBeDefined();
|
|
133
|
+
});
|
|
134
|
+
describe("decodeString - Empty Column Edge Cases", () => {
|
|
135
|
+
it("should handle empty column with numStreams = 0 (returns null)", () => {
|
|
136
|
+
const fullStream = new Uint8Array([]);
|
|
137
|
+
const offset = new IntWrapper(0);
|
|
138
|
+
const result = decodeString("testColumn", fullStream, offset, 0);
|
|
139
|
+
expect(result).toBeNull();
|
|
140
|
+
});
|
|
141
|
+
it("should handle column with all zero-length streams (returns null)", () => {
|
|
142
|
+
const emptyStream = createStream(PhysicalStreamType.LENGTH, new Uint8Array([]), {
|
|
143
|
+
logical: new LogicalStreamType(undefined, undefined, LengthType.VAR_BINARY),
|
|
157
144
|
});
|
|
145
|
+
const offset = new IntWrapper(0);
|
|
146
|
+
const result = decodeString("testColumn", emptyStream, offset, 1);
|
|
147
|
+
expect(result).toBeNull();
|
|
158
148
|
});
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
149
|
+
it("should handle single value plain string column", () => {
|
|
150
|
+
const strings = ["single"];
|
|
151
|
+
const encodedStrings = encodePlainStrings(strings);
|
|
152
|
+
const offset = new IntWrapper(0);
|
|
153
|
+
const result = decodeString("testColumn", encodedStrings, offset, 2);
|
|
154
|
+
expect(result).toBeInstanceOf(StringFlatVector);
|
|
155
|
+
expect(result.getValue(0)).toBe("single");
|
|
156
|
+
});
|
|
157
|
+
it("should handle single null value in plain string column (returns null)", () => {
|
|
158
|
+
const strings = [null];
|
|
159
|
+
const encodedStrings = encodePlainStrings(strings);
|
|
160
|
+
const offset = new IntWrapper(0);
|
|
161
|
+
const result = decodeString("testColumn", encodedStrings, offset, 3);
|
|
162
|
+
expect(result).toBeNull();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe("decodeString - Integration Tests", () => {
|
|
166
|
+
it("should correctly track offset through multiple streams", () => {
|
|
167
|
+
const strings = ["hello", "world"];
|
|
168
|
+
const encodedStrings = encodePlainStrings(strings);
|
|
169
|
+
const offset = new IntWrapper(0);
|
|
170
|
+
const initialOffset = offset.get();
|
|
171
|
+
const result = decodeString("testColumn", encodedStrings, offset, 2);
|
|
172
|
+
expect(result).toBeInstanceOf(StringFlatVector);
|
|
173
|
+
expect(offset.get()).toBeGreaterThan(initialOffset);
|
|
174
|
+
expect(offset.get()).toBe(encodedStrings.length);
|
|
175
|
+
});
|
|
176
|
+
it("should correctly track offset through nullable streams", () => {
|
|
177
|
+
const strings = ["test", null, "encodedStrings"];
|
|
178
|
+
const encodedStrings = encodePlainStrings(strings);
|
|
179
|
+
const offset = new IntWrapper(0);
|
|
180
|
+
const initialOffset = offset.get();
|
|
181
|
+
const result = decodeString("testColumn", encodedStrings, offset, 3);
|
|
182
|
+
expect(result).not.toBeNull();
|
|
183
|
+
expect(offset.get()).toBeGreaterThan(initialOffset);
|
|
184
|
+
expect(offset.get()).toBe(encodedStrings.length);
|
|
185
|
+
});
|
|
186
|
+
it("should correctly track offset through FSST dictionary streams", () => {
|
|
187
|
+
const encodedStrings = encodeFsstStrings();
|
|
188
|
+
const offset = new IntWrapper(0);
|
|
189
|
+
const initialOffset = offset.get();
|
|
190
|
+
const result = decodeString("testColumn", encodedStrings, offset, 6);
|
|
191
|
+
expect(result).toBeInstanceOf(StringFsstDictionaryVector);
|
|
192
|
+
expect(offset.get()).toBeGreaterThan(initialOffset);
|
|
193
|
+
expect(offset.get()).toBe(encodedStrings.length);
|
|
194
|
+
});
|
|
195
|
+
it("should handle consecutive decoding operations with shared offset tracker", () => {
|
|
196
|
+
const stream1 = encodePlainStrings(["first"]);
|
|
197
|
+
const stream2 = encodePlainStrings(["second"]);
|
|
198
|
+
const combinedStream = concatenateBuffers(stream1, stream2);
|
|
199
|
+
const offset = new IntWrapper(0);
|
|
200
|
+
const result1 = decodeString("column1", combinedStream, offset, 2);
|
|
201
|
+
expect(result1.getValue(0)).toBe("first");
|
|
202
|
+
const offsetAfterFirst = offset.get();
|
|
203
|
+
const result2 = decodeString("column2", combinedStream, offset, 2);
|
|
204
|
+
expect(result2.getValue(0)).toBe("second");
|
|
205
|
+
expect(offset.get()).toBeGreaterThan(offsetAfterFirst);
|
|
206
|
+
expect(offset.get()).toBe(combinedStream.length);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
describe("decodeSharedDictionary", () => {
|
|
210
|
+
describe("basic functionality", () => {
|
|
211
|
+
it("should decode single field with shared dictionary", () => {
|
|
212
|
+
const dictionaryStrings = ["apple", "banana", "peach", "date"];
|
|
213
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
214
|
+
const fieldStreams = encodeStructField([0, 1, 2, 3], [true, true, true, true]);
|
|
215
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);
|
|
216
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("address", [{ name: "street" }]);
|
|
217
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 4);
|
|
218
|
+
expect(result).toHaveLength(1);
|
|
219
|
+
expect(result[0]).toBeInstanceOf(StringDictionaryVector);
|
|
220
|
+
expect(result[0].name).toBe("address:street");
|
|
221
|
+
for (let i = 0; i < dictionaryStrings.length; i++) {
|
|
222
|
+
expect(result[0].getValue(i)).toBe(dictionaryStrings[i]);
|
|
223
|
+
}
|
|
172
224
|
});
|
|
173
|
-
it("should
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
expect(result).toBeDefined();
|
|
225
|
+
it("should handle empty child field name (common prefix stripped)", () => {
|
|
226
|
+
const dictionaryStrings = ["Berlin", "London", "Paris"];
|
|
227
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
228
|
+
const fieldStreams = encodeStructField([0, 1, 2], [true, true, true]);
|
|
229
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);
|
|
230
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("name", [{ name: "" }]);
|
|
231
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 3);
|
|
232
|
+
expect(result).toHaveLength(1);
|
|
233
|
+
expect(result[0].name).toBe("name");
|
|
234
|
+
for (let i = 0; i < dictionaryStrings.length; i++) {
|
|
235
|
+
expect(result[0].getValue(i)).toBe(dictionaryStrings[i]);
|
|
236
|
+
}
|
|
186
237
|
});
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
const
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const result =
|
|
200
|
-
expect(
|
|
201
|
-
expect(
|
|
202
|
-
expect(
|
|
203
|
-
expect(result).
|
|
238
|
+
it("should handle mix of empty and delimited child names", () => {
|
|
239
|
+
const dict = ["value1", "value2", "value3"];
|
|
240
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dict);
|
|
241
|
+
const field1 = encodeStructField([0], [true]);
|
|
242
|
+
const field2 = encodeStructField([1], [true]);
|
|
243
|
+
const field3 = encodeStructField([2], [true]);
|
|
244
|
+
const complete = concatenateBuffers(lengthStream, dataStream, field1, field2, field3);
|
|
245
|
+
const metadata = createColumnMetadataForStruct("name", [
|
|
246
|
+
{ name: "" },
|
|
247
|
+
{ name: ":en" },
|
|
248
|
+
{ name: ":de" },
|
|
249
|
+
]);
|
|
250
|
+
const result = decodeSharedDictionary(complete, new IntWrapper(0), metadata, 1);
|
|
251
|
+
expect(result).toHaveLength(3);
|
|
252
|
+
expect(result[0].name).toBe("name");
|
|
253
|
+
expect(result[1].name).toBe("name:en");
|
|
254
|
+
expect(result[2].name).toBe("name:de");
|
|
204
255
|
});
|
|
205
256
|
});
|
|
206
|
-
describe("
|
|
207
|
-
it("should
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, numFeatures, propertyColumnNames);
|
|
220
|
-
expect(result).toBeDefined();
|
|
257
|
+
describe("nullability", () => {
|
|
258
|
+
it("should handle nullable fields with PRESENT stream", () => {
|
|
259
|
+
const dictionaryStrings = ["red", "green", "blue"];
|
|
260
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
261
|
+
const fieldStreams = encodeStructField([0, 2], [true, false, true, false]);
|
|
262
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);
|
|
263
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("colors", [{ name: "primary" }]);
|
|
264
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 4);
|
|
265
|
+
expect(result).toHaveLength(1);
|
|
266
|
+
const expected = ["red", null, "blue", null];
|
|
267
|
+
for (let i = 0; i < expected.length; i++) {
|
|
268
|
+
expect(result[0].getValue(i)).toBe(expected[i]);
|
|
269
|
+
}
|
|
221
270
|
});
|
|
222
|
-
it("should
|
|
223
|
-
const
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, numFeatures, propertyColumnNames);
|
|
236
|
-
expect(result).toBeDefined();
|
|
271
|
+
it("should detect nullable fields when offsetCount < numFeatures", () => {
|
|
272
|
+
const dictionaryStrings = ["alpha", "beta"];
|
|
273
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
274
|
+
// Simulating implicit nullability by mismatched counts
|
|
275
|
+
const fieldStreams = encodeStructField([0, 1], [true, false, false, true]);
|
|
276
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);
|
|
277
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("greek", [{ name: "letter" }]);
|
|
278
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 4);
|
|
279
|
+
expect(result).toHaveLength(1);
|
|
280
|
+
const expected = ["alpha", null, null, "beta"];
|
|
281
|
+
for (let i = 0; i < expected.length; i++) {
|
|
282
|
+
expect(result[0].getValue(i)).toBe(expected[i]);
|
|
283
|
+
}
|
|
237
284
|
});
|
|
238
285
|
});
|
|
239
|
-
describe("
|
|
240
|
-
it("should
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
expect(mockOffset.add).toHaveBeenCalledWith(100);
|
|
286
|
+
describe("FSST encoding", () => {
|
|
287
|
+
it("should decode FSST-compressed shared dictionary", () => {
|
|
288
|
+
const dictionaryStrings = ["compressed1", "compressed2"];
|
|
289
|
+
const { lengthStream, dataStream, symbolLengthStream, symbolDataStream } = encodeSharedDictionary(dictionaryStrings, { useFsst: true });
|
|
290
|
+
const fieldStreams = encodeStructField([0, 1], [true, true]);
|
|
291
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, symbolLengthStream, symbolDataStream, dataStream, fieldStreams);
|
|
292
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("encodedStrings", [{ name: "value" }]);
|
|
293
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 2);
|
|
294
|
+
expect(result).toHaveLength(1);
|
|
295
|
+
expect(result[0]).toBeInstanceOf(StringFsstDictionaryVector);
|
|
296
|
+
expect(result[0].name).toBe("encodedStrings:value");
|
|
251
297
|
});
|
|
252
298
|
});
|
|
253
|
-
describe("
|
|
254
|
-
it("should
|
|
255
|
-
const
|
|
256
|
-
const
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
299
|
+
describe("field filtering", () => {
|
|
300
|
+
it("should filter fields by propertyColumnNames", () => {
|
|
301
|
+
const dictionaryStrings = ["val1", "val2"];
|
|
302
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
303
|
+
const field1Streams = encodeStructField([0], [true]);
|
|
304
|
+
const field2Streams = encodeStructField([1], [true]);
|
|
305
|
+
const field3Streams = encodeStructField([0], [true]);
|
|
306
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, field1Streams, field2Streams, field3Streams);
|
|
307
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("multi", [
|
|
308
|
+
{ name: "field1" },
|
|
309
|
+
{ name: "field2" },
|
|
310
|
+
{ name: "field3" },
|
|
311
|
+
]);
|
|
312
|
+
const propertyColumnNames = new Set(["multi:field1", "multi:field3"]);
|
|
313
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 1, propertyColumnNames);
|
|
314
|
+
expect(result).toHaveLength(2);
|
|
315
|
+
expect(result[0].name).toBe("multi:field1");
|
|
316
|
+
expect(result[1].name).toBe("multi:field3");
|
|
265
317
|
});
|
|
266
|
-
it("should
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
const
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const result =
|
|
279
|
-
expect(result).
|
|
318
|
+
it("should skip fields with numStreams=0", () => {
|
|
319
|
+
const dictionaryStrings = ["present"];
|
|
320
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
321
|
+
const field1Streams = encodeStructField([0], [true], true);
|
|
322
|
+
const field2Streams = encodeStructField([], [], false); // numStreams=0
|
|
323
|
+
const field3Streams = encodeStructField([0], [true], true);
|
|
324
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, field1Streams, field2Streams, field3Streams);
|
|
325
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("test", [
|
|
326
|
+
{ name: "field1" },
|
|
327
|
+
{ name: "field2" },
|
|
328
|
+
{ name: "field3" },
|
|
329
|
+
]);
|
|
330
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 1);
|
|
331
|
+
expect(result).toHaveLength(2);
|
|
332
|
+
expect(result[0].name).toBe("test:field1");
|
|
333
|
+
expect(result[1].name).toBe("test:field3");
|
|
280
334
|
});
|
|
281
|
-
it("should handle
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
const
|
|
285
|
-
const
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
335
|
+
it("should handle mixed present and filtered fields", () => {
|
|
336
|
+
const dictionaryStrings = ["encodedStrings"];
|
|
337
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
338
|
+
const field1Streams = encodeStructField([0], [true], true);
|
|
339
|
+
const field2Streams = encodeStructField([], [], false);
|
|
340
|
+
const field3Streams = encodeStructField([0], [true], true);
|
|
341
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, field1Streams, field2Streams, field3Streams);
|
|
342
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("mixed", [
|
|
343
|
+
{ name: "field1" },
|
|
344
|
+
{ name: "field2" },
|
|
345
|
+
{ name: "field3" },
|
|
346
|
+
]);
|
|
347
|
+
const propertyColumnNames = new Set(["mixed:field3"]);
|
|
348
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 1, propertyColumnNames);
|
|
349
|
+
expect(result).toHaveLength(1);
|
|
350
|
+
expect(result[0].name).toBe("mixed:field3");
|
|
294
351
|
});
|
|
295
352
|
});
|
|
296
|
-
describe("
|
|
297
|
-
it("should
|
|
298
|
-
const
|
|
299
|
-
const
|
|
300
|
-
const
|
|
301
|
-
const
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
setupVarintDecodeMock(0);
|
|
306
|
-
const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, numFeatures);
|
|
307
|
-
expect(result).toBeDefined();
|
|
308
|
-
});
|
|
309
|
-
it("should throw error for non-string scalar fields", () => {
|
|
310
|
-
const childFieldNonString = createMockChildField("fieldName", "scalarField", ScalarType.INT_32);
|
|
311
|
-
const columnWithNonStringField = createMockColumn("testColumn", [childFieldNonString]);
|
|
312
|
-
const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
|
|
313
|
-
const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 20);
|
|
314
|
-
const dictionaryLogicalType = { dictionaryType: DictionaryType.SHARED };
|
|
315
|
-
const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 40);
|
|
316
|
-
const skipStream1 = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 15);
|
|
317
|
-
const skipStream2 = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 25);
|
|
318
|
-
const offsetBuffer = new Int32Array([0, 10, 20, 40]);
|
|
319
|
-
setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata, skipStream1, skipStream2]);
|
|
320
|
-
setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
|
|
321
|
-
setupVarintDecodeMock([2, 2]); // 2 streams in field, 2 streams to skip
|
|
353
|
+
describe("error handling", () => {
|
|
354
|
+
it("should throw error for non-string field types", () => {
|
|
355
|
+
const dictionaryStrings = ["value"];
|
|
356
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
357
|
+
const fieldStreams = encodeStructField([0], [true]);
|
|
358
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);
|
|
359
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("invalid", [
|
|
360
|
+
{ name: "field1", type: ScalarType.INT_32 },
|
|
361
|
+
]);
|
|
322
362
|
expect(() => {
|
|
323
|
-
|
|
363
|
+
decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 1);
|
|
324
364
|
}).toThrow("Currently only optional string fields are implemented for a struct.");
|
|
325
365
|
});
|
|
326
366
|
});
|
|
327
|
-
describe("
|
|
328
|
-
it("should
|
|
329
|
-
const
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 40);
|
|
345
|
-
const offsetBuffer = new Int32Array([0, 10, 20, 40]);
|
|
346
|
-
setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata]);
|
|
347
|
-
setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
|
|
348
|
-
setupVarintDecodeMock(0);
|
|
349
|
-
const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, numFeatures);
|
|
350
|
-
expect(result).not.toBeNull();
|
|
351
|
-
expect(result).not.toBeUndefined();
|
|
367
|
+
describe("offset tracking", () => {
|
|
368
|
+
it("should correctly advance offset through all streams", () => {
|
|
369
|
+
const dictionaryStrings = ["a", "b", "c"];
|
|
370
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
371
|
+
const field1Streams = encodeStructField([0, 1], [true, true]);
|
|
372
|
+
const field2Streams = encodeStructField([1, 2], [true, true]);
|
|
373
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, field1Streams, field2Streams);
|
|
374
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("track", [
|
|
375
|
+
{ name: "field1" },
|
|
376
|
+
{ name: "field2" },
|
|
377
|
+
]);
|
|
378
|
+
const offset = new IntWrapper(0);
|
|
379
|
+
const initialOffset = offset.get();
|
|
380
|
+
const result = decodeSharedDictionary(completeencodedStrings, offset, columnMetaencodedStrings, 2);
|
|
381
|
+
expect(result).toHaveLength(2);
|
|
382
|
+
expect(offset.get()).toBeGreaterThan(initialOffset);
|
|
383
|
+
expect(offset.get()).toBe(completeencodedStrings.length);
|
|
352
384
|
});
|
|
353
385
|
});
|
|
354
386
|
});
|