Skip to main content

osom_lib_primitives/
length.rs

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