@hqtsm/crc
Version:
CRC functions with a parameterized engine optimized for both speed and size
299 lines (279 loc) • 5.31 kB
text/typescript
/**
* @module
*
* The core CRC functions and types.
*/
/**
* An array-like object of 8-bit numbers.
*/
export type CrcData = {
/**
* Number of bytes.
*/
length: number;
/**
* Bytes.
*/
[index: number]: number;
};
/**
* CRC table.
*/
export type CrcTable<T extends number | bigint> = () => T[];
/**
* CRC reflect.
*
* @param data Bits to reflect.
* @param size Number of bits.
* @returns Reflected bits.
*/
export type CrcReflect<T extends number | bigint> = (
data: T,
size: number | bigint,
) => T;
/**
* CRC init.
*
* @returns Initial value.
*/
export type CrcInit<T extends number | bigint> = () => T;
/**
* CRC update.
*
* @param crc Current value.
* @param data Bytes.
* @returns Updated value.
*/
export type CrcUpdate<T extends number | bigint> = (
crc: T,
data: Readonly<CrcData>,
) => T;
/**
* CRC finalize.
*
* @param crc Value to finalize.
* @returns Finalized CRC.
*/
export type CrcFinalize<T extends number | bigint> = (crc: T) => T;
/**
* CRC interface.
*/
export type CrcInterface<T extends number | bigint> = {
/**
* Bit size.
*/
readonly size: T;
/**
* Polynomial.
*/
readonly poly: T;
/**
* Reflect input.
*/
readonly refi: boolean;
/**
* XOR input.
*/
readonly xori: T;
/**
* Reflect output.
*/
readonly refo: boolean;
/**
* XOR output.
*/
readonly xoro: T;
/**
* CRC reflect.
*/
readonly reflect: CrcReflect<T>;
/**
* CRC table.
*/
readonly table: CrcTable<T>;
/**
* CRC init.
*/
readonly init: CrcInit<T>;
/**
* CRC update.
*/
readonly update: CrcUpdate<T>;
/**
* CRC finalize.
*/
readonly finalize: CrcFinalize<T>;
};
/**
* CRC factory.
*
* @param size Bit size.
* @param poly Polynomial.
* @param refi Reflect input.
* @param xori XOR input.
* @param refo Reflect output.
* @param xoro XOR output.
*/
export function crc<T extends number>(
size: number,
poly: number,
refi: boolean,
xori: number,
refo: boolean,
xoro: number,
): CrcInterface<number>;
export function crc<T extends bigint>(
size: bigint,
poly: bigint,
refi: boolean,
xori: bigint,
refo: boolean,
xoro: bigint,
): CrcInterface<bigint>;
export function crc<T extends number | bigint>(
size: T,
poly: T,
refi: boolean,
xori: T,
refo: boolean,
xoro: T,
): CrcInterface<T> {
let t: T[];
let s: number;
let i8 = size - size;
let i0 = i8++;
let i1 = i8++;
let i255 = (i1 << (i8 <<= i8)) - i1;
let h = i1 << size - i1;
let m = h - i1 << i1 | i1;
let n = i0 === 0;
let reflect = (data: T, width: number | bigint) => {
let r = data & i1;
while (--width) {
r = r << i1 | ((data as number) >>= i1) & i1;
}
return (n ? r >>> 0 : r) as T;
};
let table = () => {
let r = size < i8 ? i8 - size : i0;
let l = size - i8 + r;
let a = h << r;
let x = poly << r;
let t = [i0];
let c;
for (let i = i255; i;) {
c = (refi ? reflect(i as T, 8) : i) << l;
for (let j = 8; j--;) {
c = c & a ? c << i1 ^ x : c << i1;
}
c >>= r;
c = refi ? reflect(c as T, size) as number : (c & m);
t[i--] = n ? c >>> 0 : c;
}
return t as T[];
};
return {
size,
poly,
refi,
xori,
refo,
xoro,
reflect,
table,
init: refi ? () => reflect(xori, size) : () => xori,
update: refi
? (
n
? (crc: T, data: Readonly<CrcData>) => {
t ??= table();
for (let i = 0, l = data.length; i < l;) {
crc = (
t[
(crc ^ data[i++]) & i255
] ^ crc >>> i8
) as T;
}
return crc >>> 0 as T;
}
: (crc: T, data: Readonly<CrcData>) => {
t ??= table();
for (let i = 0, l = data.length; i < l;) {
crc = (
t[
Number(crc & i255) ^
data[i++] & 255
] ^ crc >> i8
) as T;
}
return crc;
}
)
: (
(s = size - i8) > 0
? (
n
? (crc: T, data: Readonly<CrcData>) => {
t ??= table();
for (let i = 0, l = data.length; i < l;) {
crc = (
t[
(crc >>> s ^ data[i++]) & i255
] ^ crc << i8 & m
) as T;
}
return crc >>> 0 as T;
}
: (crc: T, data: Readonly<CrcData>) => {
t ??= table();
for (let i = 0, l = data.length; i < l;) {
crc = (
t[
Number(crc >> s & i255) ^
data[i++] & 255
] ^ crc << i8 & m
) as T;
}
return crc as T;
}
)
: (
(s = -s, n)
? (crc: T, data: Readonly<CrcData>) => {
t ??= table();
for (let i = 0, l = data.length; i < l;) {
crc = (
t[
(crc << s ^ data[i++]) & i255
]
) as T;
}
return crc as T;
}
: (crc: T, data: Readonly<CrcData>) => {
t ??= table();
for (let i = 0, l = data.length; i < l;) {
crc = (
t[
Number(crc << s & i255) ^
data[i++] & 255
]
) as T;
}
return crc as T;
}
)
),
finalize: refo === refi
? (
n
? (crc: T) => (crc ^ xoro) >>> 0 as T
: (crc: T) => (crc ^ xoro) as T
)
: (
n
? (crc: T) => (reflect(crc, size) ^ xoro) >>> 0 as T
: (crc: T) => (reflect(crc, size) ^ xoro) as T
),
};
}