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