@thi.ng/ksuid
Version:
Configurable K-sortable unique IDs, ULIDs, binary & base-N encoded, 32/48/64bit time resolutions
64 lines (63 loc) • 1.75 kB
JavaScript
import { BASE62 } from "@thi.ng/base-n/62";
import { assert } from "@thi.ng/errors/assert";
import { randomBytes, randomBytesFrom } from "@thi.ng/random/random-bytes";
import { padLeft } from "@thi.ng/strings/pad-left";
class AKSUID {
constructor(epochSize, opts) {
this.epochSize = epochSize;
this.base = opts.base || BASE62;
this.rnd = opts.rnd;
this.epoch = opts.epoch;
this.size = this.epochSize + opts.bytes;
this.encodedSize = this.base.size(2 ** (this.size * 8) - 1);
this.pad = padLeft(this.encodedSize, this.base.base[0]);
this.tmp = new Uint8Array(this.size);
}
size;
encodedSize;
base;
epoch;
tmp;
rnd;
pad;
next() {
return this.format(this.nextBinary(this.tmp));
}
nextBinary(buf) {
return this.fromEpochBinary(void 0, buf);
}
timeOnly(epoch) {
return this.format(
this.timeOnlyBinary(epoch, this.tmp.fill(0, this.epochSize))
);
}
fromEpoch(epoch) {
return this.format(this.fromEpochBinary(epoch, this.tmp));
}
fromEpochBinary(epoch, buf) {
buf = this.timeOnlyBinary(epoch, buf);
return this.rnd ? randomBytesFrom(this.rnd, buf, this.epochSize) : randomBytes(buf, this.epochSize);
}
format(buf) {
this.ensureSize(buf);
return this.pad(this.base.encodeBytes(buf));
}
ensureSize(buf) {
assert(
buf.length == this.size,
`illegal KSUID size, expected ${this.size} bytes`
);
return buf;
}
ensureTime(t, max) {
assert(t >= 0, "configured base epoch must be in the past");
max && assert(t <= max, `given epoch is out of range ([0...${max}])`);
return t;
}
u32(buf, i = 0) {
return (buf[i] << 24 | buf[i + 1] << 16 | buf[i + 2] << 8 | buf[i + 3]) >>> 0;
}
}
export {
AKSUID
};