@bjornpagen/bumbledb-log 0.18.0 → 0.19.1
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/README.md +5 -4
- package/package.json +4 -3
- package/src/braids.ts +17 -2
- package/src/bytes.ts +78 -10
- package/src/chain.ts +173 -56
- package/src/codec.ts +122 -41
- package/src/descriptor.ts +182 -107
- package/src/errors.ts +133 -5
- package/src/index.ts +15 -2
- package/src/keys.ts +161 -18
- package/src/manifest.ts +165 -73
- package/src/replica.ts +577 -264
- package/src/store-s3.ts +191 -36
- package/src/store.ts +458 -84
- package/src/tenants.ts +322 -26
- package/src/value.ts +65 -14
- package/src/vector.ts +173 -0
- package/src/writer.ts +453 -92
package/src/vector.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-braid generation map. Sum, domination, checkpoint order,
|
|
3
|
+
* apply's increment, and the coordinate's one binary encoding live
|
|
4
|
+
* here. Overflow is a refusal of `sum` and of nowhere else.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ByteReader, ByteWriter, U64_MAX } from "#bytes.ts"
|
|
8
|
+
import type { Braid } from "#descriptor.ts"
|
|
9
|
+
import { braidHex } from "#descriptor.ts"
|
|
10
|
+
import type { Generation } from "#keys.ts"
|
|
11
|
+
import { generation } from "#keys.ts"
|
|
12
|
+
|
|
13
|
+
/** One braid id and its applied count on the wire: u32le + u64le. */
|
|
14
|
+
const PAIR_BYTES = 12n
|
|
15
|
+
|
|
16
|
+
const Overflow = { tag: "overflow" } as const
|
|
17
|
+
type Overflow = typeof Overflow
|
|
18
|
+
|
|
19
|
+
type CheckpointOrder = "before" | "equal" | "after"
|
|
20
|
+
|
|
21
|
+
type VectorParse =
|
|
22
|
+
| { readonly tag: "ok"; readonly vector: Vector }
|
|
23
|
+
| { readonly tag: "truncated" }
|
|
24
|
+
| { readonly tag: "trailing"; readonly bytes: number }
|
|
25
|
+
| { readonly tag: "malformed" }
|
|
26
|
+
| { readonly tag: "overflow" }
|
|
27
|
+
|
|
28
|
+
function braidIdOf(id: Braid): number {
|
|
29
|
+
return Number.parseInt(id.slice(1), 16)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Applied counts keyed by braid. Any vector is a legal restore point. */
|
|
33
|
+
class Vector {
|
|
34
|
+
readonly #counts: Map<Braid, bigint>
|
|
35
|
+
|
|
36
|
+
constructor(entries: Iterable<readonly [Braid, bigint]> = []) {
|
|
37
|
+
const counts = new Map<Braid, bigint>()
|
|
38
|
+
for (const [braid, g] of [...entries].sort(function byBraid(left, right) {
|
|
39
|
+
if (left[0] < right[0]) {
|
|
40
|
+
return -1
|
|
41
|
+
}
|
|
42
|
+
if (left[0] > right[0]) {
|
|
43
|
+
return 1
|
|
44
|
+
}
|
|
45
|
+
return 0
|
|
46
|
+
})) {
|
|
47
|
+
counts.set(braid, g)
|
|
48
|
+
}
|
|
49
|
+
this.#counts = counts
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static from(map: ReadonlyMap<Braid, bigint>): Vector {
|
|
53
|
+
return new Vector(map)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The wholeness arithmetic. The one overflow site. */
|
|
57
|
+
sum(): bigint | Overflow {
|
|
58
|
+
let acc = 0n
|
|
59
|
+
for (const g of this.#counts.values()) {
|
|
60
|
+
const next = acc + g
|
|
61
|
+
if (next > U64_MAX) {
|
|
62
|
+
return Overflow
|
|
63
|
+
}
|
|
64
|
+
acc = next
|
|
65
|
+
}
|
|
66
|
+
return acc
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Pointwise `this[braid] >= other[braid]`; an absent braid is zero. */
|
|
70
|
+
dominates(other: Vector): boolean {
|
|
71
|
+
for (const [braid, g] of other.#counts) {
|
|
72
|
+
if (this.at(braid) < g) {
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return true
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The total order the manifest CAS installs. */
|
|
80
|
+
order(other: Vector): CheckpointOrder {
|
|
81
|
+
const left = this.sum()
|
|
82
|
+
const right = other.sum()
|
|
83
|
+
const leftOverflow = typeof left !== "bigint"
|
|
84
|
+
const rightOverflow = typeof right !== "bigint"
|
|
85
|
+
if (leftOverflow && rightOverflow) {
|
|
86
|
+
return "equal"
|
|
87
|
+
}
|
|
88
|
+
if (leftOverflow) {
|
|
89
|
+
return "after"
|
|
90
|
+
}
|
|
91
|
+
if (rightOverflow) {
|
|
92
|
+
return "before"
|
|
93
|
+
}
|
|
94
|
+
if (left < right) {
|
|
95
|
+
return "before"
|
|
96
|
+
}
|
|
97
|
+
if (left > right) {
|
|
98
|
+
return "after"
|
|
99
|
+
}
|
|
100
|
+
return "equal"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The applied count for `braid`; absent is zero. */
|
|
104
|
+
at(braid: Braid): Generation {
|
|
105
|
+
return generation(this.#counts.get(braid) ?? 0n)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Apply's one mutation: this braid's count advances by one. */
|
|
109
|
+
advance(braid: Braid): Vector {
|
|
110
|
+
const next = new Map(this.#counts)
|
|
111
|
+
const current = next.get(braid) ?? 0n
|
|
112
|
+
next.set(braid, current === U64_MAX ? U64_MAX : current + 1n)
|
|
113
|
+
return new Vector(next)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** `u32le` count, then `(u32le braid, u64le g)` pairs in braid order. */
|
|
117
|
+
encode(): Uint8Array {
|
|
118
|
+
const braids = [...this.#counts.keys()].sort()
|
|
119
|
+
const out = new ByteWriter(4 + braids.length * 12)
|
|
120
|
+
out.u32le(braids.length)
|
|
121
|
+
for (const id of braids) {
|
|
122
|
+
out.u32le(braidIdOf(id))
|
|
123
|
+
out.u64le(this.#counts.get(id) ?? 0n)
|
|
124
|
+
}
|
|
125
|
+
return out.finish()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Inverse of `encode`. A count the remaining bytes cannot open is
|
|
130
|
+
* truncated; entries must ascend; overflow is `sum`.
|
|
131
|
+
*/
|
|
132
|
+
static parse(bytes: Uint8Array): VectorParse {
|
|
133
|
+
const short = { tag: "short" } as const
|
|
134
|
+
const reader = new ByteReader(bytes, {
|
|
135
|
+
fail(): never {
|
|
136
|
+
throw short
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
try {
|
|
140
|
+
const count = BigInt(reader.u32le("count"))
|
|
141
|
+
if (count > 0n && BigInt(reader.remaining()) / PAIR_BYTES < count) {
|
|
142
|
+
return { tag: "truncated" }
|
|
143
|
+
}
|
|
144
|
+
const entries: Array<readonly [Braid, bigint]> = []
|
|
145
|
+
let last: Braid | undefined
|
|
146
|
+
for (let i = 0n; i < count; i++) {
|
|
147
|
+
const name = braidHex(reader.u32le("braid"))
|
|
148
|
+
const g = reader.u64le("g")
|
|
149
|
+
if (last !== undefined && last >= name) {
|
|
150
|
+
return { tag: "malformed" }
|
|
151
|
+
}
|
|
152
|
+
entries.push([name, g])
|
|
153
|
+
last = name
|
|
154
|
+
}
|
|
155
|
+
if (reader.remaining() !== 0) {
|
|
156
|
+
return { tag: "trailing", bytes: reader.remaining() }
|
|
157
|
+
}
|
|
158
|
+
const vector = new Vector(entries)
|
|
159
|
+
if (typeof vector.sum() !== "bigint") {
|
|
160
|
+
return { tag: "overflow" }
|
|
161
|
+
}
|
|
162
|
+
return { tag: "ok", vector }
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (error === short) {
|
|
165
|
+
return { tag: "truncated" }
|
|
166
|
+
}
|
|
167
|
+
throw error
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export type { CheckpointOrder, VectorParse }
|
|
173
|
+
export { Overflow, Vector }
|