1use crate::debug_str_fmt::ForEscaping;
4
5#[cfg(feature = "rust_1_64")]
6#[cfg(test)]
7mod utils_1_64_tests;
8
9#[cfg(test)]
10mod usize_parsing_tests;
11
12#[cfg(feature = "non_basic")]
13mod non_basic_utils;
14
15#[cfg(feature = "non_basic")]
16#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
17pub use self::non_basic_utils::*;
18
19pub const fn min_usize(l: usize, r: usize) -> usize {
21 if l < r {
22 l
23 } else {
24 r
25 }
26}
27
28pub const fn max_usize(l: usize, r: usize) -> usize {
30 if l > r {
31 l
32 } else {
33 r
34 }
35}
36
37#[derive(Copy, Clone)]
38pub(crate) struct TailShortString<const LEN: usize> {
39 start: u8,
40 buffer: [u8; LEN],
41}
42
43pub(crate) type PreFmtString = TailShortString<{ string_cap::PREFMT }>;
44
45pub(crate) mod string_cap {
46 #[cfg(feature = "non_basic")]
48 pub const TINY: usize = 16;
49
50 pub(crate) const PREFMT: usize = 21;
52
53 pub(crate) const MEDIUM: usize = 66;
55
56 pub(crate) const LARGE: usize = 130;
58}
59
60impl<const LEN: usize> TailShortString<LEN> {
61 #[inline(always)]
66 pub(crate) const unsafe fn new(start: u8, buffer: [u8; LEN]) -> Self {
67 Self { start, buffer }
68 }
69
70 pub(crate) const fn len(&self) -> usize {
71 LEN - self.start as usize
72 }
73
74 pub(crate) const fn ranged(&self) -> RangedBytes<&[u8]> {
75 RangedBytes {
76 start: self.start as usize,
77 end: LEN,
78 bytes: &self.buffer,
79 }
80 }
81}
82
83#[repr(packed)]
86#[derive(Copy)]
87pub(crate) struct Packed<T>(pub(crate) T);
88
89impl<T: Copy> Clone for Packed<T> {
90 fn clone(&self) -> Self {
91 *self
92 }
93}
94
95#[derive(Copy, Clone)]
98pub(crate) struct RangedBytes<B> {
99 pub(crate) start: usize,
100 pub(crate) end: usize,
101 pub(crate) bytes: B,
102}
103
104impl<B> RangedBytes<B> {
105 pub(crate) const fn len(&self) -> usize {
106 self.end - self.start
107 }
108}
109impl RangedBytes<&'static [u8]> {
110 pub const EMPTY: Self = RangedBytes {
111 start: 0,
112 end: 0,
113 bytes: &[],
114 };
115}
116
117#[derive(Copy, Clone)]
120pub(crate) enum Sign {
121 Positive,
122 Negative = 1,
123}
124
125#[derive(Copy, Clone)]
126pub(crate) enum WasTruncated {
127 Yes(usize),
128 No,
129}
130
131impl WasTruncated {
132 pub(crate) const fn get_length(self, len: usize) -> usize {
133 match self {
134 WasTruncated::Yes(x) => x,
135 WasTruncated::No => len,
136 }
137 }
138}
139
140const fn is_char_boundary(b: u8) -> bool {
141 (b as i8) >= -0x40
142}
143
144pub(crate) const fn truncated_str_len(
147 ranged: RangedBytes<&[u8]>,
148 truncate_to: usize,
149) -> WasTruncated {
150 if ranged.len() <= truncate_to {
151 WasTruncated::No
152 } else {
153 let mut i = ranged.start + truncate_to;
154 while i != ranged.start {
155 if is_char_boundary(ranged.bytes[i]) {
157 break;
158 }
159 i -= 1;
160 }
161
162 WasTruncated::Yes(i - ranged.start)
163 }
164}
165
166pub(crate) const fn truncated_debug_str_len(
167 ranged: RangedBytes<&[u8]>,
168 truncate_to: usize,
169) -> WasTruncated {
170 let blen = ranged.end;
171
172 if blen * 4 + 2 <= truncate_to {
175 WasTruncated::No
176 } else if truncate_to == 0 {
177 WasTruncated::Yes(0)
178 } else {
179 let mut i = ranged.start;
180 let mut fmtlen = 1;
182 loop {
183 let next_i = next_char_boundary(ranged, min_usize(i + 1, ranged.end));
184
185 let mut j = i;
186 while j < next_i {
187 fmtlen += ForEscaping::byte_len(ranged.bytes[j]);
188 j += 1;
189 }
190
191 if fmtlen > truncate_to {
192 break;
193 } else if next_i == ranged.end {
194 i = next_i;
195 break;
196 } else {
197 i = next_i;
198 }
199 }
200
201 if i == blen && fmtlen < truncate_to {
202 WasTruncated::No
203 } else {
204 WasTruncated::Yes(i - ranged.start)
205 }
206 }
207}
208
209const fn next_char_boundary(ranged: RangedBytes<&[u8]>, mut i: usize) -> usize {
210 while i < ranged.end && !is_char_boundary(ranged.bytes[i]) {
211 i += 1;
212 }
213 i
214}
215
216#[cfg_attr(feature = "test", derive(Debug, PartialEq))]
217pub(crate) struct StartAndBytes<const LEN: usize> {
218 pub start: u8,
219 pub bytes: [u8; LEN],
220}
221
222#[track_caller]
223pub(crate) const fn tail_byte_array<const TO: usize>(
224 len: usize,
225 input: &[u8],
226) -> StartAndBytes<TO> {
227 assert!(len <= TO);
228
229 let mut bytes = [0u8; TO];
230
231 let start = TO - len;
232 let mut i = start;
233 let mut j = 0;
234 while j < len {
235 bytes[i] = input[j];
236 i += 1;
237 j += 1;
238 }
239
240 assert!(start < 256);
241 StartAndBytes {
242 start: start as u8,
243 bytes,
244 }
245}
246
247pub(crate) const fn parse_usize(input: &str) -> Option<usize> {
250 #[cfg(not(feature = "rust_1_82"))]
251 {
252 use crate::macros::unwrapping::try_opt;
253
254 if input.is_empty() {
255 return None;
256 }
257
258 let mut input = input.as_bytes();
259
260 let mut ret = 0usize;
261
262 while let [b, ref rem @ ..] = *input {
263 ret = try_opt!(ret.checked_mul(10));
264
265 ret = match b {
266 b'0'..=b'9' => try_opt!(ret.checked_add((b - b'0') as usize)),
267 _ => return None,
268 };
269
270 input = rem;
271 }
272
273 Some(ret)
274 }
275
276 #[cfg(feature = "rust_1_82")]
277 {
278 match usize::from_str_radix(input, 10) {
279 Ok(x) => Some(x),
280 Err(_) => None,
281 }
282 }
283}
284
285pub const fn bytes_up_to(buffer: &[u8], upto: usize) -> &[u8] {
307 if upto > buffer.len() {
308 return buffer;
309 }
310
311 #[cfg(not(feature = "rust_1_64"))]
312 {
313 let mut to_truncate = buffer.len() - upto;
314 let mut out: &[u8] = buffer;
315
316 while to_truncate != 0 {
317 if let [rem @ .., _] = out {
318 out = rem;
319 }
320 to_truncate -= 1;
321 }
322
323 if out.len() != upto {
324 panic!("BUG!")
325 }
326
327 out
328 }
329
330 #[cfg(feature = "rust_1_64")]
331 {
332 unsafe { core::slice::from_raw_parts(buffer.as_ptr(), upto) }
335 }
336}