Skip to main content

fast_unicode_general_category/
lib.rs

1#![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}