uqa_storage/catalog/
sequence_values.rs1const LOGGED_AHEAD: i128 = 32;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct SequenceValuePosition {
15 pub current: i64,
16 pub called: bool,
17 pub log_count: i64,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct SequenceValueReservation {
23 pub first_value: i64,
24 pub last_value: i64,
25 pub count: i64,
26 pub log_count: i64,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub struct SequenceValueAllocation {
32 pub reservation: SequenceValueReservation,
33 pub logged_value: Option<i64>,
35}
36
37impl SequenceValueAllocation {
38 #[must_use]
40 pub fn record_value(
41 &self,
42 position: SequenceValuePosition,
43 covering: Option<i64>,
44 increment: i64,
45 min_value: i64,
46 max_value: i64,
47 ) -> Option<i64> {
48 if self.logged_value.is_some() {
49 return self.logged_value;
50 }
51 let reservation = self.reservation;
52 let ascending = increment > 0;
53 let wrapped = position.called
55 && if ascending {
56 reservation.first_value <= position.current
57 } else {
58 reservation.first_value >= position.current
59 };
60 let covered = covering.is_some_and(|covering| {
61 !wrapped
62 && if ascending {
63 reservation.last_value <= covering
64 } else {
65 reservation.last_value >= covering
66 }
67 });
68 if covered {
69 return None;
70 }
71 let ahead = i128::from(reservation.last_value)
73 + i128::from(increment) * i128::from(reservation.log_count);
74 let bounded = ahead.clamp(i128::from(min_value), i128::from(max_value));
75 Some(i64::try_from(bounded).expect("a value within the sequence bounds"))
76 }
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum SequenceReservationResult {
81 Missing,
82 DefinitionChanged,
83 Exhausted,
84 Reserved(SequenceValueReservation),
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub enum SequenceSetValueResult {
90 Missing,
91 DefinitionChanged,
92 Set(i64),
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum SequenceLogResult {
98 Missing,
99 DefinitionChanged,
100 Changed(SequenceValuePosition),
102 Logged,
103}
104
105#[must_use]
107pub fn sequence_value_reservation(
108 position: SequenceValuePosition,
109 increment: i64,
110 min_value: i64,
111 max_value: i64,
112 cycle: bool,
113 cache_size: i64,
114) -> Option<SequenceValueReservation> {
115 sequence_value_allocation(position, increment, min_value, max_value, cycle, cache_size)
116 .map(|allocation| allocation.reservation)
117}
118
119#[must_use]
121pub fn sequence_value_allocation(
122 position: SequenceValuePosition,
123 increment: i64,
124 min_value: i64,
125 max_value: i64,
126 cycle: bool,
127 cache_size: i64,
128) -> Option<SequenceValueAllocation> {
129 let SequenceValuePosition {
130 current,
131 called,
132 log_count,
133 } = position;
134 debug_assert_ne!(increment, 0);
135 debug_assert!(cache_size > 0);
136 let first_value = if called {
137 match current
138 .checked_add(increment)
139 .filter(|value| (min_value..=max_value).contains(value))
140 {
141 Some(value) => value,
142 None if cycle && increment > 0 => min_value,
143 None if cycle => max_value,
144 None => return None,
145 }
146 } else {
147 current
148 };
149 let distance = if increment > 0 {
150 i128::from(max_value) - i128::from(first_value)
151 } else {
152 i128::from(first_value) - i128::from(min_value)
153 };
154 let step = i128::from(increment).abs();
155 let available = distance / step + 1;
156 let count = available.min(i128::from(cache_size));
157 let last_value = i128::from(first_value) + i128::from(increment) * (count - 1);
158 let initial_count = i128::from(!called);
159 let cache_fetch = i128::from(cache_size) - initial_count;
160 let mut fetch = cache_fetch;
161 let mut next_log_count = i128::from(log_count);
162 let logged = i128::from(log_count) < cache_fetch || !called;
163 if logged {
164 fetch += LOGGED_AHEAD;
165 next_log_count = fetch;
166 }
167 let fetched = fetch.min(available - initial_count);
168 next_log_count -= fetched.min(cache_fetch);
169 next_log_count -= fetch - fetched;
170 let logged_value = logged.then(|| {
172 let ahead = i128::from(first_value) + i128::from(increment) * (fetched - 1 + initial_count);
173 i64::try_from(ahead).expect("logged sequence value stays in bounds")
174 });
175 Some(SequenceValueAllocation {
176 reservation: SequenceValueReservation {
177 first_value,
178 last_value: i64::try_from(last_value).expect("reserved sequence value stays in bounds"),
179 count: i64::try_from(count).expect("reservation count cannot exceed cache size"),
180 log_count: i64::try_from(next_log_count)
181 .expect("persisted sequence log count cannot exceed the cache request"),
182 },
183 logged_value,
184 })
185}
186
187#[cfg(test)]
188mod tests;