1use std::fs::File;
2use std::io::{Read, Write};
3use std::ops::Deref;
4use std::path::Path;
5
6#[cfg(feature = "mmap")]
7use memmap2::Mmap;
8
9use anyhow::anyhow;
10use encoding_rs::Encoding;
11use serde::{Deserialize, Serialize};
12
13use crate::LinderaResult;
14use crate::error::LinderaErrorKind;
15
16use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
17
18#[inline]
35pub(crate) fn detail_field_count(joined_details: &[u8]) -> usize {
36 memchr::memchr_iter(0, joined_details).count() + 1
37}
38
39#[inline]
57pub(crate) fn words_idx_offset(words_idx_data: &[u8], word_id: usize) -> Option<usize> {
58 let start = word_id.checked_mul(4)?;
59 let bytes = words_idx_data.get(start..start.checked_add(4)?)?;
60 let offset: [u8; 4] = bytes.try_into().ok()?;
61 Some(u32::from_le_bytes(offset) as usize)
62}
63
64#[inline]
83pub(crate) fn joined_details_at(words_data: &[u8], offset: usize) -> Option<&str> {
84 let header: [u8; 4] = words_data
85 .get(offset..offset.checked_add(4)?)?
86 .try_into()
87 .ok()?;
88 let start = offset + 4;
89 let len = u32::from_le_bytes(header) as usize;
90 let bytes = words_data.get(start..start.checked_add(len)?)?;
91 str::from_utf8(bytes).ok()
92}
93
94pub fn write_data<W: Write>(buffer: &[u8], writer: &mut W) -> LinderaResult<()> {
96 writer.write_all(buffer).map_err(|err| {
97 LinderaErrorKind::Io
98 .with_error(err)
99 .add_context("Failed to write data to output")
100 })?;
101 Ok(())
102}
103
104pub fn read_file(filename: &Path) -> LinderaResult<Vec<u8>> {
105 let mut input_read = File::open(filename).map_err(|err| {
106 LinderaErrorKind::Io
107 .with_error(err)
108 .add_context(format!("Failed to open file: {}", filename.display()))
109 })?;
110 let mut buffer = Vec::new();
111 input_read.read_to_end(&mut buffer).map_err(|err| {
112 LinderaErrorKind::Io.with_error(err).add_context(format!(
113 "Failed to read file contents: {}",
114 filename.display()
115 ))
116 })?;
117 Ok(buffer)
118}
119
120pub fn read_aligned_file(filename: &Path) -> LinderaResult<rkyv::util::AlignedVec<16>> {
123 let raw_data = read_file(filename)?;
124
125 let mut aligned_data = rkyv::util::AlignedVec::<16>::new();
126 aligned_data.extend_from_slice(&raw_data);
127
128 Ok(aligned_data)
129}
130
131#[cfg(feature = "mmap")]
132pub fn mmap_file(filename: &Path) -> LinderaResult<Mmap> {
133 let file = File::open(filename).map_err(|err| {
134 LinderaErrorKind::Io.with_error(err).add_context(format!(
135 "Failed to open file for memory mapping: {}",
136 filename.display()
137 ))
138 })?;
139 let mmap = unsafe { Mmap::map(&file) }.map_err(|err| {
140 LinderaErrorKind::Io
141 .with_error(err)
142 .add_context(format!("Failed to memory map file: {}", filename.display()))
143 })?;
144 Ok(mmap)
145}
146
147pub fn read_file_with_encoding(filepath: &Path, encoding_name: &str) -> LinderaResult<String> {
148 let encoding = Encoding::for_label_no_replacement(encoding_name.as_bytes());
149 let encoding = encoding.ok_or_else(|| {
150 LinderaErrorKind::Decode.with_error(anyhow!("Invalid encoding: {encoding_name}"))
151 })?;
152
153 let buffer = read_file(filepath)?;
154 Ok(encoding.decode(&buffer).0.into_owned())
155}
156
157use std::sync::Arc;
158
159#[derive(Clone)]
160pub enum Data {
161 Static(&'static [u8]),
162 Vec(Vec<u8>),
163 #[cfg(feature = "mmap")]
164 Map(Arc<Mmap>),
165}
166
167impl Archive for Data {
168 type Archived = rkyv::vec::ArchivedVec<u8>;
169 type Resolver = rkyv::vec::VecResolver;
170
171 fn resolve(&self, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
172 rkyv::vec::ArchivedVec::resolve_from_slice(self.deref(), resolver, out);
173 }
174}
175
176impl<S> RkyvSerialize<S> for Data
177where
178 S: rkyv::rancor::Fallible + rkyv::ser::Writer + rkyv::ser::Allocator + ?Sized,
179{
180 fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
181 rkyv::vec::ArchivedVec::serialize_from_slice(self.deref(), serializer)
182 }
183}
184
185impl<D: rkyv::rancor::Fallible + ?Sized> RkyvDeserialize<Data, D> for rkyv::vec::ArchivedVec<u8> {
186 fn deserialize(&self, _deserializer: &mut D) -> Result<Data, D::Error> {
187 let mut vec = Vec::with_capacity(self.len());
188 vec.extend_from_slice(self.as_slice());
189 Ok(Data::Vec(vec))
190 }
191}
192
193impl Deref for Data {
194 type Target = [u8];
195 fn deref(&self) -> &Self::Target {
196 match self {
197 Data::Static(s) => s,
198 Data::Vec(v) => v,
199 #[cfg(feature = "mmap")]
200 Data::Map(m) => m,
201 }
202 }
203}
204
205impl From<&'static [u8]> for Data {
206 fn from(s: &'static [u8]) -> Self {
207 Self::Static(s)
208 }
209}
210
211impl<T: Deref<Target = [u8]>> From<&'static T> for Data {
212 fn from(t: &'static T) -> Self {
213 Self::Static(t)
214 }
215}
216
217impl From<Vec<u8>> for Data {
218 fn from(v: Vec<u8>) -> Self {
219 Self::Vec(v)
220 }
221}
222
223#[cfg(feature = "mmap")]
224impl From<Mmap> for Data {
225 fn from(m: Mmap) -> Self {
226 Self::Map(Arc::new(m))
227 }
228}
229
230impl Serialize for Data {
231 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
232 where
233 S: serde::Serializer,
234 {
235 serializer.serialize_bytes(self.deref())
236 }
237}
238
239impl<'de> Deserialize<'de> for Data {
240 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
241 where
242 D: serde::Deserializer<'de>,
243 {
244 let v = <Vec<u8> as serde::Deserialize>::deserialize(deserializer)?;
245 Ok(Data::Vec(v))
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::{detail_field_count, joined_details_at, words_idx_offset};
252
253 #[test]
256 fn detail_field_count_matches_split() {
257 for blob in [
258 &b""[..],
259 &b"a"[..],
260 &b"a\0b"[..],
261 &b"\0"[..],
262 &b"\0\0"[..],
263 &b"a\0\0b"[..],
264 &b"NOUN\0general\0*\0*\0*\0*\0base\0reading\0pron"[..],
265 ] {
266 assert_eq!(
267 detail_field_count(blob),
268 blob.split(|&b| b == 0).count(),
269 "blob {blob:?}"
270 );
271 }
272 }
273
274 #[test]
278 fn detail_field_count_is_never_zero() {
279 assert_eq!(detail_field_count(b""), 1);
280 }
281
282 #[test]
284 fn detail_field_count_ipadic_shape() {
285 let blob = b"\xe5\x90\x8d\xe8\xa9\x9e\0*\0*\0*\0*\0*\0a\0b\0c";
286 assert_eq!(detail_field_count(blob), 9);
287 }
288
289 fn packed(entries: &[&[&str]]) -> (Vec<u8>, Vec<usize>) {
293 let mut data = Vec::new();
294 let mut offsets = Vec::new();
295 for fields in entries {
296 offsets.push(data.len());
297 let joined = fields.join("\0");
298 data.extend_from_slice(&(joined.len() as u32).to_le_bytes());
299 data.extend_from_slice(joined.as_bytes());
300 }
301 (data, offsets)
302 }
303
304 #[test]
306 fn joined_details_at_reads_the_declared_length() {
307 let (data, offsets) = packed(&[&["名詞", "一般"], &["動詞", "自立", "*"]]);
308
309 assert_eq!(joined_details_at(&data, offsets[0]), Some("名詞\0一般"));
310 assert_eq!(joined_details_at(&data, offsets[1]), Some("動詞\0自立\0*"));
311 }
312
313 #[test]
317 fn joined_details_at_rejects_out_of_range_offsets_and_lengths() {
318 let (mut data, offsets) = packed(&[&["名詞", "一般"]]);
319
320 assert_eq!(joined_details_at(&data, data.len()), None);
322 assert_eq!(joined_details_at(&data, data.len() - 2), None);
323 assert_eq!(joined_details_at(&data, usize::MAX), None);
325
326 let past_end = (data.len() + 1) as u32;
328 data[offsets[0]..offsets[0] + 4].copy_from_slice(&past_end.to_le_bytes());
329 assert_eq!(joined_details_at(&data, offsets[0]), None);
330
331 data[offsets[0]..offsets[0] + 4].copy_from_slice(&u32::MAX.to_le_bytes());
333 assert_eq!(joined_details_at(&data, offsets[0]), None);
334 }
335
336 #[test]
339 fn joined_details_at_rejects_invalid_utf8() {
340 let (mut data, offsets) = packed(&[&["ok", "fields"]]);
341 let last = data.len() - 1;
342 data[last] = 0xff;
343
344 assert_eq!(joined_details_at(&data, offsets[0]), None);
345 }
346
347 #[test]
350 fn words_idx_offset_reads_and_bounds_checks() {
351 let table: Vec<u8> = [7u32, 42u32].iter().flat_map(|v| v.to_le_bytes()).collect();
352
353 assert_eq!(words_idx_offset(&table, 0), Some(7));
354 assert_eq!(words_idx_offset(&table, 1), Some(42));
355 assert_eq!(words_idx_offset(&table, 2), None);
356 assert_eq!(words_idx_offset(&table, usize::MAX), None);
358 assert_eq!(words_idx_offset(&table, usize::MAX / 4), None);
359 }
360}