1use crate::error::ModelsError;
4use massa_hash::Hash;
5use massa_serialization::{
6 Deserializer, SerializeError, Serializer, U64VarIntDeserializer, U64VarIntSerializer,
7};
8use nom::bytes::complete::take;
9use nom::error::{context, ContextError, ParseError};
10use serde::{Deserialize, Serialize};
11use std::ops::{Bound, RangeBounds};
12use std::str::FromStr;
13use std::{cmp::Ordering, convert::TryInto};
14
15#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
17pub struct Slot {
18 pub period: u64,
20 pub thread: u8,
22}
23
24pub const SLOT_KEY_SIZE: usize = 9;
26
27#[derive(Clone)]
29pub struct SlotSerializer {
30 u64_serializer: U64VarIntSerializer,
31}
32
33impl SlotSerializer {
34 pub const fn new() -> Self {
36 Self {
37 u64_serializer: U64VarIntSerializer::new(),
38 }
39 }
40}
41
42impl Default for SlotSerializer {
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48impl Serializer<Slot> for SlotSerializer {
49 fn serialize(&self, value: &Slot, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
60 self.u64_serializer.serialize(&value.period, buffer)?;
61 buffer.push(value.thread);
62 Ok(())
63 }
64}
65
66#[derive(Clone)]
68pub struct SlotDeserializer {
69 period_deserializer: U64VarIntDeserializer,
70 range_thread: (Bound<u8>, Bound<u8>),
71}
72
73impl SlotDeserializer {
74 pub const fn new(
76 range_period: (Bound<u64>, Bound<u64>),
77 range_thread: (Bound<u8>, Bound<u8>),
78 ) -> Self {
79 Self {
80 period_deserializer: U64VarIntDeserializer::new(range_period.0, range_period.1),
81 range_thread,
82 }
83 }
84}
85
86impl Deserializer<Slot> for SlotDeserializer {
87 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
102 &self,
103 buffer: &'a [u8],
104 ) -> nom::IResult<&'a [u8], Slot, E> {
105 context("Failed Slot deserialization", |input: &'a [u8]| {
106 let (rest, period) = self.period_deserializer.deserialize(input)?;
107 let (rest2, thread_) = take(1usize)(rest)?;
108 let thread = thread_[0];
109 if !self.range_thread.contains(&thread) {
110 return Err(nom::Err::Error(ParseError::from_error_kind(
111 &rest[0..1],
112 nom::error::ErrorKind::Digit,
113 )));
114 }
115 Ok((rest2, Slot { period, thread }))
117 })(buffer)
118 }
119}
120
121impl PartialOrd for Slot {
122 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
123 Some(self.cmp(other))
124 }
125}
126
127impl Ord for Slot {
128 fn cmp(&self, other: &Self) -> Ordering {
129 (self.period, self.thread).cmp(&(other.period, other.thread))
130 }
131}
132
133impl std::fmt::Display for Slot {
134 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
135 write!(f, "(period: {}, thread: {})", self.period, self.thread)?;
136 Ok(())
137 }
138}
139
140impl FromStr for Slot {
141 type Err = ModelsError;
142 fn from_str(s: &str) -> Result<Self, Self::Err> {
143 let v: Vec<_> = s.split(',').collect();
144 if v.len() != 2 {
145 Err(ModelsError::DeserializeError(
146 "invalid slot format".to_string(),
147 ))
148 } else {
149 Ok(Slot::new(
150 v[0].parse::<u64>()
151 .map_err(|_| ModelsError::DeserializeError("invalid period".to_string()))?,
152 v[1].parse::<u8>()
153 .map_err(|_| ModelsError::DeserializeError("invalid thread".to_string()))?,
154 ))
155 }
156 }
157}
158
159impl Slot {
160 pub fn new(period: u64, thread: u8) -> Slot {
162 Slot { period, thread }
163 }
164
165 pub fn new_last_of_cycle(
167 cycle: u64,
168 periods_per_cycle: u64,
169 thread_count: u8,
170 ) -> Result<Slot, ModelsError> {
171 let period = cycle
172 .checked_mul(periods_per_cycle)
173 .ok_or(ModelsError::PeriodOverflowError)?
174 .checked_add(periods_per_cycle.saturating_sub(1))
175 .ok_or(ModelsError::PeriodOverflowError)?;
176 Ok(Slot {
177 period,
178 thread: thread_count.saturating_sub(1),
179 })
180 }
181
182 pub fn new_first_of_cycle(cycle: u64, periods_per_cycle: u64) -> Result<Slot, ModelsError> {
184 let period = cycle
185 .checked_mul(periods_per_cycle)
186 .ok_or(ModelsError::PeriodOverflowError)?;
187 Ok(Slot { period, thread: 0 })
188 }
189
190 pub const fn min() -> Slot {
192 Slot {
193 period: 0,
194 thread: 0,
195 }
196 }
197
198 pub const fn max(thread_count: u8) -> Slot {
200 Slot {
201 period: u64::MAX,
202 thread: thread_count.saturating_sub(1),
203 }
204 }
205
206 pub fn get_first_bit(&self) -> bool {
208 Hash::compute_from(&self.to_bytes_key()).to_bytes()[0] >> 7 == 1
209 }
210
211 pub fn get_cycle(&self, periods_per_cycle: u64) -> u64 {
213 self.period / periods_per_cycle
214 }
215
216 pub fn is_last_of_cycle(&self, periods_per_cycle: u64, thread_count: u8) -> bool {
218 self.period % periods_per_cycle == (periods_per_cycle.saturating_sub(1))
219 && self.thread == (thread_count.saturating_sub(1))
220 }
221
222 pub fn is_first_of_cycle(&self, periods_per_cycle: u64) -> bool {
224 self.period.is_multiple_of(periods_per_cycle) && self.thread == 0
225 }
226
227 pub fn to_bytes_key(&self) -> [u8; SLOT_KEY_SIZE] {
238 let mut res = [0u8; SLOT_KEY_SIZE];
239 res[..8].clone_from_slice(&self.period.to_be_bytes());
240 res[8] = self.thread;
241 res
242 }
243
244 pub fn from_bytes_key(buffer: &[u8; SLOT_KEY_SIZE]) -> Self {
255 Slot {
256 period: u64::from_be_bytes(buffer[..8].try_into().unwrap()), thread: buffer[8],
258 }
259 }
260
261 pub fn get_next_slot(&self, thread_count: u8) -> Result<Slot, ModelsError> {
272 if self.thread.saturating_add(1u8) >= thread_count {
273 Ok(Slot::new(
274 self.period
275 .checked_add(1u64)
276 .ok_or(ModelsError::PeriodOverflowError)?,
277 0u8,
278 ))
279 } else {
280 Ok(Slot::new(
281 self.period,
282 self.thread
283 .checked_add(1u8)
284 .ok_or(ModelsError::ThreadOverflowError)?,
285 ))
286 }
287 }
288
289 pub fn get_prev_slot(&self, thread_count: u8) -> Result<Slot, ModelsError> {
300 match self.thread.checked_sub(1u8) {
301 Some(t) => Ok(Slot::new(self.period, t)),
302 None => Ok(Slot::new(
303 self.period
304 .checked_sub(1)
305 .ok_or(ModelsError::PeriodOverflowError)?,
306 thread_count.saturating_sub(1),
307 )),
308 }
309 }
310
311 pub fn slots_since(&self, s: &Slot, thread_count: u8) -> Result<u64, ModelsError> {
315 if s > self {
317 return Err(ModelsError::PeriodOverflowError);
318 }
319
320 Ok((self.period - s.period)
322 .checked_mul(thread_count as u64)
323 .ok_or(ModelsError::PeriodOverflowError)?
324 .checked_add(self.thread as u64)
325 .ok_or(ModelsError::PeriodOverflowError)?
326 .saturating_sub(s.thread as u64))
327 }
328
329 pub fn skip(&self, n: u64, thread_count: u8) -> Result<Slot, ModelsError> {
338 let mut res_period = self
339 .period
340 .checked_add(n / (thread_count as u64))
341 .ok_or(ModelsError::PeriodOverflowError)?;
342 let mut res_thread = (self.thread as u64)
343 .checked_add(n % (thread_count as u64))
344 .ok_or(ModelsError::ThreadOverflowError)?;
345
346 if res_thread >= thread_count as u64 {
347 res_period = res_period
348 .checked_add(1)
349 .ok_or(ModelsError::PeriodOverflowError)?;
350 res_thread -= thread_count as u64;
351 }
352
353 Ok(Slot::new(res_period, res_thread as u8))
354 }
355}
356
357#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq)]
359pub struct IndexedSlot {
360 pub slot: Slot,
362 pub index: usize,
364}
365
366impl std::fmt::Display for IndexedSlot {
367 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
368 writeln!(f, "Slot: {}, Index: {}", self.slot, self.index)
369 }
370}
371
372#[cfg(test)]
373mod test {
374 use super::*;
375
376 #[test]
377 fn test_new_last_of_cycle() {
378 let expected_slot = Slot {
380 period: 767,
381 thread: 31,
382 };
383 let actual_slot = Slot::new_last_of_cycle(5, 128, 32).unwrap();
384 assert_eq!(actual_slot, expected_slot);
385
386 let expected_error_mul = "period overflow error".to_string();
388 let actual_error_overflow_mul = Slot::new_last_of_cycle(u64::MAX, 128, 32)
389 .unwrap_err()
390 .to_string();
391
392 assert_eq!(actual_error_overflow_mul, expected_error_mul);
393
394 let actual_error_overflow_add = Slot::new_last_of_cycle(u64::MAX - 1, u64::MAX, 32)
396 .unwrap_err()
397 .to_string();
398
399 assert_eq!(actual_error_overflow_add, expected_error_mul);
400 }
401
402 #[test]
403 fn test_new_first_of_cycle() {
404 let expected_slot = Slot {
406 period: 640,
407 thread: 0,
408 };
409 let actual_slot = Slot::new_first_of_cycle(5, 128).unwrap();
410 assert_eq!(actual_slot, expected_slot);
411
412 let expected_error = "period overflow error".to_string();
414 let actual_error = Slot::new_first_of_cycle(u64::MAX, 128)
415 .unwrap_err()
416 .to_string();
417
418 assert_eq!(actual_error, expected_error);
419 }
420
421 #[test]
422 fn test_slot_serde() {
423 let expected_slot = Slot::new(12, 32);
424
425 let serialized = serde_json::to_string(&expected_slot).unwrap();
426 let actual_slot: Slot = serde_json::from_str(&serialized).unwrap();
427
428 assert_eq!(actual_slot, expected_slot);
429 }
430}