lindera_core/dictionary/
character_definition.rs1use std::collections::{BTreeSet, HashMap};
2
3use byteorder::{ByteOrder, LittleEndian};
4use encoding_rs::UTF_16LE;
5use serde::{Deserialize, Serialize};
6
7use crate::error::LinderaErrorKind;
8use crate::LinderaResult;
9
10const DEFAULT_CATEGORY_NAME: &str = "DEFAULT";
11
12fn ucs2_to_unicode(ucs2_codepoint: u16) -> LinderaResult<u32> {
13 let mut buf = [0u8; 2];
14 LittleEndian::write_u16(&mut buf[..], ucs2_codepoint);
15
16 let s = UTF_16LE.decode(&buf[..]).0.into_owned();
17 let chrs: Vec<char> = s.chars().collect();
18
19 match chrs.len() {
20 1 => Ok(chrs[0] as u32),
21 _ => Err(LinderaErrorKind::Parse.with_error(anyhow::anyhow!("unusual char length"))),
22 }
23}
24
25fn parse_hex_codepoint(s: &str) -> LinderaResult<u32> {
26 let removed_0x = s.trim_start_matches("0x");
27 let ucs2_codepoint = u16::from_str_radix(removed_0x, 16)
28 .map_err(|err| LinderaErrorKind::Parse.with_error(anyhow::anyhow!(err)))?;
29
30 ucs2_to_unicode(ucs2_codepoint)
31}
32
33#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
34pub struct CategoryData {
35 pub invoke: bool,
36 pub group: bool,
37 pub length: u32,
38}
39
40#[derive(Serialize, Deserialize, Clone, Debug, Hash, Copy, PartialOrd, Ord, Eq, PartialEq)]
41pub struct CategoryId(pub usize);
42
43#[derive(Clone, Serialize, Deserialize)]
44pub struct CharacterDefinitions {
45 pub category_definitions: Vec<CategoryData>,
46 pub category_names: Vec<String>,
47 pub mapping: LookupTable<CategoryId>,
48}
49
50#[derive(Serialize, Deserialize, Clone)]
51pub struct LookupTable<T: Copy + Clone> {
52 boundaries: Vec<u32>,
53 values: Vec<Vec<T>>,
54}
55
56impl<T: Copy + Clone> LookupTable<T> {
57 pub fn from_fn(mut boundaries: Vec<u32>, funct: &dyn Fn(u32, &mut Vec<T>)) -> LookupTable<T> {
58 if !boundaries.contains(&0) {
59 boundaries.push(0);
60 }
61 boundaries.sort_unstable();
62 let mut values = Vec::new();
63 for &boundary in &boundaries {
64 let mut output = Vec::default();
65 funct(boundary, &mut output);
66 values.push(output);
67 }
68 LookupTable { boundaries, values }
69 }
70
71 pub fn eval(&self, target: u32) -> &[T] {
72 let idx = self
73 .boundaries
74 .binary_search(&target)
75 .unwrap_or_else(|val| val - 1);
76 &self.values[idx][..]
77 }
78}
79
80impl CharacterDefinitions {
81 pub fn categories(&self) -> &[String] {
82 &self.category_names[..]
83 }
84
85 pub fn load(char_def_data: &[u8]) -> LinderaResult<CharacterDefinitions> {
86 bincode::deserialize(char_def_data)
87 .map_err(|err| LinderaErrorKind::Deserialize.with_error(anyhow::anyhow!(err)))
88 }
89
90 pub fn lookup_definition(&self, category_id: CategoryId) -> &CategoryData {
91 &self.category_definitions[category_id.0]
92 }
93
94 pub fn category_name(&self, category_id: CategoryId) -> &str {
95 &self.category_names[category_id.0]
96 }
97
98 pub fn lookup_categories(&self, c: char) -> &[CategoryId] {
99 self.mapping.eval(c as u32)
100 }
101}
102
103#[derive(Default)]
104pub struct CharacterDefinitionsBuilder {
105 category_definition: Vec<CategoryData>,
106 category_index: HashMap<String, CategoryId>,
107 char_ranges: Vec<(u32, u32, Vec<CategoryId>)>,
108}
109
110impl CharacterDefinitionsBuilder {
111 pub fn category_id(&mut self, category_name: &str) -> CategoryId {
112 let num_categories = self.category_index.len();
113 *self
114 .category_index
115 .entry(category_name.to_string())
116 .or_insert(CategoryId(num_categories))
117 }
118
119 fn lookup_categories(&self, c: u32, categories_buffer: &mut Vec<CategoryId>) {
120 categories_buffer.clear();
121 for (start, stop, category_ids) in &self.char_ranges {
122 if *start <= c && *stop >= c {
123 for cat in category_ids {
124 if !categories_buffer.contains(cat) {
125 categories_buffer.push(*cat);
126 }
127 }
128 }
129 }
130 if categories_buffer.is_empty() {
131 if let Some(default_category) = self.category_index.get(DEFAULT_CATEGORY_NAME) {
132 categories_buffer.push(*default_category);
133 }
134 }
135 }
136
137 fn build_lookup_table(&self) -> LookupTable<CategoryId> {
138 let boundaries_set: BTreeSet<u32> = self
139 .char_ranges
140 .iter()
141 .flat_map(|(low, high, _)| vec![*low, *high + 1u32])
142 .collect();
143 let boundaries: Vec<u32> = boundaries_set.into_iter().collect();
144 LookupTable::from_fn(boundaries, &|c, buff| self.lookup_categories(c, buff))
145 }
146
147 pub fn parse(&mut self, content: &str) -> LinderaResult<()> {
148 for line in content.lines() {
149 let line_str = line
150 .split('#')
151 .next()
152 .ok_or_else(|| {
153 LinderaErrorKind::Parse.with_error(anyhow::anyhow!("failed to parse line"))
154 })?
155 .trim();
156 if line_str.is_empty() {
157 continue;
158 }
159 if line_str.starts_with("0x") {
160 self.parse_range(line_str)?;
161 } else {
162 self.parse_category(line_str)?;
163 }
164 }
165 Ok(())
166 }
167
168 fn parse_range(&mut self, line: &str) -> LinderaResult<()> {
169 let fields: Vec<&str> = line.split_whitespace().collect();
170 let range_bounds: Vec<&str> = fields[0].split("..").collect();
171 let lower_bound: u32;
172 let higher_bound: u32;
173 match range_bounds.len() {
174 1 => {
175 lower_bound = parse_hex_codepoint(range_bounds[0])?;
176 higher_bound = lower_bound;
177 }
178 2 => {
179 lower_bound = parse_hex_codepoint(range_bounds[0])?;
180 higher_bound = parse_hex_codepoint(range_bounds[1])?;
182 }
183 _ => {
184 return Err(
185 LinderaErrorKind::Content.with_error(anyhow::anyhow!("Invalid line: {}", line))
186 );
187 }
188 }
189 let category_ids: Vec<CategoryId> = fields[1..]
190 .iter()
191 .map(|category| self.category_id(category))
192 .collect();
193
194 self.char_ranges
195 .push((lower_bound, higher_bound, category_ids));
196
197 Ok(())
198 }
199
200 fn parse_category(&mut self, line: &str) -> LinderaResult<()> {
201 let fields = line.split_ascii_whitespace().collect::<Vec<&str>>();
202 if fields.len() != 4 {
203 return Err(LinderaErrorKind::Content.with_error(anyhow::anyhow!(
204 "Expected 4 fields. Got {} in {}",
205 fields.len(),
206 line
207 )));
208 }
209 let invoke = fields[1]
210 .parse::<u32>()
211 .map_err(|err| LinderaErrorKind::Parse.with_error(anyhow::anyhow!(err)))?
212 == 1;
213 let group = fields[2]
214 .parse::<u32>()
215 .map_err(|err| LinderaErrorKind::Parse.with_error(anyhow::anyhow!(err)))?
216 == 1;
217 let length = fields[3]
218 .parse::<u32>()
219 .map_err(|err| LinderaErrorKind::Parse.with_error(anyhow::anyhow!(err)))?;
220 let category_data = CategoryData {
221 invoke,
222 group,
223 length,
224 };
225 self.category_id(fields[0]);
227 self.category_definition.push(category_data);
228
229 Ok(())
230 }
231
232 pub fn build(self) -> CharacterDefinitions {
233 let mut category_names: Vec<String> = (0..self.category_index.len())
234 .map(|_| String::new())
235 .collect();
236 for (category_name, category_id) in &self.category_index {
237 category_names[category_id.0] = category_name.clone();
238 }
239 let mapping = self.build_lookup_table();
240 CharacterDefinitions {
241 category_definitions: self.category_definition,
242 category_names,
243 mapping,
244 }
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use crate::dictionary::character_definition::LookupTable;
251
252 #[test]
253 fn test_lookup_table() {
254 let funct = |c: u32, output: &mut Vec<u32>| {
255 if c >= 10u32 {
256 output.push(1u32);
257 } else {
258 output.push(0u32);
259 }
260 };
261 let lookup_table = LookupTable::from_fn(vec![0u32, 10u32], &funct);
262 for i in 0..100 {
263 let mut v = Vec::default();
264 funct(i, &mut v);
265 assert_eq!(lookup_table.eval(i), &v[..]);
266 }
267 }
268}