@hugoalh/sdbm
Version:
A module to get the non-cryptographic hash of the data with algorithm SDBM.
105 lines (104 loc) • 3.2 kB
JavaScript
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;