qvdjs 2.2.2 → 2.2.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/README.md +14 -11
- package/dist/index.cjs +289 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +289 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,28 +42,31 @@ thing worth reading:
|
|
|
42
42
|
|
|
43
43
|
## Checked against Qlik Sense
|
|
44
44
|
|
|
45
|
-
qvdjs is compared with Qlik Sense itself, not only with its own reading of the format.
|
|
46
|
-
|
|
47
|
-
timestamps and NULLs - are defined twice, as Qlik
|
|
48
|
-
values a caller would write. Qlik Sense and qvdjs each
|
|
49
|
-
checked:
|
|
45
|
+
qvdjs is compared with Qlik Sense itself, not only with its own reading of the format. 48 fields in
|
|
46
|
+
8 datasets - integers, doubles, text (non-ASCII, empty and number-like included), booleans, dates,
|
|
47
|
+
timestamps, and NULLs beside numbers, text, dates and timestamps - are defined twice, as Qlik
|
|
48
|
+
load-script expressions and as the JavaScript values a caller would write. Qlik Sense and qvdjs each
|
|
49
|
+
write them to a QVD, and three things are checked:
|
|
50
50
|
|
|
51
51
|
- **qvdjs writes the content Qlik writes** - the same symbols, of the same types, with the same text,
|
|
52
52
|
on the same rows.
|
|
53
|
-
- **Given Qlik's tags and number formats, qvdjs writes them unchanged** - the bit layout
|
|
54
|
-
the only
|
|
53
|
+
- **Given Qlik's tags and number formats, qvdjs writes them unchanged** - the bit layout and a tag the
|
|
54
|
+
values contradict, below, are the only parts of a field's header left to differ.
|
|
55
55
|
- **Qlik reads qvdjs's file as it reads its own** - loaded into Qlik and stored again, with `Text()`,
|
|
56
56
|
`Num()`, `IsNum()`, `IsText()`, `IsNull()` and `Year()` the same on every row.
|
|
57
57
|
|
|
58
58
|
The live comparison runs on demand against a Qlik Sense server; its last run, against Qlik Sense
|
|
59
|
-
Enterprise on Windows build 50699, passed for all
|
|
59
|
+
Enterprise on Windows build 50699, passed for all 48 fields. Qlik's files from that run are kept in
|
|
60
60
|
the test suite, so **every CI run compares qvdjs's output with them again** - on Linux, Windows and
|
|
61
61
|
macOS, under Node 22, 24 and 26 - along with reference files Qlik wrote for booleans, dates and
|
|
62
62
|
timestamps.
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
where qvdjs uses the fewest
|
|
66
|
-
|
|
64
|
+
Three differences are by design. Qlik often gives a field more bits in the index than its values need,
|
|
65
|
+
where qvdjs uses the fewest. qvdjs adds no tags or number formats of its own - Qlik works tags out when
|
|
66
|
+
it loads a file, and keeps any the file carries. And qvdjs drops a tag the values contradict, even one
|
|
67
|
+
Qlik wrote: Qlik tags dates outside 1980 to 2080 as text when their field has no date format, although
|
|
68
|
+
its own `IsNum()` reads every one as a number. QVD is also QlikView's format, but QlikView is not part
|
|
69
|
+
of the comparison.
|
|
67
70
|
|
|
68
71
|
→ [Checked against Qlik Sense](https://qvdjs.ptarmiganlabs.com/v2.0/overview/checked-against-qlik-sense/)
|
|
69
72
|
|
package/dist/index.cjs
CHANGED
|
@@ -1201,6 +1201,212 @@ var init_bitUtils = __esm({
|
|
|
1201
1201
|
}
|
|
1202
1202
|
});
|
|
1203
1203
|
|
|
1204
|
+
// src/util/seenNumbers.js
|
|
1205
|
+
function firstJudgementRow(rows) {
|
|
1206
|
+
return Math.min(WINDOW_ROWS, Math.max(MIN_JUDGED_LOOKUPS, Math.floor(rows / 2)));
|
|
1207
|
+
}
|
|
1208
|
+
function slotOf(value, mask) {
|
|
1209
|
+
const product = value * MULTIPLIER;
|
|
1210
|
+
return product < EXACT && product > -EXACT ? product & mask : ((product | 0) ^ (product * UPPER_HALF | 0)) & mask;
|
|
1211
|
+
}
|
|
1212
|
+
function slotsFor(count, most) {
|
|
1213
|
+
let slots = MIN_SLOTS;
|
|
1214
|
+
while (slots < count && slots < most) {
|
|
1215
|
+
slots *= 2;
|
|
1216
|
+
}
|
|
1217
|
+
return slots;
|
|
1218
|
+
}
|
|
1219
|
+
function shareOf(columns) {
|
|
1220
|
+
let slots = MAX_SLOTS;
|
|
1221
|
+
while (slots > MIN_SLOTS && slots * columns > TOTAL_SLOTS) {
|
|
1222
|
+
slots /= 2;
|
|
1223
|
+
}
|
|
1224
|
+
return slots;
|
|
1225
|
+
}
|
|
1226
|
+
function symbolPassTable(rows, pool) {
|
|
1227
|
+
return pool.take(MIN_SLOTS) ? new SeenNumbers(MIN_SLOTS, slotsFor(rows, MAX_SLOTS), SYMBOL_PASS_MISS_LIMIT, pool) : null;
|
|
1228
|
+
}
|
|
1229
|
+
function numericCellsAfter(data, column, stopped, numbers) {
|
|
1230
|
+
const most = stopped.cells + data.length - stopped.row;
|
|
1231
|
+
if (!indexPassWants(numbers, most)) {
|
|
1232
|
+
return most;
|
|
1233
|
+
}
|
|
1234
|
+
let cells = stopped.cells;
|
|
1235
|
+
for (let row = stopped.row; row < data.length; row++) {
|
|
1236
|
+
if (typeof data[row]?.[column] === "number") {
|
|
1237
|
+
cells++;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
return cells;
|
|
1241
|
+
}
|
|
1242
|
+
function numbersIn(map) {
|
|
1243
|
+
let numbers = 0;
|
|
1244
|
+
for (const key of map.keys()) {
|
|
1245
|
+
if (typeof key === "number") {
|
|
1246
|
+
numbers++;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return numbers;
|
|
1250
|
+
}
|
|
1251
|
+
function indexPassWants(numbers, cells) {
|
|
1252
|
+
return numbers > 0 && numbers <= INDEX_PASS_MISS_LIMIT * cells;
|
|
1253
|
+
}
|
|
1254
|
+
function indexPassTable(numbers, cells, symbols, share, pool) {
|
|
1255
|
+
const slots = Math.min(slotsFor(numbers * SLOTS_PER_NUMBER, share), slotsFor(cells, share));
|
|
1256
|
+
return pool.take(slots) ? new SeenNumbers(slots, slots, INDEX_PASS_MISS_LIMIT - numbers / cells, pool, symbols) : null;
|
|
1257
|
+
}
|
|
1258
|
+
function endWindow(tables, row = 0, stopped = null) {
|
|
1259
|
+
for (let column = 0; column < tables.length; column++) {
|
|
1260
|
+
const table = tables[column];
|
|
1261
|
+
if (table === null || table === NO_TABLE_YET) {
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1264
|
+
const lookups = table.cells - table.judgedCells;
|
|
1265
|
+
if (lookups < MIN_JUDGED_LOOKUPS) {
|
|
1266
|
+
continue;
|
|
1267
|
+
}
|
|
1268
|
+
if (table.misses > (table.judged ? table.limit : FIRST_WINDOW_MISS_LIMIT) * lookups) {
|
|
1269
|
+
tables[column] = null;
|
|
1270
|
+
table.pool.give(table.values.length);
|
|
1271
|
+
if (stopped !== null) {
|
|
1272
|
+
stopped[column] = { row, cells: table.cells };
|
|
1273
|
+
}
|
|
1274
|
+
} else {
|
|
1275
|
+
table.judgedCells = table.cells;
|
|
1276
|
+
table.misses = 0;
|
|
1277
|
+
table.judged = true;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
var MULTIPLIER, EXACT, UPPER_HALF, WINDOW_ROWS, MIN_JUDGED_LOOKUPS, MIN_SLOTS, MAX_SLOTS, TOTAL_SLOTS, SLOTS_PER_NUMBER, FIRST_WINDOW_MISS_LIMIT, SYMBOL_PASS_MISS_LIMIT, INDEX_PASS_MISS_LIMIT, SlotPool, SeenNumbers, NO_TABLE_YET;
|
|
1282
|
+
var init_seenNumbers = __esm({
|
|
1283
|
+
"src/util/seenNumbers.js"() {
|
|
1284
|
+
MULTIPLIER = 2654435761;
|
|
1285
|
+
EXACT = 2 ** 53;
|
|
1286
|
+
UPPER_HALF = 2 ** -32;
|
|
1287
|
+
WINDOW_ROWS = 16384;
|
|
1288
|
+
MIN_JUDGED_LOOKUPS = 1024;
|
|
1289
|
+
__name(firstJudgementRow, "firstJudgementRow");
|
|
1290
|
+
MIN_SLOTS = 64;
|
|
1291
|
+
MAX_SLOTS = 65536;
|
|
1292
|
+
TOTAL_SLOTS = 2 ** 21;
|
|
1293
|
+
SLOTS_PER_NUMBER = 8;
|
|
1294
|
+
FIRST_WINDOW_MISS_LIMIT = 0.9;
|
|
1295
|
+
SYMBOL_PASS_MISS_LIMIT = 0.3;
|
|
1296
|
+
INDEX_PASS_MISS_LIMIT = 0.75;
|
|
1297
|
+
__name(slotOf, "slotOf");
|
|
1298
|
+
__name(slotsFor, "slotsFor");
|
|
1299
|
+
__name(shareOf, "shareOf");
|
|
1300
|
+
SlotPool = class {
|
|
1301
|
+
static {
|
|
1302
|
+
__name(this, "SlotPool");
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* @param {number} [slots] How many there are to give.
|
|
1306
|
+
*/
|
|
1307
|
+
constructor(slots = TOTAL_SLOTS) {
|
|
1308
|
+
this.left = slots;
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Takes slots, if there are that many left.
|
|
1312
|
+
*
|
|
1313
|
+
* @param {number} slots How many.
|
|
1314
|
+
* @return {boolean} Whether they were taken.
|
|
1315
|
+
*/
|
|
1316
|
+
take(slots) {
|
|
1317
|
+
if (slots > this.left) {
|
|
1318
|
+
return false;
|
|
1319
|
+
}
|
|
1320
|
+
this.left -= slots;
|
|
1321
|
+
return true;
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Gives slots back.
|
|
1325
|
+
*
|
|
1326
|
+
* @param {number} slots How many.
|
|
1327
|
+
*/
|
|
1328
|
+
give(slots) {
|
|
1329
|
+
this.left += slots;
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
SeenNumbers = class _SeenNumbers {
|
|
1333
|
+
static {
|
|
1334
|
+
__name(this, "SeenNumbers");
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* @param {number} slots How many slots: a power of two from `MIN_SLOTS` to `MAX_SLOTS`, already taken from
|
|
1338
|
+
* `pool`.
|
|
1339
|
+
* @param {number} maxSlots The most it may grow to, in the symbol table pass.
|
|
1340
|
+
* @param {number} limit The share of its lookups a judged window may miss.
|
|
1341
|
+
* @param {SlotPool} pool Where its slots came from, and where they go back when it is switched off.
|
|
1342
|
+
* @param {number} [symbols] In the index table pass, the column's symbols, one bit each. -1, the default, in
|
|
1343
|
+
* the symbol table pass.
|
|
1344
|
+
*/
|
|
1345
|
+
constructor(slots, maxSlots, limit, pool, symbols = -1) {
|
|
1346
|
+
this.values = new Float64Array(slots).fill(NaN);
|
|
1347
|
+
this.indices = new Int32Array(symbols >= 0 ? slots : 0);
|
|
1348
|
+
this.met = new Uint32Array(symbols >= 0 ? Math.ceil(symbols / 32) : 0);
|
|
1349
|
+
this.mask = slots - 1;
|
|
1350
|
+
this.maxSlots = maxSlots;
|
|
1351
|
+
this.limit = limit;
|
|
1352
|
+
this.pool = pool;
|
|
1353
|
+
this.cells = 0;
|
|
1354
|
+
this.judgedCells = 0;
|
|
1355
|
+
this.misses = 0;
|
|
1356
|
+
this.judged = symbols >= 0;
|
|
1357
|
+
this.inserted = 0;
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* Remembers a number the symbol table pass has just put in the map, growing the table instead once more
|
|
1361
|
+
* than an eighth of its slots would hold distinct numbers - if its pool has the slots. A table the pool
|
|
1362
|
+
* cannot grow stops trying, and keeps remembering in the slots it has.
|
|
1363
|
+
*
|
|
1364
|
+
* @param {number} slot The number's slot, from `slotOf`.
|
|
1365
|
+
* @param {number} value The number.
|
|
1366
|
+
* @return {SeenNumbers} The table to use from now on: this one, or a larger, empty one.
|
|
1367
|
+
*/
|
|
1368
|
+
remember(slot, value) {
|
|
1369
|
+
this.inserted++;
|
|
1370
|
+
if (this.inserted * SLOTS_PER_NUMBER > this.values.length && this.values.length < this.maxSlots) {
|
|
1371
|
+
const slots = Math.min(this.values.length * 4, this.maxSlots);
|
|
1372
|
+
if (this.pool.take(slots - this.values.length)) {
|
|
1373
|
+
const grown = new _SeenNumbers(slots, this.maxSlots, this.limit, this.pool);
|
|
1374
|
+
grown.inserted = this.inserted;
|
|
1375
|
+
grown.cells = this.cells;
|
|
1376
|
+
grown.judgedCells = this.judgedCells;
|
|
1377
|
+
grown.misses = this.misses;
|
|
1378
|
+
grown.judged = this.judged;
|
|
1379
|
+
return grown;
|
|
1380
|
+
}
|
|
1381
|
+
this.maxSlots = this.values.length;
|
|
1382
|
+
}
|
|
1383
|
+
this.values[slot] = value;
|
|
1384
|
+
return this;
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Marks a symbol met through this table, in the index table pass.
|
|
1388
|
+
*
|
|
1389
|
+
* @param {number} index The symbol the map answered.
|
|
1390
|
+
* @return {boolean} Whether it had been met before; false for a first sight.
|
|
1391
|
+
*/
|
|
1392
|
+
metBefore(index) {
|
|
1393
|
+
const word = index >>> 5;
|
|
1394
|
+
const bit = 1 << (index & 31);
|
|
1395
|
+
const before = (this.met[word] & bit) !== 0;
|
|
1396
|
+
this.met[word] |= bit;
|
|
1397
|
+
return before;
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
NO_TABLE_YET = new SeenNumbers(1, 1, 0, new SlotPool(1));
|
|
1401
|
+
__name(symbolPassTable, "symbolPassTable");
|
|
1402
|
+
__name(numericCellsAfter, "numericCellsAfter");
|
|
1403
|
+
__name(numbersIn, "numbersIn");
|
|
1404
|
+
__name(indexPassWants, "indexPassWants");
|
|
1405
|
+
__name(indexPassTable, "indexPassTable");
|
|
1406
|
+
__name(endWindow, "endWindow");
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1409
|
+
|
|
1204
1410
|
// src/QvdFileWriter.js
|
|
1205
1411
|
var QvdFileWriter_exports = {};
|
|
1206
1412
|
__export(QvdFileWriter_exports, {
|
|
@@ -1488,6 +1694,7 @@ var init_QvdFileWriter = __esm({
|
|
|
1488
1694
|
init_cellRules();
|
|
1489
1695
|
init_symbolBytes();
|
|
1490
1696
|
init_storedSymbols();
|
|
1697
|
+
init_seenNumbers();
|
|
1491
1698
|
__name(notXmlIndex, "notXmlIndex");
|
|
1492
1699
|
__name(checkHeaderText, "checkHeaderText");
|
|
1493
1700
|
__name(checkHeaderTexts, "checkHeaderTexts");
|
|
@@ -1950,7 +2157,14 @@ var init_QvdFileWriter = __esm({
|
|
|
1950
2157
|
const byCells = state.map((column) => column.byCell);
|
|
1951
2158
|
const byObjects = state.map((column) => column.byObject);
|
|
1952
2159
|
const containsNull = columns.map(() => false);
|
|
2160
|
+
const pool = new SlotPool();
|
|
2161
|
+
const seen = columns.map(() => NO_TABLE_YET);
|
|
2162
|
+
const firstJudgement = firstJudgementRow(numRows);
|
|
2163
|
+
const stopped = columns.map(() => null);
|
|
1953
2164
|
for (let row = 0; row < numRows; row++) {
|
|
2165
|
+
if (row % WINDOW_ROWS === 0 && row !== 0 || row === firstJudgement) {
|
|
2166
|
+
endWindow(seen, row, stopped);
|
|
2167
|
+
}
|
|
1954
2168
|
const values = data[row];
|
|
1955
2169
|
if (values !== null && values !== void 0 && !Array.isArray(values)) {
|
|
1956
2170
|
throw new exports.QvdValidationError("Each row must be an array of values", {
|
|
@@ -1981,7 +2195,34 @@ var init_QvdFileWriter = __esm({
|
|
|
1981
2195
|
}
|
|
1982
2196
|
continue;
|
|
1983
2197
|
}
|
|
2198
|
+
let table = null;
|
|
2199
|
+
let slot = 0;
|
|
2200
|
+
if (typeof value === "number") {
|
|
2201
|
+
table = seen[column];
|
|
2202
|
+
if (table !== null) {
|
|
2203
|
+
table.cells++;
|
|
2204
|
+
slot = slotOf(value, table.mask);
|
|
2205
|
+
if (table.values[slot] === value) {
|
|
2206
|
+
continue;
|
|
2207
|
+
}
|
|
2208
|
+
table.misses++;
|
|
2209
|
+
if (table === NO_TABLE_YET) {
|
|
2210
|
+
table = symbolPassTable(numRows, pool);
|
|
2211
|
+
seen[column] = table;
|
|
2212
|
+
if (table === null) {
|
|
2213
|
+
stopped[column] = { row, cells: 0 };
|
|
2214
|
+
} else {
|
|
2215
|
+
table.cells = 1;
|
|
2216
|
+
table.misses = 1;
|
|
2217
|
+
slot = slotOf(value, table.mask);
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
1984
2222
|
if (byCells[column].has(value)) {
|
|
2223
|
+
if (table !== null) {
|
|
2224
|
+
table.values[slot] = value;
|
|
2225
|
+
}
|
|
1985
2226
|
continue;
|
|
1986
2227
|
}
|
|
1987
2228
|
if (typeof value === "number") {
|
|
@@ -2001,6 +2242,9 @@ var init_QvdFileWriter = __esm({
|
|
|
2001
2242
|
refuseCell(value, columns[column], row, this._path);
|
|
2002
2243
|
}
|
|
2003
2244
|
byCells[column].set(value, slotForCell(state[column], value, row, this._path));
|
|
2245
|
+
if (table !== null && typeof value === "number") {
|
|
2246
|
+
seen[column] = table.remember(slot, value);
|
|
2247
|
+
}
|
|
2004
2248
|
}
|
|
2005
2249
|
}
|
|
2006
2250
|
const columnBuffers = [];
|
|
@@ -2009,10 +2253,12 @@ var init_QvdFileWriter = __esm({
|
|
|
2009
2253
|
const { keys, texts } = state[column];
|
|
2010
2254
|
const kinds = new Uint8Array(keys.length);
|
|
2011
2255
|
let byteLength = 0;
|
|
2256
|
+
let numberKeys = 0;
|
|
2012
2257
|
for (let slot = 0; slot < keys.length; slot++) {
|
|
2013
2258
|
const key = keys[slot];
|
|
2014
2259
|
const text = texts[slot];
|
|
2015
2260
|
if (typeof key === "number") {
|
|
2261
|
+
numberKeys++;
|
|
2016
2262
|
if (numberProblem(key) !== null) {
|
|
2017
2263
|
checkNumber(key, null, { column: columns[column], file: this._path, stage: "buildSymbolTable" });
|
|
2018
2264
|
}
|
|
@@ -2041,10 +2287,20 @@ var init_QvdFileWriter = __esm({
|
|
|
2041
2287
|
this._symbolTableMetadata?.push([symbolsOffset, byteLength, containsNull[column]]);
|
|
2042
2288
|
this._symbolCounts?.push(keys.length);
|
|
2043
2289
|
this._symbolFacts?.push(state[column].facts);
|
|
2290
|
+
const numbers = state[column].byCell === state[column].byKey ? numberKeys : numbersIn(state[column].byCell);
|
|
2291
|
+
const table = seen[column];
|
|
2044
2292
|
this._symbolIndexByValue?.push({
|
|
2045
2293
|
byCell: state[column].byCell,
|
|
2046
2294
|
byKey: state[column].byKey,
|
|
2047
|
-
byObject: state[column].byObject
|
|
2295
|
+
byObject: state[column].byObject,
|
|
2296
|
+
numbers,
|
|
2297
|
+
cells: table === NO_TABLE_YET ? 0 : table !== null ? table.cells : numericCellsAfter(
|
|
2298
|
+
data,
|
|
2299
|
+
column,
|
|
2300
|
+
/** @type {{row: number, cells: number}} */
|
|
2301
|
+
stopped[column],
|
|
2302
|
+
numbers
|
|
2303
|
+
)
|
|
2048
2304
|
});
|
|
2049
2305
|
symbolsOffset += byteLength;
|
|
2050
2306
|
this._emitProgress("symbol-table", column + 1, numColumns);
|
|
@@ -2094,7 +2350,7 @@ var init_QvdFileWriter = __esm({
|
|
|
2094
2350
|
const maxStoredIndex = symbolCount === 0 ? 0 : symbolCount - 1 + nullShift;
|
|
2095
2351
|
const bitWidth = maxStoredIndex === 0 ? 0 : 32 - Math.clz32(maxStoredIndex);
|
|
2096
2352
|
const lookup = this._symbolIndexByValue[column];
|
|
2097
|
-
const maps = lookup.byCell === lookup.byKey ? [lookup.byKey, lookup.byObject] :
|
|
2353
|
+
const maps = lookup.byCell === lookup.byKey ? [lookup.byKey, lookup.byObject] : [lookup.byCell, lookup.byKey, lookup.byObject];
|
|
2098
2354
|
for (const map of maps) {
|
|
2099
2355
|
for (const index of map.values()) {
|
|
2100
2356
|
if (index + nullShift > maxStoredIndex) {
|
|
@@ -2120,14 +2376,44 @@ var init_QvdFileWriter = __esm({
|
|
|
2120
2376
|
const byCells = this._symbolIndexByValue.map((lookup) => lookup.byCell);
|
|
2121
2377
|
const byKeys = this._symbolIndexByValue.map((lookup) => lookup.byKey);
|
|
2122
2378
|
const byObjects = this._symbolIndexByValue.map((lookup) => lookup.byObject);
|
|
2379
|
+
const symbolCounts = this._symbolCounts;
|
|
2380
|
+
const wanted = this._symbolIndexByValue.map((lookup) => indexPassWants(lookup.numbers, lookup.cells));
|
|
2381
|
+
const share = shareOf(wanted.filter(Boolean).length);
|
|
2382
|
+
const pool = new SlotPool();
|
|
2383
|
+
const seen = this._symbolIndexByValue.map(
|
|
2384
|
+
(lookup, column) => wanted[column] ? indexPassTable(lookup.numbers, lookup.cells, symbolCounts[column], share, pool) : null
|
|
2385
|
+
);
|
|
2386
|
+
const firstJudgement = firstJudgementRow(numRows);
|
|
2123
2387
|
for (let row = 0, recordBase = 0; row < numRows; row++, recordBase += recordByteSize) {
|
|
2388
|
+
if (row % WINDOW_ROWS === 0 && row !== 0 || row === firstJudgement) {
|
|
2389
|
+
endWindow(seen);
|
|
2390
|
+
}
|
|
2124
2391
|
const values = data[row];
|
|
2125
2392
|
for (let column = 0; column < numColumns; column++) {
|
|
2126
2393
|
const value = values?.[column];
|
|
2127
2394
|
if (value === null || value === void 0) {
|
|
2128
2395
|
continue;
|
|
2129
2396
|
}
|
|
2130
|
-
const
|
|
2397
|
+
const table = typeof value === "number" ? seen[column] : null;
|
|
2398
|
+
let index;
|
|
2399
|
+
if (table !== null) {
|
|
2400
|
+
table.cells++;
|
|
2401
|
+
const slot = slotOf(value, table.mask);
|
|
2402
|
+
if (table.values[slot] === value) {
|
|
2403
|
+
index = table.indices[slot];
|
|
2404
|
+
} else {
|
|
2405
|
+
index = byCells[column].get(value);
|
|
2406
|
+
if (index !== void 0) {
|
|
2407
|
+
table.values[slot] = value;
|
|
2408
|
+
table.indices[slot] = index;
|
|
2409
|
+
if (table.metBefore(index)) {
|
|
2410
|
+
table.misses++;
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
} else {
|
|
2415
|
+
index = typeof value === "object" ? byObjects[column].get(value) ?? byKeys[column].get(value.number) : byCells[column].get(value);
|
|
2416
|
+
}
|
|
2131
2417
|
if (index === void 0) {
|
|
2132
2418
|
throw new exports.QvdValidationError("A value is missing from the symbol table", {
|
|
2133
2419
|
field: columns[column],
|