Skip to content

Commit 3d73b17

Browse files
refactor(ksuid): use milliseconds as unified epoch offsets
- clarify KSUIDOpts.epoch docs - refactor KSUID32 to use milliseconds as `epoch` offset - update tests
1 parent e416e91 commit 3d73b17

File tree

3 files changed

+69
-50
lines changed

3 files changed

+69
-50
lines changed

packages/ksuid/src/api.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,15 @@ export interface KSUIDOpts {
9797
*/
9898
bytes: number;
9999
/**
100-
* Time offset in seconds, relative to standard Unix epoch. This is used to
101-
* extend the time headroom of IDs into the future.
100+
* Time offset in milliseconds, relative to standard Unix epoch. This is
101+
* used to extend the time headroom of IDs into the future.
102102
*
103103
* @remarks
104104
* The default value (for both 32 & 64bit impls) is approx. 2020-09-13,
105-
* meaning this is the T0 epoch for all IDs (providing an additional ~50
106-
* year lifespan compared to the standard 1970-01-01 epoch)
105+
* meaning this is the `t0` base epoch for all generated IDs (providing an
106+
* additional ~50 year lifespan compared to the standard 1970-01-01 epoch)
107107
*
108-
* @defaultValue 1_600_000_000 or 1_600_000_000_000
108+
* @defaultValue 1_600_000_000_000
109109
*/
110110
epoch: number;
111111
}

packages/ksuid/src/ksuid32.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import type { KSUIDOpts } from "./api.js";
44
export class KSUID32 extends AKSUID {
55
constructor(opts?: Partial<KSUIDOpts>) {
66
super(4, {
7-
epoch: 1_600_000_000,
7+
epoch: 1_600_000_000_000,
88
bytes: 16,
99
...opts,
1010
});
1111
}
1212

1313
timeOnlyBinary(epoch = Date.now()) {
1414
const buf = new Uint8Array(this.size);
15-
const t = this.ensureTime((epoch / 1000 - this.epoch) | 0);
15+
const t = this.ensureTime(((epoch - this.epoch) / 1000) | 0);
1616
buf.set([t >>> 24, (t >> 16) & 0xff, (t >> 8) & 0xff, t & 0xff]);
1717
return buf;
1818
}
@@ -21,7 +21,7 @@ export class KSUID32 extends AKSUID {
2121
const buf = new Uint8Array(this.size);
2222
this.base.decodeBytes(id, buf);
2323
return {
24-
epoch: (this.u32(buf) + this.epoch) * 1000,
24+
epoch: this.u32(buf) * 1000 + this.epoch,
2525
id: buf.slice(4),
2626
};
2727
}

packages/ksuid/test/index.ts

Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,65 +3,84 @@ import { group } from "@thi.ng/testament";
33
import * as assert from "assert";
44
import { defKSUID32, defKSUID64, defULID, IKSUID } from "../src/index.js";
55

6-
const check = (
7-
id: IKSUID,
8-
eps: number,
9-
buf: Uint8Array,
10-
epochBuf: Uint8Array
11-
) => {
6+
const check = ({
7+
idgen,
8+
eps,
9+
epoch,
10+
epochId,
11+
id1,
12+
id2,
13+
}: {
14+
idgen: IKSUID;
15+
eps: number;
16+
epoch: number;
17+
epochId: string;
18+
id1: Uint8Array;
19+
id2: Uint8Array;
20+
}) => {
1221
const t = Date.now();
13-
const a = id.timeOnly(t);
14-
assert.strictEqual(a.length, id.encodedSize);
15-
let res = id.parse(a);
22+
let a = idgen.timeOnly(t);
23+
assert.strictEqual(a.length, idgen.encodedSize);
24+
let res = idgen.parse(a);
1625
assert.ok(Math.abs(res.epoch - t) < eps);
17-
assert.deepStrictEqual(res.id, new Uint8Array(id.size - id.epochSize));
18-
const b = id.nextBinary();
19-
assert.deepStrictEqual(b.slice(id.epochSize), buf);
20-
res = id.parse(id.format(b));
26+
assert.deepStrictEqual(
27+
res.id,
28+
new Uint8Array(idgen.size - idgen.epochSize)
29+
);
30+
const b = idgen.nextBinary();
31+
assert.deepStrictEqual(b.slice(idgen.epochSize), id1);
32+
res = idgen.parse(idgen.format(b));
2133
assert.ok(Math.abs(res.epoch - t) < eps);
22-
assert.deepStrictEqual(res.id, buf);
23-
assert.deepStrictEqual(id.fromEpochBinary(1673827200000), epochBuf);
34+
assert.deepStrictEqual(res.id, id1);
35+
36+
a = idgen.fromEpoch(epoch);
37+
assert.strictEqual(a, epochId);
38+
res = idgen.parse(a);
39+
assert.ok(Math.abs(res.epoch - epoch) < 1000);
40+
assert.deepStrictEqual(res.id, id2);
2441
};
2542

2643
group("ksuid", {
2744
ksuid32: () => {
28-
check(
29-
defKSUID32({ rnd: new XsAdd(0xdecafbad) }),
30-
1000 * 2,
31-
new Uint8Array([
45+
check({
46+
idgen: defKSUID32({ rnd: new XsAdd(0xdecafbad) }),
47+
eps: 1000 * 2,
48+
epoch: 1673827200987,
49+
epochId: "0cvXkpEgU5CRFeBfpf2KrwummtA",
50+
id1: new Uint8Array([
3251
170, 213, 122, 63, 189, 122, 161, 143, 91, 187, 80, 231, 61, 17,
3352
112, 238,
3453
]),
35-
new Uint8Array([
36-
4, 102, 131, 128, 226, 90, 28, 179, 222, 71, 112, 20, 59, 2, 22,
37-
112, 98, 25, 104, 28,
38-
])
39-
);
54+
id2: new Uint8Array([
55+
226, 90, 28, 179, 222, 71, 112, 20, 59, 2, 22, 112, 98, 25, 104,
56+
28,
57+
]),
58+
});
4059
},
4160

4261
ksuid64: () => {
43-
check(
44-
defKSUID64({ rnd: new XsAdd(0xdecafbad) }),
45-
1 * 2,
46-
new Uint8Array([
62+
check({
63+
idgen: defKSUID64({ rnd: new XsAdd(0xdecafbad) }),
64+
eps: 1 * 2,
65+
epoch: 1673827200987,
66+
epochId: "000029vWC12Ap6k6ZH00XfKuZGp",
67+
id1: new Uint8Array([
4768
189, 122, 161, 143, 91, 187, 80, 231, 61, 17, 112, 238,
4869
]),
49-
new Uint8Array([
50-
0, 0, 0, 17, 48, 113, 172, 0, 59, 2, 22, 112, 98, 25, 104, 28,
51-
170, 213, 122, 63,
52-
])
53-
);
70+
id2: new Uint8Array([
71+
59, 2, 22, 112, 98, 25, 104, 28, 170, 213, 122, 63,
72+
]),
73+
});
5474
},
5575

5676
ulid: () => {
57-
check(
58-
defULID({ rnd: new XsAdd(0xdecafbad) }),
59-
1 * 2,
60-
new Uint8Array([161, 143, 91, 187, 80, 231, 61, 17, 112, 238]),
61-
new Uint8Array([
62-
1, 133, 183, 224, 44, 0, 98, 25, 104, 28, 170, 213, 122, 63,
63-
189, 122,
64-
])
65-
);
77+
check({
78+
idgen: defULID({ rnd: new XsAdd(0xdecafbad) }),
79+
eps: 1 * 2,
80+
epoch: 1673827200987,
81+
epochId: "01GPVY0BYVC8CPG75ATNX3ZFBT",
82+
id1: new Uint8Array([161, 143, 91, 187, 80, 231, 61, 17, 112, 238]),
83+
id2: new Uint8Array([98, 25, 104, 28, 170, 213, 122, 63, 189, 122]),
84+
});
6685
},
6786
});

0 commit comments

Comments
 (0)