1use crate::interpreter::RuntimeValue;
18use logicaffeine_data::crdt::{DeltaCrdt, Merge, MVRegister, ORSet, RemoveWins, ReplicaId, VClock, RGA};
19use std::sync::atomic::{AtomicU64, Ordering};
20
21static REPLICA_SEQ: AtomicU64 = AtomicU64::new(1);
27
28pub fn next_replica_id() -> ReplicaId {
30 REPLICA_SEQ.fetch_add(1, Ordering::Relaxed)
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
37pub enum CrdtScalar {
38 Int(i64),
39 Text(String),
40 Bool(bool),
41}
42
43impl CrdtScalar {
44 pub fn from_runtime(value: &RuntimeValue) -> Result<CrdtScalar, String> {
48 match value {
49 RuntimeValue::Int(n) => Ok(CrdtScalar::Int(*n)),
50 RuntimeValue::Text(s) => Ok(CrdtScalar::Text((**s).clone())),
51 RuntimeValue::Bool(b) => Ok(CrdtScalar::Bool(*b)),
52 other => Err(format!(
53 "a shared collection holds Int, Text, or Bool elements, not {}",
54 other.type_name()
55 )),
56 }
57 }
58
59 pub fn to_runtime(&self) -> RuntimeValue {
61 match self {
62 CrdtScalar::Int(n) => RuntimeValue::Int(*n),
63 CrdtScalar::Text(s) => RuntimeValue::Text(std::rc::Rc::new(s.clone())),
64 CrdtScalar::Bool(b) => RuntimeValue::Bool(*b),
65 }
66 }
67
68 pub fn render(&self) -> String {
71 match self {
72 CrdtScalar::Int(n) => n.to_string(),
73 CrdtScalar::Text(s) => s.clone(),
74 CrdtScalar::Bool(b) => b.to_string(),
75 }
76 }
77}
78
79#[derive(Debug, Clone)]
82pub enum CrdtValue {
83 Set(ORSet<CrdtScalar>),
86 SetRemoveWins(ORSet<CrdtScalar, RemoveWins>),
89 Seq(RGA<CrdtScalar>),
92 Register(MVRegister<CrdtScalar>),
95}
96
97impl CrdtValue {
98 pub fn new_set(replica: ReplicaId) -> CrdtValue {
99 CrdtValue::Set(ORSet::new(replica))
100 }
101
102 pub fn new_set_remove_wins(replica: ReplicaId) -> CrdtValue {
103 CrdtValue::SetRemoveWins(ORSet::new(replica))
104 }
105
106 pub fn new_seq(replica: ReplicaId) -> CrdtValue {
107 CrdtValue::Seq(RGA::new(replica))
108 }
109
110 pub fn new_register(replica: ReplicaId) -> CrdtValue {
111 CrdtValue::Register(MVRegister::new(replica))
112 }
113
114 pub fn kind(&self) -> &'static str {
116 match self {
117 CrdtValue::Set(_) | CrdtValue::SetRemoveWins(_) => "SharedSet",
118 CrdtValue::Seq(_) => "SharedSequence",
119 CrdtValue::Register(_) => "Divergent",
120 }
121 }
122
123 pub fn insert(&mut self, value: &RuntimeValue) -> Result<(), String> {
125 let e = CrdtScalar::from_runtime(value)?;
126 match self {
127 CrdtValue::Set(s) => Ok(s.insert(e)),
128 CrdtValue::SetRemoveWins(s) => Ok(s.insert(e)),
129 other => Err(format!("cannot add an element to a {}", other.kind())),
130 }
131 }
132
133 pub fn remove(&mut self, value: &RuntimeValue) -> Result<(), String> {
135 let e = CrdtScalar::from_runtime(value)?;
136 match self {
137 CrdtValue::Set(s) => Ok(s.remove(&e)),
138 CrdtValue::SetRemoveWins(s) => Ok(s.remove(&e)),
139 other => Err(format!("cannot remove an element from a {}", other.kind())),
140 }
141 }
142
143 pub fn contains(&self, value: &RuntimeValue) -> Result<bool, String> {
145 let e = CrdtScalar::from_runtime(value)?;
146 match self {
147 CrdtValue::Set(s) => Ok(s.contains(&e)),
148 CrdtValue::SetRemoveWins(s) => Ok(s.contains(&e)),
149 other => Err(format!("a {} has no membership test", other.kind())),
150 }
151 }
152
153 pub fn append(&mut self, value: &RuntimeValue) -> Result<(), String> {
155 match self {
156 CrdtValue::Seq(s) => {
157 s.append(CrdtScalar::from_runtime(value)?);
158 Ok(())
159 }
160 other => Err(format!("cannot append to a {}", other.kind())),
161 }
162 }
163
164 pub fn resolve(&mut self, value: &RuntimeValue) -> Result<(), String> {
167 match self {
168 CrdtValue::Register(r) => {
169 r.resolve(CrdtScalar::from_runtime(value)?);
170 Ok(())
171 }
172 other => Err(format!("cannot resolve a {}", other.kind())),
173 }
174 }
175
176 pub fn len(&self) -> usize {
178 match self {
179 CrdtValue::Set(s) => s.len(),
180 CrdtValue::SetRemoveWins(s) => s.len(),
181 CrdtValue::Seq(s) => s.len(),
182 CrdtValue::Register(r) => r.values().len(),
183 }
184 }
185
186 pub fn is_empty(&self) -> bool {
187 self.len() == 0
188 }
189
190 pub fn to_runtime_vec(&self) -> Vec<RuntimeValue> {
193 match self {
194 CrdtValue::Set(s) => {
195 let mut elems: Vec<&CrdtScalar> = s.iter().collect();
196 elems.sort();
197 elems.into_iter().map(CrdtScalar::to_runtime).collect()
198 }
199 CrdtValue::SetRemoveWins(s) => {
200 let mut elems: Vec<&CrdtScalar> = s.iter().collect();
201 elems.sort();
202 elems.into_iter().map(CrdtScalar::to_runtime).collect()
203 }
204 CrdtValue::Seq(s) => s.to_vec().iter().map(CrdtScalar::to_runtime).collect(),
205 CrdtValue::Register(r) => {
206 let mut vals: Vec<&CrdtScalar> = r.values();
207 vals.sort();
208 vals.into_iter().map(CrdtScalar::to_runtime).collect()
209 }
210 }
211 }
212
213 pub fn register_value(&self) -> Option<RuntimeValue> {
216 match self {
217 CrdtValue::Register(r) => {
218 let vals = r.values();
219 if vals.len() == 1 {
220 Some(vals[0].to_runtime())
221 } else {
222 None
223 }
224 }
225 _ => None,
226 }
227 }
228
229 pub fn merge(&mut self, other: &CrdtValue) -> Result<(), String> {
232 match (self, other) {
233 (CrdtValue::Set(a), CrdtValue::Set(b)) => {
234 a.merge(b);
235 Ok(())
236 }
237 (CrdtValue::SetRemoveWins(a), CrdtValue::SetRemoveWins(b)) => {
238 a.merge(b);
239 Ok(())
240 }
241 (CrdtValue::Seq(a), CrdtValue::Seq(b)) => {
242 a.merge(b);
243 Ok(())
244 }
245 (CrdtValue::Register(a), CrdtValue::Register(b)) => {
246 a.merge(b);
247 Ok(())
248 }
249 (a, b) => Err(format!("cannot merge a {} with a {}", a.kind(), b.kind())),
250 }
251 }
252
253 pub fn version(&self) -> VClock {
258 match self {
259 CrdtValue::Set(s) => s.version(),
260 CrdtValue::SetRemoveWins(s) => s.version(),
261 CrdtValue::Seq(s) => s.version(),
262 CrdtValue::Register(s) => s.version(),
263 }
264 }
265
266 pub fn delta_since_bytes(&self, since: &VClock) -> Option<Vec<u8>> {
270 fn tagged<D: serde::Serialize>(kind: u8, delta: &D) -> Option<Vec<u8>> {
274 let mut out = vec![kind];
275 out.extend(bincode::serialize(delta).ok()?);
276 Some(out)
277 }
278 match self {
279 CrdtValue::Set(s) => tagged(0, &s.delta_since(since)?),
280 CrdtValue::SetRemoveWins(s) => tagged(1, &s.delta_since(since)?),
281 CrdtValue::Seq(s) => tagged(2, &s.delta_since(since)?),
282 CrdtValue::Register(s) => tagged(3, &s.delta_since(since)?),
283 }
284 }
285
286 pub fn apply_delta_bytes(&mut self, bytes: &[u8]) -> bool {
290 let Some((&kind, body)) = bytes.split_first() else {
291 return false;
292 };
293 match (self, kind) {
294 (CrdtValue::Set(s), 0) => {
295 match bincode::deserialize::<<ORSet<CrdtScalar> as DeltaCrdt>::Delta>(body) {
296 Ok(d) => {
297 s.apply_delta(&d);
298 true
299 }
300 Err(_) => false,
301 }
302 }
303 (CrdtValue::SetRemoveWins(s), 1) => {
304 match bincode::deserialize::<<ORSet<CrdtScalar, RemoveWins> as DeltaCrdt>::Delta>(body) {
305 Ok(d) => {
306 s.apply_delta(&d);
307 true
308 }
309 Err(_) => false,
310 }
311 }
312 (CrdtValue::Seq(s), 2) => {
313 match bincode::deserialize::<<RGA<CrdtScalar> as DeltaCrdt>::Delta>(body) {
314 Ok(d) => {
315 s.apply_delta(&d);
316 true
317 }
318 Err(_) => false,
319 }
320 }
321 (CrdtValue::Register(s), 3) => {
322 match bincode::deserialize::<<MVRegister<CrdtScalar> as DeltaCrdt>::Delta>(body) {
323 Ok(d) => {
324 s.apply_delta(&d);
325 true
326 }
327 Err(_) => false,
328 }
329 }
330 _ => false,
331 }
332 }
333
334 pub fn render(&self) -> String {
338 let parts = || -> String {
339 self.to_runtime_vec().iter().map(render_runtime).collect::<Vec<_>>().join(", ")
340 };
341 match self {
342 CrdtValue::Register(_) => match self.register_value() {
343 Some(v) => render_runtime(&v),
344 None => format!("{{{}}}", parts()),
345 },
346 CrdtValue::Set(_) | CrdtValue::SetRemoveWins(_) => format!("{{{}}}", parts()),
347 CrdtValue::Seq(_) => format!("[{}]", parts()),
348 }
349 }
350}
351
352pub fn crdt_values_equal(a: &CrdtValue, b: &CrdtValue) -> bool {
355 match (a, b) {
356 (CrdtValue::Set(_), CrdtValue::Set(_))
357 | (CrdtValue::SetRemoveWins(_), CrdtValue::SetRemoveWins(_))
358 | (CrdtValue::Seq(_), CrdtValue::Seq(_))
359 | (CrdtValue::Register(_), CrdtValue::Register(_)) => {
360 a.to_runtime_vec() == b.to_runtime_vec()
361 }
362 _ => false,
363 }
364}
365
366fn render_runtime(v: &RuntimeValue) -> String {
368 match v {
369 RuntimeValue::Int(n) => n.to_string(),
370 RuntimeValue::Text(s) => (**s).clone(),
371 RuntimeValue::Bool(b) => b.to_string(),
372 other => other.type_name().to_string(),
373 }
374}
375
376#[cfg(test)]
377mod tests {
378 use super::*;
379
380 struct Rng(u64);
383 impl Rng {
384 fn next(&mut self) -> u64 {
385 self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
386 let mut z = self.0;
387 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
388 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
389 z ^ (z >> 31)
390 }
391 fn below(&mut self, n: u64) -> u64 {
392 self.next() % n
393 }
394 }
395
396 fn int(n: u64) -> RuntimeValue {
397 RuntimeValue::Int(n as i64)
398 }
399
400 fn rand_set(rng: &mut Rng, domain: u64, ops: usize) -> CrdtValue {
403 let mut s = CrdtValue::new_set(next_replica_id());
404 for _ in 0..ops {
405 let e = int(rng.below(domain));
406 if rng.below(3) == 0 {
407 let _ = s.remove(&e);
408 } else {
409 let _ = s.insert(&e);
410 }
411 }
412 s
413 }
414
415 fn rand_seq(rng: &mut Rng, domain: u64, ops: usize) -> CrdtValue {
417 let mut s = CrdtValue::new_seq(next_replica_id());
418 for _ in 0..ops {
419 let _ = s.append(&int(rng.below(domain)));
420 }
421 s
422 }
423
424 fn join(a: &CrdtValue, b: &CrdtValue) -> CrdtValue {
426 let mut out = a.clone();
427 out.merge(b).unwrap();
428 out
429 }
430
431 #[test]
432 fn orset_merge_obeys_the_crdt_laws() {
433 let mut rng = Rng(0xDEAD_BEEF);
434 for _ in 0..500 {
435 let a = rand_set(&mut rng, 6, 8);
436 let b = rand_set(&mut rng, 6, 8);
437 let c = rand_set(&mut rng, 6, 8);
438 assert!(crdt_values_equal(&join(&a, &b), &join(&b, &a)), "commutative");
439 assert!(
440 crdt_values_equal(&join(&join(&a, &b), &c), &join(&a, &join(&b, &c))),
441 "associative"
442 );
443 assert!(crdt_values_equal(&join(&a, &a), &a), "idempotent");
444 }
445 }
446
447 #[test]
448 fn rga_merge_obeys_the_crdt_laws() {
449 let mut rng = Rng(0x0123_4567);
450 for _ in 0..500 {
451 let a = rand_seq(&mut rng, 6, 6);
452 let b = rand_seq(&mut rng, 6, 6);
453 let c = rand_seq(&mut rng, 6, 6);
454 assert!(crdt_values_equal(&join(&a, &b), &join(&b, &a)), "commutative");
455 assert!(
456 crdt_values_equal(&join(&join(&a, &b), &c), &join(&a, &join(&b, &c))),
457 "associative"
458 );
459 assert!(crdt_values_equal(&join(&a, &a), &a), "idempotent");
460 }
461 }
462
463 #[test]
467 fn orset_add_wins_over_concurrent_remove() {
468 let x = int(42);
469 let mut a = CrdtValue::new_set(next_replica_id());
470 let mut b = CrdtValue::new_set(next_replica_id());
471 a.insert(&x).unwrap();
472 b.insert(&x).unwrap();
473 a.remove(&x).unwrap();
474 let m = join(&a, &b);
475 assert!(m.contains(&x).unwrap(), "the concurrent add must survive the remove");
476 }
477
478 #[test]
479 fn scalar_conversions_round_trip() {
480 for v in [
481 RuntimeValue::Int(-7),
482 RuntimeValue::Text(std::rc::Rc::new("héllo".to_string())),
483 RuntimeValue::Bool(true),
484 ] {
485 let s = CrdtScalar::from_runtime(&v).unwrap();
486 assert_eq!(s.to_runtime(), v);
487 }
488 assert!(CrdtScalar::from_runtime(&RuntimeValue::Float(1.5)).is_err());
490 }
491}