@sythos/js_barcode_universal 1.0.0 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +16 -17
- package/NOTICE.md +24 -22
- package/README.md +137 -60
- package/bundle/sythos-barcode.esm.js +3380 -216
- package/bundle/sythos-barcode.js +3368 -216
- package/examples/create.html +2 -0
- package/examples/read.html +341 -341
- package/licenses/README.md +58 -29
- package/licenses/aztec-code.license +74 -0
- package/licenses/codabar.license +9 -9
- package/licenses/code-11.license +9 -9
- package/licenses/code-128.license +6 -6
- package/licenses/code-39.license +6 -6
- package/licenses/code-93.license +7 -7
- package/licenses/data-matrix.license +11 -11
- package/licenses/ean-13.license +6 -6
- package/licenses/ean-8.license +6 -6
- package/licenses/gs1-128.license +6 -6
- package/licenses/isbn.license +7 -7
- package/licenses/itf-14.license +6 -6
- package/licenses/itf.license +6 -6
- package/licenses/micropdf417.license +96 -0
- package/licenses/msi-plessey.license +7 -7
- package/licenses/pdf417.license +37 -0
- package/licenses/pharmacode.license +7 -7
- package/licenses/qr-code.license +6 -6
- package/licenses/upc-a.license +7 -7
- package/licenses/upc-e.license +6 -6
- package/package.json +13 -3
- package/src/aztec/decoder.js +317 -0
- package/src/aztec/detector.js +224 -0
- package/src/aztec/encoder.js +257 -0
- package/src/aztec/high-level.js +211 -0
- package/src/aztec/index.js +45 -0
- package/src/aztec/tables.js +210 -0
- package/src/core/galois-field.js +3 -0
- package/src/core/reed-solomon.js +64 -50
- package/src/datamatrix/decoder.js +262 -262
- package/src/datamatrix/detector.js +225 -225
- package/src/datamatrix/encoder.js +191 -191
- package/src/datamatrix/index.js +42 -42
- package/src/datamatrix/tables.js +123 -123
- package/src/index.js +113 -3
- package/src/micropdf417/compaction.js +116 -0
- package/src/micropdf417/decoder.js +183 -0
- package/src/micropdf417/detector.js +149 -0
- package/src/micropdf417/encoder.js +209 -0
- package/src/micropdf417/error-correction.js +55 -0
- package/src/micropdf417/index.js +49 -0
- package/src/micropdf417/tables.js +184 -0
- package/src/pdf417/compaction.js +298 -0
- package/src/pdf417/decoder.js +75 -0
- package/src/pdf417/detector.js +468 -0
- package/src/pdf417/encoder.js +91 -0
- package/src/pdf417/error-correction.js +47 -0
- package/src/pdf417/index.js +6 -0
- package/src/pdf417/tables.js +317 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Sythos Barcode Suite
|
|
3
|
+
*
|
|
4
|
+
* MIT License
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2026 Sythos
|
|
7
|
+
*
|
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
* in the Software without restriction, including without limitation the rights
|
|
11
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
* furnished to do so, subject to the following conditions:
|
|
14
|
+
*
|
|
15
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
* copies or substantial portions of the Software.
|
|
17
|
+
*
|
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
* SOFTWARE.
|
|
25
|
+
*
|
|
26
|
+
* SPDX-License-Identifier: MIT
|
|
27
|
+
*
|
|
28
|
+
* Original work. No code from any other barcode implementation.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Aztec Code layer geometry and Reed-Solomon parameters.
|
|
33
|
+
*
|
|
34
|
+
* `totalBits` counts the payload ring before its leading pad bits are added;
|
|
35
|
+
* consequently only `usableBits` can be partitioned into codewords. Compact
|
|
36
|
+
* symbols have no reference grid. Full symbols insert alternating reference
|
|
37
|
+
* rows and columns every 16 modules around the centre.
|
|
38
|
+
*
|
|
39
|
+
* The five data fields use generator base 1. GF(256)/DataMatrix is also the
|
|
40
|
+
* Aztec 8-bit field: both use primitive polynomial 0x12d.
|
|
41
|
+
*
|
|
42
|
+
* @module aztec/tables
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import { GF16, GF64, GF256_AZTEC, GF1024, GF4096 } from '../core/galois-field.js';
|
|
46
|
+
|
|
47
|
+
/** Reed-Solomon generator base defined for Aztec parameter and data fields. */
|
|
48
|
+
export const AZTEC_RS_GENERATOR_BASE = 1;
|
|
49
|
+
|
|
50
|
+
/** Minimum recommended error correction: 23 percent plus three codewords. */
|
|
51
|
+
export const AZTEC_DEFAULT_ECC_PERCENT = 23;
|
|
52
|
+
export const AZTEC_MIN_ECC_WORDS = 3;
|
|
53
|
+
|
|
54
|
+
/** Word size selected solely by the number of layers. */
|
|
55
|
+
export function wordSizeForLayers(layers) {
|
|
56
|
+
if (!Number.isInteger(layers) || layers < 1 || layers > 32) {
|
|
57
|
+
throw new RangeError(`Aztec: layers must be an integer from 1 to 32 (got ${layers})`);
|
|
58
|
+
}
|
|
59
|
+
if (layers <= 2) return 6;
|
|
60
|
+
if (layers <= 8) return 8;
|
|
61
|
+
if (layers <= 22) return 10;
|
|
62
|
+
return 12;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Return the field used by Aztec codewords of `wordSize` bits. */
|
|
66
|
+
export function fieldForWordSize(wordSize) {
|
|
67
|
+
switch (wordSize) {
|
|
68
|
+
case 4: return GF16; // Mode message only.
|
|
69
|
+
case 6: return GF64;
|
|
70
|
+
case 8: return GF256_AZTEC;
|
|
71
|
+
case 10: return GF1024;
|
|
72
|
+
case 12: return GF4096;
|
|
73
|
+
default: throw new RangeError(`Aztec: unsupported codeword size ${wordSize}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Return the data field selected for a symbol with `layers` layers. */
|
|
78
|
+
export function fieldForLayers(layers) {
|
|
79
|
+
return fieldForWordSize(wordSizeForLayers(layers));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Matrix side length, including Full-mode reference grid lines. */
|
|
83
|
+
export function aztecSymbolSize(layers, compact = false) {
|
|
84
|
+
if (!Number.isInteger(layers) || layers < 1 || layers > (compact ? 4 : 32)) {
|
|
85
|
+
throw new RangeError(`Aztec: ${compact ? 'Compact' : 'Full'} layers out of range: ${layers}`);
|
|
86
|
+
}
|
|
87
|
+
if (compact) return 11 + 4 * layers;
|
|
88
|
+
const baseMatrixSize = 14 + 4 * layers;
|
|
89
|
+
return baseMatrixSize + 1 + 2 * Math.floor((baseMatrixSize / 2 - 1) / 15);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function layer(layers, compact) {
|
|
93
|
+
const wordSize = wordSizeForLayers(layers);
|
|
94
|
+
const totalBits = ((compact ? 88 : 112) + 16 * layers) * layers;
|
|
95
|
+
const usableBits = totalBits - totalBits % wordSize;
|
|
96
|
+
const totalCodewords = usableBits / wordSize;
|
|
97
|
+
const baseMatrixSize = (compact ? 11 : 14) + 4 * layers;
|
|
98
|
+
return Object.freeze({
|
|
99
|
+
compact,
|
|
100
|
+
layers,
|
|
101
|
+
wordSize,
|
|
102
|
+
totalBits,
|
|
103
|
+
usableBits,
|
|
104
|
+
totalCodewords,
|
|
105
|
+
// Compact mode encodes the count in six bits and can therefore hold no
|
|
106
|
+
// more than 64 data codewords even where the ring itself is larger.
|
|
107
|
+
maxDataCodewords: compact ? Math.min(totalCodewords, 64) : totalCodewords,
|
|
108
|
+
baseMatrixSize,
|
|
109
|
+
symbolSize: aztecSymbolSize(layers, compact),
|
|
110
|
+
modeMessageDataWords: compact ? 2 : 4,
|
|
111
|
+
modeMessageWords: compact ? 7 : 10,
|
|
112
|
+
modeMessageBits: compact ? 28 : 40,
|
|
113
|
+
rsGeneratorBase: AZTEC_RS_GENERATOR_BASE,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Compact Aztec layers 1 through 4, in encoding preference order. */
|
|
118
|
+
export const AZTEC_COMPACT_LAYERS = Object.freeze(
|
|
119
|
+
Array.from({ length: 4 }, (_, i) => layer(i + 1, true)),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
/** Full Aztec layers 1 through 32, in ascending layer order. */
|
|
123
|
+
export const AZTEC_FULL_LAYERS = Object.freeze(
|
|
124
|
+
Array.from({ length: 32 }, (_, i) => layer(i + 1, false)),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
/** All allowed symbols. Compact entries precede Full entries for automatic selection. */
|
|
128
|
+
export const AZTEC_LAYERS = Object.freeze([
|
|
129
|
+
...AZTEC_COMPACT_LAYERS,
|
|
130
|
+
...AZTEC_FULL_LAYERS,
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
/** Return one immutable layer record. */
|
|
134
|
+
export function aztecLayer(layers, compact = false) {
|
|
135
|
+
if (!Number.isInteger(layers) || layers < 1 || layers > (compact ? 4 : 32)) {
|
|
136
|
+
throw new RangeError(`Aztec: ${compact ? 'Compact' : 'Full'} layers out of range: ${layers}`);
|
|
137
|
+
}
|
|
138
|
+
return (compact ? AZTEC_COMPACT_LAYERS : AZTEC_FULL_LAYERS)[layers - 1];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Calculate the minimum parity count for a data word count.
|
|
143
|
+
*
|
|
144
|
+
* The percentage is rounded up because a fractional codeword cannot be
|
|
145
|
+
* emitted. The mandatory three words protect short payloads, where a bare
|
|
146
|
+
* percentage would otherwise round to zero.
|
|
147
|
+
*/
|
|
148
|
+
export function eccCodewordsFor(dataCodewords, eccPercent = AZTEC_DEFAULT_ECC_PERCENT) {
|
|
149
|
+
if (!Number.isInteger(dataCodewords) || dataCodewords < 0) {
|
|
150
|
+
throw new RangeError(`Aztec: data codewords must be a non-negative integer (got ${dataCodewords})`);
|
|
151
|
+
}
|
|
152
|
+
if (!Number.isFinite(eccPercent) || eccPercent < 0 || eccPercent > 100) {
|
|
153
|
+
throw new RangeError(`Aztec: ECC percent must be between 0 and 100 (got ${eccPercent})`);
|
|
154
|
+
}
|
|
155
|
+
return Math.ceil(dataCodewords * eccPercent / 100) + AZTEC_MIN_ECC_WORDS;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Choose the first symbol which holds an already stuffed payload.
|
|
160
|
+
*
|
|
161
|
+
* `dataBits` must be a multiple of the candidate word size; callers which
|
|
162
|
+
* start from high-level bits must stuff separately per candidate word size.
|
|
163
|
+
*/
|
|
164
|
+
export function selectAztecLayer(dataBits, {
|
|
165
|
+
eccPercent = AZTEC_DEFAULT_ECC_PERCENT,
|
|
166
|
+
layers = null,
|
|
167
|
+
compact = null,
|
|
168
|
+
} = {}) {
|
|
169
|
+
if (!Number.isInteger(dataBits) || dataBits < 0) {
|
|
170
|
+
throw new RangeError(`Aztec: data bits must be a non-negative integer (got ${dataBits})`);
|
|
171
|
+
}
|
|
172
|
+
if (compact !== null && typeof compact !== 'boolean') {
|
|
173
|
+
throw new TypeError('Aztec: compact must be true, false or null');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let candidates;
|
|
177
|
+
if (layers !== null) {
|
|
178
|
+
if (compact === null) throw new TypeError('Aztec: compact must be specified when layers is specified');
|
|
179
|
+
candidates = [aztecLayer(layers, compact)];
|
|
180
|
+
} else if (compact === null) {
|
|
181
|
+
candidates = AZTEC_LAYERS;
|
|
182
|
+
} else {
|
|
183
|
+
candidates = compact ? AZTEC_COMPACT_LAYERS : AZTEC_FULL_LAYERS;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
for (const candidate of candidates) {
|
|
187
|
+
if (dataBits % candidate.wordSize !== 0) continue;
|
|
188
|
+
const dataCodewords = dataBits / candidate.wordSize;
|
|
189
|
+
const eccCodewords = eccCodewordsFor(dataCodewords, eccPercent);
|
|
190
|
+
if (dataCodewords <= candidate.maxDataCodewords &&
|
|
191
|
+
dataCodewords + eccCodewords <= candidate.totalCodewords) {
|
|
192
|
+
return Object.freeze({ ...candidate, dataCodewords, eccCodewords });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
throw new RangeError('Aztec: payload and requested error correction do not fit an available symbol');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Check static identities so table corruption fails explicitly in tests. */
|
|
200
|
+
export function validateAztecTables() {
|
|
201
|
+
const issues = [];
|
|
202
|
+
for (const entry of AZTEC_LAYERS) {
|
|
203
|
+
if (entry.usableBits % entry.wordSize !== 0) issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: unaligned usable bits`);
|
|
204
|
+
if (entry.totalCodewords !== entry.usableBits / entry.wordSize) issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: codeword mismatch`);
|
|
205
|
+
if (entry.symbolSize !== aztecSymbolSize(entry.layers, entry.compact)) issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: matrix size mismatch`);
|
|
206
|
+
if (entry.rsGeneratorBase !== AZTEC_RS_GENERATOR_BASE) issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: generator base mismatch`);
|
|
207
|
+
if (entry.compact && entry.maxDataCodewords > 64) issues.push(`C${entry.layers}: Compact data-word limit exceeded`);
|
|
208
|
+
}
|
|
209
|
+
return issues;
|
|
210
|
+
}
|
package/src/core/galois-field.js
CHANGED
|
@@ -194,6 +194,9 @@ export const GF256_QR = new GaloisField({ size: 256, primitive: 0x011d, name: 'G
|
|
|
194
194
|
/** Data Matrix ECC200. x^8 + x^5 + x^3 + x^2 + 1 */
|
|
195
195
|
export const GF256_DM = new GaloisField({ size: 256, primitive: 0x012d, name: 'GF(256)/DataMatrix' });
|
|
196
196
|
|
|
197
|
+
/** Aztec's eight-bit data field is algebraically identical to Data Matrix's. */
|
|
198
|
+
export const GF256_AZTEC = GF256_DM;
|
|
199
|
+
|
|
197
200
|
/** PDF417. Prime field; 3 is a primitive root modulo 929. */
|
|
198
201
|
export const GF929 = new GaloisField({ size: 929, prime: true, generator: 3, name: 'GF(929)' });
|
|
199
202
|
|
package/src/core/reed-solomon.js
CHANGED
|
@@ -56,7 +56,7 @@ import { ChecksumError } from './errors.js';
|
|
|
56
56
|
*
|
|
57
57
|
* g(x) = product over i of (x - a^(base + i)), i = 0 .. eccLen-1
|
|
58
58
|
*
|
|
59
|
-
* `base` is 0 for QR
|
|
59
|
+
* `base` is 0 for QR; 1 for Aztec, Data Matrix and PDF417.
|
|
60
60
|
*
|
|
61
61
|
* @param {number} eccLen
|
|
62
62
|
* @param {import('./galois-field.js').GaloisField} field
|
|
@@ -153,6 +153,44 @@ function evalPoly(poly, x, field) {
|
|
|
153
153
|
return acc;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
function multiplyAscending(left, right, field, limit) {
|
|
157
|
+
const out = new Array(Math.min(limit, left.length + right.length - 1)).fill(0);
|
|
158
|
+
for (let i = 0; i < left.length; i++) for (let j = 0; j < right.length && i + j < out.length; j++) {
|
|
159
|
+
out[i + j] = field.add(out[i + j], field.mul(left[i], right[j]));
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function berlekampMassey(syndromes, field) {
|
|
165
|
+
const limit = syndromes.length;
|
|
166
|
+
const lambda = new Array(limit + 1).fill(0);
|
|
167
|
+
const previous = new Array(limit + 1).fill(0);
|
|
168
|
+
const temporary = new Array(limit + 1).fill(0);
|
|
169
|
+
lambda[0] = 1;
|
|
170
|
+
previous[0] = 1;
|
|
171
|
+
let errorCount = 0;
|
|
172
|
+
let shift = 1;
|
|
173
|
+
let lastDiscrepancy = 1;
|
|
174
|
+
|
|
175
|
+
for (let step = 0; step < limit; step++) {
|
|
176
|
+
let discrepancy = syndromes[step];
|
|
177
|
+
for (let i = 1; i <= errorCount; i++) discrepancy = field.add(discrepancy, field.mul(lambda[i], syndromes[step - i]));
|
|
178
|
+
if (discrepancy === 0) { shift++; continue; }
|
|
179
|
+
const scale = field.div(discrepancy, lastDiscrepancy);
|
|
180
|
+
for (let i = 0; i <= limit; i++) temporary[i] = lambda[i];
|
|
181
|
+
for (let i = 0; i + shift <= limit; i++) if (previous[i] !== 0) {
|
|
182
|
+
lambda[i + shift] = field.sub(lambda[i + shift], field.mul(scale, previous[i]));
|
|
183
|
+
}
|
|
184
|
+
if (2 * errorCount <= step) {
|
|
185
|
+
errorCount = step + 1 - errorCount;
|
|
186
|
+
for (let i = 0; i <= limit; i++) previous[i] = temporary[i];
|
|
187
|
+
lastDiscrepancy = discrepancy;
|
|
188
|
+
shift = 1;
|
|
189
|
+
} else shift++;
|
|
190
|
+
}
|
|
191
|
+
return { locator: lambda.slice(0, errorCount + 1), errorCount };
|
|
192
|
+
}
|
|
193
|
+
|
|
156
194
|
/**
|
|
157
195
|
* Correct errors in a received codeword, in place.
|
|
158
196
|
*
|
|
@@ -160,11 +198,16 @@ function evalPoly(poly, x, field) {
|
|
|
160
198
|
* @param {number} eccLen
|
|
161
199
|
* @param {import('./galois-field.js').GaloisField} field
|
|
162
200
|
* @param {number} [base]
|
|
201
|
+
* @param {number[]} [erasures] Known damaged indexes, counted from wire order.
|
|
163
202
|
* @returns {number} Number of symbols corrected.
|
|
164
203
|
* @throws {ChecksumError} If the damage exceeds the correction capacity.
|
|
165
204
|
*/
|
|
166
|
-
export function rsDecode(received, eccLen, field, base = 0) {
|
|
205
|
+
export function rsDecode(received, eccLen, field, base = 0, erasures = []) {
|
|
167
206
|
const n = received.length;
|
|
207
|
+
if (!Array.isArray(erasures) || new Set(erasures).size !== erasures.length || erasures.some((index) => !Number.isInteger(index) || index < 0 || index >= n)) {
|
|
208
|
+
throw new ChecksumError('Reed-Solomon: erasure positions must be unique codeword indexes');
|
|
209
|
+
}
|
|
210
|
+
if (erasures.length > eccLen) throw new ChecksumError(`Reed-Solomon: ${erasures.length} erasures exceeds correction capacity ${eccLen} (${field.name})`);
|
|
168
211
|
|
|
169
212
|
// --- Syndromes. S[i] = R(a^(base+i)); all zero means an intact codeword.
|
|
170
213
|
const syn = new Array(eccLen).fill(0);
|
|
@@ -176,53 +219,24 @@ export function rsDecode(received, eccLen, field, base = 0) {
|
|
|
176
219
|
}
|
|
177
220
|
if (!damaged) return 0;
|
|
178
221
|
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
let errCount = 0; // current LFSR length
|
|
187
|
-
let shift = 1; // steps since `prev` was last updated
|
|
188
|
-
let lastDisc = 1; // discrepancy at that update
|
|
189
|
-
|
|
190
|
-
for (let step = 0; step < eccLen; step++) {
|
|
191
|
-
let disc = syn[step];
|
|
192
|
-
for (let i = 1; i <= errCount; i++) {
|
|
193
|
-
disc = field.add(disc, field.mul(lambda[i], syn[step - i]));
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (disc === 0) {
|
|
197
|
-
shift++;
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const scale = field.div(disc, lastDisc);
|
|
202
|
-
tmp.fill(0);
|
|
203
|
-
for (let i = 0; i <= eccLen; i++) tmp[i] = lambda[i];
|
|
204
|
-
|
|
205
|
-
for (let i = 0; i + shift <= eccLen; i++) {
|
|
206
|
-
if (prev[i] === 0) continue;
|
|
207
|
-
lambda[i + shift] = field.sub(lambda[i + shift], field.mul(scale, prev[i]));
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (2 * errCount <= step) {
|
|
211
|
-
errCount = step + 1 - errCount;
|
|
212
|
-
for (let i = 0; i <= eccLen; i++) prev[i] = tmp[i];
|
|
213
|
-
lastDisc = disc;
|
|
214
|
-
shift = 1;
|
|
215
|
-
} else {
|
|
216
|
-
shift++;
|
|
217
|
-
}
|
|
222
|
+
// Remove the known roots before locating unknown errors. The leading
|
|
223
|
+
// erasureCount terms contain only the known-location transient and are not
|
|
224
|
+
// part of the error-only recurrence.
|
|
225
|
+
let erasureLocator = [1];
|
|
226
|
+
for (const index of erasures) {
|
|
227
|
+
const location = field.exp(n - 1 - index);
|
|
228
|
+
erasureLocator = multiplyAscending(erasureLocator, [1, field.neg(location)], field, eccLen + 1);
|
|
218
229
|
}
|
|
219
|
-
|
|
220
|
-
|
|
230
|
+
const modified = multiplyAscending(syn, erasureLocator, field, eccLen).slice(erasures.length);
|
|
231
|
+
const { locator: errorLocator, errorCount } = berlekampMassey(modified, field);
|
|
232
|
+
if (2 * errorCount + erasures.length > eccLen) {
|
|
221
233
|
throw new ChecksumError(
|
|
222
|
-
`Reed-Solomon: ${
|
|
223
|
-
`${
|
|
234
|
+
`Reed-Solomon: ${errorCount} errors and ${erasures.length} erasures exceed correction capacity ` +
|
|
235
|
+
`${eccLen} (${field.name})`
|
|
224
236
|
);
|
|
225
237
|
}
|
|
238
|
+
const lambda = multiplyAscending(erasureLocator, errorLocator, field, eccLen + 1);
|
|
239
|
+
const totalCount = errorCount + erasures.length;
|
|
226
240
|
|
|
227
241
|
// --- Chien search. Position p (counted from the low-order end) is in error
|
|
228
242
|
// when lambda(a^-p) == 0.
|
|
@@ -231,16 +245,16 @@ export function rsDecode(received, eccLen, field, base = 0) {
|
|
|
231
245
|
const xInv = field.exp(-p);
|
|
232
246
|
let acc = 0;
|
|
233
247
|
let term = 1;
|
|
234
|
-
for (let i = 0; i <=
|
|
248
|
+
for (let i = 0; i <= totalCount; i++) {
|
|
235
249
|
acc = field.add(acc, field.mul(lambda[i], term));
|
|
236
250
|
term = field.mul(term, xInv);
|
|
237
251
|
}
|
|
238
252
|
if (acc === 0) positions.push(p);
|
|
239
253
|
}
|
|
240
254
|
|
|
241
|
-
if (positions.length !==
|
|
255
|
+
if (positions.length !== totalCount) {
|
|
242
256
|
throw new ChecksumError(
|
|
243
|
-
`Reed-Solomon: located ${positions.length} of ${
|
|
257
|
+
`Reed-Solomon: located ${positions.length} of ${totalCount} error positions`
|
|
244
258
|
);
|
|
245
259
|
}
|
|
246
260
|
|
|
@@ -249,7 +263,7 @@ export function rsDecode(received, eccLen, field, base = 0) {
|
|
|
249
263
|
const omega = new Array(eccLen).fill(0);
|
|
250
264
|
for (let i = 0; i < eccLen; i++) {
|
|
251
265
|
let acc = 0;
|
|
252
|
-
for (let j = 0; j <= i && j <=
|
|
266
|
+
for (let j = 0; j <= i && j <= totalCount; j++) {
|
|
253
267
|
acc = field.add(acc, field.mul(lambda[j], syn[i - j]));
|
|
254
268
|
}
|
|
255
269
|
omega[i] = acc;
|
|
@@ -273,7 +287,7 @@ export function rsDecode(received, eccLen, field, base = 0) {
|
|
|
273
287
|
// in a prime field every term contributes with an integer multiplier.
|
|
274
288
|
let den = 0;
|
|
275
289
|
term = 1;
|
|
276
|
-
for (let i = 1; i <=
|
|
290
|
+
for (let i = 1; i <= totalCount; i++) {
|
|
277
291
|
if (field.prime) {
|
|
278
292
|
// i * lambda[i] * x^(i-1), where `i` is repeated addition.
|
|
279
293
|
let mult = 0;
|