Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
64959e0
chore(logs): remove the use of a global setInterval
JoTurk Apr 4, 2025
b7cb3fe
chore(Utils): test Utils.ts
JoTurk Apr 4, 2025
3adb92f
fix(utils.ts): fix bugs across all utils, based on the test units
JoTurk Apr 4, 2025
69ee659
chore(BinaryReader): test BinaryReader
JoTurk Apr 4, 2025
94ebef9
chore(typescript): temporarily suppress the TypeScript errors trigger…
JoTurk Apr 4, 2025
7126ff7
chore(test): test FixMap.spec.ts
JoTurk Apr 4, 2025
0535611
fix(test): add 2ms to prevent flaky test runs
JoTurk Apr 4, 2025
fabd17d
chore(EpochTime): add tests
JoTurk Apr 7, 2025
4980dce
chore(SDP): add test
JoTurk Apr 7, 2025
299ebc6
chore(BitReader): add tests
JoTurk Apr 7, 2025
3423509
fix(BitReader): fix memory memory overflow in read(bits)
JoTurk Apr 7, 2025
1057187
chore(ByteRate): add tests
JoTurk Apr 8, 2025
f297929
fix(ByteRate): soft rewrite and fixes
JoTurk Apr 8, 2025
3eb33f9
chore(Queue): add tests
JoTurk Apr 8, 2025
376754d
fix(Queue): queue ignored push to queues without capacity
JoTurk Apr 8, 2025
9ef329c
chore(code): avoid weak equality
JoTurk Apr 8, 2025
e59e043
fix(Log): add tests
JoTurk Apr 8, 2025
da26e3a
chore(Number): add tests
JoTurk Apr 8, 2025
70268a0
fix(Number): fix numbers stats, minimum was always 0
JoTurk Apr 8, 2025
157a027
chore(Number): document why we return 0 for default minimum and maximum
JoTurk Apr 8, 2025
abbf50b
chore(WebSocketReliable): add test
JoTurk Apr 8, 2025
fdba2ff
fix(tests): adjust to the original origin logic
JoTurk Apr 10, 2025
0af343b
chore(test): add canvas for epoch testing
JoTurk Apr 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
587 changes: 587 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@typescript-eslint/eslint-plugin": "~6.19.0",
"@typescript-eslint/parser": "~6.19.0",
"@vitest/coverage-istanbul": "^3.1.1",
"canvas": "^3.1.0",
"eslint": "~8.56.0",
"eslint-plugin-headers": "~1.0.4",
"husky": "~8.0.3",
Expand Down
184 changes: 184 additions & 0 deletions src/BinaryReader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/**
* Copyright 2024 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/

import { describe, it, expect } from 'vitest';
import { BinaryReader } from './BinaryReader';

describe('BinaryReader', () => {
describe('constructor', () => {
it('should create reader from Uint8Array', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BinaryReader(data);
expect(reader.size()).toBe(4);
expect(reader.position()).toBe(0);
});

it('should create reader from ArrayBuffer', () => {
const buffer = new ArrayBuffer(4);
new Uint8Array(buffer).set([1, 2, 3, 4]);
const reader = new BinaryReader(buffer);
expect(reader.size()).toBe(4);
expect(reader.position()).toBe(0);
});
});

describe('basic operations', () => {
it('should read and return correct data', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BinaryReader(data);
expect(reader.data()).toEqual(data);
});

it('should return correct size', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BinaryReader(data);
expect(reader.size()).toBe(4);
});

it('should return correct available bytes', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BinaryReader(data);
expect(reader.available()).toBe(4);
reader.next(2);
expect(reader.available()).toBe(2);
});

it('should return correct value at position', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BinaryReader(data);
expect(reader.value(0)).toBe(1);
expect(reader.value(2)).toBe(3);
});
});

describe('position management', () => {
it('should reset position correctly', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BinaryReader(data);
reader.next(2);
reader.reset();
expect(reader.position()).toBe(0);
});

it('should handle reset with custom position', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BinaryReader(data);
reader.reset(2);
expect(reader.position()).toBe(2);
});

it('should clamp reset position to valid range', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BinaryReader(data);
reader.reset(10);
expect(reader.position()).toBe(4);
});
});

describe('reading operations', () => {
it('should read 8-bit values correctly', () => {
const data = new Uint8Array([255, 128, 64, 32]);
const reader = new BinaryReader(data);
expect(reader.read8()).toBe(255);
expect(reader.read8()).toBe(128);
expect(reader.read8()).toBe(64);
expect(reader.read8()).toBe(32);
});

it('should read 16-bit values correctly', () => {
const data = new Uint8Array([0xff, 0xff, 0x80, 0x00, 0x40, 0x00]);
const reader = new BinaryReader(data);
expect(reader.read16()).toBe(65535);
expect(reader.read16()).toBe(32768);
expect(reader.read16()).toBe(16384);
});

it('should read 24-bit values correctly', () => {
const data = new Uint8Array([0xff, 0xff, 0xff, 0x80, 0x00, 0x00]);
const reader = new BinaryReader(data);
expect(reader.read24()).toBe(16777215);
expect(reader.read24()).toBe(8388608);
});

it('should read 32-bit values correctly', () => {
const data = new Uint8Array([0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00]);
const reader = new BinaryReader(data);
expect(reader.read32()).toBe(4294967295);
expect(reader.read32()).toBe(2147483648);
});

it('should read 64-bit values correctly', () => {
const data = new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]);
const reader = new BinaryReader(data);
expect(reader.read64()).toBe(255);
});

it('should read floating point values correctly', () => {
const data = new Uint8Array([0x3f, 0x80, 0x00, 0x00]); // 1.0 in IEEE 754
const reader = new BinaryReader(data);
expect(reader.readFloat()).toBe(1.0);
});

it('should read double values correctly', () => {
const data = new Uint8Array([0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); // 1.0 in IEEE 754
const reader = new BinaryReader(data);
expect(reader.readDouble()).toBe(1.0);
});
});

describe('special reading operations', () => {
it('should read 7-bit encoded values correctly', () => {
const data = new Uint8Array([0x81, 0x01]); // 129 in 7-bit encoding
const reader = new BinaryReader(data);
expect(reader.read7Bit()).toBe(129);
});

it('should read strings correctly', () => {
const text = 'Hello\0';
const data = new Uint8Array(text.split('').map(c => c.charCodeAt(0)));
const reader = new BinaryReader(data);
expect(reader.readString()).toBe('Hello');
});

it('should read hex values correctly', () => {
const data = new Uint8Array([0xff, 0x00, 0xaa]);
const reader = new BinaryReader(data);
expect(reader.readHex(3)).toBe('ff00aa');
});

it('should read bytes correctly', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BinaryReader(data);
expect(reader.read(2)).toEqual(new Uint8Array([1, 2]));
});
});

describe('edge cases', () => {
it('should handle reading beyond buffer size', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BinaryReader(data);
reader.next(4);
expect(reader.read8()).toBe(0);
expect(reader.read16()).toBe(0);
expect(reader.read32()).toBe(0);
});

it('should handle shrinking buffer', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BinaryReader(data);
reader.next(2);
expect(reader.shrink(1)).toBe(1);
expect(reader.available()).toBe(1);
});

it('should handle empty buffer', () => {
const reader = new BinaryReader(new Uint8Array(0));
expect(reader.size()).toBe(0);
expect(reader.available()).toBe(0);
expect(reader.read8()).toBe(0);
});
});
});
167 changes: 167 additions & 0 deletions src/BitReader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/**
* Copyright 2024 Ceeblue B.V.
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
*/

import { describe, it, expect } from 'vitest';
import { BitReader } from './BitReader';

describe('BitReader', () => {
describe('constructor', () => {
it('should create reader from Uint8Array', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BitReader(data);
expect(reader.size()).toBe(4);
expect(reader.available()).toBe(32);
});

it('should create reader from ArrayBuffer', () => {
const buffer = new ArrayBuffer(4);
new Uint8Array(buffer).set([1, 2, 3, 4]);
const reader = new BitReader(buffer);
expect(reader.size()).toBe(4);
expect(reader.available()).toBe(32);
});
});

describe('basic operations', () => {
it('should read and return correct data', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BitReader(data);
expect(reader.data()).toEqual(data);
});

it('should return correct size', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BitReader(data);
expect(reader.size()).toBe(4);
});

it('should return correct available bits', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BitReader(data);
expect(reader.available()).toBe(32);
reader.next(8);
expect(reader.available()).toBe(24);
});

it('should handle next operation correctly', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BitReader(data);
expect(reader.next(8)).toBe(8);
expect(reader.available()).toBe(24);
});
});

describe('bit reading operations', () => {
it('should read single bits correctly', () => {
const data = new Uint8Array([0b10101010]);
const reader = new BitReader(data);
expect(reader.read(1)).toBe(1);
expect(reader.read(1)).toBe(0);
expect(reader.read(1)).toBe(1);
expect(reader.read(1)).toBe(0);
expect(reader.read(1)).toBe(1);
expect(reader.read(1)).toBe(0);
expect(reader.read(1)).toBe(1);
expect(reader.read(1)).toBe(0);
});

it('should read multiple bits correctly', () => {
const data = new Uint8Array([0b10101010]);
const reader = new BitReader(data);
expect(reader.read(2)).toBe(0b10);
expect(reader.read(2)).toBe(0b10);
expect(reader.read(2)).toBe(0b10);
expect(reader.read(2)).toBe(0b10);
});

it('should read across byte boundaries correctly', () => {
const data = new Uint8Array([0b11110000, 0b00001111]);
const reader = new BitReader(data);
expect(reader.read(4)).toBe(0b1111);
expect(reader.read(4)).toBe(0b0000);
expect(reader.read(4)).toBe(0b0000);
expect(reader.read(4)).toBe(0b1111);
});
});

describe('numeric reading operations', () => {
it('should read 8-bit values correctly', () => {
const data = new Uint8Array([0xff, 0x80, 0x40, 0x20]);
const reader = new BitReader(data);
expect(reader.read8()).toBe(0xff);
expect(reader.read8()).toBe(0x80);
expect(reader.read8()).toBe(0x40);
expect(reader.read8()).toBe(0x20);
});

it('should read 16-bit values correctly', () => {
const data = new Uint8Array([0xff, 0xff, 0x80, 0x00, 0x40, 0x00]);
const reader = new BitReader(data);
expect(reader.read16()).toBe(0xffff);
expect(reader.read16()).toBe(0x8000);
expect(reader.read16()).toBe(0x4000);
});

it('should read 24-bit values correctly', () => {
const data = new Uint8Array([0xff, 0xff, 0xff, 0x80, 0x00, 0x00]);
const reader = new BitReader(data);
expect(reader.read24()).toBe(0xffffff);
expect(reader.read24()).toBe(0x800000);
});

it('should read 32-bit values correctly', () => {
const data = new Uint8Array([0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00]);
const reader = new BitReader(data);
expect(reader.read32()).toBe(0xffffffff);
expect(reader.read32()).toBe(0x80000000);
});
});

describe('exponential golomb coding', () => {
it('should read exp golomb codes correctly', () => {
// Test case: 0, 1, 2, 3, 4
// Binary representation: 1, 010, 011, 00100, 00101
const data = new Uint8Array([0b10100110, 0b01000010, 0b10000000]);
const reader = new BitReader(data);
expect(reader.readExpGolomb()).toBe(0);
expect(reader.readExpGolomb()).toBe(1);
expect(reader.readExpGolomb()).toBe(2);
expect(reader.readExpGolomb()).toBe(3);
expect(reader.readExpGolomb()).toBe(4);
});

it('should handle exp golomb codes exceeding 16 bits', () => {
const data = new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00]);
const reader = new BitReader(data);
expect(reader.readExpGolomb()).toBe(0);
});
});

describe('edge cases', () => {
it('should handle reading beyond buffer size', () => {
const data = new Uint8Array([1, 2, 3, 4]);
const reader = new BitReader(data);
reader.next(32);
expect(reader.read(1)).toBe(0);
expect(reader.read(8)).toBe(0);
});

it('should handle empty buffer', () => {
const reader = new BitReader(new Uint8Array(0));
expect(reader.size()).toBe(0);
expect(reader.available()).toBe(0);
expect(reader.read(1)).toBe(0);
});

it('should handle partial byte reads', () => {
const data = new Uint8Array([0b11110000]);
const reader = new BitReader(data);
expect(reader.read(4)).toBe(0b1111);
expect(reader.read(4)).toBe(0b0000);
expect(reader.read(4)).toBe(0);
});
});
});
4 changes: 2 additions & 2 deletions src/BitReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ export class BitReader extends Loggable {
read(count = 1): number {
let result = 0;
while (this._position !== this._size && count--) {
result <<= 1;
result = result * 2; // Multiply instead of shifting
if (this._data[this._position] & (0x80 >> this._bit++)) {
result |= 1;
result += 1;
}
if (this._bit === 8) {
this._bit = 0;
Expand Down
Loading