@maplibre/mlt 1.1.2 → 1.1.4
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 +13 -23
- package/dist/decoding/propertyDecoder.js.map +1 -1
- package/dist/decoding/propertyDecoder.spec.js +397 -608
- package/dist/decoding/propertyDecoder.spec.js.map +1 -1
- package/dist/decoding/stringDecoder.js +5 -9
- package/dist/decoding/stringDecoder.js.map +1 -1
- package/dist/decoding/stringDecoder.spec.js +322 -321
- 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 +1 -2
- 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,355 @@
|
|
|
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
|
-
setupStreamMetadataDecodeMock([streamMetadata, dataStreamMetadata]);
|
|
138
|
-
setupLengthStreamDecodeMock(expectedOffsetBuffer, streamMetadata);
|
|
139
|
-
setupVarintDecodeMock(0);
|
|
140
|
-
const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, numFeatures);
|
|
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();
|
|
157
|
-
});
|
|
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();
|
|
158
140
|
});
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 20);
|
|
163
|
-
const dictionaryLogicalType = { dictionaryType: DictionaryType.SINGLE };
|
|
164
|
-
const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 40);
|
|
165
|
-
const offsetBuffer = new Int32Array([0, 10, 20, 40]);
|
|
166
|
-
setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata]);
|
|
167
|
-
setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
|
|
168
|
-
setupVarintDecodeMock(0);
|
|
169
|
-
const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, numFeatures);
|
|
170
|
-
expect(mockOffset.add).toHaveBeenCalledWith(40);
|
|
171
|
-
expect(result).toBeDefined();
|
|
172
|
-
});
|
|
173
|
-
it("should advance offset correctly through LENGTH and DATA streams", () => {
|
|
174
|
-
const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
|
|
175
|
-
const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 20);
|
|
176
|
-
const dictionaryLogicalType = { dictionaryType: DictionaryType.SINGLE };
|
|
177
|
-
const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 40);
|
|
178
|
-
const offsetBuffer = new Int32Array([0, 10, 20, 40]);
|
|
179
|
-
setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata]);
|
|
180
|
-
setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
|
|
181
|
-
setupVarintDecodeMock(0);
|
|
182
|
-
const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, numFeatures);
|
|
183
|
-
expect(mockOffset.add).toHaveBeenNthCalledWith(1, 20);
|
|
184
|
-
expect(mockOffset.add).toHaveBeenNthCalledWith(2, 40);
|
|
185
|
-
expect(result).toBeDefined();
|
|
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),
|
|
186
144
|
});
|
|
145
|
+
const offset = new IntWrapper(0);
|
|
146
|
+
const result = decodeString("testColumn", emptyStream, offset, 1);
|
|
147
|
+
expect(result).toBeNull();
|
|
148
|
+
});
|
|
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);
|
|
187
185
|
});
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
+
}
|
|
204
224
|
});
|
|
205
225
|
});
|
|
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();
|
|
226
|
+
describe("nullability", () => {
|
|
227
|
+
it("should handle nullable fields with PRESENT stream", () => {
|
|
228
|
+
const dictionaryStrings = ["red", "green", "blue"];
|
|
229
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
230
|
+
const fieldStreams = encodeStructField([0, 2], [true, false, true, false]);
|
|
231
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);
|
|
232
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("colors", [{ name: "primary" }]);
|
|
233
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 4);
|
|
234
|
+
expect(result).toHaveLength(1);
|
|
235
|
+
const expected = ["red", null, "blue", null];
|
|
236
|
+
for (let i = 0; i < expected.length; i++) {
|
|
237
|
+
expect(result[0].getValue(i)).toBe(expected[i]);
|
|
238
|
+
}
|
|
221
239
|
});
|
|
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();
|
|
240
|
+
it("should detect nullable fields when offsetCount < numFeatures", () => {
|
|
241
|
+
const dictionaryStrings = ["alpha", "beta"];
|
|
242
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
243
|
+
// Simulating implicit nullability by mismatched counts
|
|
244
|
+
const fieldStreams = encodeStructField([0, 1], [true, false, false, true]);
|
|
245
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);
|
|
246
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("greek", [{ name: "letter" }]);
|
|
247
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 4);
|
|
248
|
+
expect(result).toHaveLength(1);
|
|
249
|
+
const expected = ["alpha", null, null, "beta"];
|
|
250
|
+
for (let i = 0; i < expected.length; i++) {
|
|
251
|
+
expect(result[0].getValue(i)).toBe(expected[i]);
|
|
252
|
+
}
|
|
237
253
|
});
|
|
238
254
|
});
|
|
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);
|
|
255
|
+
describe("FSST encoding", () => {
|
|
256
|
+
it("should decode FSST-compressed shared dictionary", () => {
|
|
257
|
+
const dictionaryStrings = ["compressed1", "compressed2"];
|
|
258
|
+
const { lengthStream, dataStream, symbolLengthStream, symbolDataStream } = encodeSharedDictionary(dictionaryStrings, { useFsst: true });
|
|
259
|
+
const fieldStreams = encodeStructField([0, 1], [true, true]);
|
|
260
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, symbolLengthStream, symbolDataStream, dataStream, fieldStreams);
|
|
261
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("encodedStrings", [{ name: "value" }]);
|
|
262
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 2);
|
|
263
|
+
expect(result).toHaveLength(1);
|
|
264
|
+
expect(result[0]).toBeInstanceOf(StringFsstDictionaryVector);
|
|
265
|
+
expect(result[0].name).toBe("encodedStrings:value");
|
|
251
266
|
});
|
|
252
267
|
});
|
|
253
|
-
describe("
|
|
254
|
-
it("should
|
|
255
|
-
const
|
|
256
|
-
const
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
268
|
+
describe("field filtering", () => {
|
|
269
|
+
it("should filter fields by propertyColumnNames", () => {
|
|
270
|
+
const dictionaryStrings = ["val1", "val2"];
|
|
271
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
272
|
+
const field1Streams = encodeStructField([0], [true]);
|
|
273
|
+
const field2Streams = encodeStructField([1], [true]);
|
|
274
|
+
const field3Streams = encodeStructField([0], [true]);
|
|
275
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, field1Streams, field2Streams, field3Streams);
|
|
276
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("multi", [
|
|
277
|
+
{ name: "field1" },
|
|
278
|
+
{ name: "field2" },
|
|
279
|
+
{ name: "field3" },
|
|
280
|
+
]);
|
|
281
|
+
const propertyColumnNames = new Set(["multi:field1", "multi:field3"]);
|
|
282
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 1, propertyColumnNames);
|
|
283
|
+
expect(result).toHaveLength(2);
|
|
284
|
+
expect(result[0].name).toBe("multi:field1");
|
|
285
|
+
expect(result[1].name).toBe("multi:field3");
|
|
265
286
|
});
|
|
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).
|
|
287
|
+
it("should skip fields with numStreams=0", () => {
|
|
288
|
+
const dictionaryStrings = ["present"];
|
|
289
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
290
|
+
const field1Streams = encodeStructField([0], [true], true);
|
|
291
|
+
const field2Streams = encodeStructField([], [], false); // numStreams=0
|
|
292
|
+
const field3Streams = encodeStructField([0], [true], true);
|
|
293
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, field1Streams, field2Streams, field3Streams);
|
|
294
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("test", [
|
|
295
|
+
{ name: "field1" },
|
|
296
|
+
{ name: "field2" },
|
|
297
|
+
{ name: "field3" },
|
|
298
|
+
]);
|
|
299
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 1);
|
|
300
|
+
expect(result).toHaveLength(2);
|
|
301
|
+
expect(result[0].name).toBe("test:field1");
|
|
302
|
+
expect(result[1].name).toBe("test:field3");
|
|
280
303
|
});
|
|
281
|
-
it("should handle
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
const
|
|
285
|
-
const
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
304
|
+
it("should handle mixed present and filtered fields", () => {
|
|
305
|
+
const dictionaryStrings = ["encodedStrings"];
|
|
306
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
307
|
+
const field1Streams = encodeStructField([0], [true], true);
|
|
308
|
+
const field2Streams = encodeStructField([], [], false);
|
|
309
|
+
const field3Streams = encodeStructField([0], [true], true);
|
|
310
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, field1Streams, field2Streams, field3Streams);
|
|
311
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("mixed", [
|
|
312
|
+
{ name: "field1" },
|
|
313
|
+
{ name: "field2" },
|
|
314
|
+
{ name: "field3" },
|
|
315
|
+
]);
|
|
316
|
+
const propertyColumnNames = new Set(["mixed:field3"]);
|
|
317
|
+
const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 1, propertyColumnNames);
|
|
318
|
+
expect(result).toHaveLength(1);
|
|
319
|
+
expect(result[0].name).toBe("mixed:field3");
|
|
294
320
|
});
|
|
295
321
|
});
|
|
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
|
|
322
|
+
describe("error handling", () => {
|
|
323
|
+
it("should throw error for non-string field types", () => {
|
|
324
|
+
const dictionaryStrings = ["value"];
|
|
325
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
326
|
+
const fieldStreams = encodeStructField([0], [true]);
|
|
327
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);
|
|
328
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("invalid", [
|
|
329
|
+
{ name: "field1", type: ScalarType.INT_32 },
|
|
330
|
+
]);
|
|
322
331
|
expect(() => {
|
|
323
|
-
|
|
332
|
+
decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings, 1);
|
|
324
333
|
}).toThrow("Currently only optional string fields are implemented for a struct.");
|
|
325
334
|
});
|
|
326
335
|
});
|
|
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();
|
|
336
|
+
describe("offset tracking", () => {
|
|
337
|
+
it("should correctly advance offset through all streams", () => {
|
|
338
|
+
const dictionaryStrings = ["a", "b", "c"];
|
|
339
|
+
const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);
|
|
340
|
+
const field1Streams = encodeStructField([0, 1], [true, true]);
|
|
341
|
+
const field2Streams = encodeStructField([1, 2], [true, true]);
|
|
342
|
+
const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, field1Streams, field2Streams);
|
|
343
|
+
const columnMetaencodedStrings = createColumnMetadataForStruct("track", [
|
|
344
|
+
{ name: "field1" },
|
|
345
|
+
{ name: "field2" },
|
|
346
|
+
]);
|
|
347
|
+
const offset = new IntWrapper(0);
|
|
348
|
+
const initialOffset = offset.get();
|
|
349
|
+
const result = decodeSharedDictionary(completeencodedStrings, offset, columnMetaencodedStrings, 2);
|
|
350
|
+
expect(result).toHaveLength(2);
|
|
351
|
+
expect(offset.get()).toBeGreaterThan(initialOffset);
|
|
352
|
+
expect(offset.get()).toBe(completeencodedStrings.length);
|
|
352
353
|
});
|
|
353
354
|
});
|
|
354
355
|
});
|