UNPKG

@hugoalh/sdbm

Version:

A module to get the non-cryptographic hash of the data with algorithm SDBM.

105 lines (104 loc) 3.2 kB
if (typeof Uint8Array.fromHex === "undefined") { //deno-lint-ignore hugoalh/no-import-dynamic -- Polyfill. await import("es-arraybuffer-base64/Uint8Array.fromHex/auto"); } /** * Get the non-cryptographic hash of the data with algorithm SDBM (32 bits). */ export class SDBM { get [Symbol.toStringTag]() { return "SDBM"; } #freezed = false; #hashHex = null; #hashUint8Array = null; #bin = 0n; /** * Initialize. * @param {SDBMAcceptDataType} [data] Data. Can append later via the method {@linkcode SDBM.update} and {@linkcode SDBM.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 non-cryptographic hash of the data, in Uint8Array. * @returns {Uint8Array} */ hash() { this.#hashUint8Array ??= Uint8Array.fromHex(this.hashHex()); return Uint8Array.from(this.#hashUint8Array); } /** * Get the non-cryptographic hash of the data, in hexadecimal with padding. * @returns {string} */ hashHex() { if (this.#hashHex === null) { this.#hashHex = BigInt.asUintN(32, this.#bin).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 {SDBMAcceptDataType} 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") ? data : new TextDecoder().decode(data); for (let index = 0; index < dataFmt.length; index += 1) { this.#bin = BigInt(dataFmt.charCodeAt(index)) + (this.#bin << 6n) + (this.#bin << 16n) - this.#bin; } return this; } /** * Append data from the readable stream. * @param {ReadableStream<SDBMAcceptDataType>} stream Data from the readable stream. * @returns {Promise<this>} */ async updateFromStream(stream) { const reader = stream.getReader(); let done = false; let textDecoder; while (!done) { const { done: end, value } = await reader.read(); done = end; if (typeof value === "undefined") { continue; } if (typeof value === "string") { this.update(value); } else { textDecoder ??= new TextDecoder(); this.update(textDecoder.decode(value, { stream: !done })); } } return this; } } export default SDBM;