src/scan/VisibleStats.bend

mylsm-lsm-store@0.6.0.0 · 74 lines · 0 definitions

1import Base
2
3# Stores exact visible record and payload totals.
4type Stats is Data:
5  Stats{entries: Nat, payload_bytes: Nat}
6
7# Reports a deterministic aggregate after one logical key mutation.
8type Decision is Data:
9  Applied{stats: Stats}
10
11# Adds a previously absent live key and its encoded payload.
12def added(current: Stats, key_bytes: Nat, value_bytes: Nat) -> Decision:
13  match current:
14    case Stats{entries, payload_bytes}:
15      Applied{Stats{Nat.add(entries, 1n),
16        Nat.add(payload_bytes, Nat.add(key_bytes, value_bytes))}}
17
18# Preserves totals when a mutation leaves a key absent.
19def unchanged(current: Stats) -> Decision:
20  Applied{current}
21
22# Removes one visible key, its key bytes, and its old value bytes.
23def removed(current: Stats, key_bytes: Nat, value_bytes: Nat) -> Decision:
24  match current:
25    case Stats{entries, payload_bytes}:
26      Applied{Stats{Nat.sub(entries, 1n),
27        Nat.sub(payload_bytes, Nat.add(key_bytes, value_bytes))}}
28
29# Replaces a value without changing the visible key count.
30def replaced(current: Stats, old_bytes: Nat, new_bytes: Nat) -> Decision:
31  match current:
32    case Stats{entries, payload_bytes}:
33      Applied{Stats{entries, Nat.add(Nat.sub(payload_bytes, old_bytes), new_bytes)}}
34
35# Applies the visible effect of one key's before and after states.
36def apply(
37  current: Stats,
38  key_bytes: Nat,
39  before: Maybe<&1, Nat>,
40  after: Maybe<&1, Nat>
41) -> Decision:
42  match before:
43    case None{}:
44      match after:
45        case None{}: unchanged(current)
46        case Some{new_bytes}: added(current, key_bytes, new_bytes)
47    case Some{old_bytes}:
48      match after:
49        case None{}: removed(current, key_bytes, old_bytes)
50        case Some{new_bytes}: replaced(current, old_bytes, new_bytes)
51
52# Projects aggregate totals from a successful mutation decision.
53def result(decision: Decision) -> Stats:
54  match decision:
55    case Applied{stats}: stats
56
57def clone.nat(+value: Nat) -> Nat & Nat:
58  (Nat.mul(1n, value), Nat.mul(1n, value))
59
60def clone.fields(
61  entries: Nat & Nat,
62  payload_bytes: Nat & Nat
63) -> Stats & Stats:
64  match entries:
65    case (entries_left, entries_right):
66      match payload_bytes:
67        case (payload_left, payload_right):
68          (Stats{entries_left, payload_left}, Stats{entries_right, payload_right})
69
70# Rebuilds independent copies of the exact aggregate totals.
71def clone(+stats: Stats) -> Stats & Stats:
72  match stats:
73    case Stats{+entries, +payload_bytes}:
74      clone.fields(clone.nat(entries), clone.nat(payload_bytes))