osom_primitives/
length.rs

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