fast_unicode_general_category/
lib.rs1#![allow(unused_assignments)]
2
3use bytes::{Bytes, Buf};
4use num_enum::TryFromPrimitive;
5
6static BASIC_PLANE: &'static [u8] = include_bytes!("./data/basic.bin");
7static SUPPLEMENTARY_PLANE: &'static [u8] = include_bytes!("./data/supplementary.bin");
8
9#[derive(Copy, Clone, PartialEq, TryFromPrimitive)]
10#[repr(u8)]
11pub enum GeneralCategory {
12 UppercaseLetter = 0,
13 LowercaseLetter = 1,
14 TitlecaseLetter = 2,
15 ModifierLetter = 3,
16 OtherLetter = 4,
17 NonspacingMark = 5,
18 SpacingCombiningMark = 6,
19 EnclosingMark = 7,
20 DecimalDigitNumber = 8,
21 LetterNumber = 9,
22 OtherNumber = 10,
23 ConnectorPunctuation = 11,
24 DashPunctuation = 12,
25 OpenPunctuation = 13,
26 ClosePunctuation = 14,
27 InitialQuotePunctuation = 15,
28 FinalQuotePunctuation = 16,
29 OtherPunctuation = 17,
30 MathSymbol = 18,
31 CurrencySymbol = 19,
32 ModifierSymbol = 20,
33 OtherSymbol = 21,
34 SpaceSeparator = 22,
35 LineSeparator = 23,
36 ParagraphSeparator = 24,
37 ControlOther = 25,
38 FormatOther = 26,
39 SurrogateOther = 27,
40 PrivateUseOther = 28,
41 NotAssignedOther = 29,
42}
43
44impl GeneralCategory {
45 pub fn is_letter(self) -> bool {
46 self as u8 <= 4
47 }
48 pub fn is_mark(self) -> bool {
49 self as u8 >= 5 && self as u8 <= 7
50 }
51 pub fn is_number(self) -> bool {
52 self as u8 >= 8 && self as u8 <= 10
53 }
54 pub fn is_punctuation(self) -> bool {
55 self as u8 >= 11 && self as u8 <= 17
56 }
57 pub fn is_symbol(self) -> bool {
58 self as u8 >= 18 && self as u8 <= 21
59 }
60 pub fn is_separator(self) -> bool {
61 self as u8 >= 22 && self as u8 <= 24
62 }
63 pub fn is_other(self) -> bool {
64 self as u8 >= 25 && self as u8 <= 29
65 }
66}
67
68impl<'a> TryFrom<&'a str> for GeneralCategory {
69 type Error = ();
70 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
71 use GeneralCategory as C;
72 (match value {
73 "Lu" => Ok(C::UppercaseLetter),
74 "Ll" => Ok(C::LowercaseLetter),
75 "Lt" => Ok(C::TitlecaseLetter),
76 "Lm" => Ok(C::ModifierLetter),
77 "Lo" => Ok(C::OtherLetter),
78 "Mn" => Ok(C::NonspacingMark),
79 "Mc" => Ok(C::SpacingCombiningMark),
80 "Me" => Ok(C::EnclosingMark),
81 "Nd" => Ok(C::DecimalDigitNumber),
82 "Nl" => Ok(C::LetterNumber),
83 "No" => Ok(C::OtherNumber),
84 "Pc" => Ok(C::ConnectorPunctuation),
85 "Pd" => Ok(C::DashPunctuation),
86 "Ps" => Ok(C::OpenPunctuation),
87 "Pe" => Ok(C::ClosePunctuation),
88 "Pi" => Ok(C::InitialQuotePunctuation),
89 "Pf" => Ok(C::FinalQuotePunctuation),
90 "Po" => Ok(C::OtherPunctuation),
91 "Sm" => Ok(C::MathSymbol),
92 "Sc" => Ok(C::CurrencySymbol),
93 "Sk" => Ok(C::ModifierSymbol),
94 "So" => Ok(C::OtherSymbol),
95 "Zs" => Ok(C::SpaceSeparator),
96 "Zl" => Ok(C::LineSeparator),
97 "Zp" => Ok(C::ParagraphSeparator),
98 "Cc" => Ok(C::ControlOther),
99 "Cf" => Ok(C::FormatOther),
100 "Cs" => Ok(C::SurrogateOther),
101 "Co" => Ok(C::PrivateUseOther),
102 "Cn" => Ok(C::NotAssignedOther),
103 _ => Err(()),
104 }).to_owned()
105 }
106}
107
108impl ToString for GeneralCategory {
109 fn to_string(&self) -> String {
110 use GeneralCategory as C;
111 (match self {
112 C::UppercaseLetter => "Lu",
113 C::LowercaseLetter => "Ll",
114 C::TitlecaseLetter => "Lt",
115 C::ModifierLetter => "Lm",
116 C::OtherLetter => "Lo",
117 C::NonspacingMark => "Mn",
118 C::SpacingCombiningMark => "Mc",
119 C::EnclosingMark => "Me",
120 C::DecimalDigitNumber => "Nd",
121 C::LetterNumber => "Nl",
122 C::OtherNumber => "No",
123 C::ConnectorPunctuation => "Pc",
124 C::DashPunctuation => "Pd",
125 C::OpenPunctuation => "Ps",
126 C::ClosePunctuation => "Pe",
127 C::InitialQuotePunctuation => "Pi",
128 C::FinalQuotePunctuation => "Pf",
129 C::OtherPunctuation => "Po",
130 C::MathSymbol => "Sm",
131 C::CurrencySymbol => "Sc",
132 C::ModifierSymbol => "Sk",
133 C::OtherSymbol => "So",
134 C::SpaceSeparator => "Zs",
135 C::LineSeparator => "Zl",
136 C::ParagraphSeparator => "Zp",
137 C::ControlOther => "Cc",
138 C::FormatOther => "Cf",
139 C::SurrogateOther => "Cs",
140 C::PrivateUseOther => "Co",
141 C::NotAssignedOther => "Cn",
142 }).to_owned()
143 }
144}
145
146impl From<char> for GeneralCategory {
147 fn from(code_point: char) -> Self {
148 let code_point = code_point as usize;
149 let mut compare_code_point: usize = 0;
150 let mut count: usize = 0;
151 let mut category_value: u8 = 0;
152 if code_point < 0x10000 {
153 let mut plane = Bytes::from_static(BASIC_PLANE);
154 loop {
155 if !plane.has_remaining() {
156 break;
157 }
158 category_value = plane.get_u8();
159 count = plane.get_u16_le() as usize;
160 compare_code_point += count;
161 if code_point < compare_code_point {
162 return GeneralCategory::try_from(category_value).unwrap();
163 }
164 }
165 } else {
166 let mut plane = Bytes::from_static(SUPPLEMENTARY_PLANE);
167 compare_code_point = 0x10000;
168 loop {
169 if !plane.has_remaining() {
170 break;
171 }
172 category_value = plane.get_u8();
173 count = bytes_get_u24_le(&mut plane);
174 compare_code_point += count;
175 if code_point < compare_code_point {
176 return GeneralCategory::try_from(category_value).unwrap();
177 }
178 }
179 }
180 GeneralCategory::NotAssignedOther
181 }
182}
183
184fn bytes_get_u24_le(bytes: &mut Bytes) -> usize {
185 (bytes.get_u16_le() as usize) | ((bytes.get_u8() as usize) << 16)
186}