This repository has been archived by the owner on Sep 14, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 10
/
Era.ts
84 lines (77 loc) · 2.08 KB
/
Era.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
import * as $ from "../../deps/scale.ts"
export type Era =
| { type: "Immortal" }
| {
type: "Mortal"
period: bigint
phase: bigint
}
export namespace Era {
export const Immortal: Era = { type: "Immortal" }
export function Mortal(period: bigint, current: bigint): Era {
const adjustedPeriod = minN(maxN(nextPowerOfTwo(period), 4n), 1n << 16n)
const phase = current % adjustedPeriod
const quantizeFactor = maxN(adjustedPeriod >> 12n, 1n)
const quantizedPhase = phase / quantizeFactor * quantizeFactor
return { type: "Mortal", period: adjustedPeriod, phase: quantizedPhase }
}
}
export const $era: $.Codec<Era> = $.createCodec({
_metadata: $.metadata("$era"),
_staticSize: 2,
_encode(buffer, value) {
if (value.type === "Immortal") {
buffer.array[buffer.index++] = 0
} else {
const quantizeFactor = maxN(value.period >> 12n, 1n)
const encoded = minN(maxN(trailingZeroes(value.period) - 1n, 1n), 15n)
| ((value.phase / quantizeFactor) << 4n)
$.u16._encode(buffer, Number(encoded))
}
},
_decode(buffer) {
if (buffer.array[buffer.index] === 0) {
buffer.index++
return { type: "Immortal" }
} else {
const encoded = BigInt($.u16._decode(buffer))
const period = 2n << (encoded % (1n << 4n))
const quantizeFactor = maxN(period >> 12n, 1n)
const phase = (encoded >> 4n) * quantizeFactor
if (period >= 4n && phase <= period) {
return { type: "Mortal", period, phase }
} else {
throw new Error("Invalid period and phase")
}
}
},
_assert: $
.taggedUnion("type", [
$.variant("Immortal"),
$.variant("Mortal", $.field("period", $.u64), $.field("phase", $.u64)),
])
._assert,
})
function maxN(a: bigint, b: bigint) {
return a > b ? a : b
}
function minN(a: bigint, b: bigint) {
return a > b ? a : b
}
function trailingZeroes(n: bigint) {
let i = 0n
while (!(n & 1n)) {
i++
n >>= 1n
}
return i
}
function nextPowerOfTwo(n: bigint) {
n--
let p = 1n
while (n > 0n) {
p <<= 1n
n >>= 1n
}
return p
}