merge_machines 0.2.0

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.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +57 -0
  3. data/Cargo.lock +294 -0
  4. data/Cargo.toml +24 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +252 -0
  7. data/ext/merge_machines_native/core/Cargo.toml +24 -0
  8. data/ext/merge_machines_native/core/src/gcounter.rs +242 -0
  9. data/ext/merge_machines_native/core/src/gset.rs +181 -0
  10. data/ext/merge_machines_native/core/src/hybrid.rs +222 -0
  11. data/ext/merge_machines_native/core/src/lib.rs +221 -0
  12. data/ext/merge_machines_native/core/src/logical.rs +139 -0
  13. data/ext/merge_machines_native/core/src/lww.rs +213 -0
  14. data/ext/merge_machines_native/core/src/orset.rs +334 -0
  15. data/ext/merge_machines_native/core/src/pncounter.rs +165 -0
  16. data/ext/merge_machines_native/extconf.rb +3 -0
  17. data/ext/merge_machines_native/ffi/Cargo.toml +20 -0
  18. data/ext/merge_machines_native/ffi/extconf.rb +21 -0
  19. data/ext/merge_machines_native/ffi/src/data.rs +44 -0
  20. data/ext/merge_machines_native/ffi/src/gcounter.rs +138 -0
  21. data/ext/merge_machines_native/ffi/src/gset.rs +187 -0
  22. data/ext/merge_machines_native/ffi/src/hash.rs +33 -0
  23. data/ext/merge_machines_native/ffi/src/hybrid.rs +110 -0
  24. data/ext/merge_machines_native/ffi/src/lib.rs +221 -0
  25. data/ext/merge_machines_native/ffi/src/logical.rs +89 -0
  26. data/ext/merge_machines_native/ffi/src/lww.rs +360 -0
  27. data/ext/merge_machines_native/ffi/src/names.rs +82 -0
  28. data/ext/merge_machines_native/ffi/src/orset.rs +240 -0
  29. data/ext/merge_machines_native/ffi/src/pncounter.rs +143 -0
  30. data/ext/merge_machines_native/ffi/src/shape.rs +116 -0
  31. data/ext/merge_machines_native/ffi/src/wall.rs +37 -0
  32. data/lib/merge_machines/version.rb +5 -0
  33. data/lib/merge_machines.rb +8 -0
  34. data/sig/merge_machines.rbs +238 -0
  35. metadata +98 -0
@@ -0,0 +1,221 @@
1
+ #![cfg_attr(not(feature = "std"), no_std)]
2
+
3
+ //! Conflict-free replicated data types and the clocks they need.
4
+ //!
5
+ //! Every type here has a [`Merge::merge`] that is commutative, associative and
6
+ //! idempotent, so replicas that have seen the same updates hold the same state
7
+ //! no matter what order the updates arrived in, or how many times.
8
+ //!
9
+ //! Nothing here talks to a network and nothing here reads a clock. The caller
10
+ //! owns the transport, the replica ids, the tags, and every clock reading.
11
+ //!
12
+ //! # Which clock
13
+ //!
14
+ //! [`LogicalClock`] counts events. [`HybridClock`] pairs a wall-clock reading
15
+ //! with a counter, where a wall-clock reading means physical time that two
16
+ //! machines can compare: nanoseconds since the Unix epoch, which jumps
17
+ //! backwards when the host's clock is corrected. That is not the monotonic
18
+ //! reading you measure elapsed time with, and no deadline, timeout or budget
19
+ //! may be derived from it. Those belong to a monotonic clock, which this crate
20
+ //! does not have and does not want.
21
+ //!
22
+ //! State is fixed-size: capacities are const generics, and a state with no free
23
+ //! slot left reports [`CapacityError`] rather than allocating. A clock whose
24
+ //! counter has run out reports [`ClockExhausted`] rather than reissuing the
25
+ //! timestamp it last gave away.
26
+ //!
27
+ //! # Putting state on a wire
28
+ //!
29
+ //! Every state encodes to a fixed-length frame of its own
30
+ //! `ENCODED_LEN` bytes, led by a tag naming the structure and, where there is
31
+ //! one, a field naming the const generic. `decode` and `decode_into` reject a
32
+ //! short buffer, another structure's tag, and a frame written at another shape.
33
+
34
+ #[cfg(feature = "alloc")]
35
+ extern crate alloc;
36
+
37
+ /// Compiles and runs the README's examples as doctests.
38
+ #[cfg(doctest)]
39
+ #[doc = include_str!("../README.md")]
40
+ struct ReadmeDoctests;
41
+
42
+ pub mod gcounter;
43
+ pub mod gset;
44
+ pub mod hybrid;
45
+ pub mod logical;
46
+ pub mod lww;
47
+ pub mod orset;
48
+ pub mod pncounter;
49
+
50
+ pub use gcounter::GCounter;
51
+ pub use gset::GSet;
52
+ pub use hybrid::{HybridClock, HybridTimestamp};
53
+ pub use logical::LogicalClock;
54
+ pub use lww::{LwwRegister, STAMP_HYBRID, STAMP_LOGICAL, Stamp};
55
+ pub use orset::{OrSet, Tag, Tagged};
56
+ pub use pncounter::PNCounter;
57
+
58
+ /// Which replica an update came from.
59
+ pub type ReplicaId = u64;
60
+
61
+ /// A state that can absorb another replica's state.
62
+ pub trait Merge {
63
+ /// Why this state could not absorb the other one.
64
+ type Error;
65
+
66
+ /// Folds `other` into this state.
67
+ ///
68
+ /// # Errors
69
+ ///
70
+ /// Returns [`CapacityError`] when the union needs more slots than this
71
+ /// state has. Nothing is rolled back: whatever fit stayed, and merging
72
+ /// again after raising the capacity completes the job.
73
+ fn merge(&mut self, other: &Self) -> Result<(), Self::Error>;
74
+ }
75
+
76
+ /// The union needed a slot and there was none left.
77
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
78
+ pub struct CapacityError;
79
+
80
+ /// A clock has issued the last timestamp its counter can hold.
81
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
82
+ pub struct ClockExhausted;
83
+
84
+ impl core::fmt::Display for ClockExhausted {
85
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
86
+ f.write_str("no timestamp left")
87
+ }
88
+ }
89
+
90
+ impl core::error::Error for ClockExhausted {}
91
+
92
+ impl core::fmt::Display for CapacityError {
93
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
94
+ f.write_str("no free slot")
95
+ }
96
+ }
97
+
98
+ impl core::error::Error for CapacityError {}
99
+
100
+ /// Why a state could not be read from or written to a byte buffer.
101
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
102
+ pub enum CodecError {
103
+ /// The buffer is shorter than the encoded form needs.
104
+ Truncated,
105
+ /// The leading byte names a different structure.
106
+ Tag,
107
+ /// The encoded shape does not match the target type's.
108
+ Parameters,
109
+ }
110
+
111
+ impl core::fmt::Display for CodecError {
112
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
113
+ f.write_str(match self {
114
+ Self::Truncated => "buffer too short",
115
+ Self::Tag => "wrong structure tag",
116
+ Self::Parameters => "shape mismatch",
117
+ })
118
+ }
119
+ }
120
+
121
+ impl core::error::Error for CodecError {}
122
+
123
+ pub(crate) const TAG_LOGICAL_CLOCK: u8 = 0x51;
124
+ pub(crate) const TAG_HYBRID_CLOCK: u8 = 0x52;
125
+ pub(crate) const TAG_GCOUNTER: u8 = 0x61;
126
+ pub(crate) const TAG_PNCOUNTER: u8 = 0x62;
127
+ pub(crate) const TAG_LWW_REGISTER: u8 = 0x71;
128
+ pub(crate) const TAG_GSET: u8 = 0x81;
129
+ pub(crate) const TAG_ORSET: u8 = 0x82;
130
+
131
+ /// Where a frame's leading structure tag sits.
132
+ pub(crate) const TAG: usize = 0;
133
+
134
+ /// Bytes a frame spends on its leading structure tag.
135
+ pub(crate) const TAG_LEN: usize = 1;
136
+
137
+ /// Bytes a frame spends naming the shape it was written at.
138
+ pub(crate) const SHAPE_LEN: usize = 2;
139
+
140
+ /// Bytes a frame spends saying how many of its slot records carry a slot.
141
+ pub(crate) const COUNT_LEN: usize = 2;
142
+
143
+ /// Bytes a frame spends on one 64-bit field.
144
+ pub(crate) const FIELD_LEN: usize = 8;
145
+
146
+ /// A frame's 64-bit field, read from `field`, which is [`FIELD_LEN`] long.
147
+ pub(crate) const fn read_u64(field: &[u8]) -> u64 {
148
+ let mut bytes = [0_u8; FIELD_LEN];
149
+ bytes.copy_from_slice(field);
150
+ u64::from_le_bytes(bytes)
151
+ }
152
+
153
+ /// The low two bytes of `value`, as a frame's shape or count field.
154
+ pub(crate) const fn small_field(value: usize) -> [u8; SHAPE_LEN] {
155
+ let [low, high, ..] = value.to_le_bytes();
156
+ [low, high]
157
+ }
158
+
159
+ /// A frame's shape or count field, read from `field`, which is [`SHAPE_LEN`]
160
+ /// long.
161
+ pub(crate) fn read_small(field: &[u8]) -> usize {
162
+ let mut bytes = [0_u8; SHAPE_LEN];
163
+ bytes.copy_from_slice(field);
164
+ usize::from(u16::from_le_bytes(bytes))
165
+ }
166
+
167
+ /// Writes a frame of `len` bytes at the front of `out` through `fill`,
168
+ /// returning the number of bytes written.
169
+ pub(crate) fn write_into(
170
+ out: &mut [u8],
171
+ len: usize,
172
+ fill: impl FnOnce(&mut [u8]),
173
+ ) -> Result<usize, CodecError> {
174
+ let Some(frame) = out.get_mut(..len) else {
175
+ return Err(CodecError::Truncated);
176
+ };
177
+ fill(frame);
178
+ Ok(len)
179
+ }
180
+
181
+ /// A frame of exactly `len` bytes, filled by `fill`.
182
+ #[cfg(feature = "alloc")]
183
+ pub(crate) fn boxed_frame(len: usize, fill: impl FnOnce(&mut [u8])) -> alloc::boxed::Box<[u8]> {
184
+ let mut frame = alloc::vec![0_u8; len].into_boxed_slice();
185
+ fill(&mut frame);
186
+ frame
187
+ }
188
+
189
+ /// The `len`-byte frame `bytes` leads with, which `tag` has to name.
190
+ pub(crate) fn tagged_frame(bytes: &[u8], len: usize, tag: u8) -> Result<&[u8], CodecError> {
191
+ let Some(frame) = bytes.get(..len) else {
192
+ return Err(CodecError::Truncated);
193
+ };
194
+ if frame[TAG] != tag {
195
+ return Err(CodecError::Tag);
196
+ }
197
+ Ok(frame)
198
+ }
199
+
200
+ /// Whether two slot arrays hold the same elements, ignoring where in the array
201
+ /// each one sits.
202
+ pub(crate) fn same_elements<T: PartialEq>(left: &[Option<T>], right: &[Option<T>]) -> bool {
203
+ let contained =
204
+ |haystack: &[Option<T>], needle: &T| haystack.iter().flatten().any(|held| held == needle);
205
+
206
+ left.iter()
207
+ .flatten()
208
+ .all(|element| contained(right, element))
209
+ && right
210
+ .iter()
211
+ .flatten()
212
+ .all(|element| contained(left, element))
213
+ }
214
+
215
+ /// Index of the first slot nothing has claimed yet.
216
+ pub(crate) fn free_slot<T>(slots: &[Option<T>]) -> Option<usize> {
217
+ slots.iter().position(Option::is_none)
218
+ }
219
+
220
+ #[cfg(test)]
221
+ mod tests;
@@ -0,0 +1,139 @@
1
+ //! Lamport's logical clock: ordering events without reading a clock.
2
+
3
+ #[cfg(feature = "alloc")]
4
+ use crate::boxed_frame;
5
+ use crate::{
6
+ ClockExhausted, CodecError, FIELD_LEN, TAG, TAG_LEN, TAG_LOGICAL_CLOCK, read_u64, tagged_frame,
7
+ write_into,
8
+ };
9
+
10
+ /// Where the counter sits in a frame.
11
+ const COUNTER: core::ops::Range<usize> = TAG_LEN..TAG_LEN + FIELD_LEN;
12
+
13
+ /// A counter that orders events across replicas, with no clock involved.
14
+ ///
15
+ /// ```
16
+ /// use merge_machines::{ClockExhausted, LogicalClock};
17
+ ///
18
+ /// let mut sender = LogicalClock::new();
19
+ /// let mut receiver = LogicalClock::new();
20
+ ///
21
+ /// let sent_at = sender.tick()?;
22
+ /// assert_eq!(sent_at, 1);
23
+ ///
24
+ /// receiver.tick()?;
25
+ /// receiver.tick()?;
26
+ ///
27
+ /// // Receiving is stamped after both the local history and the send.
28
+ /// assert_eq!(receiver.observe(sent_at), Ok(3));
29
+ /// assert_eq!(receiver.observe(99), Ok(100));
30
+ ///
31
+ /// // A counter with nothing left reports it instead of repeating itself.
32
+ /// assert_eq!(receiver.observe(u64::MAX), Err(ClockExhausted));
33
+ /// assert_eq!(receiver.now(), 100);
34
+ /// # Ok::<(), ClockExhausted>(())
35
+ /// ```
36
+ #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
37
+ pub struct LogicalClock {
38
+ counter: u64,
39
+ }
40
+
41
+ impl LogicalClock {
42
+ #[must_use]
43
+ pub const fn new() -> Self {
44
+ Self { counter: 0 }
45
+ }
46
+
47
+ /// The last timestamp this clock issued.
48
+ #[must_use]
49
+ pub const fn now(&self) -> u64 {
50
+ self.counter
51
+ }
52
+
53
+ /// Stamps a local event.
54
+ ///
55
+ /// # Errors
56
+ ///
57
+ /// [`ClockExhausted`] when the counter holds its highest value, which
58
+ /// leaves the clock where it was.
59
+ pub const fn tick(&mut self) -> Result<u64, ClockExhausted> {
60
+ self.issue(self.counter)
61
+ }
62
+
63
+ /// Stamps the receipt of `observed`, a timestamp from another replica.
64
+ ///
65
+ /// # Errors
66
+ ///
67
+ /// The same as [`tick`](Self::tick), and the observed timestamp is not
68
+ /// adopted either.
69
+ pub const fn observe(&mut self, observed: u64) -> Result<u64, ClockExhausted> {
70
+ self.issue(if observed > self.counter {
71
+ observed
72
+ } else {
73
+ self.counter
74
+ })
75
+ }
76
+
77
+ /// Issues the timestamp that follows `base`.
78
+ const fn issue(&mut self, base: u64) -> Result<u64, ClockExhausted> {
79
+ let Some(next) = base.checked_add(1) else {
80
+ return Err(ClockExhausted);
81
+ };
82
+ self.counter = next;
83
+ Ok(next)
84
+ }
85
+
86
+ /// Length of the [`encode`](Self::encode) frame.
87
+ pub const ENCODED_LEN: usize = TAG_LEN + FIELD_LEN;
88
+
89
+ /// Writes the clock into `out`, returning the number of bytes written.
90
+ ///
91
+ /// # Errors
92
+ ///
93
+ /// [`CodecError::Truncated`] when `out` is shorter than
94
+ /// [`ENCODED_LEN`](Self::ENCODED_LEN).
95
+ pub fn encode(&self, out: &mut [u8]) -> Result<usize, CodecError> {
96
+ write_into(out, Self::ENCODED_LEN, |frame| self.write_frame(frame))
97
+ }
98
+
99
+ /// The clock's frame, exactly [`ENCODED_LEN`](Self::ENCODED_LEN) bytes.
100
+ #[cfg(feature = "alloc")]
101
+ #[must_use]
102
+ pub fn encoded(&self) -> alloc::boxed::Box<[u8]> {
103
+ boxed_frame(Self::ENCODED_LEN, |frame| self.write_frame(frame))
104
+ }
105
+
106
+ /// Fills a frame of exactly [`ENCODED_LEN`](Self::ENCODED_LEN) bytes.
107
+ fn write_frame(self, frame: &mut [u8]) {
108
+ frame[TAG] = TAG_LOGICAL_CLOCK;
109
+ frame[COUNTER].copy_from_slice(&self.counter.to_le_bytes());
110
+ }
111
+
112
+ /// Reads a clock written by [`encode`](Self::encode).
113
+ ///
114
+ /// # Errors
115
+ ///
116
+ /// [`CodecError::Truncated`] when `bytes` is too short, [`CodecError::Tag`]
117
+ /// when it holds another structure.
118
+ pub fn decode(bytes: &[u8]) -> Result<Self, CodecError> {
119
+ let mut clock = Self::new();
120
+ clock.decode_into(bytes)?;
121
+ Ok(clock)
122
+ }
123
+
124
+ /// Reads a frame over a clock the caller already holds.
125
+ ///
126
+ /// The clock is untouched when the frame is rejected.
127
+ ///
128
+ /// # Errors
129
+ ///
130
+ /// The same as [`decode`](Self::decode).
131
+ pub fn decode_into(&mut self, bytes: &[u8]) -> Result<(), CodecError> {
132
+ let frame = tagged_frame(bytes, Self::ENCODED_LEN, TAG_LOGICAL_CLOCK)?;
133
+ self.counter = read_u64(&frame[COUNTER]);
134
+ Ok(())
135
+ }
136
+ }
137
+
138
+ #[cfg(test)]
139
+ mod tests;
@@ -0,0 +1,213 @@
1
+ //! Last writer wins, with the replica id as the tie-break.
2
+
3
+ #[cfg(feature = "alloc")]
4
+ use crate::boxed_frame;
5
+ use crate::{
6
+ CodecError, FIELD_LEN, Merge, ReplicaId, SHAPE_LEN, TAG, TAG_LEN, TAG_LWW_REGISTER, read_small,
7
+ read_u64, small_field, tagged_frame, write_into,
8
+ };
9
+ use core::convert::Infallible;
10
+
11
+ /// A stamp naming one 64-bit counter, as [`LogicalClock`](crate::LogicalClock)
12
+ /// issues.
13
+ pub const STAMP_LOGICAL: u8 = 0x01;
14
+
15
+ /// A stamp naming a wall-clock reading and its counter, as
16
+ /// [`HybridClock`](crate::HybridClock) issues.
17
+ pub const STAMP_HYBRID: u8 = 0x02;
18
+
19
+ /// A stamp a register's frame can carry.
20
+ pub trait Stamp: Copy + Default + Ord {
21
+ /// Byte naming this stamp in a register frame.
22
+ const KIND: u8;
23
+
24
+ /// Bytes this stamp occupies in a register frame.
25
+ const ENCODED_LEN: usize;
26
+
27
+ /// Writes this stamp into `field`, which is
28
+ /// [`ENCODED_LEN`](Self::ENCODED_LEN) long.
29
+ fn write(self, field: &mut [u8]);
30
+
31
+ /// Reads a stamp from `field`, which is
32
+ /// [`ENCODED_LEN`](Self::ENCODED_LEN) long.
33
+ fn read(field: &[u8]) -> Self;
34
+ }
35
+
36
+ impl Stamp for u64 {
37
+ const KIND: u8 = STAMP_LOGICAL;
38
+ const ENCODED_LEN: usize = FIELD_LEN;
39
+
40
+ fn write(self, field: &mut [u8]) {
41
+ field.copy_from_slice(&self.to_le_bytes());
42
+ }
43
+
44
+ fn read(field: &[u8]) -> Self {
45
+ read_u64(field)
46
+ }
47
+ }
48
+
49
+ /// Where the stamp kind sits in a frame.
50
+ const STAMP_KIND: core::ops::Range<usize> = TAG_LEN..TAG_LEN + SHAPE_LEN;
51
+
52
+ /// Where the value sits in a frame.
53
+ const VALUE: core::ops::Range<usize> = STAMP_KIND.end..STAMP_KIND.end + FIELD_LEN;
54
+
55
+ /// Where the writing replica sits in a frame.
56
+ const REPLICA: core::ops::Range<usize> = VALUE.end..VALUE.end + FIELD_LEN;
57
+
58
+ /// One value, held by whichever write was stamped last.
59
+ ///
60
+ /// ```
61
+ /// use merge_machines::{LwwRegister, Merge};
62
+ ///
63
+ /// let mut left = LwwRegister::new(10_u32, 4_u64, 1);
64
+ /// let right = LwwRegister::new(20, 9, 2);
65
+ ///
66
+ /// assert_eq!(left.merge(&right), Ok(()));
67
+ /// assert_eq!(*left.value(), 20);
68
+ ///
69
+ /// // A stamp from before the current one changes nothing.
70
+ /// left.set(30, 1, 1);
71
+ /// assert_eq!(*left.value(), 20);
72
+ ///
73
+ /// // Equal stamps go to the higher replica id, from either side.
74
+ /// let mut low = LwwRegister::new(1_u32, 5_u64, 1);
75
+ /// let high = LwwRegister::new(2, 5, 2);
76
+ /// assert_eq!(low.merge(&high), Ok(()));
77
+ /// assert_eq!(*low.value(), 2);
78
+ /// ```
79
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
80
+ pub struct LwwRegister<T, S> {
81
+ value: T,
82
+ stamp: S,
83
+ replica: ReplicaId,
84
+ }
85
+
86
+ impl<T: Clone, S: Ord + Copy> LwwRegister<T, S> {
87
+ #[must_use]
88
+ pub const fn new(value: T, stamp: S, replica: ReplicaId) -> Self {
89
+ Self {
90
+ value,
91
+ stamp,
92
+ replica,
93
+ }
94
+ }
95
+
96
+ /// Writes `value` if `(stamp, replica)` beats the write already held.
97
+ pub fn set(&mut self, value: T, stamp: S, replica: ReplicaId) {
98
+ if (stamp, replica) > (self.stamp, self.replica) {
99
+ self.value = value;
100
+ self.stamp = stamp;
101
+ self.replica = replica;
102
+ }
103
+ }
104
+
105
+ #[must_use]
106
+ pub const fn value(&self) -> &T {
107
+ &self.value
108
+ }
109
+
110
+ /// The stamp of the write being held.
111
+ #[must_use]
112
+ pub const fn stamp(&self) -> &S {
113
+ &self.stamp
114
+ }
115
+
116
+ /// The replica that wrote what is being held.
117
+ #[must_use]
118
+ pub const fn replica(&self) -> ReplicaId {
119
+ self.replica
120
+ }
121
+ }
122
+
123
+ impl<S: Stamp> LwwRegister<u64, S> {
124
+ /// Length of the [`encode`](Self::encode) frame.
125
+ pub const ENCODED_LEN: usize = REPLICA.end + S::ENCODED_LEN;
126
+
127
+ /// Where the stamp sits in a frame.
128
+ const STAMP: core::ops::Range<usize> = REPLICA.end..Self::ENCODED_LEN;
129
+
130
+ /// A register holding nothing, for [`decode`](Self::decode) to read a frame
131
+ /// over.
132
+ fn empty() -> Self {
133
+ Self {
134
+ value: 0,
135
+ stamp: S::default(),
136
+ replica: 0,
137
+ }
138
+ }
139
+
140
+ /// Writes the register into `out`, returning the number of bytes written.
141
+ ///
142
+ /// # Errors
143
+ ///
144
+ /// [`CodecError::Truncated`] when `out` is shorter than
145
+ /// [`ENCODED_LEN`](Self::ENCODED_LEN).
146
+ pub fn encode(&self, out: &mut [u8]) -> Result<usize, CodecError> {
147
+ write_into(out, Self::ENCODED_LEN, |frame| self.write_frame(frame))
148
+ }
149
+
150
+ /// The register's frame, exactly [`ENCODED_LEN`](Self::ENCODED_LEN) bytes.
151
+ #[cfg(feature = "alloc")]
152
+ #[must_use]
153
+ pub fn encoded(&self) -> alloc::boxed::Box<[u8]> {
154
+ boxed_frame(Self::ENCODED_LEN, |frame| self.write_frame(frame))
155
+ }
156
+
157
+ /// Fills a frame of exactly [`ENCODED_LEN`](Self::ENCODED_LEN) bytes.
158
+ fn write_frame(&self, frame: &mut [u8]) {
159
+ frame[TAG] = TAG_LWW_REGISTER;
160
+ frame[STAMP_KIND].copy_from_slice(&small_field(usize::from(S::KIND)));
161
+ frame[VALUE].copy_from_slice(&self.value.to_le_bytes());
162
+ frame[REPLICA].copy_from_slice(&self.replica.to_le_bytes());
163
+ self.stamp.write(&mut frame[Self::STAMP]);
164
+ }
165
+
166
+ /// Reads a register written by [`encode`](Self::encode).
167
+ ///
168
+ /// # Errors
169
+ ///
170
+ /// [`CodecError::Truncated`] when `bytes` is too short, [`CodecError::Tag`]
171
+ /// when it holds another structure, [`CodecError::Parameters`] when it was
172
+ /// written with another kind of stamp.
173
+ pub fn decode(bytes: &[u8]) -> Result<Self, CodecError> {
174
+ let mut register = Self::empty();
175
+ register.decode_into(bytes)?;
176
+ Ok(register)
177
+ }
178
+
179
+ /// Reads a frame over a register the caller already holds.
180
+ ///
181
+ /// The register is untouched when the frame is rejected.
182
+ ///
183
+ /// # Errors
184
+ ///
185
+ /// The same as [`decode`](Self::decode).
186
+ pub fn decode_into(&mut self, bytes: &[u8]) -> Result<(), CodecError> {
187
+ let frame = tagged_frame(bytes, Self::ENCODED_LEN, TAG_LWW_REGISTER)?;
188
+ if read_small(&frame[STAMP_KIND]) != usize::from(S::KIND) {
189
+ return Err(CodecError::Parameters);
190
+ }
191
+
192
+ self.value = read_u64(&frame[VALUE]);
193
+ self.replica = read_u64(&frame[REPLICA]);
194
+ self.stamp = S::read(&frame[Self::STAMP]);
195
+ Ok(())
196
+ }
197
+ }
198
+
199
+ impl<T: Clone, S: Ord + Copy> Merge for LwwRegister<T, S> {
200
+ type Error = Infallible;
201
+
202
+ fn merge(&mut self, other: &Self) -> Result<(), Self::Error> {
203
+ if (other.stamp, other.replica) > (self.stamp, self.replica) {
204
+ self.value.clone_from(&other.value);
205
+ self.stamp = other.stamp;
206
+ self.replica = other.replica;
207
+ }
208
+ Ok(())
209
+ }
210
+ }
211
+
212
+ #[cfg(test)]
213
+ mod tests;