Skip to main content

rusty_xml_parser/
chvalid.rs

1//! Character classes transcribed from libxml2 v2.15.3 `chvalid.h` / `chvalid.c`.
2//! `xml_is_char` uses the `xmlIsCharQ` formula, not the range tables.
3
4use crate::chvalid_tables::{
5    XML_IS_BASE_CHAR_LRNG, XML_IS_BASE_CHAR_SRNG, XML_IS_COMBINING_LRNG, XML_IS_COMBINING_SRNG,
6    XML_IS_DIGIT_LRNG, XML_IS_DIGIT_SRNG, XML_IS_EXTENDER_LRNG, XML_IS_EXTENDER_SRNG,
7    XML_IS_PUBID_CHAR_TAB,
8};
9
10/// Binary search matching `xmlCharInRange` in `chvalid.c`.
11#[doc(alias = "xmlCharInRange")]
12pub fn xml_char_in_range(val: u32, short: &[(u16, u16)], long: &[(u32, u32)]) -> bool {
13    if val < 0x10000 {
14        if short.is_empty() {
15            return false;
16        }
17        let mut low: i32 = 0;
18        let mut high: i32 = short.len() as i32 - 1;
19        let v = val as u16;
20        while low <= high {
21            let mid = ((low + high) / 2) as usize;
22            if v < short[mid].0 {
23                high = mid as i32 - 1;
24            } else if v > short[mid].1 {
25                low = mid as i32 + 1;
26            } else {
27                return true;
28            }
29        }
30        false
31    } else {
32        if long.is_empty() {
33            return false;
34        }
35        let mut low: i32 = 0;
36        let mut high: i32 = long.len() as i32 - 1;
37        while low <= high {
38            let mid = ((low + high) / 2) as usize;
39            if val < long[mid].0 {
40                high = mid as i32 - 1;
41            } else if val > long[mid].1 {
42                low = mid as i32 + 1;
43            } else {
44                return true;
45            }
46        }
47        false
48    }
49}
50
51/// `xmlIsCharQ`.
52#[doc(alias = "xmlIsChar")]
53pub fn xml_is_char(c: u32) -> bool {
54    if c < 0x100 {
55        (0x09..=0x0a).contains(&c) || c == 0x0d || c >= 0x20
56    } else {
57        (0x100..=0xd7ff).contains(&c)
58            || (0xe000..=0xfffd).contains(&c)
59            || (0x10000..=0x10ffff).contains(&c)
60    }
61}
62
63/// `xmlIsBlankQ`.
64#[doc(alias = "xmlIsBlank")]
65pub fn xml_is_blank(c: u32) -> bool {
66    c == 0x20 || (0x09..=0x0a).contains(&c) || c == 0x0d
67}
68
69/// `xmlIsBaseCharQ`.
70#[doc(alias = "xmlIsBaseChar")]
71pub fn xml_is_base_char(c: u32) -> bool {
72    if c < 0x100 {
73        (0x41..=0x5a).contains(&c)
74            || (0x61..=0x7a).contains(&c)
75            || (0xc0..=0xd6).contains(&c)
76            || (0xd8..=0xf6).contains(&c)
77            || c >= 0xf8
78    } else {
79        xml_char_in_range(c, XML_IS_BASE_CHAR_SRNG, XML_IS_BASE_CHAR_LRNG)
80    }
81}
82
83/// `xmlIsDigitQ`.
84#[doc(alias = "xmlIsDigit")]
85pub fn xml_is_digit(c: u32) -> bool {
86    if c < 0x100 {
87        (0x30..=0x39).contains(&c)
88    } else {
89        xml_char_in_range(c, XML_IS_DIGIT_SRNG, XML_IS_DIGIT_LRNG)
90    }
91}
92
93/// `xmlIsCombiningQ`.
94#[doc(alias = "xmlIsCombining")]
95pub fn xml_is_combining(c: u32) -> bool {
96    if c < 0x100 {
97        false
98    } else {
99        xml_char_in_range(c, XML_IS_COMBINING_SRNG, XML_IS_COMBINING_LRNG)
100    }
101}
102
103/// `xmlIsExtenderQ`.
104#[doc(alias = "xmlIsExtender")]
105pub fn xml_is_extender(c: u32) -> bool {
106    if c < 0x100 {
107        c == 0xb7
108    } else {
109        xml_char_in_range(c, XML_IS_EXTENDER_SRNG, XML_IS_EXTENDER_LRNG)
110    }
111}
112
113/// `xmlIsIdeographicQ`.
114#[doc(alias = "xmlIsIdeographic")]
115pub fn xml_is_ideographic(c: u32) -> bool {
116    if c < 0x100 {
117        false
118    } else {
119        (0x4e00..=0x9fa5).contains(&c) || c == 0x3007 || (0x3021..=0x3029).contains(&c)
120    }
121}
122
123/// `IS_LETTER`.
124pub fn xml_is_letter(c: u32) -> bool {
125    xml_is_base_char(c) || xml_is_ideographic(c)
126}
127
128/// `xmlIsPubidCharQ`.
129#[doc(alias = "xmlIsPubidChar")]
130pub fn xml_is_pubid_char(c: u32) -> bool {
131    if c < 0x100 {
132        XML_IS_PUBID_CHAR_TAB[c as usize] != 0
133    } else {
134        false
135    }
136}
137
138/// XML 1.0 5th edition NameStartChar (default). `old10` uses Letter | '_' | ':'.
139pub fn xml_is_name_start_char(c: u32, old10: bool) -> bool {
140    if c == b' ' as u32 || c == b'>' as u32 || c == b'/' as u32 {
141        return false;
142    }
143    if old10 {
144        return xml_is_letter(c) || c == b'_' as u32 || c == b':' as u32;
145    }
146    (c >= b'a' as u32 && c <= b'z' as u32)
147        || (c >= b'A' as u32 && c <= b'Z' as u32)
148        || c == b'_' as u32
149        || c == b':' as u32
150        || (0xc0..=0xd6).contains(&c)
151        || (0xd8..=0xf6).contains(&c)
152        || (0xf8..=0x2ff).contains(&c)
153        || (0x370..=0x37d).contains(&c)
154        || (0x37f..=0x1fff).contains(&c)
155        || (0x200c..=0x200d).contains(&c)
156        || (0x2070..=0x218f).contains(&c)
157        || (0x2c00..=0x2fef).contains(&c)
158        || (0x3001..=0xd7ff).contains(&c)
159        || (0xf900..=0xfdcf).contains(&c)
160        || (0xfdf0..=0xfffd).contains(&c)
161        || (0x10000..=0xeffff).contains(&c)
162}
163
164/// XML 1.0 5th edition NameChar (default).
165pub fn xml_is_name_char(c: u32, old10: bool) -> bool {
166    if c == b' ' as u32 || c == b'>' as u32 || c == b'/' as u32 {
167        return false;
168    }
169    if old10 {
170        return xml_is_letter(c)
171            || xml_is_digit(c)
172            || c == b'.' as u32
173            || c == b'-' as u32
174            || c == b'_' as u32
175            || c == b':' as u32
176            || xml_is_combining(c)
177            || xml_is_extender(c);
178    }
179    xml_is_name_start_char(c, false)
180        || (c >= b'0' as u32 && c <= b'9' as u32)
181        || c == b'-' as u32
182        || c == b'.' as u32
183        || c == 0xb7
184        || (0x300..=0x36f).contains(&c)
185        || (0x203f..=0x2040).contains(&c)
186}