Skip to main content

kiri_engine/dictionary/
header.rs

1//! Dictionary header reader.
2//! Binary layout (272 bytes, little-endian):
3//!   [0..8)   i64  version
4//!   [8..16)  i64  createTime
5//!   [16..272) UTF-8 description (null-terminated, 256-byte buffer)
6
7use crate::types::{DictionaryHeader, DICTIONARY_HEADER_SIZE};
8
9const DESCRIPTION_BUFFER_SIZE: usize = 256;
10
11/// Parse a dictionary header from a byte slice at the given offset.
12pub fn read_dictionary_header(data: &[u8], offset: usize) -> Result<DictionaryHeader, String> {
13    if data.len() < offset + DICTIONARY_HEADER_SIZE {
14        return Err("Data too short for dictionary header".into());
15    }
16
17    let version = i64::from_le_bytes(
18        data[offset..offset + 8]
19            .try_into()
20            .map_err(|_| "Failed to read version")?,
21    );
22    let create_time = i64::from_le_bytes(
23        data[offset + 8..offset + 16]
24            .try_into()
25            .map_err(|_| "Failed to read createTime")?,
26    );
27
28    // Description: null-terminated UTF-8 in a 256-byte buffer
29    let desc_bytes = &data[offset + 16..offset + 16 + DESCRIPTION_BUFFER_SIZE];
30    let null_pos = desc_bytes
31        .iter()
32        .position(|&b| b == 0)
33        .unwrap_or(DESCRIPTION_BUFFER_SIZE);
34    let description = String::from_utf8_lossy(&desc_bytes[..null_pos]).into_owned();
35
36    Ok(DictionaryHeader {
37        version,
38        create_time,
39        description,
40    })
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46    use crate::types::dictionary_version;
47
48    #[test]
49    fn test_read_header() {
50        let mut buf = vec![0u8; DICTIONARY_HEADER_SIZE];
51
52        // Write version
53        let version = dictionary_version::SYSTEM_DICT_VERSION_2;
54        buf[0..8].copy_from_slice(&version.to_le_bytes());
55
56        // Write createTime
57        let create_time: i64 = 1234567890;
58        buf[8..16].copy_from_slice(&create_time.to_le_bytes());
59
60        // Write description
61        let desc = b"test dictionary";
62        buf[16..16 + desc.len()].copy_from_slice(desc);
63
64        let header = read_dictionary_header(&buf, 0).unwrap();
65        assert_eq!(header.version, version);
66        assert_eq!(header.create_time, create_time);
67        assert_eq!(header.description, "test dictionary");
68    }
69
70    #[test]
71    fn test_read_header_too_short() {
72        let buf = vec![0u8; 100];
73        assert!(read_dictionary_header(&buf, 0).is_err());
74    }
75}