osom_tools_runtime/
length.rs

1#![allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss)]
2
3/// Represents length used by `osom_tools`.
4///
5/// # Notes
6/// Unlike std, [`Length`] has `i32` as the underlying type.
7/// Thus it can handle a lot less items than `usize`
8/// on 64-bit platforms.
9///
10/// But it takes less space in memory, which ultimately is
11/// more useful.
12#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
13#[repr(transparent)]
14#[must_use]
15pub struct Length {
16    value: i32,
17}
18
19impl core::fmt::Debug for Length {
20    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
21        write!(f, "{}", self.value())
22    }
23}
24
25/// Represents errors when building new [`Length`] instance.
26#[derive(Debug, Clone, PartialEq, Eq, Hash)]
27#[repr(u8)]
28pub enum LengthError {
29    /// The length exceeds [`Length::MAX`].
30    TooLarge,
31
32    /// The length is negative.
33    Negative,
34}
35
36impl Length {
37    pub const MAX: usize = (i32::MAX - 1024) as usize;
38    pub const ZERO: Self = unsafe { Self::new_unchecked(0) };
39
40    /// Tries to convert a `usize` to a [`Length`].
41    ///
42    /// # Errors
43    ///
44    /// For details see [`LengthError`].
45    pub const fn try_from_usize(len: usize) -> Result<Self, LengthError> {
46        if len > Self::MAX {
47            return Err(LengthError::TooLarge);
48        }
49
50        Ok(unsafe { Self::new_unchecked(len as i32) })
51    }
52
53    /// Tries to convert a `i32` to a [`Length`].
54    ///
55    /// # Errors
56    ///
57    /// For details see [`LengthError`].
58    pub const fn try_from_i32(len: i32) -> Result<Self, LengthError> {
59        if len < 0 {
60            return Err(LengthError::Negative);
61        }
62
63        Ok(unsafe { Self::new_unchecked(len) })
64    }
65
66    /// Creates a new [`Length`] from a `i32`.
67    ///
68    /// # Safety
69    ///
70    /// The `len` must be non-negative otherwise the behavior is undefined.
71    #[inline(always)]
72    pub const unsafe fn new_unchecked(len: i32) -> Self {
73        Self { value: len }
74    }
75
76    /// Returns the value of the [`Length`].
77    #[inline(always)]
78    #[must_use]
79    pub const fn value(&self) -> i32 {
80        unsafe { core::hint::assert_unchecked(self.value >= 0) };
81        self.value
82    }
83
84    /// Increments the value of the [`Length`] by the given `value`.
85    #[inline(always)]
86    pub const fn inc(&mut self, value: i32) {
87        self.value += value;
88    }
89
90    /// Decrements the value of the [`Length`] by the given `value`.
91    #[inline(always)]
92    pub const fn dec(&mut self, value: i32) {
93        self.value -= value;
94    }
95}
96
97impl TryFrom<usize> for Length {
98    type Error = LengthError;
99
100    fn try_from(value: usize) -> Result<Self, Self::Error> {
101        Self::try_from_usize(value)
102    }
103}
104
105impl TryFrom<i32> for Length {
106    type Error = LengthError;
107
108    fn try_from(value: i32) -> Result<Self, Self::Error> {
109        Self::try_from_i32(value)
110    }
111}
112
113impl From<Length> for usize {
114    fn from(value: Length) -> Self {
115        value.value() as usize
116    }
117}
118
119impl From<Length> for i32 {
120    fn from(value: Length) -> Self {
121        value.value()
122    }
123}
124
125impl From<Length> for u32 {
126    fn from(value: Length) -> Self {
127        value.value() as u32
128    }
129}