fixed/
bytes.rs

1// Copyright © 2018–2025 Trevor Spiteri
2
3// This library is free software: you can redistribute it and/or
4// modify it under the terms of either
5//
6//   * the Apache License, Version 2.0 or
7//   * the MIT License
8//
9// at your option.
10//
11// You should have recieved copies of the Apache License and the MIT
12// License along with the library. If not, see
13// <https://www.apache.org/licenses/LICENSE-2.0> and
14// <https://opensource.org/licenses/MIT>.
15
16// Kept trimmed: no underscores at beginning or end of slice
17#[derive(Clone, Copy, Debug)]
18pub struct DigitsUnds<'a> {
19    bytes: &'a [u8],
20    digits: usize,
21}
22
23impl<'a> DigitsUnds<'a> {
24    pub const EMPTY: DigitsUnds<'a> = DigitsUnds::new(&[]);
25
26    pub const fn new(bytes: &'a [u8]) -> DigitsUnds<'a> {
27        let mut digits = 0;
28        let mut leading_unds = 0;
29        let mut trailing_unds = 0;
30        let mut rem_bytes = bytes;
31        while let Some((&byte, rem)) = rem_bytes.split_first() {
32            rem_bytes = rem;
33
34            if byte == b'_' {
35                trailing_unds += 1;
36            } else {
37                if digits == 0 {
38                    leading_unds = trailing_unds;
39                }
40                digits += 1;
41                trailing_unds = 0;
42            }
43        }
44        let without_trailing_unds = bytes.split_at(bytes.len() - trailing_unds).0;
45        let without_leading_unds = without_trailing_unds.split_at(leading_unds).1;
46        DigitsUnds {
47            bytes: without_leading_unds,
48            digits,
49        }
50    }
51
52    #[inline]
53    pub const fn len(self) -> usize {
54        self.digits
55    }
56
57    #[inline]
58    pub const fn is_empty(self) -> bool {
59        self.digits == 0
60    }
61
62    pub const fn split_at(self, mid: usize) -> (DigitsUnds<'a>, DigitsUnds<'a>) {
63        let mut remaining_digits = mid;
64        let mut unds = 0;
65        let mut rem_bytes = self.bytes;
66        while let Some((&byte, rem)) = rem_bytes.split_first() {
67            rem_bytes = rem;
68
69            if byte == b'_' {
70                unds += 1;
71            } else {
72                remaining_digits -= 1;
73                if remaining_digits == 0 {
74                    break;
75                }
76            }
77        }
78        if remaining_digits > 0 {
79            panic!("index out of bounds");
80        }
81        let first = DigitsUnds {
82            bytes: self.bytes.split_at(mid + unds).0,
83            digits: mid,
84        };
85
86        // skip over underscores between first part and last part
87        while let Some((&b'_', rem)) = rem_bytes.split_first() {
88            rem_bytes = rem;
89        }
90        (
91            first,
92            DigitsUnds {
93                bytes: rem_bytes,
94                digits: self.digits - mid,
95            },
96        )
97    }
98
99    #[inline]
100    pub const fn split_first(self) -> Option<(u8, DigitsUnds<'a>)> {
101        let Some((&first, mut rem_bytes)) = self.bytes.split_first() else {
102            return None;
103        };
104
105        // first byte is never underscore
106        debug_assert!(first != b'_');
107
108        // skip over underscores between first digit and last part
109        while let Some((&b'_', rem)) = rem_bytes.split_first() {
110            rem_bytes = rem;
111        }
112        Some((
113            first,
114            DigitsUnds {
115                bytes: rem_bytes,
116                digits: self.digits - 1,
117            },
118        ))
119    }
120
121    #[inline]
122    const fn split_last(self) -> Option<(u8, DigitsUnds<'a>)> {
123        let Some((&last, mut rem_bytes)) = self.bytes.split_last() else {
124            return None;
125        };
126
127        // last byte is never underscore
128        debug_assert!(last != b'_');
129
130        // skip over underscores between first part and last digit
131        while let Some((&b'_', rem)) = rem_bytes.split_last() {
132            rem_bytes = rem;
133        }
134        Some((
135            last,
136            DigitsUnds {
137                bytes: rem_bytes,
138                digits: self.digits - 1,
139            },
140        ))
141    }
142
143    const fn split_leading_zeros(self) -> (usize, DigitsUnds<'a>) {
144        let mut zeros = 0;
145        let mut rem = self;
146        while let Some((b'0', rest)) = rem.split_first() {
147            zeros += 1;
148            rem = rest;
149        }
150        (zeros, rem)
151    }
152
153    const fn split_trailing_zeros(self) -> (usize, DigitsUnds<'a>) {
154        let mut zeros = 0;
155        let mut rem = self;
156        while let Some((b'0', rest)) = rem.split_last() {
157            zeros += 1;
158            rem = rest;
159        }
160        (zeros, rem)
161    }
162}
163
164#[derive(Clone, Copy, Debug)]
165pub struct DigitsExp<'a> {
166    leading_zeros: usize,
167    part1: DigitsUnds<'a>,
168    part2: DigitsUnds<'a>,
169    trailing_zeros: usize,
170}
171
172impl<'a> DigitsExp<'a> {
173    const EMPTY: DigitsExp<'a> = DigitsExp {
174        leading_zeros: 0,
175        part1: DigitsUnds::EMPTY,
176        part2: DigitsUnds::EMPTY,
177        trailing_zeros: 0,
178    };
179
180    const fn new1(digits: DigitsUnds<'a>) -> DigitsExp<'a> {
181        let (leading_zeros, rest) = digits.split_leading_zeros();
182        let (trailing_zeros, rest) = rest.split_trailing_zeros();
183        DigitsExp {
184            leading_zeros,
185            part1: rest,
186            part2: DigitsUnds::EMPTY,
187            trailing_zeros,
188        }
189    }
190
191    const fn new2(digits1: DigitsUnds<'a>, digits2: DigitsUnds<'a>) -> DigitsExp<'a> {
192        let (mut leading_zeros, mut digits1) = digits1.split_leading_zeros();
193        let digits2 = if digits1.is_empty() {
194            let (more_leading_zeros, new_digits1) = digits2.split_leading_zeros();
195            leading_zeros += more_leading_zeros;
196            digits1 = new_digits1;
197            DigitsUnds::EMPTY
198        } else {
199            digits2
200        };
201        let (mut trailing_zeros, digits2) = digits2.split_trailing_zeros();
202        if digits2.is_empty() {
203            let (more_trailing_zeros, new_digits1) = digits1.split_trailing_zeros();
204            trailing_zeros += more_trailing_zeros;
205            digits1 = new_digits1;
206        }
207        DigitsExp {
208            leading_zeros,
209            part1: digits1,
210            part2: digits2,
211            trailing_zeros,
212        }
213    }
214
215    // exp.unsigned_abs() must fit in usize, and results must have lengths that fit in usize
216    pub const fn new_int_frac(
217        int: DigitsUnds<'a>,
218        frac: DigitsUnds<'a>,
219        exp: i32,
220    ) -> Option<(DigitsExp<'a>, DigitsExp<'a>)> {
221        if int.len() > usize::MAX - frac.len() {
222            return None;
223        }
224        let abs_exp = exp.unsigned_abs() as usize;
225        if abs_exp as u32 != exp.unsigned_abs() {
226            return None;
227        }
228
229        let (mut int, mut frac) = if exp == 0 {
230            (DigitsExp::new1(int), DigitsExp::new1(frac))
231        } else if exp < 0 {
232            if let Some(extra_zeros) = abs_exp.checked_sub(int.len()) {
233                let mut frac = DigitsExp::new2(int, frac);
234                frac.trailing_zeros = 0;
235                if extra_zeros > usize::MAX - frac.len() {
236                    return None;
237                }
238                frac.leading_zeros += extra_zeros;
239                (DigitsExp::EMPTY, frac)
240            } else {
241                let int = int.split_at(int.len() - abs_exp);
242                (DigitsExp::new1(int.0), DigitsExp::new2(int.1, frac))
243            }
244        } else {
245            // exp > 0
246            if let Some(extra_zeros) = abs_exp.checked_sub(frac.len()) {
247                let mut int = DigitsExp::new2(int, frac);
248                int.leading_zeros = 0;
249                if extra_zeros > usize::MAX - int.len() {
250                    return None;
251                }
252                int.trailing_zeros += extra_zeros;
253                (int, DigitsExp::EMPTY)
254            } else {
255                let frac = frac.split_at(abs_exp);
256                (DigitsExp::new2(int, frac.0), DigitsExp::new1(frac.1))
257            }
258        };
259        int.leading_zeros = 0;
260        if int.part1.is_empty() && int.part2.is_empty() {
261            int.trailing_zeros = 0;
262        }
263        frac.trailing_zeros = 0;
264        if frac.part2.is_empty() && frac.part1.is_empty() {
265            frac.leading_zeros = 0;
266        }
267        Some((int, frac))
268    }
269
270    #[inline]
271    pub const fn len(self) -> usize {
272        self.leading_zeros + self.part1.len() + self.part2.len() + self.trailing_zeros
273    }
274
275    #[inline]
276    pub const fn is_empty(self) -> bool {
277        self.len() == 0
278    }
279
280    pub const fn split_at(self, mut mid: usize) -> (DigitsExp<'a>, DigitsExp<'a>) {
281        let mut first = DigitsExp::EMPTY;
282        let mut last = self;
283        if mid == 0 {
284            return (first, last);
285        }
286
287        if mid < self.leading_zeros {
288            (first.leading_zeros, last.leading_zeros) = (mid, self.leading_zeros - mid);
289            return (first, last);
290        }
291
292        (first.leading_zeros, last.leading_zeros) = (self.leading_zeros, 0);
293        mid -= self.leading_zeros;
294        if mid == 0 {
295            return (first, last);
296        }
297
298        if mid < self.part1.len() {
299            (first.part1, last.part1) = self.part1.split_at(mid);
300            return (first, last);
301        }
302
303        first.part1 = self.part1;
304        last.part1 = self.part2;
305        last.part2 = DigitsUnds::EMPTY;
306        mid -= self.part1.len();
307        if mid == 0 {
308            return (first, last);
309        }
310
311        if mid < self.part2.len() {
312            (first.part2, last.part1) = self.part2.split_at(mid);
313            return (first, last);
314        }
315
316        first.part2 = self.part2;
317        last.leading_zeros = self.trailing_zeros;
318        last.part1 = DigitsUnds::EMPTY;
319        last.trailing_zeros = 0;
320        mid -= self.part2.len();
321        if mid == 0 {
322            return (first, last);
323        }
324
325        if mid < self.trailing_zeros {
326            (first.trailing_zeros, last.leading_zeros) = (mid, self.trailing_zeros - mid);
327            return (first, last);
328        }
329
330        (first.trailing_zeros, last.leading_zeros) = (self.trailing_zeros, 0);
331        mid -= self.trailing_zeros;
332        if mid == 0 {
333            return (first, last);
334        }
335
336        panic!("index out of bounds");
337    }
338
339    // no automatic renormalization done after split_first
340    #[inline]
341    pub const fn split_first(self) -> Option<(u8, DigitsExp<'a>)> {
342        if self.leading_zeros > 0 {
343            return Some((
344                b'0',
345                DigitsExp {
346                    leading_zeros: self.leading_zeros - 1,
347                    ..self
348                },
349            ));
350        }
351        if let Some((first, rest)) = self.part1.split_first() {
352            return Some((
353                first,
354                DigitsExp {
355                    part1: rest,
356                    ..self
357                },
358            ));
359        }
360        if let Some((first, rest)) = self.part2.split_first() {
361            return Some((
362                first,
363                DigitsExp {
364                    part2: rest,
365                    ..self
366                },
367            ));
368        }
369        if self.trailing_zeros > 0 {
370            return Some((
371                b'0',
372                DigitsExp {
373                    trailing_zeros: self.trailing_zeros - 1,
374                    ..self
375                },
376            ));
377        }
378        None
379    }
380}