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 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 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 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}