1use alloc::string::String;
2use core::cmp::Ordering;
3use core::hash::{Hash, Hasher};
4
5use self::map::lookup;
6mod map;
7
8#[derive(Clone, Copy, Debug, Default)]
9pub struct Unicode<S>(pub S);
10
11impl<S: AsRef<str>> Unicode<S> {
12 fn to_folded_chars(&self) -> impl Iterator<Item = char> + '_ {
13 self.0.as_ref().chars().flat_map(lookup)
14 }
15
16 pub fn to_folded_case(&self) -> String {
17 self.to_folded_chars().collect()
18 }
19}
20
21impl<S1: AsRef<str>, S2: AsRef<str>> PartialEq<Unicode<S2>> for Unicode<S1> {
22 #[inline]
23 fn eq(&self, other: &Unicode<S2>) -> bool {
24 self.to_folded_chars().eq(other.to_folded_chars())
25 }
26}
27
28impl<S: AsRef<str>> Eq for Unicode<S> {}
29
30impl<T: AsRef<str>> PartialOrd for Unicode<T> {
31 #[inline]
32 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
33 Some(self.cmp(other))
34 }
35}
36
37impl<T: AsRef<str>> Ord for Unicode<T> {
38 #[inline]
39 fn cmp(&self, other: &Self) -> Ordering {
40 self.to_folded_chars().cmp(other.to_folded_chars())
41 }
42}
43
44impl<S: AsRef<str>> Hash for Unicode<S> {
45 #[inline]
46 fn hash<H: Hasher>(&self, hasher: &mut H) {
47 let mut buf = [0; 4];
48 for c in self.to_folded_chars() {
49 let len = c.encode_utf8(&mut buf).len();
50 for &b in &buf[..len] {
54 hasher.write_u8(b);
55 }
56 }
57 hasher.write_u8(0xFF);
59 }
60}
61
62mod fold {
65 #[derive(Clone, Copy)]
66 pub enum Fold {
67 Zero,
68 One(char),
69 Two(char, char),
70 Three(char, char, char),
71 }
72
73 impl Iterator for Fold {
74 type Item = char;
75 #[inline]
76 fn next(&mut self) -> Option<char> {
77 match *self {
78 Fold::Zero => None,
79 Fold::One(one) => {
80 *self = Fold::Zero;
81 Some(one)
82 }
83 Fold::Two(one, two) => {
84 *self = Fold::One(two);
85 Some(one)
86 }
87 Fold::Three(one, two, three) => {
88 *self = Fold::Two(two, three);
89 Some(one)
90 }
91 }
92 }
93
94 #[inline]
95 fn size_hint(&self) -> (usize, Option<usize>) {
96 match *self {
97 Fold::Zero => (0, Some(0)),
98 Fold::One(..) => (1, Some(1)),
99 Fold::Two(..) => (2, Some(2)),
100 Fold::Three(..) => (3, Some(3)),
101 }
102 }
103 }
104 impl From<(char,)> for Fold {
105 #[inline]
106 fn from((one,): (char,)) -> Fold {
107 Fold::One(one)
108 }
109 }
110
111 impl From<(char, char)> for Fold {
112 #[inline]
113 fn from((one, two): (char, char)) -> Fold {
114 Fold::Two(one, two)
115 }
116 }
117
118 impl From<(char, char, char)> for Fold {
119 #[inline]
120 fn from((one, two, three): (char, char, char)) -> Fold {
121 Fold::Three(one, two, three)
122 }
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::Unicode;
129
130 macro_rules! eq {
131 ($left:expr, $right:expr) => {{
132 assert_eq!(Unicode($left), Unicode($right));
133 }};
134 }
135
136 #[test]
137 fn test_ascii_folding() {
138 eq!("foo bar", "FoO BAR");
139 }
140
141 #[test]
142 fn test_simple_case_folding() {
143 eq!("στιγμας", "στιγμασ");
144 }
145
146 #[test]
147 fn test_full_case_folding() {
148 eq!("flour", "flour");
149 eq!("Maße", "MASSE");
150 eq!("ᾲ στο διάολο", "ὰι στο διάολο");
151 }
152
153 #[test]
154 fn test_three_char_case_folding() {
155 eq!("ffi", "ffi");
157 eq!("ffl", "FFL");
158 eq!("ΐ", "ι\u{308}\u{301}");
159 eq!("ᾷ", "α\u{342}ι");
160 assert_eq!(Unicode("ffi").to_folded_case(), "ffi");
161 }
162
163 #[test]
164 fn test_to_folded_case() {
165 assert_eq!(Unicode("Maße").to_folded_case(), "masse");
166 }
167
168 #[cfg(feature = "nightly")]
169 #[bench]
170 fn bench_ascii_folding(b: &mut ::test::Bencher) {
171 b.bytes = b"foo bar".len() as u64;
172 b.iter(|| eq!("foo bar", "FoO BAR"));
173 }
174
175 #[cfg(feature = "nightly")]
176 #[bench]
177 fn bench_simple_case_folding(b: &mut ::test::Bencher) {
178 b.bytes = "στιγμας".len() as u64;
179 b.iter(|| eq!("στιγμας", "στιγμασ"));
180 }
181}