1use crate::hlc::HlcTimestamp;
13use zerocopy::{FromBytes, IntoBytes};
14
15#[repr(C)]
22#[derive(
23 Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, IntoBytes, FromBytes,
24)]
25pub struct SortKey {
26 pub hlc: HlcTimestamp,
28 pub node_seq: u64,
31}
32
33impl SortKey {
34 const SEQUENCE_BITS: u32 = 48;
36
37 const SEQUENCE_MASK: u64 = (1 << Self::SEQUENCE_BITS) - 1;
39
40 #[inline]
48 #[must_use]
49 pub const fn new(hlc: HlcTimestamp, node_id: u16, sequence: u64) -> Self {
50 let node_seq = ((node_id as u64) << Self::SEQUENCE_BITS) | (sequence & Self::SEQUENCE_MASK);
51 Self { hlc, node_seq }
52 }
53
54 #[inline]
58 #[must_use]
59 pub const fn from_timestamp_ns(timestamp_ns: u64, sequence: u32) -> Self {
60 let physical_ms = timestamp_ns / 1_000_000;
61 let hlc = HlcTimestamp::new(physical_ms, 0);
62 Self::new(hlc, 0, sequence as u64)
63 }
64
65 #[inline]
67 #[must_use]
68 pub const fn zero() -> Self {
69 Self {
70 hlc: HlcTimestamp::zero(),
71 node_seq: 0,
72 }
73 }
74
75 #[inline]
77 #[must_use]
78 pub const fn max() -> Self {
79 Self {
80 hlc: HlcTimestamp::max(),
81 node_seq: u64::MAX,
82 }
83 }
84
85 #[inline]
87 #[must_use]
88 pub const fn hlc(&self) -> HlcTimestamp {
89 self.hlc
90 }
91
92 #[inline]
94 #[must_use]
95 pub const fn node_id(&self) -> u16 {
96 (self.node_seq >> Self::SEQUENCE_BITS) as u16
97 }
98
99 #[inline]
101 #[must_use]
102 pub const fn sequence(&self) -> u64 {
103 self.node_seq & Self::SEQUENCE_MASK
104 }
105
106 #[inline]
110 #[must_use]
111 pub const fn timestamp_ns(&self) -> u64 {
112 self.hlc.to_nanos_approx()
113 }
114
115 #[inline]
117 #[must_use]
118 pub const fn to_u128(&self) -> u128 {
119 ((self.hlc.as_u64() as u128) << 64) | (self.node_seq as u128)
120 }
121
122 #[inline]
124 #[must_use]
125 #[allow(clippy::cast_possible_truncation)]
126 pub const fn from_u128(value: u128) -> Self {
127 let hlc = HlcTimestamp::from_raw((value >> 64) as u64);
128 let node_seq = value as u64;
129 Self { hlc, node_seq }
130 }
131
132 #[inline]
134 #[must_use]
135 pub const fn size() -> usize {
136 std::mem::size_of::<Self>()
137 }
138}
139
140impl std::fmt::Display for SortKey {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 write!(
143 f,
144 "SortKey({}, node={}, seq={})",
145 self.hlc,
146 self.node_id(),
147 self.sequence()
148 )
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn test_sort_key_ordering() {
158 let hlc1 = HlcTimestamp::new(100, 0);
159 let hlc2 = HlcTimestamp::new(100, 1);
160 let hlc3 = HlcTimestamp::new(200, 0);
161
162 let k1 = SortKey::new(hlc1, 1, 0);
163 let k2 = SortKey::new(hlc2, 1, 0);
164 let k3 = SortKey::new(hlc3, 1, 0);
165
166 assert!(k1 < k2, "Higher logical counter should be greater");
167 assert!(k2 < k3, "Higher physical time should be greater");
168 assert!(k1 < k3);
169 }
170
171 #[test]
172 fn test_sort_key_node_id_tiebreak() {
173 let hlc = HlcTimestamp::new(100, 0);
174
175 let k1 = SortKey::new(hlc, 1, 0);
176 let k2 = SortKey::new(hlc, 2, 0);
177
178 assert!(k1 < k2, "Lower node_id should win tie-break");
179 }
180
181 #[test]
182 fn test_sort_key_sequence_ordering() {
183 let hlc = HlcTimestamp::new(100, 0);
184
185 let k1 = SortKey::new(hlc, 1, 0);
186 let k2 = SortKey::new(hlc, 1, 1);
187 let k3 = SortKey::new(hlc, 1, 100);
188
189 assert!(k1 < k2);
190 assert!(k2 < k3);
191 }
192
193 #[test]
194 fn test_sort_key_component_extraction() {
195 let hlc = HlcTimestamp::new(12345, 42);
196 let key = SortKey::new(hlc, 7, 999);
197
198 assert_eq!(key.hlc().physical_ms(), 12345);
199 assert_eq!(key.hlc().logical(), 42);
200 assert_eq!(key.node_id(), 7);
201 assert_eq!(key.sequence(), 999);
202 }
203
204 #[test]
205 fn test_sort_key_u128_roundtrip() {
206 let hlc = HlcTimestamp::new(12345, 42);
207 let original = SortKey::new(hlc, 7, 999);
208
209 let as_u128 = original.to_u128();
210 let restored = SortKey::from_u128(as_u128);
211
212 assert_eq!(original, restored);
213 }
214
215 #[test]
216 fn test_sort_key_size() {
217 assert_eq!(SortKey::size(), 16);
218 }
219
220 #[test]
221 fn test_sort_key_legacy_compatibility() {
222 let key = SortKey::from_timestamp_ns(1_000_000_000, 42);
223
224 assert_eq!(key.hlc().physical_ms(), 1000);
226 assert_eq!(key.sequence(), 42);
227 }
228}