20
loading...
This website collects cookies to deliver better user experience
Uint8Array
s, but if you wish you can use just regular arrays:const LOG = new Uint8Array(256);
const EXP = new Uint8Array(256);
for (let exponent = 1, value = 1; exponent < 256; exponent++) {
value = value > 127 ? ((value << 1) ^ 285) : value << 1;
LOG[value] = exponent % 255;
EXP[exponent % 255] = value;
}
value
at each iteration (shifting by 1 to the left). If value
goes over 255, we XOR it with 285. Why 285? I won't go into details (if you're curious, you can find them here), as it has something to do with the relation between elements of a Galois field and polynomials, but rest assured that we'll get all 255 non-zero elements like this.> LOG
< Uint8Array(256) [0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, ...]
> EXP
< Uint8Array(256) [1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, ...]
function mul(a, b) {
return a && b ? EXP[(LOG[a] + LOG[b]) % 255] : 0;
}
function div(a, b) {
return EXP[(LOG[a] + LOG[b] * 254) % 255];
}
[1, 3, 2]
. Again, since the coefficients can't go over 255, we can use Uint8Array
to optimize performances.mul
function defined above.function polyMul(poly1, poly2) {
// This is going to be the product polynomial, that we pre-allocate.
// We know it's going to be `poly1.length + poly2.length - 1` long.
const coeffs = new Uint8Array(poly1.length + poly2.length - 1);
// Instead of executing all the steps in the example, we can jump to
// computing the coefficients of the result
for (let index = 0; index < coeffs.length; index++) {
let coeff = 0;
for (let p1index = 0; p1index <= index; p1index++) {
const p2index = index - p1index;
// We *should* do better here, as `p1index` and `p2index` could
// be out of range, but `mul` defined above will handle that case.
// Just beware of that when implementing in other languages.
coeff ^= mul(poly1[p1index], poly2[p2index]);
}
coeffs[index] = coeff;
}
return coeffs;
}
function polyRest(dividend, divisor) {
const quotientLength = dividend.length - divisor.length + 1;
// Let's just say that the dividend is the rest right away
let rest = new Uint8Array(dividend);
for (let count = 0; count < quotientLength; count++) {
// If the first term is 0, we can just skip this iteration
if (rest[0]) {
const factor = div(rest[0], divisor[0]);
const subtr = new Uint8Array(rest.length);
subtr.set(polyMul(divisor, [factor]), 0);
rest = rest.map((value, index) => value ^ subtr[index]).slice(1);
} else {
rest = rest.slice(1);
}
}
return rest;
}
Level | Letter | Data recovery |
---|---|---|
Low | L | ~7% |
Medium | M | ~15% |
Quartile | Q | ~25% |
High | H | ~30% |
EXP
table computed before. Anyway, let's get our polyMul
function rolling!function getGeneratorPoly(degree) {
let lastPoly = new Uint8Array([1]);
for (let index = 0; index < degree; index++) {
lastPoly = polyMul(lastPoly, new Uint8Array([1, EXP[index]]));
}
return lastPoly;
}
getGeneratorPoly(16);
// Uint8Array(17) [1, 59, 13, 104, 189, 68, 209, 30, 8, 163, 65, 41, 229, 98, 50, 36, 59]
function getEDC(data, codewords) {
const degree = codewords - data.length;
const messagePoly = new Uint8Array(codewords);
messagePoly.set(data, 0);
return polyRest(messagePoly, getGeneratorPoly(degree));
}
const data = getByteData('https://www.qrcode.com/', 8, 28);
getEDC(data, 44);
// Uint8Array(16) [52, 61, 242, 187, 29, 7, 216, 249, 103, 87, 95, 69, 188, 134, 57, 20]