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}