t/dataoptions.js

81 lines
2.2 KiB
JavaScript

import { PLAINTEXT, LANGUAGES } from "./languages.js";
/**
* Data options are stored in a binary header before the payload.
*
* Header format:
*
* Byte 1
* |xxx----y|
* x = amount of extra bytes (other than this byte) in the header (uint)
* y = highest bit of language ID (the rest in byte 2)
*
* Byte 2 (+ lowest bit of byte 1) is language ID (uint), i.e. index of the
* language in the LANGUAGE_NAMES list.
*
* ---
*
* NOTE: If options are set to their default values, the header is minimised
* to not include those bytes if possible.
*/
export class DataOptions {
language = PLAINTEXT;
/**
* Parse options from uncompressed bytes.
* @param {Uint8Array} data
* @returns {Uint8Array} The data without the header.
*/
parseFrom(data) {
const byte1 = data[0];
const totalBytes = (byte1 & 0b11100000) >>> 5;
if (totalBytes >= 1) {
const languageIDLowByte = data[1];
const languageIDHighBit = (byte1 & 0b00000001);
const languageID = (languageIDHighBit << 8) | languageIDLowByte;
if (LANGUAGES.has(languageID)) {
this.language = LANGUAGES.get(languageID);
} else {
this.language = PLAINTEXT;
}
} else {
this.language = PLAINTEXT;
}
return data.subarray(totalBytes + 1);
}
/**
* Serialize options to uncompressed bytes.
* @param {Uint8Array} data
* @returns {Uint8Array} Data with the options in a header.
*/
serializeTo(data) {
let byte1LowBit = null;
const extra_bytes = [];
if (this.language !== PLAINTEXT) {
const languageID = LANGUAGES.get(this.language);
const languageIDLowByte = languageID & 0b011111111;
const languageIDHighBit = languageID & 0b100000000;
byte1LowBit = languageIDHighBit >>> 8;
extra_bytes.unshift(languageIDLowByte);
}
let byte1 = (extra_bytes.length & 0b00000111) << 5;
if (byte1LowBit !== null) {
byte1 |= byte1LowBit;
}
const headerBytes = new Uint8Array([byte1, ...extra_bytes]);
const combined = new Uint8Array(1 + extra_bytes.length + data.length);
combined.set(headerBytes, 0);
combined.set(data, headerBytes.length);
return combined;
}
}