const_format/__str_methods/
str_split.rs

1use super::{Pattern, PatternCtor, PatternNorm};
2
3use konst::slice::{bytes_find, bytes_find_skip};
4
5pub struct SplitInputConv<T>(pub &'static str, pub T);
6
7macro_rules! ctor {
8    ($ty:ty) => {
9        impl SplitInputConv<$ty> {
10            pub const fn conv(self) -> SplitInput {
11                SplitInput {
12                    str: self.0,
13                    pattern: PatternCtor(self.1).conv(),
14                    length: usize::MAX,
15                }
16                .compute_length()
17            }
18        }
19    };
20}
21
22ctor! {u8}
23ctor! {&'static str}
24ctor! {char}
25
26#[derive(Copy, Clone)]
27pub struct SplitInput {
28    str: &'static str,
29    pattern: Pattern,
30    length: usize,
31}
32
33impl SplitInput {
34    const fn compute_length(mut self) -> Self {
35        self.length = count_splits(self);
36        self
37    }
38
39    pub const fn split_it<const LEN: usize>(self) -> [&'static str; LEN] {
40        split_it(self)
41    }
42
43    pub const fn length(&self) -> usize {
44        self.length
45    }
46}
47
48pub const fn count_splits(SplitInput { str, pattern, .. }: SplitInput) -> usize {
49    let mut count = 1;
50
51    match pattern.normalize() {
52        PatternNorm::AsciiByte(ascii_c) => {
53            let mut bytes = str.as_bytes();
54            let ascii_c = ascii_c.get();
55
56            while let [byte, rem @ ..] = bytes {
57                bytes = rem;
58
59                if *byte == ascii_c {
60                    count += 1;
61                }
62            }
63        }
64        PatternNorm::Str(str_pat) => {
65            if str_pat.is_empty() {
66                let mut char_i = 0;
67                count += 1;
68                while let Some(next) = find_next_char_boundary(str, char_i) {
69                    char_i = next;
70                    count += 1;
71                }
72            } else {
73                let mut str = str.as_bytes();
74                while let Some(next) = bytes_find_skip(str, str_pat) {
75                    str = next;
76                    count += 1;
77                }
78            }
79        }
80    }
81
82    count
83}
84
85const fn find_u8(mut slice: &[u8], byte: u8) -> Option<usize> {
86    let mut i = 0;
87
88    while let [b, ref rem @ ..] = *slice {
89        if byte == b {
90            return Some(i);
91        }
92        slice = rem;
93        i += 1;
94    }
95    None
96}
97
98const fn find_next_char_boundary(str: &str, mut index: usize) -> Option<usize> {
99    if index == str.len() {
100        None
101    } else {
102        loop {
103            index += 1;
104            if index == str.len() || (str.as_bytes()[index] as i8) >= -0x40 {
105                break Some(index);
106            }
107        }
108    }
109}
110
111pub const fn split_it<const LEN: usize>(args: SplitInput) -> [&'static str; LEN] {
112    let SplitInput {
113        mut str,
114        pattern,
115        length: _,
116    } = args;
117
118    let mut out = [""; LEN];
119    let mut out_i = 0;
120
121    macro_rules! write_out {
122        ($string:expr) => {
123            out[out_i] = $string;
124            out_i += 1;
125        };
126    }
127
128    match pattern.normalize() {
129        PatternNorm::AsciiByte(ascii_c) => {
130            let ascii_c = ascii_c.get();
131
132            while let Some(found_at) = find_u8(str.as_bytes(), ascii_c) {
133                write_out! {konst::string::str_up_to(str, found_at)}
134                str = konst::string::str_from(str, found_at + 1);
135            }
136        }
137        PatternNorm::Str(str_pat) => {
138            if str_pat.is_empty() {
139                out_i += 1;
140                while let Some(next) = find_next_char_boundary(str, 0) {
141                    write_out! {konst::string::str_up_to(str, next)}
142                    str = konst::string::str_from(str, next);
143                }
144            } else {
145                while let Some(found_at) = bytes_find(str.as_bytes(), str_pat, 0) {
146                    write_out! {konst::string::str_up_to(str, found_at)}
147                    str = konst::string::str_from(str, found_at + str_pat.len());
148                }
149            }
150        }
151    }
152
153    write_out! {str}
154
155    assert!(out_i == LEN);
156    out
157}