our_string/
bytes.rs

1use core::cmp::{Ordering, PartialEq, Eq, PartialOrd, Ord};
2use core::fmt::{self, Debug};
3use core::borrow::Borrow;
4use core::num::NonZero;
5use core::ops::Deref;
6use core::hash::Hash;
7
8use crate::Comrade;
9
10#[derive(Clone)]
11enum OurInner<T, const N: usize> {
12    Inline { len: core::num::NonZero<u8>, content: [u8; N] },
13    Outline { content: T },
14}
15
16/// A customizable immutable shared byte collection.
17///
18/// Data is backed inline up to `N` bytes (max 254), or stored dynamically by (shared) [`Comrade`] `T`.
19///
20/// This type can be constructed via the [`From`] trait given either a `&[u8]` (in which case inlining is attempted but may result in a shared `T` allocation)
21/// or a shared handle of type `T` (in which case no inlining is used and the shared handle is simply wrapped).
22///
23/// Because of this, it is recommended to not use the `T` constructor unless you are already sharing the value around as type `T` elsewhere.
24#[derive(Clone)]
25pub struct OurBytes<T: Comrade, const N: usize>(OurInner<T, N>);
26
27impl<T: Comrade, const N: usize> OurBytes<T, N> {
28    /// Creates a new empty instance of [`OurBytes`] with inlined data.
29    pub const fn new() -> Self {
30        Self(OurInner::Inline { len: NonZero::<u8>::MAX, content: [0; N] })
31    }
32    /// Converts this [`OurBytes`] instance into another [`OurBytes`] type which uses the same shared type `T`.
33    ///
34    /// If the content of this instance is already allocated via shared handle `T`, that handle will simply be reused without inlining.
35    /// Otherwise, re-inlining will be attempted, but may fail if `M < N` and result in a new shared `T` allocation.
36    ///
37    /// Because of this, it is advised to minimize the use of this function (e.g., by only using one [`OurBytes`] type throughout your codebase).
38    pub fn convert<const M: usize>(self) -> OurBytes<T, M> {
39        match self.0 {
40            OurInner::Inline { .. } => OurBytes::from(self.as_slice()),
41            OurInner::Outline { content } => OurBytes::from(content),
42        }
43    }
44    /// Gets a shared reference to the content.
45    pub fn as_slice(&self) -> &[u8] {
46        self
47    }
48}
49
50impl<T: Comrade, const N: usize> Deref for OurBytes<T, N> {
51    type Target = [u8];
52    fn deref(&self) -> &Self::Target {
53        match &self.0 {
54            OurInner::Inline { len, content } => &content[..(!len.get()) as usize],
55            OurInner::Outline { content } => content.as_slice(),
56        }
57    }
58}
59
60impl<T: Comrade, const N: usize> From<&[u8]> for OurBytes<T, N> {
61    fn from(value: &[u8]) -> Self {
62        if value.len() <= N && value.len() < u8::MAX as usize {
63            let mut content = [0; N];
64            content[..value.len()].copy_from_slice(value);
65            Self(OurInner::Inline { len: NonZero::new(!(value.len() as u8)).unwrap(), content })
66        } else {
67            Self(OurInner::Outline { content: T::from_slice(value) })
68        }
69    }
70}
71
72impl<T: Comrade, const N: usize> From<T> for OurBytes<T, N> {
73    fn from(content: T) -> Self {
74        Self(OurInner::Outline { content })
75    }
76}
77
78impl<T: Comrade, const N: usize> Default for OurBytes<T, N> {
79    fn default() -> Self {
80        Self::new()
81    }
82}
83
84impl<T: Comrade, const N: usize> AsRef<[u8]> for OurBytes<T, N> {
85    fn as_ref(&self) -> &[u8] {
86        self
87    }
88}
89
90impl<T: Comrade, const N: usize> Borrow<[u8]> for OurBytes<T, N> {
91    fn borrow(&self) -> &[u8] {
92        self
93    }
94}
95
96impl<T: Comrade, const N: usize> Debug for OurBytes<T, N> {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        <[u8] as Debug>::fmt(&**self, f)
99    }
100}
101
102impl<T: Comrade, const N: usize> Hash for OurBytes<T, N> {
103    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
104        (**self).hash(state)
105    }
106}
107
108impl<U: Deref<Target = [u8]>, T: Comrade, const N: usize> PartialEq<U> for OurBytes<T, N> {
109    fn eq(&self, other: &U) -> bool {
110        (**self).eq(&**other)
111    }
112}
113
114impl<T: Comrade, const N: usize> PartialEq<OurBytes<T, N>> for &[u8] {
115    fn eq(&self, other: &OurBytes<T, N>) -> bool {
116        (**self).eq(&**other)
117    }
118}
119
120impl<T: Comrade, const N: usize> Eq for OurBytes<T, N> {}
121
122impl<U: Deref<Target = [u8]>, T: Comrade, const N: usize> PartialOrd<U> for OurBytes<T, N> {
123    fn partial_cmp(&self, other: &U) -> Option<Ordering> {
124        (**self).partial_cmp(&**other)
125    }
126}
127
128impl<T: Comrade, const N: usize> PartialOrd<OurBytes<T, N>> for &[u8] {
129    fn partial_cmp(&self, other: &OurBytes<T, N>) -> Option<Ordering> {
130        (**self).partial_cmp(&**other)
131    }
132}
133
134impl<T: Comrade, const N: usize> Ord for OurBytes<T, N> {
135    fn cmp(&self, other: &Self) -> Ordering {
136        (**self).cmp(&**other)
137    }
138}