@thi.ng/ksuid 3.1.15 → 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-06T09:21:31Z
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
@@ -1,6 +1,7 @@
1
1
  <!-- This file is generated - DO NOT EDIT! -->
2
+ <!-- Please see: https://github.com/thi-ng/umbrella/blob/develop/CONTRIBUTING.md#changes-to-readme-files -->
2
3
 
3
- # ![@thi.ng/ksuid](https://media.thi.ng/umbrella/banners-20220914/thing-ksuid.svg?2a899961)
4
+ # ![@thi.ng/ksuid](https://media.thi.ng/umbrella/banners-20230807/thing-ksuid.svg?2a899961)
4
5
 
5
6
  [![npm version](https://img.shields.io/npm/v/@thi.ng/ksuid.svg)](https://www.npmjs.com/package/@thi.ng/ksuid)
6
7
  ![npm downloads](https://img.shields.io/npm/dm/@thi.ng/ksuid.svg)
@@ -87,7 +88,7 @@ implementations already used milliseconds.
87
88
 
88
89
  ## Related packages
89
90
 
90
- - [@thi.ng/base-n](https://github.com/thi-ng/umbrella/tree/develop/packages/base-n) - Arbitrary base-n conversions w/ presets for base16/32/36/58/62/64/85, support for arrays & bigints
91
+ - [@thi.ng/base-n](https://github.com/thi-ng/umbrella/tree/develop/packages/base-n) - Arbitrary base-n conversions w/ presets for base8/16/32/36/58/62/64/85, support for bigints and encoding/decoding of byte arrays
91
92
  - [@thi.ng/idgen](https://github.com/thi-ng/umbrella/tree/develop/packages/idgen) - Generator of opaque numeric identifiers with optional support for ID versioning and efficient re-use
92
93
  - [@thi.ng/random](https://github.com/thi-ng/umbrella/tree/develop/packages/random) - Pseudo-random number generators w/ unified API, distributions, weighted choices, ID generation
93
94
 
@@ -111,7 +112,7 @@ For Node.js REPL:
111
112
  const ksuid = await import("@thi.ng/ksuid");
112
113
  ```
113
114
 
114
- Package sizes (brotli'd, pre-treeshake): ESM: 809 bytes
115
+ Package sizes (brotli'd, pre-treeshake): ESM: 888 bytes
115
116
 
116
117
  ## Dependencies
117
118
 
@@ -168,40 +169,23 @@ Creating custom IDs:
168
169
  ```ts
169
170
  import { BASE36 } from "@thi.ng/base-n";
170
171
 
171
- // no time shift, 64bit random
172
+ // using base36, no time shift, 64bit random part
172
173
  const id36 = defKSUID32({ base: BASE36, epoch: 0, bytes: 8 });
173
174
 
174
- id32.next();
175
+ id36.next();
175
176
  // '2VOUKH4K59AG0RXR4XH'
176
177
  ```
177
178
 
178
179
  ## Benchmarks
179
180
 
180
- ```text
181
- yarn bench
182
-
183
- benchmarking: b62, 128bit, n=10000
184
- warmup... 659.22ms (10 runs)
185
- executing...
186
- total: 6402.18ms, runs: 100
187
- mean: 64.02ms, median: 63.50ms, range: [59.98..96.15]
188
- q1: 62.64ms, q3: 64.41ms
189
- sd: 6.93%
190
- benchmarking: b62, 64bit, n=10000
191
- warmup... 363.35ms (10 runs)
192
- executing...
193
- total: 3469.28ms, runs: 100
194
- mean: 34.69ms, median: 34.41ms, range: [32.61..56.58]
195
- q1: 33.35ms, q3: 35.41ms
196
- sd: 7.47%
197
- benchmarking: b62, 32bit, n=10000
198
- warmup... 218.78ms (10 runs)
199
- executing...
200
- total: 2118.93ms, runs: 100
201
- mean: 21.19ms, median: 20.95ms, range: [20.20..25.74]
202
- q1: 20.71ms, q3: 21.30ms
203
- sd: 4.14%
204
- ```
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|
205
189
 
206
190
  ## Authors
207
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.15",
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.7",
40
- "@thi.ng/errors": "^2.3.0",
41
- "@thi.ng/random": "^3.5.1",
42
- "@thi.ng/strings": "^3.4.8"
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.18",
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": "888293c74f2e9a481d500d4ec319f1e2bd765ec6\n"
117
+ "gitHead": "399f8c53d66b9b763d16c21bb59f145df5f59514\n"
118
118
  }