mirror of
https://github.com/Merricx/qrazybox.git
synced 2024-11-26 20:42:56 +01:00
511 lines
No EOL
9.7 KiB
JavaScript
511 lines
No EOL
9.7 KiB
JavaScript
/***************************************************************************************************
|
|
------------------Javascript Reed-Solomon Universal Encoder/Decoder--------------------
|
|
|
|
Written by : Merricx
|
|
|
|
Heavily referenced from https://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders
|
|
And ported partly from https://github.com/tomerfiliba/reedsolomon
|
|
|
|
***************************************************************************************************/
|
|
|
|
var gf_exp = new Uint8Array(512);
|
|
var gf_log = new Uint8Array(256);
|
|
|
|
//Source: http://stackoverflow.com/questions/2044760/default-array-values
|
|
Array.prototype.repeat = function(def, len){
|
|
while(len)
|
|
this[--len]= def;
|
|
return this;
|
|
};
|
|
|
|
/*
|
|
======================================================
|
|
GALOIS FIELD
|
|
======================================================
|
|
*/
|
|
|
|
|
|
function init_tables(prim){
|
|
prim = prim || 0x11d;
|
|
|
|
gf_exp = new Uint8Array(512);
|
|
gf_log = new Uint8Array(256);
|
|
|
|
var x = 1;
|
|
for(var i=0; i < 255;i++){
|
|
gf_exp[i] = x;
|
|
gf_log[x] = i;
|
|
x = gf_mult_noLUT(x,2,prim);
|
|
}
|
|
|
|
for(var i=255; i < 512; i++){
|
|
gf_exp[i] = gf_exp[i - 255];
|
|
}
|
|
|
|
return [gf_log, gf_exp];
|
|
}
|
|
|
|
function gf_add(x,y){
|
|
return x ^ y;
|
|
}
|
|
|
|
function gf_sub(x,y){
|
|
return x ^ y;
|
|
}
|
|
|
|
function gf_mult_noLUT(x,y,prim){
|
|
prim = prim || 0;
|
|
|
|
function cl_mult(x,y){
|
|
var z = 0;
|
|
var i = 0;
|
|
while((y>>i) > 0){
|
|
if(y & (1<<i)){
|
|
z ^= x<<i;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return z;
|
|
}
|
|
|
|
function bit_length(n){
|
|
var bits = 0;
|
|
while(n>>bits){
|
|
bits += 1;
|
|
}
|
|
return bits;
|
|
}
|
|
|
|
function cl_div(dividend,divisor){
|
|
|
|
var dl1 = bit_length(dividend);
|
|
var dl2 = bit_length(divisor);
|
|
|
|
if(dl1 < dl2){
|
|
return dividend;
|
|
}
|
|
|
|
for(var i=dl1-dl2; i > -1; i--){
|
|
if(dividend & (1 << i+dl2-1)){
|
|
dividend ^= divisor << i;
|
|
}
|
|
}
|
|
|
|
return dividend;
|
|
}
|
|
|
|
var result = cl_mult(x,y);
|
|
|
|
if(prim > 0){
|
|
result = cl_div(result, prim);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function gf_mul(x,y){
|
|
if(x == 0 || y == 0)
|
|
return 0;
|
|
|
|
return gf_exp[gf_log[x] + gf_log[y]];
|
|
}
|
|
|
|
function gf_div(x,y){
|
|
if(y == 0){
|
|
throw "Division by zero";
|
|
}
|
|
if(x == 0){
|
|
return 0;
|
|
}
|
|
|
|
return gf_exp[(gf_log[x] + 255 - gf_log[y]) % 255];
|
|
}
|
|
|
|
function gf_pow(x, power){
|
|
return gf_exp[(((gf_log[x] * power) % 255) + 255) % 255];
|
|
}
|
|
|
|
function gf_inverse(x){
|
|
return gf_exp[255 - gf_log[x]];
|
|
}
|
|
|
|
/*
|
|
======================================================
|
|
POLYNOMIAL OPERATION
|
|
======================================================
|
|
*/
|
|
|
|
function gf_poly_scale(p,x){
|
|
var r = [];
|
|
for(var i=0; i < p.length; i++){
|
|
r[i] = gf_mul(p[i], x);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
function gf_poly_add(p,q){
|
|
var r = new Array(Math.max(p.length, q.length));
|
|
for(var i=0; i < p.length; i++){
|
|
r[i+r.length-p.length] = p[i];
|
|
}
|
|
for(var i=0; i < q.length; i++){
|
|
r[i+r.length-q.length] ^= q[i];
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
function gf_poly_mul(p,q){
|
|
var r = new Array(p.length+q.length-1);
|
|
|
|
for(var j=0; j < q.length;j++){
|
|
for(var i=0; i < p.length; i++){
|
|
r[j+i] ^= gf_mul(p[i], q[j]);
|
|
}
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
function gf_poly_div(dividend, divisor){
|
|
|
|
var msg_out = Array.prototype.slice.call(dividend);
|
|
|
|
for(var i=0; i < dividend.length - (divisor.length-1); i++){
|
|
coef = msg_out[i];
|
|
if(coef != 0){
|
|
for(var j=1; j < divisor.length; j++){
|
|
if(divisor[j] != 0){
|
|
msg_out[i+j] ^= gf_mul(divisor[j], coef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var separator = divisor.length - 1;
|
|
var result = [[],[]];
|
|
for(var i=0; i < msg_out.length-separator; i++){
|
|
result[0][i] = msg_out[i];
|
|
}
|
|
|
|
for(var i=msg_out.length-separator; i < msg_out.length; i++){
|
|
result[1].push(msg_out[i]);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function gf_poly_eval(poly, x){
|
|
var y = poly[0];
|
|
for(var i=1; i < poly.length; i++){
|
|
y = gf_mul(y,x) ^ poly[i];
|
|
}
|
|
|
|
return y;
|
|
}
|
|
|
|
/*
|
|
======================================================
|
|
REED-SOLOMON ENCODING
|
|
======================================================
|
|
*/
|
|
|
|
function rs_generator_poly(nysm){
|
|
var g = [1];
|
|
for(var i=0; i < nysm; i++){
|
|
g = gf_poly_mul(g, [1, gf_pow(2, i)])
|
|
}
|
|
|
|
return g;
|
|
}
|
|
|
|
function rs_encode_msg(msg_in, nysm){
|
|
|
|
if(msg_in.length + nysm > 255)
|
|
throw "Message is too long...";
|
|
|
|
var gen = rs_generator_poly(nysm);
|
|
var msg_out = [].repeat(0, (msg_in.length+gen.length)-1)
|
|
for(var i=0; i < msg_in.length; i++){
|
|
msg_out[i] = msg_in[i];
|
|
}
|
|
|
|
for(var i=0; i < msg_in.length; i++){
|
|
var coef = msg_out[i];
|
|
|
|
if(coef != 0){
|
|
for(var j=1; j < gen.length; j++){
|
|
msg_out[i+j] ^= gf_mul(gen[j], coef);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(var i=0; i < msg_in.length; i++){
|
|
msg_out[i] = msg_in[i];
|
|
}
|
|
|
|
return msg_out;
|
|
}
|
|
|
|
/*
|
|
======================================================
|
|
REED-SOLOMON DECODING
|
|
======================================================
|
|
*/
|
|
|
|
function rs_calc_syndromes(msg, nysm){
|
|
var synd = [].repeat(0, nysm);
|
|
|
|
for(var i=0; i < nysm; i++){
|
|
synd[i] = gf_poly_eval(msg, gf_pow(2,i));
|
|
}
|
|
|
|
return [0].concat(synd);
|
|
}
|
|
|
|
function rs_check(msg, nysm){
|
|
var check = rs_calc_syndromes(msg, nysm);
|
|
var no_error = true;
|
|
for(var i=0; i < check.length; i++){
|
|
if(check[i] != 0){
|
|
no_error = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return no_error;
|
|
}
|
|
|
|
function rs_find_errata_locator(e_pos){
|
|
var e_loc = [1];
|
|
|
|
for(var i=0; i < e_pos.length; i++){
|
|
e_loc = gf_poly_mul(e_loc, gf_poly_add([1], [gf_pow(2,e_pos[i]), 0]))
|
|
}
|
|
|
|
return e_loc;
|
|
}
|
|
|
|
function rs_find_error_evaluator(synd, err_loc, nysm){
|
|
var x = [].repeat(0, nysm+1);
|
|
var remainder = gf_poly_div(gf_poly_mul(synd, err_loc), ([1].concat(x)))[1];
|
|
|
|
|
|
return remainder;
|
|
}
|
|
|
|
function rs_correct_errata(msg_in, synd, err_pos){
|
|
var coef_pos = [];
|
|
for(var i=0; i < err_pos.length; i++){
|
|
coef_pos[i] = msg_in.length - 1 - err_pos[i];
|
|
}
|
|
|
|
var err_loc = rs_find_errata_locator(coef_pos);
|
|
var err_eval = rs_find_error_evaluator(synd.reverse(), err_loc, err_loc.length-1);
|
|
|
|
var X = [];
|
|
for(var i=0; i < coef_pos.length; i++){
|
|
var l = 255 - coef_pos[i];
|
|
X.push(gf_pow(2, -l));
|
|
}
|
|
|
|
var E = [].repeat(0, msg_in.length);
|
|
|
|
for(var i=0; i < X.length; i++){
|
|
var Xi_inv = gf_inverse(X[i]);
|
|
|
|
err_loc_prime_tmp = [];
|
|
for(var j=0; j < X.length; j++){
|
|
if(j != i){
|
|
err_loc_prime_tmp.push(gf_sub(1, gf_mul(Xi_inv, X[j])));
|
|
}
|
|
}
|
|
|
|
var err_loc_prime = 1;
|
|
for(var j=0; j < err_loc_prime_tmp.length; j++){
|
|
err_loc_prime = gf_mul(err_loc_prime, err_loc_prime_tmp[j]);
|
|
}
|
|
|
|
var y = gf_poly_eval(err_eval, Xi_inv);
|
|
y = gf_mul(gf_pow(X[i], 1), y);
|
|
|
|
var magnitude = gf_div(y, err_loc_prime);
|
|
|
|
E[err_pos[i]] = magnitude;
|
|
}
|
|
|
|
msg_in = gf_poly_add(msg_in, E);
|
|
|
|
return msg_in;
|
|
}
|
|
|
|
function rs_find_error_locator(synd, nysm, erase_loc, erase_count){
|
|
|
|
erase_loc = erase_loc || undefined;
|
|
erase_count = erase_count || 0;
|
|
|
|
if(erase_loc != undefined){
|
|
err_loc = Array.prototype.slice.call(erase_loc);
|
|
old_loc = Array.prototype.slice.call(erase_loc);
|
|
} else {
|
|
err_loc = [1];
|
|
old_loc = [1];
|
|
}
|
|
|
|
synd_shift = 0
|
|
if(synd.length > nysm){
|
|
synd_shift = synd.length - nysm;
|
|
}
|
|
|
|
for(var i=0; i < nysm-erase_count;i++){
|
|
if(erase_loc != undefined){
|
|
K = erase_count+i+synd_shift;
|
|
} else {
|
|
K = i+synd_shift;
|
|
}
|
|
|
|
delta = synd[K];
|
|
|
|
for(var j=1;j < err_loc.length; j++){
|
|
delta ^= gf_mul(err_loc[err_loc.length-(j+1)], synd[K-j]);
|
|
}
|
|
|
|
old_loc = old_loc.concat([0]);
|
|
|
|
if(delta != 0){
|
|
if(old_loc.length > err_loc.length){
|
|
new_loc = gf_poly_scale(old_loc, delta);
|
|
old_loc = gf_poly_scale(err_loc, gf_inverse(delta));
|
|
err_loc = new_loc;
|
|
}
|
|
err_loc = gf_poly_add(err_loc, gf_poly_scale(old_loc, delta));
|
|
}
|
|
}
|
|
|
|
while(err_loc.length != 0 && err_loc[0] == 0){
|
|
err_loc.shift();
|
|
}
|
|
errs = err_loc.length - 1;
|
|
if((errs - erase_count) * 2 + erase_count > nysm){
|
|
throw new Error("Too many errors to correct");
|
|
}
|
|
|
|
return err_loc;
|
|
|
|
}
|
|
|
|
function rs_find_errors(err_loc, nmess){
|
|
errs = err_loc.length - 1;
|
|
err_pos = [];
|
|
for(var i=0; i < nmess; i++){
|
|
if(gf_poly_eval(err_loc, gf_pow(2, i)) == 0){
|
|
err_pos.push(nmess - 1 - i);
|
|
}
|
|
}
|
|
|
|
if(err_pos.length != errs){
|
|
throw new Error("Could not locate error!");
|
|
}
|
|
|
|
return err_pos;
|
|
}
|
|
|
|
function rs_forney_syndromes(synd, pos, nmess){
|
|
|
|
erase_pos_reversed = [];
|
|
for(var i=0; i < pos.length; i++){
|
|
erase_pos_reversed.push(nmess-1-pos[i]);
|
|
}
|
|
|
|
fsynd = Array.prototype.slice.call(synd);
|
|
fsynd.shift();
|
|
|
|
for(var i=0; i < pos.length; i++){
|
|
x = gf_pow(2, erase_pos_reversed[i]);
|
|
for(var j=0; j < fsynd.length-1; j++){
|
|
fsynd[j] = gf_mul(fsynd[j], x) ^ fsynd[j+1];
|
|
}
|
|
}
|
|
|
|
return fsynd;
|
|
}
|
|
|
|
function rs_correct_msg(msg_in, nysm, erase_pos){
|
|
|
|
console.log(msg_in);
|
|
|
|
erase_pos = erase_pos || undefined;
|
|
|
|
var msg_len = msg_in.length - nysm;
|
|
|
|
if(msg_in.length > 255){
|
|
return "Message is too long...";
|
|
}
|
|
|
|
msg_out = Array.prototype.slice.call(msg_in);
|
|
|
|
if(erase_pos == undefined){
|
|
erase_pos = [];
|
|
} else {
|
|
for(var i=0; i < erase_pos.length; i++){
|
|
msg_out[erase_pos[i]] = 0;
|
|
}
|
|
}
|
|
|
|
if(erase_pos.length > nysm)
|
|
return "Too many erasures to correct";
|
|
|
|
synd = rs_calc_syndromes(msg_out, nysm);
|
|
|
|
if(rs_check(msg_out, nysm)){
|
|
msg_in = msg_in.slice(0, msg_len);
|
|
return msg_in;
|
|
}
|
|
|
|
fsynd = rs_forney_syndromes(synd, erase_pos, msg_out.length);
|
|
try {
|
|
err_loc = rs_find_error_locator(fsynd, nysm, undefined ,erase_pos.length);
|
|
}
|
|
catch (error){
|
|
return error.message;
|
|
}
|
|
|
|
try {
|
|
err_pos = rs_find_errors(err_loc.reverse(), msg_out.length);
|
|
} catch (error) {
|
|
return error.message;
|
|
}
|
|
|
|
|
|
msg_out = rs_correct_errata(msg_out, synd, erase_pos.concat(err_pos));
|
|
synd = rs_calc_syndromes(msg_out, nysm);
|
|
|
|
if(!rs_check(msg_out, nysm)){
|
|
return "Could not correct message";
|
|
}
|
|
|
|
return msg_out.slice(0, msg_len);
|
|
}
|
|
|
|
|
|
/*
|
|
======================================================
|
|
MAIN FUNCTION
|
|
======================================================
|
|
*/
|
|
|
|
//Generate pre-computed tables
|
|
init_tables();
|
|
var RS = {
|
|
//Encoding
|
|
encode: function(data, nysm){
|
|
return rs_encode_msg(data, nysm);
|
|
},
|
|
|
|
//Decoding
|
|
decode: function(data, nysm, erase_pos){
|
|
return rs_correct_msg(data, nysm, erase_pos);
|
|
}
|
|
|
|
} |