const_format/
utils.rs

1//! Miscelaneous functions.
2
3use core::ops::Range;
4
5/// Newtype wrapper to get around limitations in `const fn`s
6pub(crate) struct Constructor<T>(#[allow(dead_code)] fn() -> T);
7
8pub use crate::slice_cmp::{str_eq, u8_slice_eq};
9
10#[doc(hidden)]
11#[inline]
12pub const fn saturate_range(s: &[u8], range: &Range<usize>) -> Range<usize> {
13    let len = s.len();
14    let end = min_usize(range.end, len);
15    min_usize(range.start, end)..end
16}
17
18#[doc(hidden)]
19#[cfg(feature = "rust_1_64")]
20#[repr(C)]
21pub union Dereference<'a, T: ?Sized> {
22    pub ptr: *const T,
23    pub reff: &'a T,
24}
25
26////////////////////////////////////////////////////////////////////////////////
27
28macro_rules! slice_up_to_len_alt_docs {
29    ($item:item) => {
30        /// A const equivalent of `&slice[..len]`.
31        ///
32        /// If `slice.len() < len`, this simply returns `slice` back.
33        ///
34        /// # Runtime
35        ///
36        /// If the "rust_1_64" feature is disabled,
37        /// thich takes linear time to remove the trailing elements,
38        /// proportional to `slice.len() - len`.
39        ///
40        /// If the "rust_1_64" feature is enabled, it takes constant time to run.
41        ///
42        /// # Example
43        ///
44        /// ```rust
45        /// use const_format::utils::slice_up_to_len_alt;
46        ///
47        /// const FIBB: &[u16] = &[3, 5, 8, 13, 21, 34, 55, 89];
48        ///
49        /// const TWO: &[u16] = slice_up_to_len_alt(FIBB, 2);
50        /// const FOUR: &[u16] = slice_up_to_len_alt(FIBB, 4);
51        /// const ALL: &[u16] = slice_up_to_len_alt(FIBB, usize::MAX);
52        ///
53        /// assert_eq!(TWO, &[3, 5]);
54        /// assert_eq!(FOUR, &[3, 5, 8, 13]);
55        /// assert_eq!(FIBB, ALL);
56        ///
57        /// ```
58        $item
59    };
60}
61
62slice_up_to_len_alt_docs! {
63    #[cfg(feature = "rust_1_64")]
64    #[inline(always)]
65    pub const fn slice_up_to_len_alt<T>(slice: &[T], len: usize) -> &[T] {
66        slice_up_to_len(slice, len)
67    }
68}
69slice_up_to_len_alt_docs! {
70    #[cfg(not(feature = "rust_1_64"))]
71    pub const fn slice_up_to_len_alt<T>(slice: &[T], len: usize) -> &[T] {
72        let mut rem = slice.len().saturating_add(1).saturating_sub(len);
73        let mut ret = slice;
74
75        if rem == 0 {
76            return slice;
77        }
78
79        macro_rules! slice_up_to_len_alt_impl{
80            (
81                $( ($len:expr, [$($ignored:tt)*]) )*
82            )=>{
83                $(
84                    while rem > $len {
85                        if let [next @ .., $($ignored)* ] = ret {
86                            ret = next;
87                            rem -= $len;
88                        }
89                    }
90                )*
91            }
92        }
93        slice_up_to_len_alt_impl!{
94            (36, [_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,_, _, _, _, _, _,])
95            (6, [_, _, _, _, _, _])
96            (1, [_])
97        }
98        ret
99    }
100}
101
102////////////////////////////////////////////////////////////////////////////////
103
104macro_rules! slice_up_to_len_docs {
105    ($item:item) => {
106        /// A conditionally-const equivalent of `&slice[..len]`.
107        ///
108        /// If `slice.len() < len`, this simply returns `slice` back.
109        ///
110        /// # Constness
111        ///
112        /// This function takes constant time,
113        /// and in order to be `const fn` it requires the "rust_1_64"
114        /// feature to be enabled.
115        ///
116        /// # Example
117        ///
118        /// ```rust
119        /// use const_format::utils::slice_up_to_len_alt;
120        ///
121        /// const FIBB: &[u16] = &[3, 5, 8, 13, 21, 34, 55, 89];
122        ///
123        /// const TWO: &[u16] = slice_up_to_len_alt(FIBB, 2);
124        /// const FOUR: &[u16] = slice_up_to_len_alt(FIBB, 4);
125        /// const ALL: &[u16] = slice_up_to_len_alt(FIBB, usize::MAX);
126        ///
127        /// assert_eq!(TWO, &[3, 5]);
128        /// assert_eq!(FOUR, &[3, 5, 8, 13]);
129        /// assert_eq!(FIBB, ALL);
130        ///
131        /// ```
132        $item
133    };
134}
135
136slice_up_to_len_docs! {
137    #[cfg(feature = "rust_1_64")]
138    #[inline]
139    pub const fn slice_up_to_len<T>(slice: &[T], len: usize) -> &[T] {
140        if len > slice.len() {
141            return slice;
142        }
143
144        // Doing this to get a slice up to length at compile-time
145        unsafe {
146            let raw_slice = core::ptr::slice_from_raw_parts(slice.as_ptr(), len);
147            Dereference { ptr: raw_slice }.reff
148        }
149    }
150}
151
152slice_up_to_len_docs! {
153    #[cfg(not(feature = "rust_1_64"))]
154    #[inline]
155    pub fn slice_up_to_len<T>(slice: &[T], len: usize) -> &[T] {
156        if len > slice.len() {
157            return slice;
158        }
159
160        &slice[..len]
161    }
162}
163
164////////////////////////////////////////////////////////////////////////////////
165
166pub(crate) const fn min_usize(l: usize, r: usize) -> usize {
167    if l < r {
168        l
169    } else {
170        r
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177
178    #[test]
179    fn test_slice_up_to_len_alt() {
180        let mut list = [0u16; 256];
181
182        (100..).zip(list.iter_mut()).for_each(|(i, m)| *m = i);
183
184        for i in 0..list.len() {
185            assert_eq!(slice_up_to_len_alt(&list, i), &list[..i]);
186        }
187    }
188
189    #[test]
190    fn slice_in_bounds() {
191        assert_eq!(slice_up_to_len(&[3, 5], 0), []);
192        assert_eq!(slice_up_to_len(&[3, 5], 1), [3]);
193        assert_eq!(slice_up_to_len(&[3, 5], 2), [3, 5]);
194        assert_eq!(slice_up_to_len(&[3, 5], 3), [3, 5]);
195        assert_eq!(slice_up_to_len(&[3, 5], 4), [3, 5]);
196    }
197}