sui_inverted_index/
event_seq.rs1use std::ops::{Bound, Range};
10
11pub const EVENT_BITS: u32 = 16;
13
14pub const MAX_EVENTS_PER_TX: u32 = 1 << EVENT_BITS;
16
17pub const MAX_TX_SEQ: u64 = u64::MAX >> EVENT_BITS;
19#[inline]
21pub fn encode_event_seq(tx_seq: u64, event_idx: u32) -> u64 {
22 debug_assert!(event_idx < MAX_EVENTS_PER_TX);
23 debug_assert!(
24 tx_seq <= MAX_TX_SEQ,
25 "tx_seq {} exceeds {} bits and would lose data when shifted by EVENT_BITS={}",
26 tx_seq,
27 64 - EVENT_BITS,
28 EVENT_BITS,
29 );
30 (tx_seq << EVENT_BITS) | (event_idx as u64)
31}
32
33#[inline]
35pub fn decode_event_seq(event_seq: u64) -> (u64, u32) {
36 let tx_seq = event_seq >> EVENT_BITS;
37 let event_idx = (event_seq & (MAX_EVENTS_PER_TX as u64 - 1)) as u32;
38 (tx_seq, event_idx)
39}
40
41#[inline]
43pub fn event_seq_lo(tx_seq: u64) -> u64 {
44 tx_seq << EVENT_BITS
45}
46
47pub fn packed_range<T: Into<(u64, u32)>>(lo: Bound<T>, hi: Bound<T>) -> Range<u64> {
52 let start = match lo.map(Into::into) {
53 Bound::Included((tx_seq, event_idx)) => saturating_lo(tx_seq, event_idx),
54 Bound::Excluded((tx_seq, event_idx)) => saturating_successor(tx_seq, event_idx),
55 Bound::Unbounded => 0,
56 };
57
58 let end = match hi.map(Into::into) {
59 Bound::Included((tx_seq, event_idx)) => saturating_successor(tx_seq, event_idx),
60 Bound::Excluded((tx_seq, event_idx)) => saturating_lo(tx_seq, event_idx),
61 Bound::Unbounded => u64::MAX,
62 };
63
64 start..end
65}
66
67#[inline]
68fn saturating_lo(tx_seq: u64, event_idx: u32) -> u64 {
69 if tx_seq > MAX_TX_SEQ {
70 return u64::MAX;
71 }
72
73 if event_idx >= MAX_EVENTS_PER_TX {
74 return tx_seq
75 .checked_add(1)
76 .filter(|next_tx| *next_tx <= MAX_TX_SEQ)
77 .map(event_seq_lo)
78 .unwrap_or(u64::MAX);
79 }
80
81 encode_event_seq(tx_seq, event_idx)
82}
83
84#[inline]
85fn saturating_successor(tx_seq: u64, event_idx: u32) -> u64 {
86 if event_idx.saturating_add(1) < MAX_EVENTS_PER_TX {
87 saturating_lo(tx_seq, event_idx + 1)
88 } else {
89 saturating_lo(tx_seq.saturating_add(1), 0)
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn test_encode_decode_event_seq_roundtrip() {
99 for (tx_seq, event_idx) in [
100 (0, 0),
101 (0, 1023),
102 (1, 0),
103 (1_000_000, 42),
104 (u64::MAX >> EVENT_BITS, MAX_EVENTS_PER_TX - 1),
105 ] {
106 let packed = encode_event_seq(tx_seq, event_idx);
107 assert_eq!(decode_event_seq(packed), (tx_seq, event_idx));
108 }
109 }
110
111 #[test]
112 fn test_event_seq_ordering() {
113 let a = encode_event_seq(100, 0);
114 let b = encode_event_seq(100, 5);
115 assert!(a < b);
116
117 let c = encode_event_seq(101, 0);
118 assert!(b < c);
119 }
120
121 #[test]
122 fn test_packed_range_whole_tx_span() {
123 assert_eq!(
124 packed_range(Bound::Included((10, 0)), Bound::Excluded((13, 0))),
125 event_seq_lo(10)..event_seq_lo(13),
126 );
127 }
128
129 #[test]
130 fn test_packed_range_excluded_start_advances() {
131 assert_eq!(
132 packed_range(Bound::Excluded((10, 1)), Bound::Excluded((11, 0))).start,
133 encode_event_seq(10, 2),
134 );
135 }
136
137 #[test]
138 fn test_packed_range_forged_extremes_saturate() {
139 let oversized_event = packed_range(
140 Bound::Included((10, u32::MAX)),
141 Bound::Excluded((10, u32::MAX)),
142 );
143 assert!(oversized_event.is_empty());
144 assert_eq!(oversized_event.start, event_seq_lo(11));
145
146 let oversized_tx = packed_range(
147 Bound::Included((u64::MAX, 0)),
148 Bound::Excluded((u64::MAX, 0)),
149 );
150 assert!(oversized_tx.is_empty());
151 assert_eq!(oversized_tx.start, u64::MAX);
152 assert_eq!(oversized_tx.end, u64::MAX);
153 }
154}