UNPKG

gnablib

Version:

A lean, zero dependency library to provide a useful base for your project.

2 lines 10.4 kB
/*! Copyright 2023-2024 the gnablib contributors MPL-1.1 */ import{hex as e}from"../codec/Hex.js";import{ContentError as t}from"../error/ContentError.js";import{ZeroError as i}from"../error/ZeroError.js";import{sLen as r,sNum as n}from"../safe/safe.js";let s,o,l,f,h,c;export class ReedSolomonError extends Error{constructor(e){super(e)}}class a{constructor(e,t,i){n("pow2Size",t).natural().atMost(8).throwNot();const r=1<<t;this.primitive=e,this.base=i,this._lastSpot=r-1,this._expTable=new Uint8Array(r),this._logTable=new Uint8Array(r);let s=1,o=0;for(;o<this._lastSpot;o++)this._expTable[o]=s,this._logTable[s]=o,s<<=1,s>=r&&(s^=e,s&=this._lastSpot);this._expTable[o]=s,this.zero=new d(this,Uint8Array.from([0])),this.one=new d(this,Uint8Array.from([1]))}newPoly(e){return new d(this,e)}newPolyArr(e){return new d(this,Uint8Array.from(e))}newArr(e){return new Uint8Array(e)}buildMonomial(e,t){if(n("degree",e).unsigned().throwNot(),0===t)return this.zero;const i=new Uint8Array(e+1);return i[0]=t,new d(this,i)}get size(){return this._lastSpot+1}exp(e){return this._expTable[e]}log(e){if(0===e)throw new i("input");return this._logTable[e]}inverse(e){if(0===e)throw new i("input");return this._expTable[this._lastSpot-this._logTable[e]]}mul(e,t){return 0===e||0===t?0:this._expTable[(this._logTable[e]+this._logTable[t])%this._lastSpot]}toString(){return`GF(0x${e.fromI32Compress(this.primitive)}, ${this._lastSpot+1}, ${this.base})`}}class d{constructor(e,t){if(r("coefficients",t).atLeast(1).throwNot(),this._field=e,t.length>1&&0===t[0]){let e=0;for(;e<t.length&&0===t[e];e++);e==t.length?this.coefficients=Uint8Array.from([0]):this.coefficients=t.slice(e)}else this.coefficients=t}get degree(){return this.coefficients.length-1}coefficient(e){return this.coefficients[this.coefficients.length-1-e]}get degreeCoefficient(){return this.coefficients[0]}get isZero(){return 0===this.coefficients[0]}evalAt(e){if(0===e)return this.coefficients[this.coefficients.length-1];const t=this.coefficients.length;let i;if(1==e){i=0;for(let e=0;e<t;e++)i^=this.coefficients[e]}else{i=this.coefficients[0];for(let r=1;r<t;r++)i=this._field.mul(e,i)^this.coefficients[r]}return i}addOrSubtract(e){if(this._field!==e._field)throw new t("fields don't match");if(this.isZero)return e;if(e.isZero)return this;let i=this.coefficients,r=e.coefficients;i.length>r.length&&(i=e.coefficients,r=this.coefficients);const n=r.length-i.length,s=new Uint8Array(r.length);s.set(r.subarray(0,n));for(let e=n;e<r.length;e++)s[e]=i[e-n]^r[e];return new d(this._field,s)}mulPoly(e){if(this._field!==e._field)throw new t("fields don't match");if(this.isZero||e.isZero)return this._field.zero;const i=this.coefficients.length,r=new Uint8Array(i+e.coefficients.length-1);for(let t=0;t<i;t++){const i=this.coefficients[t];for(let n=0;n<e.coefficients.length;n++)r[t+n]^=this._field.mul(i,e.coefficients[n])}return new d(this._field,r)}mulScalar(e){if(0===e)return this._field.zero;if(1==e)return this;const t=this.coefficients.length,i=new Uint8Array(t);for(let r=0;r<t;r++)i[r]=this._field.mul(this.coefficients[r],e);return new d(this._field,i)}mulMonomial(e,t){if(n("degree",e).unsigned().throwNot(),0===t)return this._field.zero;const i=this.coefficients.length,r=new Uint8Array(i+e);for(let e=0;e<i;e++)r[e]=this._field.mul(this.coefficients[e],t);return this._field.newPoly(r)}div(e){if(this._field!==e._field)throw new t("fields don't match");if(e.isZero)throw new i("other");let r=this._field.zero,n=this;const s=e.degreeCoefficient,o=this._field.inverse(s);let l=this.degree-e.degree;for(;l>=0&&!n.isZero;){const t=this._field.mul(n.degreeCoefficient,o),i=e.mulMonomial(l,t),s=this._field.buildMonomial(l,t);r=r.addOrSubtract(s),n=n.addOrSubtract(i),l=n.degree-e.degree}return{quotient:r,remainder:n}}toString(){return`Poly(${this._field.toString()}, ${this.coefficients.toString()})`}}class u{constructor(e,t,i){n("pow2Size",t).natural().atMost(16).throwNot();const r=1<<t;this.primitive=e,this.base=i,this._lastSpot=r-1,this._expTable=new Uint16Array(r),this._logTable=new Uint16Array(r);let s=1,o=0;for(;o<this._lastSpot;o++)this._expTable[o]=s,this._logTable[s]=o,s<<=1,s>=r&&(s^=e,s&=this._lastSpot);this._expTable[o]=s,this.zero=new _(this,Uint16Array.from([0])),this.one=new _(this,Uint16Array.from([1]))}newPoly(e){return new _(this,e)}newPolyArr(e){return new _(this,Uint16Array.from(e))}newArr(e){return new Uint16Array(e)}buildMonomial(e,t){if(n("degree",e).unsigned().throwNot(),0===t)return this.zero;const i=new Uint16Array(e+1);return i[0]=t,new _(this,i)}get size(){return this._lastSpot+1}exp(e){return this._expTable[e]}log(e){if(0===e)throw new i("input");return this._logTable[e]}inverse(e){if(0===e)throw new i("input");return this._expTable[this._lastSpot-this._logTable[e]]}mul(e,t){return 0===e||0===t?0:this._expTable[(this._logTable[e]+this._logTable[t])%this._lastSpot]}toString(){return`GF(0x${e.fromI32Compress(this.primitive)}, ${this._lastSpot+1}, ${this.base})`}}class _{constructor(e,t){if(r("coefficients",t).atLeast(1).throwNot(),this._field=e,t.length>1&&0===t[0]){let e=0;for(;e<t.length&&0===t[e];e++);e==t.length?this.coefficients=Uint16Array.from([0]):this.coefficients=t.slice(e)}else this.coefficients=t}get degree(){return this.coefficients.length-1}coefficient(e){return this.coefficients[this.coefficients.length-1-e]}get degreeCoefficient(){return this.coefficients[0]}get isZero(){return 0===this.coefficients[0]}evalAt(e){if(0===e)return this.coefficients[this.coefficients.length-1];const t=this.coefficients.length;let i;if(1==e){i=0;for(let e=0;e<t;e++)i^=this.coefficients[e]}else{i=this.coefficients[0];for(let r=1;r<t;r++)i=this._field.mul(e,i)^this.coefficients[r]}return i}addOrSubtract(e){if(this._field!==e._field)throw new t("fields don't match");if(this.isZero)return e;if(e.isZero)return this;let i=this.coefficients,r=e.coefficients;i.length>r.length&&(i=e.coefficients,r=this.coefficients);const n=r.length-i.length,s=new Uint16Array(r.length);s.set(r.subarray(0,n));for(let e=n;e<r.length;e++)s[e]=i[e-n]^r[e];return this._field.newPoly(s)}mulPoly(e){if(this._field!==e._field)throw new t("fields don't match");if(this.isZero||e.isZero)return this._field.zero;const i=this.coefficients.length,r=new Uint16Array(i+e.coefficients.length-1);for(let t=0;t<i;t++){const i=this.coefficients[t];for(let n=0;n<e.coefficients.length;n++)r[t+n]^=this._field.mul(i,e.coefficients[n])}return this._field.newPoly(r)}mulScalar(e){if(0===e)return this._field.zero;if(1==e)return this;const t=this.coefficients.length,i=new Uint16Array(t);for(let r=0;r<t;r++)i[r]=this._field.mul(this.coefficients[r],e);return this._field.newPoly(i)}mulMonomial(e,t){if(n("degree",e).unsigned().throwNot(),0===t)return this._field.zero;const i=this.coefficients.length,r=new Uint16Array(i+e);for(let e=0;e<i;e++)r[e]=this._field.mul(this.coefficients[e],t);return this._field.newPoly(r)}div(e){if(this._field!==e._field)throw new t("fields don't match");if(e.isZero)throw new i("other");let r=this._field.zero,n=this;const s=e.degreeCoefficient,o=this._field.inverse(s);let l=this.degree-e.degree;for(;l>=0&&!n.isZero;){const t=this._field.mul(n.degreeCoefficient,o),i=e.mulMonomial(l,t),s=this._field.buildMonomial(l,t);r=r.addOrSubtract(s),n=n.addOrSubtract(i),l=n.degree-e.degree}return{quotient:r,remainder:n}}toString(){return`Poly(${this._field.toString()}, ${this.coefficients.toString()})`}}export function qrCode(){return s||(s=new a(285,8,0)),s}export function dataMatrix(){return c||(c=new a(301,8,1)),c}export function aztecParam(){return h||(h=new a(19,4,1)),h}export function aztecData6(){return o||(o=new a(67,6,1)),o}export function aztecData8(){return dataMatrix()}export function aztecData10(){return l||(l=new u(1033,10,1)),l}export function aztecData12(){return f||(f=new u(4201,12,1)),f}export function maxicodeField(){return aztecData6()}export class ReedSolomon{constructor(e){this._cache=[],this._field=e,this._cache.push(e.one)}_generator(e){if(e>=this._cache.length){let t=this._cache[this._cache.length-1];for(let i=this._cache.length;i<=e;i++){const e=t.mulPoly(this._field.newPolyArr([1,this._field.exp(i-1+this._field.base)]));this._cache.push(e),t=e}}return this._cache[e]}encode(e,r){if(0===r)throw new i("ecByteLen");const n=e.length-r;if(n<=0)throw new t("Missing data bytes");const s=this._generator(r);let o=this._field.newPolyArr(e.subarray(0,n));o=o.mulMonomial(r,1);const{remainder:l}=o.div(s),f=r-l.coefficients.length;for(let t=0;t<f;t++)e[n+t]=0;e.set(l.coefficients,n+f)}_findErrorMagnitudes(e,t){const i=t.length,r=this._field.newArr(i);for(let n=0;n<i;n++){const s=this._field.inverse(t[n]);let o=1;for(let e=0;e<i;e++)n!==e&&(o=this._field.mul(o,1^this._field.mul(t[e],s)));r[n]=this._field.mul(e.evalAt(s),this._field.inverse(o)),0!==this._field.base&&(r[n]=this._field.mul(r[n],s))}return r}_findErrorLocations(e){const t=e.degree,i=this._field.newArr(t);if(1===t)return i[0]=e.coefficient(1),i;let r=0;const n=this._field.size;for(let s=1;s<n;s++)if(0===e.evalAt(s)&&(i[r]=this._field.inverse(s),r++,r>=t))return i;throw new ReedSolomonError("Error locator degree does not match number of roots")}_runEuclideanAlgorithm(e,t,i){const r=i>>1;let n=e,s=t;e.degree<t.degree&&(n=t,s=e);let o=this._field.zero,l=this._field.one;for(;s.degree>=r;){const e=n,t=o;if(n=s,o=l,n.isZero)throw new ReedSolomonError("rLast is zero");s=e;let i=this._field.zero;const r=n.coefficient(n.degree),f=this._field.inverse(r);for(;s.degree>=n.degree&&!s.isZero;){const e=s.degree-n.degree,t=this._field.mul(s.degreeCoefficient,f);i=i.addOrSubtract(this._field.buildMonomial(e,t)),s=s.addOrSubtract(n.mulMonomial(e,t))}if(l=i.mulPoly(o).addOrSubtract(t),s.degree>=n.degree)throw new Error("Division algorithm failed to reduce polynomial")}const f=l.coefficient(0);if(0===f)throw new ReedSolomonError("sigmaTilde(0) is zero");const h=this._field.inverse(f);return{sigma:l.mulScalar(h),omega:s.mulScalar(h)}}decode(e,t){const i=this._field.newPoly(e),r=this._field.newArr(t);let n=0;for(let e=0;e<t;e++){const t=i.evalAt(this._field.exp(e+this._field.base));r[r.length-1-e]=t,n|=t}if(0===n)return;const s=this._field.newPoly(r),{sigma:o,omega:l}=this._runEuclideanAlgorithm(this._field.buildMonomial(t,1),s,t),f=this._findErrorLocations(o),h=this._findErrorMagnitudes(l,f);for(let t=0;t<f.length;t++){const i=e.length-1-this._field.log(f[t]);if(i<0)throw new ReedSolomonError("Bad error location");e[i]^=h[t]}}}