undedoloremque
Version:
Green Field JS SDK
189 lines (156 loc) • 5.29 kB
JavaScript
import { sha256 } from 'ethereum-cryptography/sha256.js';
import { galMulSlice, galMulSliceXor } from './galois';
import { buildMatrix } from './matrix';
import { concat, getIntegrityUint8Array, splitPrice, toBase64 } from './utils';
export class ReedSolomon {
constructor(
dataShards = 4,
parityShards = 2,
// 16 * 1024 * 1024 , 16m
segmentSize = 16777216,
) {
this.parityShards = parityShards;
this.dataShards = dataShards;
this.totalShards = dataShards + parityShards;
this.segmentSize = segmentSize;
}
_allocAligned(shards, each) {
// const eachAligned = (parseInt((each + 63) / 64)) * 64; // Use Math.ceil instead of ((each + 63) / 64) * 64
const eachAligned = ((each + 63) >>> 6) << 6;
let total = new ArrayBuffer(eachAligned * shards + 63);
// Allocate slices
let res = new Array(shards);
for (let i = 0; i < shards; i++) {
res[i] = new Uint8Array(total, i * eachAligned, each);
total = new Uint8Array(total.slice(eachAligned));
}
return res;
}
_split(data) {
if (data.length === 0) {
return [];
}
if (this.totalShards === 1) {
return [data];
}
const dataLen = data.length;
const perShard = parseInt((dataLen + this.dataShards - 1) / this.dataShards);
const needTotal = this.totalShards * perShard;
// console.log('dataLen', dataLen)
// console.log('needTotal', needTotal)
// console.log('perShard', perShard)
let tmp = Array.prototype.slice.call(data);
if (this.segmentSize > data.length) {
if (this.segmentSize > needTotal) {
tmp = tmp.slice(0, needTotal);
for (let i = data.length; i < needTotal; i++) {
tmp.push(0);
}
} else {
for (let i = data.length; i < this.segmentSize; i++) {
tmp[i] = 0;
}
}
}
data = Uint8Array.from(tmp);
let padding = [];
if (data.length < needTotal) {
const fullShards = data.length / perShard;
// padding = new Array(this.totalShards - fullShards).fill(0);
padding = this._allocAligned(this.totalShards - fullShards, perShard);
if (dataLen > perShard * fullShards) {
const copyFrom = data.slice(perShard * fullShards, dataLen);
for (let i = 0; i < padding.length; i++) {
if (copyFrom.length > 0) {
padding[i] = copyFrom.slice(0, perShard);
copyFrom = copyFrom.slice(perShard);
}
}
}
}
// split data to same length price
const dst = new Array(this.totalShards);
let i = 0;
for (; i < dst.length && data.length >= perShard; i++) {
dst[i] = data.slice(0, perShard);
data = data.slice(perShard);
}
for (let j = 0; i + j < dst.length; j++) {
dst[i + j] = padding[j];
padding[j] = padding[j + 1];
}
return dst;
}
_codeSomeShards(matrixRows, inputs, outputs) {
let start = 0;
let end = inputs[0].length;
while (start < inputs[0].length) {
for (let c = 0; c < inputs.length; c++) {
const ins = inputs[c].slice(start, end);
for (let iRow = 0; iRow < outputs.length; iRow++) {
if (c === 0) {
outputs[iRow] = galMulSlice(matrixRows[iRow][c], ins, outputs[iRow].slice(start, end));
} else {
outputs[iRow] = galMulSliceXor(
matrixRows[iRow][c],
ins,
outputs[iRow].slice(start, end),
);
}
}
}
start = end;
// end += r.o.perRound;
end += 1398144;
if (end > inputs[0].length) {
end = inputs[0].length;
}
}
return concat(inputs, outputs);
}
encodeSegment(data) {
if (data.length == 0) throw new Error('data buffer length is 0');
const shared = this._split(data);
const output = shared.slice(this.dataShards);
// r.m
const matrix = buildMatrix(this.totalShards, this.dataShards);
let parity = [];
for (let i = 0; i < this.parityShards; i++) {
parity.push(matrix[this.dataShards + i]);
}
return this._codeSomeShards(
parity,
shared.slice(0, this.dataShards),
output.slice(0, this.parityShards),
shared[0].length,
);
}
encode(sourceData) {
if (sourceData.length == 0) {
throw new Error('file buffer is empty');
}
const chunkList = splitPrice(sourceData, this.segmentSize);
let encodeDataHashList = new Array(this.totalShards);
for (let i = 0; i < encodeDataHashList.length; i++) {
encodeDataHashList[i] = [];
}
let hashList = [];
let segChecksumList = [];
for (let i = 0; i < chunkList.length; i++) {
const data = chunkList[i];
// console.log('data i', i)
const encodeShards = this.encodeSegment(data);
// console.log('data done', i)
segChecksumList.push(sha256(data));
for (let i = 0; i < encodeShards.length; i++) {
const priceHash = sha256(encodeShards[i]);
encodeDataHashList[i].push(priceHash);
}
}
hashList[0] = sha256(getIntegrityUint8Array(segChecksumList));
for (let i = 0; i < encodeDataHashList.length; i++) {
hashList[i + 1] = sha256(getIntegrityUint8Array(encodeDataHashList[i]));
}
return toBase64(hashList);
}
}