1 | pub struct StrIndexArgsConv<T> { |
2 | pub str: &'static str, |
3 | pub arg: T, |
4 | } |
5 | |
6 | #[allow (non_snake_case)] |
7 | pub const fn StrIndexArgsConv<T>(str: &'static str, arg: T) -> StrIndexArgsConv<T> { |
8 | StrIndexArgsConv { str, arg } |
9 | } |
10 | |
11 | pub struct StrIndexArgs { |
12 | pub str: &'static str, |
13 | pub index_validity: IndexValidity, |
14 | pub used_rstart: usize, |
15 | pub used_rlen: usize, |
16 | pub used_rend: usize, |
17 | } |
18 | |
19 | #[derive (Copy, Clone)] |
20 | #[cfg_attr (test, derive(Debug, PartialEq))] |
21 | pub enum IndexValidity { |
22 | Valid, |
23 | StartOob(usize), |
24 | StartInsideChar(usize), |
25 | EndOob(usize), |
26 | EndInsideChar(usize), |
27 | } |
28 | |
29 | impl IndexValidity { |
30 | pub const fn is_valid(self) -> bool { |
31 | matches!(self, Self::Valid) |
32 | } |
33 | |
34 | pub const fn assert_valid(self) { |
35 | match self { |
36 | Self::Valid => (), |
37 | Self::StartOob(index: usize) => [/*start index is out of bounds*/][index], |
38 | Self::StartInsideChar(index: usize) => [/*start index is not on a char boundary*/][index], |
39 | Self::EndOob(index: usize) => [/*end index is out of bounds*/][index], |
40 | Self::EndInsideChar(index: usize) => [/*end index is not on a char boundary*/][index], |
41 | } |
42 | } |
43 | } |
44 | |
45 | macro_rules! pass_range_types { |
46 | ($macro:ident) => { |
47 | const _: () = { |
48 | use core::ops; |
49 | |
50 | #[allow(unused_imports)] |
51 | use crate::__hidden_utils::{is_char_boundary_no_len_check, max_usize, saturating_add}; |
52 | |
53 | $macro! { |
54 | fn(self, usize) { |
55 | let mut end = saturating_add(self.arg, 1); |
56 | let bytes = self.str.as_bytes(); |
57 | |
58 | if end < self.str.len() { |
59 | while !is_char_boundary_no_len_check(bytes, end) { |
60 | end = saturating_add(end, 1); |
61 | } |
62 | } |
63 | |
64 | self.arg .. end |
65 | } |
66 | |
67 | fn(self, ops::Range<usize>) { |
68 | let ops::Range{start, end} = self.arg; |
69 | start .. max_usize(start, end) |
70 | } |
71 | |
72 | fn(self, ops::RangeTo<usize>) { |
73 | 0..self.arg.end |
74 | } |
75 | |
76 | fn(self, ops::RangeFrom<usize>) { |
77 | self.arg.start..self.str.len() |
78 | } |
79 | |
80 | fn(self, ops::RangeInclusive<usize>) { |
81 | let start = *self.arg.start(); |
82 | start .. max_usize(saturating_add(*self.arg.end(), 1), start) |
83 | } |
84 | |
85 | fn(self, ops::RangeToInclusive<usize>) { |
86 | 0 .. saturating_add(self.arg.end, 1) |
87 | } |
88 | |
89 | fn(self, ops::RangeFull) { |
90 | 0 .. self.str.len() |
91 | } |
92 | } |
93 | }; |
94 | }; |
95 | } |
96 | pub(super) use pass_range_types; |
97 | |
98 | macro_rules! define_conversions { |
99 | ( |
100 | $( fn($self:ident, $ty:ty) $block:block )* |
101 | ) => { |
102 | |
103 | $( |
104 | impl StrIndexArgsConv<$ty> { |
105 | pub const fn conv($self) -> StrIndexArgs { |
106 | use crate::__hidden_utils::is_char_boundary_no_len_check; |
107 | |
108 | let range = $block; |
109 | |
110 | let str_len = $self.str.len(); |
111 | |
112 | let mut used_rstart = 0; |
113 | let mut used_rend = str_len; |
114 | |
115 | let mut index_validity = IndexValidity::Valid; |
116 | let bytes = $self.str.as_bytes(); |
117 | |
118 | if range.end > str_len { |
119 | index_validity = IndexValidity::EndOob(range.end); |
120 | } else if is_char_boundary_no_len_check(bytes, range.end) { |
121 | used_rend = range.end; |
122 | } else { |
123 | index_validity = IndexValidity::EndInsideChar(range.end); |
124 | }; |
125 | |
126 | if range.start > str_len { |
127 | index_validity = IndexValidity::StartOob(range.start); |
128 | } else if is_char_boundary_no_len_check(bytes, range.start) { |
129 | used_rstart = range.start; |
130 | } else { |
131 | index_validity = IndexValidity::StartInsideChar(range.start); |
132 | }; |
133 | |
134 | StrIndexArgs { |
135 | str: $self.str, |
136 | index_validity, |
137 | used_rstart, |
138 | used_rend, |
139 | used_rlen: used_rend - used_rstart, |
140 | } |
141 | } |
142 | } |
143 | )* |
144 | }; |
145 | } |
146 | |
147 | pass_range_types! {define_conversions} |
148 | |
149 | #[cfg (test)] |
150 | mod tests { |
151 | use super::*; |
152 | |
153 | #[test ] |
154 | fn index_validity_test() { |
155 | macro_rules! miv { |
156 | ($str:expr, $range:expr) => { |
157 | StrIndexArgsConv($str, $range).conv().index_validity |
158 | }; |
159 | } |
160 | |
161 | assert_eq!(miv!("効率的" , 3), IndexValidity::Valid); |
162 | assert_eq!(miv!("効率的" , 6), IndexValidity::Valid); |
163 | assert_eq!(miv!("効率的" , 3..6), IndexValidity::Valid); |
164 | |
165 | assert_eq!(miv!("効率的" , 4..6), IndexValidity::StartInsideChar(4)); |
166 | assert_eq!(miv!("効率的" , 3..5), IndexValidity::EndInsideChar(5)); |
167 | assert_eq!(miv!("効率的" , 7..9), IndexValidity::StartInsideChar(7)); |
168 | |
169 | assert_eq!(miv!("効率的" , 100..9), IndexValidity::StartOob(100)); |
170 | assert_eq!(miv!("効率的" , 3..10), IndexValidity::EndOob(10)); |
171 | assert_eq!(miv!("効率的" , 9), IndexValidity::EndOob(10)); |
172 | assert_eq!(miv!("効率的" , 10), IndexValidity::StartOob(10)); |
173 | assert_eq!(miv!("効率的" , 100..900), IndexValidity::StartOob(100)); |
174 | } |
175 | } |
176 | |