konst_kernel/
string.rs

1#[cfg(feature = "__for_konst")]
2pub mod string_for_konst;
3
4#[cfg(feature = "__for_konst")]
5pub use self::string_for_konst::*;
6
7#[inline]
8pub const fn str_up_to(string: &str, len: usize) -> &str {
9    let bytes = string.as_bytes();
10    if __is_char_boundary_forgiving(bytes, len) {
11        // Safety: __is_char_boundary_forgiving checks that `len` falls on a char boundary.
12        unsafe { __from_u8_subslice_of_str(crate::slice::slice_up_to(bytes, len)) }
13    } else {
14        non_char_boundary_panic("index", len)
15    }
16}
17
18#[inline]
19pub const fn str_from(string: &str, start: usize) -> &str {
20    let bytes = string.as_bytes();
21    if __is_char_boundary_forgiving(bytes, start) {
22        // Safety: __is_char_boundary_forgiving checks that `start` falls on a char boundary.
23        unsafe { __from_u8_subslice_of_str(crate::slice::slice_from(bytes, start)) }
24    } else {
25        non_char_boundary_panic("start", start)
26    }
27}
28
29#[inline]
30pub const fn str_range(string: &str, start: usize, end: usize) -> &str {
31    let bytes = string.as_bytes();
32    let start_inbounds = __is_char_boundary_forgiving(bytes, start);
33    if start_inbounds && __is_char_boundary_forgiving(bytes, end) {
34        // Safety: __is_char_boundary_forgiving checks that
35        // `start` and `end` fall on a char boundaries.
36        unsafe { __from_u8_subslice_of_str(crate::slice::slice_range(bytes, start, end)) }
37    } else if start_inbounds {
38        non_char_boundary_panic("end", end)
39    } else {
40        non_char_boundary_panic("start", start)
41    }
42}
43
44#[inline]
45pub const fn is_char_boundary(string: &str, position: usize) -> bool {
46    __is_char_boundary_bytes(string.as_bytes(), position)
47}
48
49macro_rules! byte_is_char_boundary {
50    ($b:expr) => {
51        ($b as i8) >= -0x40
52    };
53}
54
55#[doc(hidden)]
56#[inline]
57pub const fn __is_char_boundary_bytes(bytes: &[u8], position: usize) -> bool {
58    position == bytes.len() || position < bytes.len() && byte_is_char_boundary!(bytes[position])
59}
60
61#[inline]
62const fn __is_char_boundary_forgiving(bytes: &[u8], position: usize) -> bool {
63    position >= bytes.len() || byte_is_char_boundary!(bytes[position])
64}
65
66#[doc(hidden)]
67pub const fn __find_next_char_boundary(bytes: &[u8], mut position: usize) -> usize {
68    loop {
69        position += 1;
70
71        if __is_char_boundary_forgiving(bytes, position) {
72            break position;
73        }
74    }
75}
76
77#[doc(hidden)]
78pub const fn __find_prev_char_boundary(bytes: &[u8], mut position: usize) -> usize {
79    position = position.saturating_sub(1);
80
81    while !__is_char_boundary_forgiving(bytes, position) {
82        position -= 1;
83    }
84
85    position
86}
87
88#[doc(hidden)]
89#[track_caller]
90pub const unsafe fn __from_u8_subslice_of_str(s: &[u8]) -> &str {
91    #[cfg(any(feature = "debug", test))]
92    if !s.is_empty() {
93        if !byte_is_char_boundary!(s[0]) {
94            panic!("string doesn't start at a byte boundary")
95        }
96
97        let cb = __find_prev_char_boundary(s, s.len() - 1);
98        if let Err(_) = core::str::from_utf8(crate::slice::slice_from(s, cb)) {
99            panic!("string doesn't end at a byte boundary")
100        }
101    }
102
103    core::str::from_utf8_unchecked(s)
104}
105
106#[cold]
107#[track_caller]
108#[doc(hidden)]
109const fn non_char_boundary_panic(extreme: &str, index: usize) -> ! {
110    use crate::utils::PanikVal;
111
112    crate::utils::basic_panic(&[
113        PanikVal::Str(extreme),
114        PanikVal::Str(" `"),
115        PanikVal::Usize(index),
116        PanikVal::Str("` is not on a char boundary"),
117    ])
118}