1use crate::debug_str_fmt::ForEscaping;
4
5#[cfg(feature = "rust_1_64")]
6#[cfg(test)]
7mod utils_1_64_tests;
8
9#[cfg(feature = "non_basic")]
10mod non_basic_utils;
11
12#[cfg(feature = "non_basic")]
13#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
14pub use self::non_basic_utils::*;
15
16pub const fn min_usize(l: usize, r: usize) -> usize {
18 if l < r {
19 l
20 } else {
21 r
22 }
23}
24
25pub const fn max_usize(l: usize, r: usize) -> usize {
27 if l > r {
28 l
29 } else {
30 r
31 }
32}
33
34#[derive(Copy, Clone)]
35pub(crate) struct TailShortString<const LEN: usize> {
36 start: u8,
37 buffer: [u8; LEN],
38}
39
40pub(crate) type PreFmtString = TailShortString<{ string_cap::PREFMT }>;
41
42pub(crate) mod string_cap {
43 #[cfg(feature = "non_basic")]
45 pub const TINY: usize = 16;
46
47 pub(crate) const PREFMT: usize = 21;
49
50 pub(crate) const MEDIUM: usize = 66;
52
53 pub(crate) const LARGE: usize = 130;
55}
56
57impl<const LEN: usize> TailShortString<LEN> {
58 #[inline(always)]
63 pub(crate) const unsafe fn new(start: u8, buffer: [u8; LEN]) -> Self {
64 Self { start, buffer }
65 }
66
67 pub(crate) const fn len(&self) -> usize {
68 LEN - self.start as usize
69 }
70
71 pub(crate) const fn ranged(&self) -> RangedBytes<&[u8]> {
72 RangedBytes {
73 start: self.start as usize,
74 end: LEN,
75 bytes: &self.buffer,
76 }
77 }
78}
79
80#[repr(packed)]
83#[derive(Copy)]
84pub(crate) struct Packed<T>(pub(crate) T);
85
86impl<T: Copy> Clone for Packed<T> {
87 fn clone(&self) -> Self {
88 *self
89 }
90}
91
92#[derive(Copy, Clone)]
95pub(crate) struct RangedBytes<B> {
96 pub(crate) start: usize,
97 pub(crate) end: usize,
98 pub(crate) bytes: B,
99}
100
101impl<B> RangedBytes<B> {
102 pub(crate) const fn len(&self) -> usize {
103 self.end - self.start
104 }
105}
106impl RangedBytes<&'static [u8]> {
107 pub const EMPTY: Self = RangedBytes {
108 start: 0,
109 end: 0,
110 bytes: &[],
111 };
112}
113
114#[derive(Copy, Clone)]
117pub(crate) enum Sign {
118 Positive,
119 Negative = 1,
120}
121
122#[derive(Copy, Clone)]
123pub(crate) enum WasTruncated {
124 Yes(usize),
125 No,
126}
127
128impl WasTruncated {
129 pub(crate) const fn get_length(self, len: usize) -> usize {
130 match self {
131 WasTruncated::Yes(x) => x,
132 WasTruncated::No => len,
133 }
134 }
135}
136
137const fn is_char_boundary(b: u8) -> bool {
138 (b as i8) >= -0x40
139}
140
141pub(crate) const fn truncated_str_len(
144 ranged: RangedBytes<&[u8]>,
145 truncate_to: usize,
146) -> WasTruncated {
147 if ranged.len() <= truncate_to {
148 WasTruncated::No
149 } else {
150 let mut i = ranged.start + truncate_to;
151 while i != ranged.start {
152 if is_char_boundary(ranged.bytes[i]) {
154 break;
155 }
156 i -= 1;
157 }
158
159 WasTruncated::Yes(i - ranged.start)
160 }
161}
162
163pub(crate) const fn truncated_debug_str_len(
164 ranged: RangedBytes<&[u8]>,
165 truncate_to: usize,
166) -> WasTruncated {
167 let blen = ranged.end;
168
169 if blen * 4 + 2 <= truncate_to {
172 WasTruncated::No
173 } else if truncate_to == 0 {
174 WasTruncated::Yes(0)
175 } else {
176 let mut i = ranged.start;
177 let mut fmtlen = 1;
179 loop {
180 let next_i = next_char_boundary(ranged, min_usize(i + 1, ranged.end));
181
182 let mut j = i;
183 while j < next_i {
184 fmtlen += ForEscaping::byte_len(ranged.bytes[j]);
185 j += 1;
186 }
187
188 if fmtlen > truncate_to {
189 break;
190 } else if next_i == ranged.end {
191 i = next_i;
192 break;
193 } else {
194 i = next_i;
195 }
196 }
197
198 if i == blen && fmtlen < truncate_to {
199 WasTruncated::No
200 } else {
201 WasTruncated::Yes(i - ranged.start)
202 }
203 }
204}
205
206const fn next_char_boundary(ranged: RangedBytes<&[u8]>, mut i: usize) -> usize {
207 while i < ranged.end && !is_char_boundary(ranged.bytes[i]) {
208 i += 1;
209 }
210 i
211}
212
213#[cfg_attr(feature = "test", derive(Debug, PartialEq))]
214pub(crate) struct StartAndBytes<const LEN: usize> {
215 pub start: u8,
216 pub bytes: [u8; LEN],
217}
218
219#[track_caller]
220pub(crate) const fn tail_byte_array<const TO: usize>(
221 len: usize,
222 input: &[u8],
223) -> StartAndBytes<TO> {
224 assert!(len <= TO);
225
226 let mut bytes = [0u8; TO];
227
228 let start = TO - len;
229 let mut i = start;
230 let mut j = 0;
231 while j < len {
232 bytes[i] = input[j];
233 i += 1;
234 j += 1;
235 }
236
237 assert!(start < 256);
238 StartAndBytes {
239 start: start as u8,
240 bytes,
241 }
242}
243
244pub const fn bytes_up_to(buffer: &[u8], upto: usize) -> &[u8] {
266 if upto > buffer.len() {
267 return buffer;
268 }
269
270 #[cfg(not(feature = "rust_1_64"))]
271 {
272 let mut to_truncate = buffer.len() - upto;
273 let mut out: &[u8] = buffer;
274
275 while to_truncate != 0 {
276 if let [rem @ .., _] = out {
277 out = rem;
278 }
279 to_truncate -= 1;
280 }
281
282 if out.len() != upto {
283 panic!("BUG!")
284 }
285
286 out
287 }
288
289 #[cfg(feature = "rust_1_64")]
290 {
291 unsafe { core::slice::from_raw_parts(buffer.as_ptr(), upto) }
294 }
295}