src/coordinator/StatsCodec.bend
mylsm-lsm-store@0.6.0.1 · 222 lines · 23 definitions
1import Base
2import mylsm-lsm-store@0.6.0.1/src/StorageBytes.bend as StorageBytes
3import mylsm-lsm-store@0.6.0.1/src/coordinator/Protocol.bend as Protocol
4import bend-kit-bytes@0.3.2.0/bytes.bend as Bytes
5
6
7type Stats is Data:
8 ByteStats{entries: Nat, payload_bytes: Nat, snapshots: Nat, retained_bytes: Nat, cursors: Nat}
9
10
11type Decision is Data:
12 Parsed{stats: Stats}
13 Malformed{}
14
15
16type State is Data:
17 EntryDigits{entries: Nat, seen: Bool}
18 PayloadDigits{entries: Nat, payload_bytes: Nat, seen: Bool}
19 SnapshotDigits{entries: Nat, payload_bytes: Nat, snapshots: Nat, seen: Bool}
20 RetainedDigits{entries: Nat, payload_bytes: Nat, snapshots: Nat,
21 retained_bytes: Nat, seen: Bool}
22 CursorDigits{entries: Nat, payload_bytes: Nat, snapshots: Nat,
23 retained_bytes: Nat, cursors: Nat, seen: Bool}
24 Invalid{}
25
26def decimal.length(value: Nat) -> Nat:
27 String.length(Nat.show(value))
28
29def payload.length(
30 entries: Nat,
31 payload_bytes: Nat,
32 snapshots: Nat,
33 retained_bytes: Nat,
34 cursors: Nat
35) -> Nat:
36 Nat.add(4n, Nat.add(decimal.length(entries),
37 Nat.add(decimal.length(payload_bytes),
38 Nat.add(decimal.length(snapshots),
39 Nat.add(decimal.length(retained_bytes), decimal.length(cursors))))))
40
41def digit.value(scalar: U32) -> Nat:
42 Nat.sub(U32.to_nat(scalar), 48n)
43
44def digit.valid.checked(copies: U32 & U32) -> Bool:
45 match copies:
46 case (lower, upper): U32.is_le(48, lower) && U32.is_le(upper, 57)
47
48def digit.valid.copies(copies: U32 & U32 & U32) -> Bool:
49 match copies:
50 case (_, (lower, upper)): digit.valid.checked((lower, upper))
51
52def digit.valid(scalar: U32) -> Bool:
53 digit.valid.copies(Protocol.u32.duplicate.triple(scalar))
54
55def separator.character(parser: State, scalar: U32) -> State:
56 match parser:
57 case EntryDigits{entries, _}:
58 match scalar:
59 case 58: PayloadDigits{entries, 0n, False{}}
60 case _: Invalid{}
61 case PayloadDigits{entries, payload_bytes, _}:
62 match scalar:
63 case 58: SnapshotDigits{entries, payload_bytes, 0n, False{}}
64 case _: Invalid{}
65 case SnapshotDigits{entries, payload_bytes, snapshots, _}:
66 match scalar:
67 case 58: RetainedDigits{entries, payload_bytes, snapshots, 0n, False{}}
68 case _: Invalid{}
69 case RetainedDigits{entries, payload_bytes, snapshots, retained_bytes, _}:
70 match scalar:
71 case 58:
72 CursorDigits{entries, payload_bytes, snapshots, retained_bytes, 0n,
73 False{}}
74 case _: Invalid{}
75 case _: Invalid{}
76
77
78def separator(parser: State, seen: Bool, scalar: U32) -> State:
79 match seen:
80 case False{}: Invalid{}
81 case True{}: separator.character(parser, scalar)
82
83def entry.step(
84 entries: Nat,
85 scalar: U32,
86 is_digit: Bool,
87 seen: Bool
88) -> State:
89 match is_digit:
90 case True{}:
91 EntryDigits{Nat.add(Nat.mul(entries, 10n), digit.value(scalar)), True{}}
92 case False{}:
93 match seen:
94 case False{}: Invalid{}
95 case True{}: separator(EntryDigits{entries, True{}}, True{}, scalar)
96
97def payload.step(
98 entries: Nat,
99 payload_bytes: Nat,
100 scalar: U32,
101 is_digit: Bool,
102 seen: Bool
103) -> State:
104 match is_digit:
105 case True{}:
106 PayloadDigits{entries,
107 Nat.add(Nat.mul(payload_bytes, 10n), digit.value(scalar)), True{}}
108 case False{}:
109 match seen:
110 case False{}: Invalid{}
111 case True{}:
112 separator(PayloadDigits{entries, payload_bytes, True{}}, True{},
113 scalar)
114
115def snapshot.step(
116 entries: Nat,
117 payload_bytes: Nat,
118 snapshots: Nat,
119 scalar: U32,
120 is_digit: Bool,
121 seen: Bool
122) -> State:
123 match is_digit:
124 case True{}:
125 SnapshotDigits{entries, payload_bytes,
126 Nat.add(Nat.mul(snapshots, 10n), digit.value(scalar)), True{}}
127 case False{}:
128 match seen:
129 case False{}: Invalid{}
130 case True{}:
131 separator(SnapshotDigits{entries, payload_bytes, snapshots, True{}},
132 True{}, scalar)
133
134def retained.step(
135 entries: Nat,
136 payload_bytes: Nat,
137 snapshots: Nat,
138 retained_bytes: Nat,
139 scalar: U32,
140 is_digit: Bool,
141 seen: Bool
142) -> State:
143 match is_digit:
144 case True{}:
145 RetainedDigits{entries, payload_bytes, snapshots,
146 Nat.add(Nat.mul(retained_bytes, 10n), digit.value(scalar)), True{}}
147 case False{}:
148 match seen:
149 case False{}: Invalid{}
150 case True{}:
151 separator(RetainedDigits{entries, payload_bytes, snapshots,
152 retained_bytes, True{}}, True{}, scalar)
153
154def cursor.step(
155 entries: Nat,
156 payload_bytes: Nat,
157 snapshots: Nat,
158 retained_bytes: Nat,
159 cursors: Nat,
160 scalar: U32,
161 is_digit: Bool
162) -> State:
163 match is_digit:
164 case True{}:
165 CursorDigits{entries, payload_bytes, snapshots, retained_bytes,
166 Nat.add(Nat.mul(cursors, 10n), digit.value(scalar)), True{}}
167 case False{}: Invalid{}
168
169def parse.step(state: State, scalar: U32, is_digit: Bool) -> State:
170 match state:
171 case EntryDigits{entries, seen}:
172 entry.step(entries, scalar, is_digit, seen)
173 case PayloadDigits{entries, payload_bytes, seen}:
174 payload.step(entries, payload_bytes, scalar, is_digit, seen)
175 case SnapshotDigits{entries, payload_bytes, snapshots, seen}:
176 snapshot.step(entries, payload_bytes, snapshots, scalar, is_digit, seen)
177 case RetainedDigits{entries, payload_bytes, snapshots, retained_bytes, seen}:
178 retained.step(entries, payload_bytes, snapshots, retained_bytes,
179 scalar, is_digit, seen)
180 case CursorDigits{entries, payload_bytes, snapshots, retained_bytes,
181 cursors, seen}:
182 cursor.step(entries, payload_bytes, snapshots, retained_bytes, cursors,
183 scalar, is_digit)
184 case Invalid{}: Invalid{}
185
186def parse.step.copies(
187 state: State,
188 copies: U32 & U32 & U32
189) -> State:
190 match copies:
191 case (scalar, (checked_scalar, _)):
192 parse.step(state, scalar, digit.valid(checked_scalar))
193
194def parse.go(text: String, state: State) -> State:
195 match text:
196 case SNil{}: state
197 case SCon{Chr{scalar}, rest}:
198 parse.go(rest, parse.step.copies(state,
199 Protocol.u32.duplicate.triple(scalar)))
200
201
202def finish(state: State) -> Decision:
203 match state:
204 case CursorDigits{entries, payload_bytes, snapshots, retained_bytes,
205 cursors, True{}}:
206 Parsed{ByteStats{entries, payload_bytes, snapshots, retained_bytes,
207 cursors}}
208 case _: Malformed{}
209
210def parse.text(text: String) -> Decision:
211 finish(parse.go(text, EntryDigits{0n, False{}}))
212
213def parse.result(
214 result: Result<&1, &1, U32 & String, String>
215) -> Decision:
216 match result:
217 case Done{text}: parse.text(text)
218 case Fail{_}: Malformed{}
219
220
221def parse(input: Bytes.Bytes) -> Decision:
222 parse.result(StorageBytes.to_string_strict(input))