Skip to content

Commit

Permalink
Add RIBLT tests
Browse files Browse the repository at this point in the history
  • Loading branch information
magnified103 committed Nov 12, 2024
1 parent 1e1b9fc commit 01e2e7a
Show file tree
Hide file tree
Showing 9 changed files with 6,025 additions and 32 deletions.
Binary file added .DS_Store
Binary file not shown.
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"biome.lspBin": "./node_modules/.pnpm/@[email protected]/node_modules/@biomejs/cli-darwin-arm64/biome",
"editor.defaultFormatter": "biomejs.biome"
}
313 changes: 313 additions & 0 deletions mypatch.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
diff --git a/packages/node/src/riblt/decoder.ts b/packages/node/src/riblt/decoder.ts
index 9393b3d..1cb2bcb 100644
--- a/packages/node/src/riblt/decoder.ts
+++ b/packages/node/src/riblt/decoder.ts
@@ -1,62 +1,106 @@
-import type { SourceSymbol, CodedSymbol, SourceSymbolFactory } from "./symbol.js";
-import { RandomMapping } from "./mapping.js";
+import type { CodedSymbol } from "./symbol.js";


-export class Decoder<T extends SourceSymbol> {
- decodedSymbols: T[];
+export class Decoder<T extends Symbol<T>> {
+ private cs: CodedSymbol<T>[] = [];
+ private local: CodingWindow<T>;
+ private window: CodingWindow<T>;
+ private remote: CodingWindow<T>;
+ private decodable: number[] = [];
+ private decoded: number = 0;

- constructor(private readonly sourceSymbolFactory: SourceSymbolFactory<T>) {
- this.decodedSymbols = [];
+ constructor() {
+ this.local = new CodingWindow<T>();
+ this.window = new CodingWindow<T>();
+ this.remote = new CodingWindow<T>();
}

- tryDecode(local: CodedSymbol<T>[], remote: CodedSymbol<T>[]): boolean {
- if (local.length !== remote.length) {
- throw Error("The length of coded symbol sequences must be equal");
- }
+ public decoded(): boolean {
+ return this.decoded === this.cs.length;
+ }
+
+ public localSymbols(): HashedSymbol<T>[] {
+ return this.local.symbols;
+ }
+
+ public remoteSymbols(): HashedSymbol<T>[] {
+ return this.remote.symbols;
+ }
+
+ public addSymbol(s: T): void {
+ const th = new HashedSymbol<T>(s, s.hash());
+ this.addHashedSymbol(th);
+ }
+
+ public addHashedSymbol(s: HashedSymbol<T>): void {
+ this.window.addHashedSymbol(s);
+ }

- for (let i = 0; i < local.length; i++) {
- local[i].xor(remote[i]);
+ public addCodedSymbol(c: CodedSymbol<T>): void {
+ c = this.window.applyWindow(c, "remove");
+ c = this.remote.applyWindow(c, "remove");
+ c = this.local.applyWindow(c, "add");
+ this.cs.push(c);
+ if ((c.count === 1 || c.count === -1) && c.hash === c.symbol.hash()) {
+ this.decodable.push(this.cs.length - 1);
+ } else if (c.count === 0 && c.hash === 0) {
+ this.decodable.push(this.cs.length - 1);
}
+ }

- this.decodedSymbols = [];
- const pureSymbols: CodedSymbol<T>[] = [];
- let remaining = 0;
- const isDecoded = new Array(local.length).fill(false);
-
- for (let i = 0; i < local.length; i++) {
- if (local[i].isZero()) {
- isDecoded[i] = true;
- } else {
- remaining++;
- if (local[i].isPure()) {
- pureSymbols.push(local[i]);
- }
+ private applyNewSymbol(t: HashedSymbol<T>, direction: number): RandomMapping {
+ const m = new RandomMapping(t.hash, 0);
+ while (m.lastIdx < this.cs.length) {
+ const cidx = m.lastIdx;
+ this.cs[cidx] = this.cs[cidx].apply(t, direction);
+ if (
+ (this.cs[cidx].count === -1 || this.cs[cidx].count === 1) &&
+ this.cs[cidx].hash === this.cs[cidx].symbol.hash()
+ ) {
+ this.decodable.push(cidx);
}
+ m.nextIndex();
}
+ return m;
+ }

- while (pureSymbols.length > 0) {
- const symbol = pureSymbols.pop() as CodedSymbol<T>;
- if (symbol.isZero()) {
- continue;
- }
- this.decodedSymbols.push(this.sourceSymbolFactory.clone(symbol.sum));
-
- const mapping = new RandomMapping(symbol.checksum, 0);
- while (mapping.lastIdx < local.length) {
- const idx = mapping.lastIdx;
- if (isDecoded[idx]) {
- continue;
- }
- local[idx].xor(symbol);
- if (local[idx].isZero()) {
- isDecoded[idx] = true;
- remaining--;
- } else if (local[idx].isPure()) {
- pureSymbols.push(local[idx]);
- }
+ public tryDecode(): void {
+ for (const didx of this.decodable) {
+ const cidx = this.decodable[didx];
+ const c = this.cs[cidx];
+ switch (c.count) {
+ case 1:
+ const ns1 = new HashedSymbol<T>();
+ ns1.symbol = ns1.symbol.xor(c.symbol);
+ ns1.hash = c.hash;
+ const m1 = this.applyNewSymbol(ns1, -1);
+ this.remote.addHashedSymbolWithMapping(ns1, m1);
+ this.decoded += 1;
+ break;
+ case -1:
+ const ns2 = new HashedSymbol<T>();
+ ns2.symbol = ns2.symbol.xor(c.symbol);
+ ns2.hash = c.hash;
+ const m2 = this.applyNewSymbol(ns2, 1);
+ this.local.addHashedSymbolWithMapping(ns2, m2);
+ this.decoded += 1;
+ break;
+ case 0:
+ this.decoded += 1;
+ break;
+ default:
+ throw new Error("Invalid degree for decodable coded symbol");
}
}
+ this.decodable = [];
+ }

- return remaining === 0;
+ public reset(): void {
+ this.cs = [];
+ this.decodable = [];
+ this.local.reset();
+ this.remote.reset();
+ this.window.reset();
+ this.decoded = 0;
}
}
diff --git a/packages/node/src/riblt/encoder.ts b/packages/node/src/riblt/encoder.ts
index 8847517..b7591ba 100644
--- a/packages/node/src/riblt/encoder.ts
+++ b/packages/node/src/riblt/encoder.ts
@@ -76,7 +76,7 @@ class MappingHeap {
}
}

-export class CodingPrefix<T extends SourceSymbol> {
+class CodingPrefix<T extends SourceSymbol> {
private sourceSymbols: HashedSymbol<T>[];
public codedSymbols: CodedSymbol<T>[];
private mapGenerators: RandomMapping[];
@@ -105,13 +105,12 @@ export class CodingPrefix<T extends SourceSymbol> {
): void {
this.sourceSymbols.push(hashedSymbol);
this.mapGenerators.push(mapping);
- this.queue.push(new SymbolMapping(this.sourceSymbols.length - 1, mapping.lastIdx));
}

extendPrefix(size: number): void {
while (this.queue.size > 0 && this.queue.top.codedIdx < size) {
const mapping = this.queue.pop();
- while (mapping.codedIdx < size) {
+ while (mapping !== undefined && mapping.codedIdx < size) {
const sourceIdx = mapping.sourceIdx;
const codedIdx = mapping.codedIdx;
this.codedSymbols[codedIdx].apply(this.sourceSymbols[sourceIdx], 1);
@@ -122,7 +121,7 @@ export class CodingPrefix<T extends SourceSymbol> {
}
}

-export class Encoder<T extends SourceSymbol> extends CodingPrefix<T> {
+class Encoder<T extends SourceSymbol> extends CodingPrefix<T> {
addSymbol(s: T): void {
super.addSymbol(s);
}
diff --git a/packages/node/src/riblt/symbol.ts b/packages/node/src/riblt/symbol.ts
index 7dbfea9..f85dc9b 100644
--- a/packages/node/src/riblt/symbol.ts
+++ b/packages/node/src/riblt/symbol.ts
@@ -1,73 +1,56 @@
export interface SourceSymbol {
- xor(s: SourceSymbol): void;
- hash(): Uint8Array;
+ xor(s: SourceSymbol): void;
+ hash(): Uint8Array;
}


export interface SourceSymbolFactory<T> {
- empty(): T;
- emptyHash(): Uint8Array;
- clone(s: T): T;
+ empty(): T;
+ emptyHash(): Uint8Array;
+ clone(s: T): T;
}


export class HashedSymbol<T extends SourceSymbol> {
- sum: T;
- checksum: Uint8Array;
-
- constructor(sum: T, checksum: Uint8Array) {
- this.sum = sum;
- this.checksum = checksum;
- }
-
- xor(s: HashedSymbol<T>): void {
- this.sum.xor(s.sum);
- for (let i = 0; i < this.checksum.length; i++) {
- this.checksum[i] ^= s.checksum[i];
- }
- }
-
- isPure(): boolean {
- const checksum = this.sum.hash();
- if (checksum.length !== this.checksum.length) {
- return false;
- }
- for (let i = 0; i < checksum.length; i++) {
- if (checksum[i] !== this.checksum[i]) {
- return false;
- }
- }
- return true;
- }
+ sum: T;
+ checksum: Uint8Array;
+
+ constructor(sum: T, checksum: Uint8Array) {
+ this.sum = sum;
+ this.checksum = checksum;
+ }
+
+ xor(s: HashedSymbol<T>) {
+ this.sum.xor(s.sum);
+ for (let i = 0; i < this.checksum.length; i++) {
+ this.checksum[i] ^= s.checksum[i];
+ }
+ }
+
+ isPure(): boolean {
+ const checksum = this.sum.hash();
+ if (checksum.length !== this.checksum.length) {
+ return false;
+ }
+ for (let i = 0; i < checksum.length; i++) {
+ if (checksum[i] !== this.checksum[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
}

export class CodedSymbol<T extends SourceSymbol> extends HashedSymbol<T> {
- count: number;
-
- constructor(sum: T, checksum: Uint8Array, count = 1) {
- super(sum, checksum);
- this.count = count;
- }
-
- apply(s: HashedSymbol<T>, direction: number) {
- super.xor(s);
- this.count += direction;
- }
+ count: number;

- xor(s: CodedSymbol<T>) {
- super.xor(s);
- this.count -= s.count;
- }
+ constructor(sum: T, checksum: Uint8Array, count = 1) {
+ super(sum, checksum);
+ this.count = count;
+ }

- isZero(): boolean {
- if (this.count !== 0) {
- return false;
- }
- for (let i = 0; i < this.checksum.length; i++) {
- if (this.checksum[i] !== 0) {
- return false;
- }
- }
- return true;
- }
+ apply(s: HashedSymbol<T>, direction: number) {
+ this.xor(s);
+ this.count += direction;
+ }
}
2 changes: 1 addition & 1 deletion packages/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"cli": "tsx ./src/run.ts",
"prebuild": "node -p \"'export const VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts",
"prepack": "tsc -b",
"test": "vitest"
"test": "vitest --disable-console-intercept"
},
"devDependencies": {
"@bufbuild/protobuf": "^2.0.0",
Expand Down
18 changes: 14 additions & 4 deletions packages/node/src/riblt/decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import { CodingPrefix } from "./encoder.js";


export class Decoder<T extends SourceSymbol> extends CodingPrefix<T> {
decodedSymbols: T[];
decodedLocalSymbols: T[];
decodedRemoteSymbols: T[];
isDecoded: boolean[];
remaining: number;
pureSymbols: CodedSymbol<T>[];

constructor(sourceSymbolFactory: SourceSymbolFactory<T>) {
super(sourceSymbolFactory);
this.decodedSymbols = [];
this.decodedLocalSymbols = [];
this.decodedRemoteSymbols = [];
this.isDecoded = [];
this.remaining = 0;
this.pureSymbols = [];
Expand Down Expand Up @@ -42,12 +44,18 @@ export class Decoder<T extends SourceSymbol> extends CodingPrefix<T> {
tryDecode(): boolean {
while (this.pureSymbols.length > 0) {
const symbol = this.pureSymbols.pop() as CodedSymbol<T>;
// console.log(`pure symbol: ${symbol.sum.data} ${symbol.count}`);
console.log(`pure symbol: ${symbol.sum.data} ${symbol.count}`);
if (symbol.isZero()) {
continue;
}
const decodedSymbol = this.sourceSymbolFactory.clone(symbol.sum)
this.decodedSymbols.push(decodedSymbol);
if (symbol.count === 1) {
this.decodedLocalSymbols.push(decodedSymbol);
} else if (symbol.count === -1) {
this.decodedRemoteSymbols.push(decodedSymbol);
} else {
throw Error(`Invalid pure symbol ${symbol.sum.data} ${symbol.count}`);
}

const mapping = new RandomMapping(symbol.checksum, 0);
while (mapping.lastIdx < this.codedSymbols.length) {
Expand All @@ -64,8 +72,10 @@ export class Decoder<T extends SourceSymbol> extends CodingPrefix<T> {
mapping.nextIndex();
}
this.addHashedSymbolWithMapping(new HashedSymbol<T>(decodedSymbol), mapping, -symbol.count);
console.log('abc')
}

console.log(this.remaining);
return this.remaining === 0;
}
}
2 changes: 1 addition & 1 deletion packages/node/src/riblt/encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class CodingPrefix<T extends SourceSymbol> {
this.addHashedSymbol(hashedSymbol);
}

addHashedSymbol(hashedSymbol: HashedSymbol<T>, direction = 1): void {
protected addHashedSymbol(hashedSymbol: HashedSymbol<T>, direction = 1): void {
const mapping = new RandomMapping(hashedSymbol.checksum, 0);
this.addHashedSymbolWithMapping(hashedSymbol, mapping, direction);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/node/src/riblt/symbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class HashedSymbol<T extends SourceSymbol> {
isPure(): boolean {
const checksum = this.sum.hash();
if (checksum.length !== this.checksum.length) {
return false;
throw Error("Checksum length mismatch");
}
for (let i = 0; i < checksum.length; i++) {
if (checksum[i] !== this.checksum[i]) {
Expand Down
Loading

0 comments on commit 01e2e7a

Please sign in to comment.