ltcode
Version:
Luby Transform Code implementation.
93 lines (92 loc) • 3.52 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Decoder = void 0;
const utils_1 = require("./utils");
const blockgraph_1 = require("./blockgraph");
const prng_1 = require("./prng");
class Decoder {
constructor() {
this.initialized = false;
this.file_size = null;
this.block_size = null;
this.num_blocks = null;
this.block_graph = new blockgraph_1.BlockGraph(0);
this.prng = null;
}
decode(data) {
if (!this.isBlockValid(data))
return false;
const next_block = this.readBlocks(data).next().value;
return this.consume_block(next_block);
}
getProgress() {
if (!this.initialized || this.num_blocks == null) {
return 0;
}
const eliminated = this.block_graph.eliminated.size;
return Math.round(eliminated / this.num_blocks * 100);
}
result() {
const bytes = this.resultBytes();
return bytes.reduce((acc, byte) => acc + String.fromCharCode(byte), '');
}
resultBytes() {
if (!this.initialized || !this.num_blocks || !this.file_size || !this.block_size) {
throw new Error("Decoder not initialized");
}
const sorted_blocks = Array.from(this.block_graph.eliminated.entries()).sort((a, b) => a[0] - b[0]);
let out_stream = [];
for (let i = 0; i < this.num_blocks; i++) {
const sorted_block = sorted_blocks[i];
const block_data = (0, utils_1.intToBytes)(sorted_block[1], this.block_size, 'big');
if (i < this.num_blocks - 1 || this.file_size % this.block_size === 0) {
out_stream.push(block_data);
}
else {
const x = block_data.subarray(0, this.file_size % this.block_size);
out_stream.push(x);
}
}
return (0, utils_1.concatUint8Arrays)(out_stream);
}
consume_block(lt_block) {
const { size, length, blockseed, block_data } = lt_block;
if (!this.initialized)
this._initialize(length, size, blockseed);
const [_, source_blocks] = this.prng.sample_source_blocks(blockseed);
return this.block_graph.add_block(source_blocks, block_data);
}
_initialize(file_size, block_size, block_seed) {
this.file_size = file_size;
this.block_size = block_size;
this.num_blocks = Math.ceil(file_size / block_size);
this.block_graph = new blockgraph_1.BlockGraph(this.num_blocks);
this.prng = new prng_1.PRNG(this.num_blocks, block_seed);
this.initialized = true;
}
*readBlocks(data) {
const { size, length, seed, data: block_data } = JSON.parse(data);
const block_data_int = BigInt(block_data);
const bytes = (0, utils_1.intToBytes)(block_data_int, Number(size), "little");
const block_data_ = (0, utils_1.intFromBytes)(bytes, "big");
return {
size: Number(size),
length: Number(length),
blockseed: Number(seed),
block_data: block_data_
};
}
isBlockValid(block) {
try {
const { length, size, seed, data } = JSON.parse(block);
return typeof length === 'number' &&
typeof size === 'number' &&
typeof seed === 'number' &&
typeof data === 'string';
}
catch (e) {
return false;
}
}
}
exports.Decoder = Decoder;