lindera_dictionary/
dictionary.rs1pub mod character_definition;
2pub mod connection_cost_matrix;
3pub mod context_id_map;
4pub mod metadata;
5pub mod prefix_dictionary;
6pub mod schema;
7pub mod unknown_dictionary;
8
9use std::fs;
10use std::path::Path;
11use std::str;
12use std::sync::Arc;
13
14use byteorder::{ByteOrder, LittleEndian};
15use once_cell::sync::Lazy;
16use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
17use serde::{Deserialize, Serialize};
18
19use crate::LinderaResult;
20use crate::dictionary::character_definition::CharacterDefinition;
21use crate::dictionary::connection_cost_matrix::ConnectionCostMatrix;
22use crate::dictionary::context_id_map::ContextIdMap;
23use crate::dictionary::metadata::Metadata;
24use crate::dictionary::prefix_dictionary::{PrefixDictionary, UserPrefixDictionary};
25use crate::dictionary::unknown_dictionary::UnknownDictionary;
26use crate::error::LinderaErrorKind;
27use crate::loader::character_definition::CharacterDefinitionLoader;
28use crate::loader::connection_cost_matrix::ConnectionCostMatrixLoader;
29use crate::loader::metadata::MetadataLoader;
30use crate::loader::prefix_dictionary::PrefixDictionaryLoader;
31use crate::loader::unknown_dictionary::UnknownDictionaryLoader;
32use crate::util::Data;
33use crate::viterbi::WordEntry;
34
35pub static UNK: Lazy<Vec<&str>> = Lazy::new(|| vec!["UNK"]);
36
37#[derive(Clone)]
43pub struct Dictionary {
44 pub prefix_dictionary: Arc<PrefixDictionary>,
45 pub connection_cost_matrix: Arc<ConnectionCostMatrix>,
46 pub character_definition: Arc<CharacterDefinition>,
47 pub unknown_dictionary: Arc<UnknownDictionary>,
48 pub metadata: Arc<Metadata>,
49}
50
51impl Dictionary {
52 pub fn unknown_word_details(&self, word_id: usize) -> Vec<&str> {
54 match self.unknown_dictionary.word_details(word_id as u32) {
55 Some(details) => details,
56 None => UNK.to_vec(),
57 }
58 }
59
60 pub fn word_details(&self, word_id: usize) -> Vec<&str> {
61 if 4 * word_id >= self.prefix_dictionary.words_idx_data.len() {
62 return vec![];
63 }
64
65 let idx: usize = match LittleEndian::read_u32(
66 &self.prefix_dictionary.words_idx_data[4 * word_id..][..4],
67 )
68 .try_into()
69 {
70 Ok(value) => value,
71 Err(_) => return UNK.to_vec(), };
73 let data = &self.prefix_dictionary.words_data[idx..];
74 let joined_details_len: usize = match LittleEndian::read_u32(data).try_into() {
75 Ok(value) => value,
76 Err(_) => return UNK.to_vec(), };
78 let joined_details_bytes: &[u8] =
79 &self.prefix_dictionary.words_data[idx + 4..idx + 4 + joined_details_len];
80
81 let mut details = Vec::new();
82 for bytes in joined_details_bytes.split(|&b| b == 0) {
83 let detail = match str::from_utf8(bytes) {
84 Ok(s) => s,
85 Err(_) => return UNK.to_vec(), };
87 details.push(detail);
88 }
89 details
90 }
91
92 pub fn load_from_path(dict_path: &Path) -> LinderaResult<Self> {
99 Self::load_from_path_with_options(dict_path, cfg!(feature = "mmap"))
100 }
101
102 pub fn load_from_path_with_options(dict_path: &Path, use_mmap: bool) -> LinderaResult<Self> {
127 if !dict_path.exists() {
129 return Err(LinderaErrorKind::Io.with_error(anyhow::anyhow!(
130 "Dictionary path does not exist: {}",
131 dict_path.display()
132 )));
133 }
134
135 if !dict_path.is_dir() {
136 return Err(LinderaErrorKind::Io.with_error(anyhow::anyhow!(
137 "Dictionary path is not a directory: {}",
138 dict_path.display()
139 )));
140 }
141
142 let metadata = MetadataLoader::load(dict_path)?;
147 metadata.validate_format_version()?;
148
149 let character_definition = CharacterDefinitionLoader::load(dict_path)?;
150
151 let connection_cost_matrix = {
152 #[cfg(feature = "mmap")]
153 if use_mmap {
154 ConnectionCostMatrixLoader::load_mmap(dict_path)?
155 } else {
156 ConnectionCostMatrixLoader::load(dict_path)?
157 }
158 #[cfg(not(feature = "mmap"))]
159 ConnectionCostMatrixLoader::load(dict_path)?
160 };
161
162 let prefix_dictionary = {
163 #[cfg(feature = "mmap")]
164 if use_mmap {
165 PrefixDictionaryLoader::load_mmap(dict_path)?
166 } else {
167 PrefixDictionaryLoader::load(dict_path)?
168 }
169 #[cfg(not(feature = "mmap"))]
170 PrefixDictionaryLoader::load(dict_path)?
171 };
172
173 let unknown_dictionary = UnknownDictionaryLoader::load(dict_path)?;
174
175 Ok(Dictionary {
176 prefix_dictionary: Arc::new(prefix_dictionary),
177 connection_cost_matrix: Arc::new(connection_cost_matrix),
178 character_definition: Arc::new(character_definition),
179 unknown_dictionary: Arc::new(unknown_dictionary),
180 metadata: Arc::new(metadata),
181 })
182 }
183
184 pub fn save_to_path(&self, dict_path: &Path) -> LinderaResult<()> {
186 fs::create_dir_all(dict_path)
188 .map_err(|err| LinderaErrorKind::Io.with_error(anyhow::anyhow!(err)))?;
189
190 todo!("Dictionary saving will be implemented when needed")
193 }
194}
195
196#[derive(Clone, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)]
202
203pub struct UserDictionary {
204 pub dict: UserPrefixDictionary,
205}
206
207impl UserDictionary {
208 pub fn remap_context_ids(&mut self, map: &ContextIdMap) {
226 const LEFT_ID_OFFSET: usize = 6;
227 const RIGHT_ID_OFFSET: usize = 8;
228
229 let mut vals = self.dict.vals_data.to_vec();
230 for entry in vals.chunks_exact_mut(WordEntry::SERIALIZED_LEN) {
231 let left = LittleEndian::read_u16(&entry[LEFT_ID_OFFSET..][..2]);
232 let right = LittleEndian::read_u16(&entry[RIGHT_ID_OFFSET..][..2]);
233 LittleEndian::write_u16(&mut entry[LEFT_ID_OFFSET..][..2], map.map_left(left));
234 LittleEndian::write_u16(&mut entry[RIGHT_ID_OFFSET..][..2], map.map_right(right));
235 }
236 self.dict.vals_data = Data::Vec(vals);
237 }
238
239 pub fn load(user_dict_data: &[u8]) -> LinderaResult<UserDictionary> {
240 let mut aligned = rkyv::util::AlignedVec::<16>::new();
241 aligned.extend_from_slice(user_dict_data);
242 rkyv::from_bytes::<UserDictionary, rkyv::rancor::Error>(&aligned).map_err(|err| {
243 LinderaErrorKind::Deserialize.with_error(anyhow::anyhow!(err.to_string()))
244 })
245 }
246
247 pub fn word_details(&self, word_id: usize) -> Vec<&str> {
248 if 4 * word_id >= self.dict.words_idx_data.len() {
249 return UNK.to_vec(); }
251 let idx = LittleEndian::read_u32(&self.dict.words_idx_data[4 * word_id..][..4]);
252 let data = &self.dict.words_data[idx as usize..];
253
254 let joined_details_len: usize = match LittleEndian::read_u32(data).try_into() {
256 Ok(value) => value,
257 Err(_) => return UNK.to_vec(), };
259 let joined_details_bytes: &[u8] =
260 &self.dict.words_data[idx as usize + 4..idx as usize + 4 + joined_details_len];
261
262 let mut details = Vec::new();
263 for bytes in joined_details_bytes.split(|&b| b == 0) {
264 let detail = match str::from_utf8(bytes) {
265 Ok(s) => s,
266 Err(_) => return UNK.to_vec(), };
268 details.push(detail);
269 }
270 details
271 }
272}