Skip to content

Instantly share code, notes, and snippets.

@intrnl
Last active November 23, 2022 10:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save intrnl/d0d3b090cb35ff8cbaaef94795039d52 to your computer and use it in GitHub Desktop.
Save intrnl/d0d3b090cb35ff8cbaaef94795039d52 to your computer and use it in GitHub Desktop.
function assert (condition, message = 'Assertion failed') {
if (!condition) {
throw new Error(message);
}
}
export class ByteReader {
/**
* @param {ArrayBuffer} buffer
* @param {number} [offset]
* @param {number} [length]
*/
constructor (buffer, offset = 0, length = buffer.byteLength) {
this.bytes = new Uint8Array(buffer, offset, length);
this.ptr = 0;
}
}
/**
* Read N amount of bytes as unsigned number
* @param {ByteReader} reader Reader interface
* @param {number} n Number of bytes to read
* @param {boolean} [peek] Don't advance the pointer
*/
export function readUnsignedNumber (reader, n, peek = false) {
let result = 0;
let i = 0;
let bytes = reader.bytes;
let ptr = reader.ptr;
let length = bytes.byteLength;
assert(n >= 0, 'n is of non-positive number');
assert(n <= 4, 'n can only read numbers up to 4 bytes long');
assert((length - ptr) > n, 'n is overflowing');
for (; i < n; i++) {
result |= bytes[ptr++] << (i * 8);
}
if (!peek) {
reader.ptr = ptr;
}
return result;
}
/**
* Read N amount of bytes as signed number
* @param {ByteReader} reader Reader interface
* @param {number} n Number of bytes to read
* @param {boolean} [peek] Don't advance the pointer
*/
export function readSignedNumber (reader, n, peek = false) {
let result = readUnsignedNumber(reader, n, peek);
let half = 2 ** ((n * 8) - 1);
let full = half * 2;
return result >= half ? result - full : result;
}
/**
* Read N amount of bytes as string
* @param {ByteReader} reader Reader interface
* @param {number} n Number of bytes to read
* @param {boolean} [peek] Don't advance the pointer
*/
export function readString (reader, n, peek = false) {
let bytes = trim(readBytes(reader, n, peek));
let decoder = new TextDecoder();
return decoder.decode(bytes);
}
/**
* Read N amount of bytes as sub-array
* @param {ByteReader} reader Reader interface
* @param {number} n Number of bytes to read
* @param {boolean} [peek] Don't advance the pointer
*/
export function readBytes (reader, n, peek = false) {
let result = new Uint8Array(n);
let bytes = reader.bytes;
let ptr = reader.ptr;
let length = bytes.byteLength;
assert(n >= 0, 'n is of non-positive number');
assert((length - ptr) > n, 'n is overflowing');
result.set(bytes.subarray(ptr, ptr + n));
if (!peek) {
reader.ptr += n;
}
return result;
}
/**
* Remove trailing null code
* @param {Uint8Array} buffer
* @returns {Uint8Array}
*/
function trim (buffer) {
let idx = 0;
let len = buffer.length;
for (; idx < len; idx++) {
if (buffer[idx] === 0) {
return buffer.subarray(0, idx);
}
}
return buffer;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment