UNPKG

@hugoalh/adler32

Version:

A module to get the checksum of the data with algorithm Adler32.

98 lines (97 loc) 2.83 kB
/** * Get the checksum of the data with algorithm Adler32. */ export class Adler32 { get [Symbol.toStringTag]() { return "Adler32"; } #freezed = false; #hashHex = null; #hashUint8Array = null; #a = 1n; #b = 0n; /** * Initialize. * @param {Adler32AcceptDataType} [data] Data. Can append later via the method {@linkcode Adler32.update} and {@linkcode Adler32.updateFromStream}. */ constructor(data) { if (typeof data !== "undefined") { this.update(data); } } /** * Whether the instance is freezed. * @returns {boolean} */ get freezed() { return this.#freezed; } /** * Freeze the instance to prevent any further update. * @returns {this} */ freeze() { this.#freezed = true; return this; } /** * Get the checksum of the data, in Uint8Array. * @returns {Uint8Array} */ hash() { if (this.#hashUint8Array === null) { const hex = this.hashHex(); const bytes = []; for (let index = 0; index < hex.length; index += 2) { bytes.push(hex.slice(index, index + 2)); } this.#hashUint8Array = Uint8Array.from(bytes.map((byte) => { return Number.parseInt(byte, 16); })); } return Uint8Array.from(this.#hashUint8Array); } /** * Get the checksum of the data, in hexadecimal with padding. * @returns {string} */ hashHex() { if (this.#hashHex === null) { this.#hashHex = (this.#b * 65536n + this.#a).toString(16).toUpperCase().padStart(8, "0"); if (this.#hashHex.length !== 8) { throw new Error(`Unexpected hash hex result \`${this.#hashHex}\`! Please submit a bug report.`); } } return this.#hashHex; } /** * Append data. * @param {Adler32AcceptDataType} data Data. * @returns {this} */ update(data) { if (this.#freezed) { throw new Error(`Instance is freezed!`); } this.#hashHex = null; this.#hashUint8Array = null; const dataFmt = (typeof data === "string") ? new TextEncoder().encode(data) : data; for (const byte of dataFmt) { this.#a = (this.#a + BigInt(byte)) % 65521n; this.#b = (this.#b + this.#a) % 65521n; } return this; } /** * Append data from the readable stream. * @param {ReadableStream<Adler32AcceptDataType>} stream Data from the readable stream. * @returns {Promise<this>} */ async updateFromStream(stream) { for await (const chunk of stream) { this.update(chunk); } return this; } } export default Adler32;