lindera_dictionary/dictionary/
prefix_dictionary.rs1use daachorse::DoubleArrayAhoCorasick;
2use rkyv::rancor::{Fallible, Source};
3use rkyv::with::{ArchiveWith, DeserializeWith, SerializeWith};
4use rkyv::{Archive, Deserialize as RkyvDeserialize, Place, Serialize as RkyvSerialize};
5use serde::{Deserialize, Serialize};
6
7use crate::{LinderaResult, error::LinderaErrorKind, util::Data, viterbi::WordEntry};
8
9#[derive(Debug, Clone)]
11pub struct Match {
12 pub word_idx: WordIdx,
13 pub end_char: usize,
14}
15
16#[derive(Debug, Clone, Copy)]
17pub struct WordIdx {
18 pub word_id: u32,
19}
20
21impl WordIdx {
22 pub fn new(word_id: u32) -> Self {
23 Self { word_id }
24 }
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub enum DaTrust {
31 Trusted,
34 Untrusted,
37}
38
39pub struct DoubleArrayArchiver;
40
41impl ArchiveWith<DoubleArrayAhoCorasick<u32>> for DoubleArrayArchiver {
42 type Archived = rkyv::vec::ArchivedVec<u8>;
43 type Resolver = rkyv::vec::VecResolver;
44
45 fn resolve_with(
46 field: &DoubleArrayAhoCorasick<u32>,
47 resolver: Self::Resolver,
48 out: Place<Self::Archived>,
49 ) {
50 let bytes = field.serialize();
51 rkyv::vec::ArchivedVec::resolve_from_slice(&bytes, resolver, out);
52 }
53}
54
55impl<S: Fallible + rkyv::ser::Writer + rkyv::ser::Allocator + ?Sized>
56 SerializeWith<DoubleArrayAhoCorasick<u32>, S> for DoubleArrayArchiver
57{
58 fn serialize_with(
59 field: &DoubleArrayAhoCorasick<u32>,
60 serializer: &mut S,
61 ) -> Result<Self::Resolver, S::Error> {
62 let bytes = field.serialize();
63 rkyv::vec::ArchivedVec::serialize_from_slice(&bytes, serializer)
64 }
65}
66
67impl<D: Fallible<Error: Source> + ?Sized>
68 DeserializeWith<rkyv::vec::ArchivedVec<u8>, DoubleArrayAhoCorasick<u32>, D>
69 for DoubleArrayArchiver
70{
71 fn deserialize_with(
77 archived: &rkyv::vec::ArchivedVec<u8>,
78 _deserializer: &mut D,
79 ) -> Result<DoubleArrayAhoCorasick<u32>, D::Error> {
80 let (da, _) = DoubleArrayAhoCorasick::deserialize(archived.as_slice()).map_err(|err| {
81 D::Error::new(std::io::Error::new(
82 std::io::ErrorKind::InvalidData,
83 err.to_string(),
84 ))
85 })?;
86 Ok(da)
87 }
88}
89
90mod double_array_serde {
91 use daachorse::DoubleArrayAhoCorasick;
92 use serde::{Deserialize, Deserializer, Serializer};
93
94 pub fn serialize<S>(da: &DoubleArrayAhoCorasick<u32>, serializer: S) -> Result<S::Ok, S::Error>
95 where
96 S: Serializer,
97 {
98 let bytes = da.serialize();
99 serializer.serialize_bytes(&bytes)
100 }
101
102 pub fn deserialize<'de, D>(deserializer: D) -> Result<DoubleArrayAhoCorasick<u32>, D::Error>
103 where
104 D: Deserializer<'de>,
105 {
106 let bytes: Vec<u8> = Deserialize::deserialize(deserializer)?;
107 let (da, _) = DoubleArrayAhoCorasick::deserialize(&bytes)
108 .map_err(|err| serde::de::Error::custom(err.to_string()))?;
109 Ok(da)
110 }
111}
112
113#[derive(Clone, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)]
114pub struct PrefixDictionary {
115 #[serde(with = "self::double_array_serde")]
116 #[rkyv(with = DoubleArrayArchiver)]
117 pub da: DoubleArrayAhoCorasick<u32>,
118 pub vals_data: Data,
119 pub words_idx_data: Data,
120 pub words_data: Data,
121 pub is_system: bool,
122}
123
124impl PrefixDictionary {
125 #[inline]
132 pub(crate) fn decode_val(&self, val: u32) -> (u32, u32) {
133 (val >> 8u32, val & ((1u32 << 8) - 1u32))
134 }
135
136 pub fn load(
153 da_data: impl Into<Data>,
154 vals_data: impl Into<Data>,
155 words_idx_data: impl Into<Data>,
156 words_data: impl Into<Data>,
157 is_system: bool,
158 trust: DaTrust,
159 ) -> LinderaResult<PrefixDictionary> {
160 let da_bytes = da_data.into();
161 let da = match trust {
162 DaTrust::Trusted => {
163 debug_assert!(
164 matches!(da_bytes, Data::Static(_)),
165 "DaTrust::Trusted should only be used for embedded (Data::Static) da_data"
166 );
167 unsafe { DoubleArrayAhoCorasick::deserialize_unchecked(&da_bytes[..]).0 }
182 }
183 DaTrust::Untrusted => {
184 DoubleArrayAhoCorasick::deserialize(&da_bytes[..])
185 .map_err(|err| {
186 LinderaErrorKind::Deserialize.with_error(anyhow::anyhow!(err.to_string()))
187 })?
188 .0
189 }
190 };
191
192 Ok(PrefixDictionary {
193 da,
194 vals_data: vals_data.into(),
195 words_idx_data: words_idx_data.into(),
196 words_data: words_data.into(),
197 is_system,
198 })
199 }
200
201 pub fn prefix<'a>(&'a self, s: &'a str) -> impl Iterator<Item = (usize, WordEntry)> + 'a {
202 self.da
203 .find_overlapping_iter(s)
204 .filter(|m| m.start() == 0)
205 .flat_map(move |m| {
206 let (offset, len) = self.decode_val(m.value());
207 let offset_bytes = (offset as usize) * WordEntry::SERIALIZED_LEN;
208 let data: &[u8] = &self.vals_data[offset_bytes..];
209 (0..len as usize).map(move |i| {
210 (
211 m.end(),
212 WordEntry::deserialize(
213 &data[WordEntry::SERIALIZED_LEN * i..],
214 self.is_system,
215 ),
216 )
217 })
218 })
219 }
220
221 pub fn find_surface(&self, surface: &str) -> Vec<WordEntry> {
223 self.find_surface_iter(surface).collect()
224 }
225
226 pub fn find_surface_iter<'a>(
229 &'a self,
230 surface: &'a str,
231 ) -> impl Iterator<Item = WordEntry> + 'a {
232 self.da
233 .find_overlapping_iter(surface)
234 .filter(|m| m.start() == 0 && m.end() == surface.len())
235 .flat_map(move |m| {
236 let (offset, len) = self.decode_val(m.value());
237 let offset_bytes = (offset as usize) * WordEntry::SERIALIZED_LEN;
238 let data = &self.vals_data[offset_bytes..];
239 (0..len as usize).map(move |i| {
240 WordEntry::deserialize(&data[WordEntry::SERIALIZED_LEN * i..], self.is_system)
241 })
242 })
243 }
244
245 pub fn common_prefix_iterator(&self, suffix: &[char]) -> Vec<Match> {
247 if self.vals_data.is_empty() {
251 return Vec::new();
252 }
253
254 let suffix_str: String = suffix.iter().collect();
255
256 self.da
257 .find_overlapping_iter(&suffix_str)
258 .filter(|m| m.start() == 0)
259 .flat_map(|m| {
260 let (offset, len) = self.decode_val(m.value());
261 let offset_bytes = (offset as usize) * WordEntry::SERIALIZED_LEN;
262
263 if offset_bytes >= self.vals_data.len() {
265 return vec![].into_iter();
266 }
267
268 let data: &[u8] = &self.vals_data[offset_bytes..];
269 (0..len as usize)
270 .filter_map(move |i| {
271 let required_bytes = WordEntry::SERIALIZED_LEN * (i + 1);
272 if required_bytes <= data.len() {
273 let word_entry = WordEntry::deserialize(
274 &data[WordEntry::SERIALIZED_LEN * i..],
275 self.is_system,
276 );
277 Some(Match {
278 word_idx: WordIdx::new(word_entry.word_id().id()),
279 end_char: m.end(), })
301 } else {
302 None
303 }
304 })
305 .collect::<Vec<_>>()
306 .into_iter()
307 })
308 .collect()
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use daachorse::DoubleArrayAhoCorasickBuilder;
315
316 use super::*;
317
318 fn build_valid_da_bytes() -> Vec<u8> {
319 let keyset: Vec<(&[u8], u32)> = vec![(b"a", 0), (b"ab", 1), (b"b", 2)];
320 let da = DoubleArrayAhoCorasickBuilder::new()
321 .build_with_values(keyset)
322 .unwrap();
323 da.serialize()
324 }
325
326 #[test]
327 fn test_prefix_dictionary_load_trusted_matches_untrusted() {
328 let da_bytes = build_valid_da_bytes();
329 let da_bytes_static: &'static [u8] = Box::leak(da_bytes.clone().into_boxed_slice());
334
335 let trusted = PrefixDictionary::load(
336 da_bytes_static,
337 Vec::<u8>::new(),
338 Vec::<u8>::new(),
339 Vec::<u8>::new(),
340 true,
341 DaTrust::Trusted,
342 )
343 .unwrap();
344 let untrusted = PrefixDictionary::load(
345 da_bytes,
346 Vec::<u8>::new(),
347 Vec::<u8>::new(),
348 Vec::<u8>::new(),
349 true,
350 DaTrust::Untrusted,
351 )
352 .unwrap();
353
354 let trusted_matches: Vec<_> = trusted.da.find_overlapping_iter("ab").collect();
355 let untrusted_matches: Vec<_> = untrusted.da.find_overlapping_iter("ab").collect();
356 assert_eq!(trusted_matches.len(), untrusted_matches.len());
357 assert!(!trusted_matches.is_empty());
358 for (t, u) in trusted_matches.iter().zip(untrusted_matches.iter()) {
359 assert_eq!(t.value(), u.value());
360 assert_eq!(t.start(), u.start());
361 assert_eq!(t.end(), u.end());
362 }
363 }
364
365 #[test]
366 fn test_prefix_dictionary_load_untrusted_rejects_corrupted_da_data() {
367 let mut da_bytes = build_valid_da_bytes();
368 da_bytes.truncate(4);
372
373 let result = PrefixDictionary::load(
374 da_bytes,
375 Vec::<u8>::new(),
376 Vec::<u8>::new(),
377 Vec::<u8>::new(),
378 true,
379 DaTrust::Untrusted,
380 );
381
382 assert!(result.is_err());
383 }
384}