@thi.ng/idgen
Advanced tools
Comparing version 2.2.14 to 2.2.15
@@ -1,2 +0,6 @@ | ||
export const EVENT_ADDED = "added"; | ||
export const EVENT_REMOVED = "removed"; | ||
const EVENT_ADDED = "added"; | ||
const EVENT_REMOVED = "removed"; | ||
export { | ||
EVENT_ADDED, | ||
EVENT_REMOVED | ||
}; |
# Change Log | ||
- **Last updated**: 2023-12-09T19:12:03Z | ||
- **Last updated**: 2023-12-11T10:07:09Z | ||
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub) | ||
@@ -5,0 +5,0 @@ |
350
idgen.js
@@ -1,181 +0,185 @@ | ||
import { __decorate } from "tslib"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __decorateClass = (decorators, target, key, kind) => { | ||
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; | ||
for (var i = decorators.length - 1, decorator; i >= 0; i--) | ||
if (decorator = decorators[i]) | ||
result = (kind ? decorator(target, key, result) : decorator(result)) || result; | ||
if (kind && result) | ||
__defProp(target, key, result); | ||
return result; | ||
}; | ||
import { INotifyMixin } from "@thi.ng/api/mixins/inotify"; | ||
import { assert } from "@thi.ng/errors/assert"; | ||
import { EVENT_ADDED, EVENT_REMOVED } from "./api.js"; | ||
let IDGen = class IDGen { | ||
ids; | ||
nextID; | ||
_freeID; | ||
start; | ||
num; | ||
_capacity; | ||
mask; | ||
vmask; | ||
shift; | ||
constructor(bits = 32, vbits = 32 - bits, cap = 2 ** bits, start = 0) { | ||
const maxCap = 2 ** bits; | ||
assert(bits > 0 && bits + vbits <= 32, "wrong total bit size [1..32]"); | ||
assert(cap <= maxCap, `requested capacity too large for bit size (max. ${maxCap})`); | ||
this.ids = []; | ||
this.nextID = start; | ||
this.start = start; | ||
this._capacity = cap; | ||
this.num = 0; | ||
this.mask = maxCap - 1; | ||
this.vmask = (1 << vbits) - 1; | ||
this.shift = bits; | ||
this._freeID = -1; | ||
let IDGen = class { | ||
ids; | ||
nextID; | ||
_freeID; | ||
start; | ||
num; | ||
_capacity; | ||
mask; | ||
vmask; | ||
shift; | ||
constructor(bits = 32, vbits = 32 - bits, cap = 2 ** bits, start = 0) { | ||
const maxCap = 2 ** bits; | ||
assert(bits > 0 && bits + vbits <= 32, "wrong total bit size [1..32]"); | ||
assert( | ||
cap <= maxCap, | ||
`requested capacity too large for bit size (max. ${maxCap})` | ||
); | ||
this.ids = []; | ||
this.nextID = start; | ||
this.start = start; | ||
this._capacity = cap; | ||
this.num = 0; | ||
this.mask = maxCap - 1; | ||
this.vmask = (1 << vbits) - 1; | ||
this.shift = bits; | ||
this._freeID = -1; | ||
} | ||
/** | ||
* Extract actual ID (without version bits). | ||
* | ||
* @param id - | ||
*/ | ||
id(id) { | ||
return id & this.mask; | ||
} | ||
/** | ||
* Extract version from ID | ||
* | ||
* @param id - | ||
*/ | ||
version(id) { | ||
return id >>> this.shift & this.vmask; | ||
} | ||
get capacity() { | ||
return this._capacity; | ||
} | ||
/** | ||
* Attempts to set new capacity to given value. Capacity can only be | ||
* increased and the operation is only supported for unversioned | ||
* instances (i.e. vbits = 0). | ||
*/ | ||
set capacity(newCap) { | ||
assert(!this.vmask, "can't change capacity w/ versioning enabled"); | ||
if (newCap >= this.mask + 1) { | ||
const bits = Math.ceil(Math.log2(newCap)); | ||
assert( | ||
bits > 0 && bits <= 32, | ||
"wrong bit size for new capacity [1..32]" | ||
); | ||
this._capacity = newCap; | ||
this.mask = 2 ** bits - 1; | ||
this.shift = bits; | ||
} else { | ||
throw new Error("can't reduce capacity"); | ||
} | ||
/** | ||
* Extract actual ID (without version bits). | ||
* | ||
* @param id - | ||
*/ | ||
id(id) { | ||
return id & this.mask; | ||
} | ||
/** | ||
* Number of remaining available IDs. | ||
*/ | ||
get available() { | ||
return this._capacity - this.num - this.start; | ||
} | ||
/** | ||
* Number of currently used IDs. | ||
*/ | ||
get used() { | ||
return this.num; | ||
} | ||
/** | ||
* Next available free ID. | ||
*/ | ||
get freeID() { | ||
return this._freeID; | ||
} | ||
*[Symbol.iterator]() { | ||
const { ids, mask } = this; | ||
for (let i = this.nextID; i-- > 0; ) { | ||
const id = ids[i]; | ||
if ((id & mask) === i) | ||
yield id; | ||
} | ||
/** | ||
* Extract version from ID | ||
* | ||
* @param id - | ||
*/ | ||
version(id) { | ||
return (id >>> this.shift) & this.vmask; | ||
} | ||
/** | ||
* Frees all existing IDs and resets counter to original start ID. | ||
*/ | ||
clear() { | ||
this.ids.length = 0; | ||
this.nextID = this.start; | ||
this.num = 0; | ||
this._freeID = -1; | ||
} | ||
/** | ||
* Returns next available ID or throws error (assertion) if no further IDs | ||
* are currently available. Emits {@link EVENT_ADDED} if successful. | ||
*/ | ||
next() { | ||
let id; | ||
if (this._freeID !== -1) { | ||
id = this._freeID; | ||
const rawID = id & this.mask; | ||
this._freeID = this.ids[rawID]; | ||
this.ids[rawID] = id; | ||
} else { | ||
assert(this.nextID < this._capacity, "max capacity reached"); | ||
id = this.nextID++; | ||
this.ids[id] = id; | ||
} | ||
get capacity() { | ||
return this._capacity; | ||
} | ||
/** | ||
* Attempts to set new capacity to given value. Capacity can only be | ||
* increased and the operation is only supported for unversioned | ||
* instances (i.e. vbits = 0). | ||
*/ | ||
set capacity(newCap) { | ||
assert(!this.vmask, "can't change capacity w/ versioning enabled"); | ||
if (newCap >= this.mask + 1) { | ||
const bits = Math.ceil(Math.log2(newCap)); | ||
assert(bits > 0 && bits <= 32, "wrong bit size for new capacity [1..32]"); | ||
this._capacity = newCap; | ||
this.mask = 2 ** bits - 1; | ||
this.shift = bits; | ||
} | ||
else { | ||
throw new Error("can't reduce capacity"); | ||
} | ||
} | ||
/** | ||
* Number of remaining available IDs. | ||
*/ | ||
get available() { | ||
return this._capacity - this.num - this.start; | ||
} | ||
/** | ||
* Number of currently used IDs. | ||
*/ | ||
get used() { | ||
return this.num; | ||
} | ||
/** | ||
* Next available free ID. | ||
*/ | ||
get freeID() { | ||
return this._freeID; | ||
} | ||
*[Symbol.iterator]() { | ||
const { ids, mask } = this; | ||
for (let i = this.nextID; i-- > 0;) { | ||
const id = ids[i]; | ||
if ((id & mask) === i) | ||
yield id; | ||
} | ||
} | ||
/** | ||
* Frees all existing IDs and resets counter to original start ID. | ||
*/ | ||
clear() { | ||
this.ids.length = 0; | ||
this.nextID = this.start; | ||
this.num = 0; | ||
this._freeID = -1; | ||
} | ||
/** | ||
* Returns next available ID or throws error (assertion) if no further IDs | ||
* are currently available. Emits {@link EVENT_ADDED} if successful. | ||
*/ | ||
next() { | ||
let id; | ||
if (this._freeID !== -1) { | ||
id = this._freeID; | ||
const rawID = id & this.mask; | ||
this._freeID = this.ids[rawID]; | ||
this.ids[rawID] = id; | ||
} | ||
else { | ||
assert(this.nextID < this._capacity, "max capacity reached"); | ||
id = this.nextID++; | ||
this.ids[id] = id; | ||
} | ||
this.num++; | ||
this.notify({ id: EVENT_ADDED, target: this, value: id }); | ||
return id; | ||
} | ||
/** | ||
* Marks given ID as available again and increases its version (if | ||
* versioning is enabled). Emits {@link EVENT_REMOVED} if successful. | ||
* | ||
* @param id - | ||
*/ | ||
free(id) { | ||
if (!this.has(id)) | ||
return false; | ||
this.ids[id & this.mask] = this._freeID; | ||
this._freeID = this.nextVersion(id); | ||
this.num--; | ||
this.notify({ id: EVENT_REMOVED, target: this, value: id }); | ||
return true; | ||
} | ||
/** | ||
* Returns true iff the given ID is valid and currently used. | ||
* | ||
* @param id - | ||
*/ | ||
has(id) { | ||
const rawID = id & this.mask; | ||
return id >= 0 && rawID < this.nextID && this.ids[rawID] === id; | ||
} | ||
/** {@inheritDoc @thi.ng/api#INotify.addListener} */ | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
addListener(id, fn, scope) { } | ||
/** {@inheritDoc @thi.ng/api#INotify.removeListener} */ | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
removeListener(id, fn, scope) { } | ||
/** {@inheritDoc @thi.ng/api#INotify.notify} */ | ||
// @ts-ignore: mixin | ||
notify(event) { } | ||
nextVersion(id) { | ||
return (((id & this.mask) | | ||
(((this.version(id) + 1) & this.vmask) << this.shift)) >>> | ||
0); | ||
} | ||
this.num++; | ||
this.notify({ id: EVENT_ADDED, target: this, value: id }); | ||
return id; | ||
} | ||
/** | ||
* Marks given ID as available again and increases its version (if | ||
* versioning is enabled). Emits {@link EVENT_REMOVED} if successful. | ||
* | ||
* @param id - | ||
*/ | ||
free(id) { | ||
if (!this.has(id)) | ||
return false; | ||
this.ids[id & this.mask] = this._freeID; | ||
this._freeID = this.nextVersion(id); | ||
this.num--; | ||
this.notify({ id: EVENT_REMOVED, target: this, value: id }); | ||
return true; | ||
} | ||
/** | ||
* Returns true iff the given ID is valid and currently used. | ||
* | ||
* @param id - | ||
*/ | ||
has(id) { | ||
const rawID = id & this.mask; | ||
return id >= 0 && rawID < this.nextID && this.ids[rawID] === id; | ||
} | ||
/** {@inheritDoc @thi.ng/api#INotify.addListener} */ | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
addListener(id, fn, scope) { | ||
} | ||
/** {@inheritDoc @thi.ng/api#INotify.removeListener} */ | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
removeListener(id, fn, scope) { | ||
} | ||
/** {@inheritDoc @thi.ng/api#INotify.notify} */ | ||
// @ts-ignore: mixin | ||
notify(event) { | ||
} | ||
nextVersion(id) { | ||
return (id & this.mask | (this.version(id) + 1 & this.vmask) << this.shift) >>> 0; | ||
} | ||
}; | ||
IDGen = __decorate([ | ||
INotifyMixin | ||
IDGen = __decorateClass([ | ||
INotifyMixin | ||
], IDGen); | ||
export { IDGen }; | ||
/** | ||
* Returns a new {@link IDGen} instance configured to use given counter & | ||
* version bits. | ||
* | ||
* @remarks | ||
* Overall ID range/capacity can be explicitly limited using `cap` (default and | ||
* maximum: 2^bits). The start ID can be defined via `start` (default: 0) and | ||
* MUST be < `cap`. | ||
* | ||
* @param bits - | ||
* @param vbits - | ||
* @param cap - | ||
* @param start - | ||
*/ | ||
export const idgen = (bits, vbits, cap, start) => new IDGen(bits, vbits, cap, start); | ||
const idgen = (bits, vbits, cap, start) => new IDGen(bits, vbits, cap, start); | ||
export { | ||
IDGen, | ||
idgen | ||
}; |
104
monotonic.js
@@ -1,56 +0,56 @@ | ||
import { __decorate } from "tslib"; | ||
import { INotifyMixin, } from "@thi.ng/api"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __decorateClass = (decorators, target, key, kind) => { | ||
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; | ||
for (var i = decorators.length - 1, decorator; i >= 0; i--) | ||
if (decorator = decorators[i]) | ||
result = (kind ? decorator(target, key, result) : decorator(result)) || result; | ||
if (kind && result) | ||
__defProp(target, key, result); | ||
return result; | ||
}; | ||
import { | ||
INotifyMixin | ||
} from "@thi.ng/api"; | ||
import { assert } from "@thi.ng/errors/assert"; | ||
import { EVENT_ADDED, EVENT_REMOVED } from "./api.js"; | ||
/** | ||
* Simple monotonically increasing numeric ID generator. Unbounded and depending | ||
* on numeric range covered, inprecise (e.g. for values outside the JS safe | ||
* integer range). | ||
* | ||
* Unlike {@link IDGen}, this class doesn't keep track of generated IDs and the | ||
* {@link MonotonicID.free} implementation only checks if a given ID could | ||
* possibly already have been generated. | ||
*/ | ||
let MonotonicID = class MonotonicID { | ||
start; | ||
step; | ||
nextID; | ||
constructor(start = 0, step = 1) { | ||
this.start = start; | ||
this.step = step; | ||
assert(step > 0, "invalid step size"); | ||
this.nextID = start; | ||
} | ||
next() { | ||
const id = this.nextID; | ||
this.nextID += this.step; | ||
this.notify({ id: EVENT_ADDED, target: this, value: id }); | ||
return id; | ||
} | ||
free(id) { | ||
if (!(id >= this.start && id < this.nextID)) | ||
return false; | ||
this.notify({ id: EVENT_REMOVED, target: this, value: id }); | ||
return true; | ||
} | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
addListener(id, fn, scope) { } | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
removeListener(id, fn, scope) { } | ||
// @ts-ignore: mixin | ||
notify(event) { } | ||
let MonotonicID = class { | ||
constructor(start = 0, step = 1) { | ||
this.start = start; | ||
this.step = step; | ||
assert(step > 0, "invalid step size"); | ||
this.nextID = start; | ||
} | ||
nextID; | ||
next() { | ||
const id = this.nextID; | ||
this.nextID += this.step; | ||
this.notify({ id: EVENT_ADDED, target: this, value: id }); | ||
return id; | ||
} | ||
free(id) { | ||
if (!(id >= this.start && id < this.nextID)) | ||
return false; | ||
this.notify({ id: EVENT_REMOVED, target: this, value: id }); | ||
return true; | ||
} | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
addListener(id, fn, scope) { | ||
} | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
removeListener(id, fn, scope) { | ||
} | ||
// @ts-ignore: mixin | ||
notify(event) { | ||
} | ||
}; | ||
MonotonicID = __decorate([ | ||
INotifyMixin | ||
MonotonicID = __decorateClass([ | ||
INotifyMixin | ||
], MonotonicID); | ||
export { MonotonicID }; | ||
/** | ||
* Returns a new {@link MonotonicID} instance with given `start` offset | ||
* (default: 0) and `step` size (default: 1). | ||
* | ||
* @param start | ||
* @param step | ||
*/ | ||
export const monotonic = (start, step) => new MonotonicID(start, step); | ||
const monotonic = (start, step) => new MonotonicID(start, step); | ||
export { | ||
MonotonicID, | ||
monotonic | ||
}; |
{ | ||
"name": "@thi.ng/idgen", | ||
"version": "2.2.14", | ||
"version": "2.2.15", | ||
"description": "Generator of opaque numeric identifiers with optional support for ID versioning and efficient re-use", | ||
@@ -27,3 +27,5 @@ "type": "module", | ||
"scripts": { | ||
"build": "yarn clean && tsc --declaration", | ||
"build": "yarn build:esbuild && yarn build:decl", | ||
"build:decl": "tsc --declaration --emitDeclarationOnly", | ||
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts", | ||
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc", | ||
@@ -37,4 +39,4 @@ "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts", | ||
"dependencies": { | ||
"@thi.ng/api": "^8.9.11", | ||
"@thi.ng/errors": "^2.4.5", | ||
"@thi.ng/api": "^8.9.12", | ||
"@thi.ng/errors": "^2.4.6", | ||
"tslib": "^2.6.2" | ||
@@ -44,2 +46,3 @@ }, | ||
"@microsoft/api-extractor": "^7.38.3", | ||
"esbuild": "^0.19.8", | ||
"rimraf": "^5.0.5", | ||
@@ -93,3 +96,3 @@ "tools": "^0.0.1", | ||
}, | ||
"gitHead": "25f2ac8ff795a432a930119661b364d4d93b59a0\n" | ||
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n" | ||
} |
@@ -1,45 +0,51 @@ | ||
import { __decorate } from "tslib"; | ||
import { INotifyMixin, } from "@thi.ng/api"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __decorateClass = (decorators, target, key, kind) => { | ||
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; | ||
for (var i = decorators.length - 1, decorator; i >= 0; i--) | ||
if (decorator = decorators[i]) | ||
result = (kind ? decorator(target, key, result) : decorator(result)) || result; | ||
if (kind && result) | ||
__defProp(target, key, result); | ||
return result; | ||
}; | ||
import { | ||
INotifyMixin | ||
} from "@thi.ng/api"; | ||
import { EVENT_ADDED, EVENT_REMOVED } from "./api.js"; | ||
/** | ||
* Wrapped ID generator for creating string-based IDs derived from a backing ID gen. | ||
*/ | ||
let PrefixedID = class PrefixedID { | ||
prefix; | ||
gen; | ||
constructor(prefix, gen) { | ||
this.prefix = prefix; | ||
this.gen = gen; | ||
} | ||
next() { | ||
const id = this.prefix + this.gen.next(); | ||
this.notify({ id: EVENT_ADDED, target: this, value: id }); | ||
return id; | ||
} | ||
free(id) { | ||
if (!(id.startsWith(this.prefix) && | ||
this.gen.free(Number(id.substring(this.prefix.length))))) | ||
return false; | ||
this.notify({ id: EVENT_REMOVED, target: this, value: id }); | ||
return true; | ||
} | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
addListener(id, fn, scope) { } | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
removeListener(id, fn, scope) { } | ||
// @ts-ignore: mixin | ||
notify(event) { } | ||
let PrefixedID = class { | ||
constructor(prefix, gen) { | ||
this.prefix = prefix; | ||
this.gen = gen; | ||
} | ||
next() { | ||
const id = this.prefix + this.gen.next(); | ||
this.notify({ id: EVENT_ADDED, target: this, value: id }); | ||
return id; | ||
} | ||
free(id) { | ||
if (!(id.startsWith(this.prefix) && this.gen.free(Number(id.substring(this.prefix.length))))) | ||
return false; | ||
this.notify({ id: EVENT_REMOVED, target: this, value: id }); | ||
return true; | ||
} | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
addListener(id, fn, scope) { | ||
} | ||
// @ts-ignore: mixin | ||
// prettier-ignore | ||
removeListener(id, fn, scope) { | ||
} | ||
// @ts-ignore: mixin | ||
notify(event) { | ||
} | ||
}; | ||
PrefixedID = __decorate([ | ||
INotifyMixin | ||
PrefixedID = __decorateClass([ | ||
INotifyMixin | ||
], PrefixedID); | ||
export { PrefixedID }; | ||
/** | ||
* Syntax sugar for {@link PrefixedID} constructor. | ||
* | ||
* @param prefix | ||
* @param gen | ||
*/ | ||
export const prefixed = (prefix, gen) => new PrefixedID(prefix, gen); | ||
const prefixed = (prefix, gen) => new PrefixedID(prefix, gen); | ||
export { | ||
PrefixedID, | ||
prefixed | ||
}; |
@@ -80,3 +80,3 @@ <!-- This file is generated - DO NOT EDIT! --> | ||
Package sizes (brotli'd, pre-treeshake): ESM: 1.09 KB | ||
Package sizes (brotli'd, pre-treeshake): ESM: 1.18 KB | ||
@@ -83,0 +83,0 @@ ## Dependencies |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
452
38127
6
Updated@thi.ng/api@^8.9.12
Updated@thi.ng/errors@^2.4.6