@thi.ng/ksuid 3.1.17 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2023-08-10T12:25:09Z
3
+ - **Last updated**: 2023-08-12T13:14:08Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -9,6 +9,22 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ## [3.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/ksuid@3.2.0) (2023-08-12)
13
+
14
+ #### 🚀 Features
15
+
16
+ - add optional buffer args for various methods ([def0db4](https://github.com/thi-ng/umbrella/commit/def0db4))
17
+ - update IKSUID interface
18
+ - update AKSUID to re-use internal byte buffer for string IDs,
19
+ avoiding allocating new temp arrays
20
+ - refactor .timeOnlyBinary() to avoid internal temp array
21
+ - update tests (re-ordered random bytes, due to [770dbe5d8](https://github.com/thi-ng/umbrella/commit/770dbe5d8))
22
+
23
+ #### ⏱ Performance improvements
24
+
25
+ - update .parse() ([da6765d](https://github.com/thi-ng/umbrella/commit/da6765d))
26
+ - avoid allocation
27
+
12
28
  ### [3.1.15](https://github.com/thi-ng/umbrella/tree/@thi.ng/ksuid@3.1.15) (2023-08-06)
13
29
 
14
30
  #### 🩹 Bug fixes
package/README.md CHANGED
@@ -112,7 +112,7 @@ For Node.js REPL:
112
112
  const ksuid = await import("@thi.ng/ksuid");
113
113
  ```
114
114
 
115
- Package sizes (brotli'd, pre-treeshake): ESM: 809 bytes
115
+ Package sizes (brotli'd, pre-treeshake): ESM: 888 bytes
116
116
 
117
117
  ## Dependencies
118
118
 
@@ -169,40 +169,23 @@ Creating custom IDs:
169
169
  ```ts
170
170
  import { BASE36 } from "@thi.ng/base-n";
171
171
 
172
- // no time shift, 64bit random
172
+ // using base36, no time shift, 64bit random part
173
173
  const id36 = defKSUID32({ base: BASE36, epoch: 0, bytes: 8 });
174
174
 
175
- id32.next();
175
+ id36.next();
176
176
  // '2VOUKH4K59AG0RXR4XH'
177
177
  ```
178
178
 
179
179
  ## Benchmarks
180
180
 
181
- ```text
182
- yarn bench
183
-
184
- benchmarking: b62, 128bit, n=10000
185
- warmup... 659.22ms (10 runs)
186
- executing...
187
- total: 6402.18ms, runs: 100
188
- mean: 64.02ms, median: 63.50ms, range: [59.98..96.15]
189
- q1: 62.64ms, q3: 64.41ms
190
- sd: 6.93%
191
- benchmarking: b62, 64bit, n=10000
192
- warmup... 363.35ms (10 runs)
193
- executing...
194
- total: 3469.28ms, runs: 100
195
- mean: 34.69ms, median: 34.41ms, range: [32.61..56.58]
196
- q1: 33.35ms, q3: 35.41ms
197
- sd: 7.47%
198
- benchmarking: b62, 32bit, n=10000
199
- warmup... 218.78ms (10 runs)
200
- executing...
201
- total: 2118.93ms, runs: 100
202
- mean: 21.19ms, median: 20.95ms, range: [20.20..25.74]
203
- q1: 20.71ms, q3: 21.30ms
204
- sd: 4.14%
205
- ```
181
+ Benchmarks can be run via `yarn bench`. All timings in milliseconds (test
182
+ config: Node v20.4.0, MBA M1 2021, 16GB). The benchmark collects N KSUIDs w/
183
+ different configs in an array, with each case being run 100 times.
184
+
185
+ | Title| Iter| Size| Total| Mean| Median| Min| Max| Q1| Q3| SD%|
186
+ |------------------------|-------:|-------:|-----------:|-------:|-------:|-------:|-------:|-------:|-------:|-------:|
187
+ | b62, 128bit, n=10000| 100| 1| 2158.68| 21.59| 21.57| 19.91| 25.91| 20.42| 21.87| 6.26|
188
+ | b62, 64bit, n=10000| 100| 1| 1200.40| 12.00| 11.95| 11.27| 14.66| 11.82| 12.10| 3.99|
206
189
 
207
190
  ## Authors
208
191
 
package/aksuid.d.ts CHANGED
@@ -11,15 +11,16 @@ export declare abstract class AKSUID implements IKSUID {
11
11
  readonly encodedSize: number;
12
12
  readonly base: BaseN;
13
13
  readonly epoch: number;
14
+ protected tmp: Uint8Array;
14
15
  protected rnd?: IRandom;
15
16
  protected pad: (x: any) => string;
16
17
  protected constructor(epochSize: number, opts: Partial<KSUIDOpts>);
17
18
  next(): string;
18
- nextBinary(): Uint8Array;
19
+ nextBinary(buf?: Uint8Array): Uint8Array;
19
20
  timeOnly(epoch?: number): string;
20
- abstract timeOnlyBinary(epoch?: number): Uint8Array;
21
+ abstract timeOnlyBinary(epoch?: number, buf?: Uint8Array): Uint8Array;
21
22
  fromEpoch(epoch?: number): string;
22
- fromEpochBinary(epoch?: number): Uint8Array;
23
+ fromEpochBinary(epoch?: number, buf?: Uint8Array): Uint8Array;
23
24
  format(buf: Uint8Array): string;
24
25
  abstract parse(id: string): {
25
26
  epoch: number;
package/aksuid.js CHANGED
@@ -15,24 +15,25 @@ export class AKSUID {
15
15
  this.size = this.epochSize + opts.bytes;
16
16
  this.encodedSize = this.base.size(2 ** (this.size * 8) - 1);
17
17
  this.pad = padLeft(this.encodedSize, this.base.base[0]);
18
+ this.tmp = new Uint8Array(this.size);
18
19
  }
19
20
  next() {
20
- return this.format(this.nextBinary());
21
+ return this.format(this.nextBinary(this.tmp));
21
22
  }
22
- nextBinary() {
23
- const buf = this.timeOnlyBinary();
23
+ nextBinary(buf) {
24
+ buf = this.timeOnlyBinary(undefined, buf);
24
25
  return this.rnd
25
26
  ? randomBytesFrom(this.rnd, buf, this.epochSize)
26
27
  : randomBytes(buf, this.epochSize);
27
28
  }
28
29
  timeOnly(epoch) {
29
- return this.format(this.timeOnlyBinary(epoch));
30
+ return this.format(this.timeOnlyBinary(epoch, this.tmp.fill(0, this.epochSize)));
30
31
  }
31
32
  fromEpoch(epoch) {
32
- return this.format(this.fromEpochBinary(epoch));
33
+ return this.format(this.fromEpochBinary(epoch, this.tmp));
33
34
  }
34
- fromEpochBinary(epoch) {
35
- const buf = this.timeOnlyBinary(epoch);
35
+ fromEpochBinary(epoch, buf) {
36
+ buf = this.timeOnlyBinary(epoch, buf);
36
37
  return this.rnd
37
38
  ? randomBytesFrom(this.rnd, buf, this.epochSize)
38
39
  : randomBytes(buf, this.epochSize);
package/api.d.ts CHANGED
@@ -15,9 +15,12 @@ export interface IKSUID {
15
15
  */
16
16
  next(): string;
17
17
  /**
18
- * Returns a new ID as byte array.
18
+ * Returns a new ID as byte array. If `buf` is given, writes result in
19
+ * there, else creates new byte array.
20
+ *
21
+ * @param buf
19
22
  */
20
- nextBinary(): Uint8Array;
23
+ nextBinary(buf?: Uint8Array): Uint8Array;
21
24
  /**
22
25
  * Returns a new baseN encoded ID string for given `epoch` (default: current
23
26
  * time) and with all random payload bytes set to 0.
@@ -27,11 +30,13 @@ export interface IKSUID {
27
30
  timeOnly(epoch?: number): string;
28
31
  /**
29
32
  * Binary version of {@link KSUI.timeOnly}, but returns byte array. The
30
- * first `epochSize` bytes will contain the timestamp.
33
+ * first `epochSize` bytes will contain the timestamp. If `buf` is given,
34
+ * writes result in there, else creates new byte array.
31
35
  *
32
36
  * @param epoch -
37
+ * @param buf -
33
38
  */
34
- timeOnlyBinary(epoch?: number): Uint8Array;
39
+ timeOnlyBinary(epoch?: number, buf?: Uint8Array): Uint8Array;
35
40
  /**
36
41
  * Returns a new formatted ID, composed from user supplied timestamp
37
42
  * (default: current time) and a random payload.
@@ -41,11 +46,13 @@ export interface IKSUID {
41
46
  fromEpoch(epoch?: number): string;
42
47
  /**
43
48
  * Returns a new ID as byte array, composed from user supplied timestamp
44
- * (default: current time) and a random payload.
49
+ * (default: current time) and a random payload. If `buf` is given, writes
50
+ * result in there, else creates new byte array.
45
51
  *
46
52
  * @param epoch
53
+ * @param buf
47
54
  */
48
- fromEpochBinary(epoch?: number): Uint8Array;
55
+ fromEpochBinary(epoch?: number, buf?: Uint8Array): Uint8Array;
49
56
  /**
50
57
  * Returns baseN encoded version of given binary ID (generated via
51
58
  * `.nextBinary()`).
package/ksuid32.d.ts CHANGED
@@ -2,7 +2,7 @@ import { AKSUID } from "./aksuid.js";
2
2
  import type { KSUIDOpts } from "./api.js";
3
3
  export declare class KSUID32 extends AKSUID {
4
4
  constructor(opts?: Partial<KSUIDOpts>);
5
- timeOnlyBinary(epoch?: number): Uint8Array;
5
+ timeOnlyBinary(epoch?: number, buf?: Uint8Array): Uint8Array;
6
6
  parse(id: string): {
7
7
  epoch: number;
8
8
  id: Uint8Array;
package/ksuid32.js CHANGED
@@ -8,14 +8,17 @@ export class KSUID32 extends AKSUID {
8
8
  ...opts,
9
9
  });
10
10
  }
11
- timeOnlyBinary(epoch = Date.now()) {
12
- const buf = new Uint8Array(this.size);
11
+ timeOnlyBinary(epoch = Date.now(), buf) {
12
+ buf = buf || new Uint8Array(this.size);
13
13
  const t = this.ensureTime((epoch - this.epoch) / 1000, MAX_EPOCH) >>> 0;
14
- buf.set([t >>> 24, (t >> 16) & 0xff, (t >> 8) & 0xff, t & 0xff]);
14
+ buf[0] = t >>> 24;
15
+ buf[1] = (t >> 16) & 0xff;
16
+ buf[2] = (t >> 8) & 0xff;
17
+ buf[3] = t & 0xff;
15
18
  return buf;
16
19
  }
17
20
  parse(id) {
18
- const buf = new Uint8Array(this.size);
21
+ const buf = this.tmp;
19
22
  this.base.decodeBytes(id, buf);
20
23
  return {
21
24
  epoch: this.u32(buf) * 1000 + this.epoch,
package/ksuid64.d.ts CHANGED
@@ -2,7 +2,7 @@ import { AKSUID } from "./aksuid.js";
2
2
  import type { KSUIDOpts } from "./api.js";
3
3
  export declare class KSUID64 extends AKSUID {
4
4
  constructor(opts?: Partial<KSUIDOpts>);
5
- timeOnlyBinary(epoch?: number): Uint8Array;
5
+ timeOnlyBinary(epoch?: number, buf?: Uint8Array): Uint8Array;
6
6
  parse(id: string): {
7
7
  epoch: number;
8
8
  id: Uint8Array;
package/ksuid64.js CHANGED
@@ -7,25 +7,23 @@ export class KSUID64 extends AKSUID {
7
7
  ...opts,
8
8
  });
9
9
  }
10
- timeOnlyBinary(epoch = Date.now()) {
11
- const buf = new Uint8Array(this.size);
10
+ timeOnlyBinary(epoch = Date.now(), buf) {
11
+ buf = buf || new Uint8Array(this.size);
12
12
  const t = this.ensureTime(epoch - this.epoch);
13
13
  const h = (t / 4294967296) >>> 0;
14
14
  const l = (t & 4294967295) >>> 0;
15
- buf.set([
16
- h >>> 24,
17
- (h >> 16) & 0xff,
18
- (h >> 8) & 0xff,
19
- h & 0xff,
20
- l >>> 24,
21
- (l >> 16) & 0xff,
22
- (l >> 8) & 0xff,
23
- l & 0xff,
24
- ]);
15
+ buf[0] = h >>> 24;
16
+ buf[1] = (h >> 16) & 0xff;
17
+ buf[2] = (h >> 8) & 0xff;
18
+ buf[3] = h & 0xff;
19
+ buf[4] = l >>> 24;
20
+ buf[5] = (l >> 16) & 0xff;
21
+ buf[6] = (l >> 8) & 0xff;
22
+ buf[7] = l & 0xff;
25
23
  return buf;
26
24
  }
27
25
  parse(id) {
28
- const buf = new Uint8Array(this.size);
26
+ const buf = this.tmp;
29
27
  this.base.decodeBytes(id, buf);
30
28
  return {
31
29
  epoch: this.u32(buf) * 4294967296 + this.u32(buf, 4) + this.epoch,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/ksuid",
3
- "version": "3.1.17",
3
+ "version": "3.2.0",
4
4
  "description": "Configurable K-sortable unique IDs, ULIDs, binary & base-N encoded, 32/48/64bit time resolutions",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -36,14 +36,14 @@
36
36
  "test": "testament test"
37
37
  },
38
38
  "dependencies": {
39
- "@thi.ng/base-n": "^2.5.9",
40
- "@thi.ng/errors": "^2.3.2",
41
- "@thi.ng/random": "^3.5.3",
42
- "@thi.ng/strings": "^3.4.10"
39
+ "@thi.ng/base-n": "^2.5.10",
40
+ "@thi.ng/errors": "^2.3.3",
41
+ "@thi.ng/random": "^3.6.0",
42
+ "@thi.ng/strings": "^3.4.11"
43
43
  },
44
44
  "devDependencies": {
45
- "@microsoft/api-extractor": "^7.36.3",
46
- "@thi.ng/testament": "^0.3.20",
45
+ "@microsoft/api-extractor": "^7.36.4",
46
+ "@thi.ng/testament": "^0.3.21",
47
47
  "rimraf": "^5.0.1",
48
48
  "tools": "^0.0.1",
49
49
  "typedoc": "^0.24.8",
@@ -114,5 +114,5 @@
114
114
  "status": "stable",
115
115
  "year": 2020
116
116
  },
117
- "gitHead": "ad9ac3232c6fc5fc8a0df75ac82fc1e0e9fb0258\n"
117
+ "gitHead": "399f8c53d66b9b763d16c21bb59f145df5f59514\n"
118
118
  }