Skip to main content

sponge256sum/
common.rs

1// SPDX-License-Identifier: 0BSD
2// sponge256sum
3// Copyright (C) 2025-2026 by LoRd_MuldeR <mulder2@gmx.de>
4
5use num::traits::SaturatingAdd;
6use sponge_hash_aes256::DEFAULT_DIGEST_SIZE;
7use std::{
8    num::NonZeroUsize,
9    process::ExitCode,
10    sync::atomic::{AtomicUsize, Ordering},
11};
12use tinyvec::{ArrayVec, TinyVec};
13
14// ---------------------------------------------------------------------------
15// Common definitions
16// ---------------------------------------------------------------------------
17
18/// Maximum allowable "snailyness" (throttling) level
19pub const MAX_SNAIL_LEVEL: u8 = 4u8;
20
21/// Maximum allowable digest size, specified in bytes
22pub const MAX_DIGEST_SIZE: usize = 8usize * DEFAULT_DIGEST_SIZE;
23
24/// Type for holding a digest
25pub type Digest = TinyVec<[u8; DEFAULT_DIGEST_SIZE]>;
26
27/// Error type to indicate that a process was aborted
28pub struct Aborted;
29
30// ---------------------------------------------------------------------------
31// Exit status
32// ---------------------------------------------------------------------------
33
34/// Constants that define the process exit status
35pub enum ExitStatus {
36    /// Everything completed successfully
37    Success,
38    /// One or more warnings have been encountered (but **no** fatal error)
39    Warning,
40    /// A fatal error has been encountered
41    Failure,
42}
43
44impl From<ExitStatus> for ExitCode {
45    fn from(value: ExitStatus) -> Self {
46        match value {
47            ExitStatus::Success => Self::from(0u8),
48            ExitStatus::Warning => Self::from(1u8),
49            ExitStatus::Failure => Self::from(2u8),
50        }
51    }
52}
53
54impl From<Aborted> for ExitCode {
55    fn from(_value: Aborted) -> Self {
56        ExitCode::from(3u8)
57    }
58}
59
60// ---------------------------------------------------------------------------
61// Cancellation flag
62// ---------------------------------------------------------------------------
63
64/// A flag which can be used to signal a cancellation request
65pub struct Flag(AtomicUsize);
66
67/// An error type indicating that the cancellation flag could not be updated
68pub struct UpdateError;
69
70// Status constants
71const STATUS_RUNNING: usize = 0usize;
72const STATUS_STOPPED: usize = 1usize;
73const STATUS_ABORTED: usize = 2usize;
74
75impl Flag {
76    /// Check whether the process is still running
77    ///
78    /// This will return `true`, unless either `stop_process()` or `abort_process()` has been triggered.
79    #[inline(always)]
80    pub fn running(&self) -> bool {
81        self.0.load(Ordering::Relaxed) == STATUS_RUNNING
82    }
83
84    /// Request the process to be stopped normally
85    #[inline]
86    pub fn stop_process(&self) -> Result<(), UpdateError> {
87        self.try_update(STATUS_STOPPED)
88    }
89
90    /// Request the process to be aborted, e.g., after a `SIGINT` was received
91    #[inline]
92    pub fn abort_process(&self) -> Result<(), UpdateError> {
93        self.try_update(STATUS_ABORTED)
94    }
95
96    #[inline(always)]
97    fn try_update(&self, new_state: usize) -> Result<(), UpdateError> {
98        match self.0.compare_exchange(STATUS_RUNNING, new_state, Ordering::AcqRel, Ordering::Acquire) {
99            Ok(_) => Ok(()),
100            Err(previous_state) => match previous_state == new_state {
101                true => Ok(()),
102                false => Err(UpdateError),
103            },
104        }
105    }
106}
107
108impl Default for Flag {
109    #[inline]
110    fn default() -> Self {
111        Self(AtomicUsize::new(STATUS_RUNNING))
112    }
113}
114
115// ---------------------------------------------------------------------------
116// TinyVec extension
117// ---------------------------------------------------------------------------
118
119pub trait TinyVecEx {
120    fn with_length(length: usize) -> Self;
121}
122
123impl<const N: usize, T: Copy + Default> TinyVecEx for TinyVec<[T; N]> {
124    #[inline(always)]
125    fn with_length(length: usize) -> Self {
126        if length <= N {
127            TinyVec::Inline(ArrayVec::from_array_len([T::default(); N], length))
128        } else {
129            TinyVec::Heap(vec![T::default(); length])
130        }
131    }
132}
133
134// ---------------------------------------------------------------------------
135// Utility functions
136// ---------------------------------------------------------------------------
137
138/// Increments the referenced counter by one (saturating)
139#[inline(always)]
140pub fn increment<T: SaturatingAdd + From<u8>>(counter: &mut T) {
141    *counter = counter.saturating_add(&T::from(1u8));
142}
143
144/// Compute the thread-count-specific capacity for a bounded channel
145#[inline]
146pub fn get_capacity(thread_count: &NonZeroUsize) -> usize {
147    let capacity = thread_count.get().saturating_mul(2usize).saturating_add(1usize);
148    if capacity > isize::MAX as usize {
149        capacity
150    } else {
151        capacity.next_power_of_two()
152    }
153}
154
155// ---------------------------------------------------------------------------
156// Helper macros
157// ---------------------------------------------------------------------------
158
159/// Conditional printing of error message
160#[macro_export]
161macro_rules! print_error {
162    ($args:ident, $fmt:literal $(,$arg:expr)*$(,)?) => {
163        if !$args.quiet {
164            eprintln!(concat!("[sponge256sum] ", $fmt) $(, $arg)*);
165        }
166    };
167}