Skip to main content

osom_lib_primitives/
length.rs

1//! Holds the [`Length`] primitive.
2
3use core::{convert::Infallible, fmt::Display};
4
5use osom_lib_reprc::macros::reprc;
6use osom_lib_try_clone::TryClone;
7
8/// Represents length internally used by osom tools. Unlike Rust `usize`
9/// type, the [`Length`] type is a thin wrapper around `u32`. In particular
10/// it is 32-bit on 64-bit machines. While limiting, the osom libs won't
11/// be using such big arrays anyway. And it saves us space.
12#[reprc]
13#[repr(transparent)]
14#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
15#[must_use]
16pub struct Length {
17    size: u32,
18}
19
20/// Represents possible [`Length`] errors.
21#[reprc]
22#[repr(u8)]
23#[derive(Debug, PartialEq, Eq, Clone, Copy)]
24pub enum LengthError {
25    /// [`Length`] is negative.
26    Negative = 0,
27
28    /// [`Length`] is bigger than [`Length::MAX_LENGTH`].
29    OutOfMaxRange = 1,
30}
31
32impl Display for LengthError {
33    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34        match self {
35            LengthError::Negative => write!(f, "LengthError::Negative"),
36            LengthError::OutOfMaxRange => write!(f, "LengthError::OutOfMaxRange"),
37        }
38    }
39}
40
41impl Length {
42    /// The amount of bytes one can store safely in a buffer,
43    /// without exceeding `u32` range. In other words [`Length::MAX_LENGTH`] plus
44    /// [`Length::SAFE_MARGIN`] is guaranteed to never exceed [`u32::MAX`].
45    ///
46    /// This can be useful if one wants to store some additional data in a buffer whose
47    /// length is represented by [`Length`].
48    pub const SAFE_MARGIN: u32 = 2048;
49
50    /// The maximal value [`Length`] can take. Basically this is
51    /// `i32::MAX - Self::SAFE_MARGIN`.
52    pub const MAX_LENGTH: Self = const {
53        const I32_MAX: u32 = i32::MAX as u32;
54        assert!(Self::SAFE_MARGIN >= 64, "SAFE_MARGIN has to be at least 64.");
55        assert!(
56            Self::SAFE_MARGIN < I32_MAX,
57            "SAFE_MARGIN has to be smaller than i32::MAX"
58        );
59        unsafe { Self::new_unchecked(I32_MAX - Self::SAFE_MARGIN) }
60    };
61
62    /// Represents [`Length`] zero.
63    pub const ZERO: Self = unsafe { Self::new_unchecked(0) };
64
65    /// Represents [`Length`] one.
66    pub const ONE: Self = unsafe { Self::new_unchecked(1) };
67
68    /// Creates a new [`Length`] out of `u32`.
69    ///
70    /// # Safety
71    ///
72    /// This function does not validate `value`. It is up to the
73    /// caller to ensure that its value is below or equal to
74    /// [`Length::MAX_LENGTH`].
75    #[inline(always)]
76    pub const unsafe fn new_unchecked(value: u32) -> Self {
77        Self { size: value }
78    }
79
80    /// Creates a new [`Length`] from `u32`.
81    ///
82    /// # Errors
83    ///
84    /// For details see [`LengthError`].
85    #[inline(always)]
86    pub const fn try_from_u32(value: u32) -> Result<Self, LengthError> {
87        if value > Self::MAX_LENGTH.as_u32() {
88            Err(LengthError::OutOfMaxRange)
89        } else {
90            Ok(unsafe { Self::new_unchecked(value) })
91        }
92    }
93
94    /// Creates a new [`Length`] from `usize`.
95    ///
96    /// # Errors
97    ///
98    /// For details see [`LengthError`].
99    #[inline(always)]
100    pub const fn try_from_usize(value: usize) -> Result<Self, LengthError> {
101        #[allow(clippy::cast_possible_truncation)]
102        if value > Self::MAX_LENGTH.as_usize() {
103            Err(LengthError::OutOfMaxRange)
104        } else {
105            Ok(unsafe { Self::new_unchecked(value as u32) })
106        }
107    }
108
109    /// Creates a new [`Length`] from `i32`.
110    ///
111    /// # Errors
112    ///
113    /// For details see [`LengthError`].
114    #[inline(always)]
115    pub const fn try_from_i32(value: i32) -> Result<Self, LengthError> {
116        if value < 0 {
117            return Err(LengthError::Negative);
118        }
119
120        #[allow(clippy::cast_sign_loss)]
121        let value = value as u32;
122
123        Self::try_from_u32(value)
124    }
125
126    /// Turns the [`Length`] into `u32`.
127    #[inline(always)]
128    #[must_use]
129    pub const fn as_u32(&self) -> u32 {
130        self.size
131    }
132
133    /// Turns the [`Length`] into `usize`.
134    #[inline(always)]
135    #[must_use]
136    pub const fn as_usize(&self) -> usize {
137        self.size as usize
138    }
139}
140
141impl TryClone for Length {
142    type Error = Infallible;
143    fn try_clone(&self) -> Result<Self, Self::Error> {
144        Ok(*self)
145    }
146}
147
148impl TryFrom<i32> for Length {
149    type Error = LengthError;
150
151    fn try_from(value: i32) -> Result<Self, Self::Error> {
152        Self::try_from_i32(value)
153    }
154}
155
156impl TryFrom<u32> for Length {
157    type Error = LengthError;
158
159    fn try_from(value: u32) -> Result<Self, Self::Error> {
160        Self::try_from_u32(value)
161    }
162}
163
164impl TryFrom<usize> for Length {
165    type Error = LengthError;
166
167    fn try_from(value: usize) -> Result<Self, Self::Error> {
168        Self::try_from_usize(value)
169    }
170}
171
172impl From<Length> for u32 {
173    fn from(value: Length) -> Self {
174        value.as_u32()
175    }
176}
177
178impl From<Length> for usize {
179    fn from(value: Length) -> Self {
180        value.as_usize()
181    }
182}
183
184impl core::fmt::Display for Length {
185    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
186        self.as_u32().fmt(f)
187    }
188}
189
190const _: () = const {
191    assert!(size_of::<Length>() == 4, "Length is expected to be of size 4");
192};