-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmod.ts
109 lines (96 loc) · 2.97 KB
/
mod.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Copyright shimataro. MIT License.
// Small parts were changed by itohatweb
// Ported from https://github.com/shimataro/maylily
const DEFAULT_BITS_MACHINE = 3; // up to 8 machines
const DEFAULT_BITS_GENERATOR = 10; // 0-1023
const DEFAULT_BITS_SEQUENCE = 8; // 0-255
const maylilyGlobalOptions = {
radix: 10,
timeBase: Date.parse("2000-01-01T00:00:00Z"),
machineId: 0,
machineBits: DEFAULT_BITS_MACHINE,
generatorId: Deno.pid % (1 << DEFAULT_BITS_GENERATOR),
generatorBits: DEFAULT_BITS_GENERATOR,
sequenceBits: DEFAULT_BITS_SEQUENCE,
};
let timePrev = 0;
let sequence = 0;
/**
* Generate an unique ID.
* All options are optional for all not given options the default will be used.
*/
export function maylily(options?: MaylilyOptions): Promise<string> {
// Merge options if specified.
if (options !== undefined) {
// if generatorBits were changed but not the generatorId we may change the generatorId automatically
if (
options.generatorBits !== undefined && options.generatorId === undefined
) {
options.generatorId = Deno.pid % (1 << options.generatorBits);
}
Object.assign(maylilyGlobalOptions, options);
}
return new Promise((resolve, reject) => {
resolveId(resolve, reject);
});
}
/**
* generate and resolve ID
*/
function resolveId(
resolve: (value: string) => void,
reject: (reason?: any) => void,
) {
const time = Date.now();
if (time < timePrev) {
reject(errorTimeBackwards(time));
return;
}
// Reset sequence when time is updated.
if (time > timePrev) {
sequence = 0;
}
const sequenceLimit = 1 << maylilyGlobalOptions.sequenceBits;
if (sequence < sequenceLimit) {
// Increment sequence when sequence DOESN'T reach to limit.
resolve(buildId(time, maylilyGlobalOptions));
return;
}
// next time...
setTimeout(resolveId, 1, resolve, reject);
}
/**
* build an unique ID
*/
function buildId(time: number, options: MaylilyOptions): string {
timePrev = time;
return ((((((BigInt(time - options.timeBase!) <<
BigInt(options.machineBits!)) + BigInt(options.machineId!)) <<
BigInt(options.generatorBits!)) + BigInt(options.generatorId!)) <<
BigInt(options.sequenceBits!)) + BigInt(sequence++))
.toString(options.radix!);
}
/**
* generate an error instance for time backwards error
*/
function errorTimeBackwards(time: number) {
const message =
`Clock moved backwards. Refusing to generate id for ${time} milliseconds`;
return new Error(message);
}
export interface MaylilyOptions {
/** radix of generated ID (2-36) */
radix?: number;
/** base time in unixtime (millisec) */
timeBase?: number;
/** identifier of machine; must be unique in service */
machineId?: number;
/** required bits to represent machineId */
machineBits?: number;
/** identifier of generator; must be unique in machine */
generatorId?: number;
/** required bits to represent generatorId */
generatorBits?: number;
/** required bits to represent sequence */
sequenceBits?: number;
}