@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.
Files changed (96) hide show
  1. package/dist/decoding/decodingTestUtils.d.ts +75 -0
  2. package/dist/decoding/decodingTestUtils.js +285 -0
  3. package/dist/decoding/decodingTestUtils.js.map +1 -0
  4. package/dist/decoding/decodingUtils.d.ts +5 -8
  5. package/dist/decoding/decodingUtils.js +22 -49
  6. package/dist/decoding/decodingUtils.js.map +1 -1
  7. package/dist/decoding/decodingUtils.spec.js +85 -69
  8. package/dist/decoding/decodingUtils.spec.js.map +1 -1
  9. package/dist/decoding/fsstDecoder.spec.js +52 -35
  10. package/dist/decoding/fsstDecoder.spec.js.map +1 -1
  11. package/dist/decoding/geometryDecoder.js +41 -30
  12. package/dist/decoding/geometryDecoder.js.map +1 -1
  13. package/dist/decoding/integerDecodingUtils.d.ts +18 -31
  14. package/dist/decoding/integerDecodingUtils.js +134 -299
  15. package/dist/decoding/integerDecodingUtils.js.map +1 -1
  16. package/dist/decoding/integerDecodingUtils.spec.js +254 -148
  17. package/dist/decoding/integerDecodingUtils.spec.js.map +1 -1
  18. package/dist/decoding/integerStreamDecoder.d.ts +4 -6
  19. package/dist/decoding/integerStreamDecoder.js +104 -122
  20. package/dist/decoding/integerStreamDecoder.js.map +1 -1
  21. package/dist/decoding/integerStreamDecoder.spec.js +370 -131
  22. package/dist/decoding/integerStreamDecoder.spec.js.map +1 -1
  23. package/dist/decoding/propertyDecoder.js +13 -23
  24. package/dist/decoding/propertyDecoder.js.map +1 -1
  25. package/dist/decoding/propertyDecoder.spec.js +397 -608
  26. package/dist/decoding/propertyDecoder.spec.js.map +1 -1
  27. package/dist/decoding/stringDecoder.js +5 -9
  28. package/dist/decoding/stringDecoder.js.map +1 -1
  29. package/dist/decoding/stringDecoder.spec.js +322 -321
  30. package/dist/decoding/stringDecoder.spec.js.map +1 -1
  31. package/dist/decoding/unpackNullableUtils.d.ts +25 -0
  32. package/dist/decoding/unpackNullableUtils.js +51 -0
  33. package/dist/decoding/unpackNullableUtils.js.map +1 -0
  34. package/dist/decoding/unpackNullableUtils.spec.js +71 -0
  35. package/dist/decoding/unpackNullableUtils.spec.js.map +1 -0
  36. package/dist/encoding/embeddedTilesetMetadataEncoder.d.ts +16 -0
  37. package/dist/encoding/embeddedTilesetMetadataEncoder.js +40 -0
  38. package/dist/encoding/embeddedTilesetMetadataEncoder.js.map +1 -0
  39. package/dist/encoding/encodingUtils.d.ts +7 -0
  40. package/dist/encoding/encodingUtils.js +107 -0
  41. package/dist/encoding/encodingUtils.js.map +1 -0
  42. package/dist/encoding/fsstEncoder.d.ts +21 -0
  43. package/dist/encoding/fsstEncoder.js +78 -0
  44. package/dist/encoding/fsstEncoder.js.map +1 -0
  45. package/dist/encoding/integerEncodingUtils.d.ts +68 -0
  46. package/dist/encoding/integerEncodingUtils.js +655 -0
  47. package/dist/encoding/integerEncodingUtils.js.map +1 -0
  48. package/dist/encoding/integerStreamEncoder.d.ts +27 -0
  49. package/dist/encoding/integerStreamEncoder.js +139 -0
  50. package/dist/encoding/integerStreamEncoder.js.map +1 -0
  51. package/dist/encoding/packNullableUtils.d.ts +4 -0
  52. package/dist/encoding/packNullableUtils.js +55 -0
  53. package/dist/encoding/packNullableUtils.js.map +1 -0
  54. package/dist/encoding/propertyEncoder.d.ts +78 -0
  55. package/dist/encoding/propertyEncoder.js +335 -0
  56. package/dist/encoding/propertyEncoder.js.map +1 -0
  57. package/dist/encoding/stringEncoder.d.ts +12 -0
  58. package/dist/encoding/stringEncoder.js +182 -0
  59. package/dist/encoding/stringEncoder.js.map +1 -0
  60. package/dist/encoding/zOrderCurveEncoder.d.ts +1 -0
  61. package/dist/encoding/zOrderCurveEncoder.js +10 -0
  62. package/dist/encoding/zOrderCurveEncoder.js.map +1 -0
  63. package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.d.ts +5 -1
  64. package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.js +29 -41
  65. package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.js.map +1 -1
  66. package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.spec.d.ts +1 -0
  67. package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.spec.js +142 -0
  68. package/dist/metadata/tileset/embeddedTilesetMetadataDecoder.spec.js.map +1 -0
  69. package/dist/mltDecoder.js +1 -2
  70. package/dist/mltDecoder.js.map +1 -1
  71. package/dist/vector/dictionary/stringDictionaryVector.d.ts +1 -1
  72. package/dist/vector/dictionary/stringDictionaryVector.js.map +1 -1
  73. package/dist/vector/flat/stringFlatVector.d.ts +1 -1
  74. package/dist/vector/flat/stringFlatVector.js.map +1 -1
  75. package/dist/vector/fsst-dictionary/stringFsstDictionaryVector.d.ts +1 -1
  76. package/dist/vector/fsst-dictionary/stringFsstDictionaryVector.js.map +1 -1
  77. package/dist/vector/fsst-dictionary/stringFsstDictionaryVector.spec.js +2 -2
  78. package/dist/vector/fsst-dictionary/stringFsstDictionaryVector.spec.js.map +1 -1
  79. package/dist/vector/geometry/constGpuVector.d.ts +2 -2
  80. package/dist/vector/geometry/constGpuVector.js.map +1 -1
  81. package/dist/vector/geometry/flatGpuVector.d.ts +2 -2
  82. package/dist/vector/geometry/flatGpuVector.js.map +1 -1
  83. package/dist/vector/geometry/gpuVector.d.ts +2 -2
  84. package/dist/vector/geometry/gpuVector.js.map +1 -1
  85. package/dist/vector/geometry/topologyVector.d.ts +4 -4
  86. package/dist/vector/geometry/topologyVector.js +0 -1
  87. package/dist/vector/geometry/topologyVector.js.map +1 -1
  88. package/dist/vector/geometry/zOrderCurve.spec.js +17 -11
  89. package/dist/vector/geometry/zOrderCurve.spec.js.map +1 -1
  90. package/dist/vector/variableSizeVector.d.ts +2 -2
  91. package/dist/vector/variableSizeVector.js +0 -1
  92. package/dist/vector/variableSizeVector.js.map +1 -1
  93. package/package.json +6 -8
  94. package/dist/decoding/geometryDecoder.spec.js +0 -5
  95. package/dist/decoding/geometryDecoder.spec.js.map +0 -1
  96. /package/dist/decoding/{geometryDecoder.spec.d.ts → unpackNullableUtils.spec.d.ts} +0 -0
@@ -1,354 +1,355 @@
1
- import { describe, it, expect, beforeEach, vi } from "vitest";
2
- import * as IntegerStreamDecoder from "./integerStreamDecoder";
3
- import * as StreamMetadataDecoder from "../metadata/tile/streamMetadataDecoder";
4
- import { LengthType } from "../metadata/tile/lengthType";
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 { DictionaryType } from "../metadata/tile/dictionaryType";
7
- import { ScalarType } from "../metadata/tile/scalarType";
8
- import * as StringDecoder from "./stringDecoder";
9
- import * as integerDecoder from "./integerDecodingUtils";
10
- function createMockStreamMetadata(physicalStreamType, logicalStreamType, byteLength) {
11
- return {
12
- physicalStreamType,
13
- logicalStreamType,
14
- byteLength,
15
- _physicalStreamType: physicalStreamType,
16
- _logicalStreamType: logicalStreamType,
17
- _logicalLevelTechnique1: undefined,
18
- _logicalLevelTechnique2: undefined,
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
- function setupLengthStreamDecodeMock(offsetBuffer, streamMetadata) {
59
- vi.spyOn(IntegerStreamDecoder, "decodeLengthStreamToOffsetBuffer").mockImplementation((data, offset, metadata) => {
60
- offset.add(metadata.byteLength);
61
- return offsetBuffer;
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
- function setupVarintDecodeMock(value = 0) {
65
- const values = Array.isArray(value) ? value : [value];
66
- let callCount = 0;
67
- vi.spyOn(integerDecoder, "decodeVarintInt32").mockImplementation(() => {
68
- const result = new Int32Array([values[callCount] ?? 0]);
69
- callCount++;
70
- return result;
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
- describe("decodePlainStringVector", () => {
74
- it.skip("should return null when plainLengthStream is null", () => {
75
- const result = StringDecoder.decodePlainStringVector("test", null, new Uint8Array([1, 2, 3]), null, null);
76
- expect(result).toBeNull();
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.skip("should return null when plainDataStream is null", () => {
79
- const result = StringDecoder.decodePlainStringVector("test", new Int32Array([0, 3]), null, null, null);
80
- expect(result).toBeNull();
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
- it.skip("should return StringDictionaryVector when offsetStream exists (non-nullable)", () => {
83
- const plainLengthStream = new Int32Array([0, 3, 7]);
84
- const plainDataStream = new Uint8Array([97, 98, 99, 100, 101, 102, 103]);
85
- const offsetStream = new Int32Array([0, 1]);
86
- const result = StringDecoder.decodePlainStringVector("test", plainLengthStream, plainDataStream, offsetStream, null);
87
- expect(result).toBeDefined();
88
- expect(result.name).toBe("test");
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.skip("should return StringDictionaryVector when offsetStream exists (nullable)", () => {
91
- const plainLengthStream = new Int32Array([0, 3, 7]);
92
- const plainDataStream = new Uint8Array([97, 98, 99, 100, 101, 102, 103]);
93
- const offsetStream = new Int32Array([0, 1]);
94
- const nullabilityBuffer = { size: () => 2, get: (i) => true };
95
- const result = StringDecoder.decodePlainStringVector("test", plainLengthStream, plainDataStream, offsetStream, nullabilityBuffer);
96
- expect(result).toBeDefined();
97
- expect(result.name).toBe("test");
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.skip("should return StringDictionaryVector with sparse offset when nullability mismatch", () => {
100
- const plainLengthStream = new Int32Array([0, 3, 7]);
101
- const plainDataStream = new Uint8Array([97, 98, 99, 100, 101, 102, 103]);
102
- const nullabilityBuffer = {
103
- size: () => 3,
104
- get: (i) => i !== 1,
105
- };
106
- const result = StringDecoder.decodePlainStringVector("test", plainLengthStream, plainDataStream, null, nullabilityBuffer);
107
- expect(result).toBeDefined();
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.skip("should return StringFlatVector (non-nullable)", () => {
111
- const plainLengthStream = new Int32Array([0, 3, 7]);
112
- const plainDataStream = new Uint8Array([97, 98, 99, 100, 101, 102, 103]);
113
- const result = StringDecoder.decodePlainStringVector("test", plainLengthStream, plainDataStream, null, null);
114
- expect(result).toBeDefined();
115
- expect(result.name).toBe("test");
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("decodeSharedDictionary", () => {
119
- let mockData;
120
- let mockOffset;
121
- let mockColumn;
122
- let numFeatures;
123
- beforeEach(() => {
124
- mockData = new Uint8Array(256);
125
- mockOffset = setupOffsetMock();
126
- mockColumn = createMockColumn("testColumn", [createMockChildField()]);
127
- numFeatures = 10;
128
- vi.clearAllMocks();
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
- describe("basic dictionary stream decoding", () => {
131
- it("should decode LENGTH stream for dictionary offset buffer", () => {
132
- const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
133
- const streamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 20);
134
- const dataLogicalType = { dictionaryType: DictionaryType.SHARED };
135
- const dataStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dataLogicalType, 50);
136
- const expectedOffsetBuffer = new Int32Array([0, 5, 10, 15, 20]);
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
- describe("dictionary buffer decoding", () => {
160
- it("should decode SINGLE dictionary type DATA stream", () => {
161
- const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
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
- describe("symbol table buffer decoding", () => {
189
- it("should decode symbol table buffer when dictionary type is not SINGLE or SHARED", () => {
190
- const symbolTableLogicalType = { dictionaryType: DictionaryType.NONE };
191
- const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, symbolTableLogicalType, 20);
192
- const symbolTableMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, symbolTableLogicalType, 35);
193
- const dictionaryLogicalType = { dictionaryType: DictionaryType.SHARED };
194
- const dictionaryDataMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 50);
195
- const offsetBuffer = new Int32Array([0, 10, 20]);
196
- setupStreamMetadataDecodeMock([lengthStreamMetadata, symbolTableMetadata, dictionaryDataMetadata]);
197
- setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
198
- setupVarintDecodeMock(0);
199
- const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, numFeatures);
200
- expect(mockOffset.add).toHaveBeenNthCalledWith(1, 20);
201
- expect(mockOffset.add).toHaveBeenNthCalledWith(2, 35);
202
- expect(mockOffset.add).toHaveBeenNthCalledWith(3, 50);
203
- expect(result).toBeDefined();
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("with propertyColumnNames filter", () => {
207
- it("should accept optional propertyColumnNames parameter", () => {
208
- const propertyColumnNames = new Set(["testColumn"]);
209
- const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
210
- const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 20);
211
- const dictionaryLogicalType = { dictionaryType: DictionaryType.SHARED };
212
- const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 40);
213
- const skipStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 15);
214
- const offsetBuffer = new Int32Array([0, 10, 20, 40]);
215
- // Provide metadata for: LENGTH, DATA (for dictionary), DATA (for skipColumn)
216
- setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata, skipStreamMetadata]);
217
- setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
218
- setupVarintDecodeMock([1, 1]); // First field has 1 stream, then 1 more stream to skip
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 skip column when propertyColumnNames does not include column", () => {
223
- const propertyColumnNames = new Set(["someOtherColumn"]);
224
- const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
225
- const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 20);
226
- const dictionaryLogicalType = { dictionaryType: DictionaryType.SHARED };
227
- const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 40);
228
- const skipStream1 = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 15);
229
- const skipStream2 = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 25);
230
- const offsetBuffer = new Int32Array([0, 10, 20, 40]);
231
- // Provide metadata for: LENGTH, DATA (for dictionary), and 2 streams to skip
232
- setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata, skipStream1, skipStream2]);
233
- setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
234
- setupVarintDecodeMock([2, 2]); // 2 streams in first field, 2 streams to skip
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("offset management", () => {
240
- it("should correctly advance offset through multiple streams", () => {
241
- const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
242
- const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 20);
243
- const dictionaryLogicalType = { dictionaryType: DictionaryType.SHARED };
244
- const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 100);
245
- const offsetBuffer = new Int32Array([0, 25, 50, 75, 100]);
246
- setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata]);
247
- setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
248
- setupVarintDecodeMock(0);
249
- StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, numFeatures);
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("edge cases", () => {
254
- it("should handle minimum feature count", () => {
255
- const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
256
- const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 4);
257
- const dictionaryLogicalType = { dictionaryType: DictionaryType.SHARED };
258
- const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 10);
259
- const offsetBuffer = new Int32Array([0, 10]);
260
- setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata]);
261
- setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
262
- setupVarintDecodeMock(0);
263
- const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, 1);
264
- expect(result).toBeDefined();
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 handle large feature count", () => {
267
- const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
268
- const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 1000);
269
- const dictionaryLogicalType = { dictionaryType: DictionaryType.SHARED };
270
- const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 5000);
271
- const largeOffsetBuffer = new Int32Array(10001);
272
- for (let i = 0; i < largeOffsetBuffer.length; i++) {
273
- largeOffsetBuffer[i] = i * 500;
274
- }
275
- setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata]);
276
- setupLengthStreamDecodeMock(largeOffsetBuffer, lengthStreamMetadata);
277
- setupVarintDecodeMock(0);
278
- const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, 10000);
279
- expect(result).toBeDefined();
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 empty child fields list", () => {
282
- const emptyColumnMock = createMockColumn("emptyColumn", []);
283
- const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
284
- const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 0);
285
- const dictionaryLogicalType = { dictionaryType: DictionaryType.SHARED };
286
- const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 0);
287
- const offsetBuffer = new Int32Array([0]);
288
- setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata]);
289
- setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
290
- setupVarintDecodeMock(0);
291
- expect(() => {
292
- StringDecoder.decodeSharedDictionary(mockData, mockOffset, emptyColumnMock, 0);
293
- }).not.toThrow();
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("stream count handling", () => {
297
- it("should skip columns with 0 streams", () => {
298
- const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
299
- const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 20);
300
- const dictionaryLogicalType = { dictionaryType: DictionaryType.SHARED };
301
- const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 40);
302
- const offsetBuffer = new Int32Array([0, 10, 20, 40]);
303
- setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata]);
304
- setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
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
- StringDecoder.decodeSharedDictionary(mockData, mockOffset, columnWithNonStringField, numFeatures);
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("return value validation", () => {
328
- it("should return Vector array", () => {
329
- const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
330
- const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 20);
331
- const dictionaryLogicalType = { dictionaryType: DictionaryType.SHARED };
332
- const dictionaryStreamMetadata = createMockStreamMetadata(PhysicalStreamType.DATA, dictionaryLogicalType, 40);
333
- const offsetBuffer = new Int32Array([0, 10, 20, 40]);
334
- setupStreamMetadataDecodeMock([lengthStreamMetadata, dictionaryStreamMetadata]);
335
- setupLengthStreamDecodeMock(offsetBuffer, lengthStreamMetadata);
336
- setupVarintDecodeMock(0);
337
- const result = StringDecoder.decodeSharedDictionary(mockData, mockOffset, mockColumn, numFeatures);
338
- expect(result).toBeInstanceOf(Array);
339
- });
340
- it("should not return null or undefined", () => {
341
- const lengthLogicalType = { lengthType: LengthType.DICTIONARY };
342
- const lengthStreamMetadata = createMockStreamMetadata(PhysicalStreamType.LENGTH, lengthLogicalType, 20);
343
- const dictionaryLogicalType = { dictionaryType: DictionaryType.SHARED };
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
  });