mmkv_parser/
mmkv.rs

1use crate::Error;
2use std::collections::HashMap;
3use byteorder::{ByteOrder, LE};
4
5/// Parse u64 from a given byte-slice.
6/// Return `(buffer_rest, result)`.
7pub fn read_u64(buffer: &[u8]) -> Result<(&[u8], u64), Error> {
8    let mut result = 0;
9    let mut shift = 0;
10
11    for (i, &byte) in buffer.iter().enumerate() {
12        result |= (u64::from(byte) & 0x7f) << shift;
13        shift += 7;
14
15        if byte & 0x80 == 0 {
16            return Ok((&buffer[i + 1..], result));
17        }
18    }
19
20    Err(Error::UnexpectedEof)
21}
22
23pub fn read_container(buffer: &[u8]) -> Result<(&[u8], &[u8]), Error> {
24    let (buffer, len) = read_u64(buffer)?;
25    let (result, buffer) = buffer.split_at(len as usize);
26    Ok((buffer, result))
27}
28
29pub fn read_string(buffer: &[u8]) -> Result<String, Error> {
30    let (_, result) = read_container(buffer)?;
31    Ok(String::from_utf8_lossy(result).into())
32}
33
34pub enum ParseControl {
35    Continue,
36    Stop,
37}
38
39/// Callback style parser.
40#[allow(clippy::needless_lifetimes)]
41pub fn parse_callback<'a, F>(buffer: &'a [u8], mut callback: F) -> Result<(), Error>
42where
43    F: FnMut(&'a [u8], &'a [u8]) -> ParseControl,
44{
45    // Skip the first 4 bytes - that's the size of the payload.
46    let mmkv_len = LE::read_u32(&buffer) as usize;
47
48    if buffer.len() < mmkv_len + 4 {
49        Err(Error::BufferTooSmall(mmkv_len + 4))?;
50    }
51
52    // Skip the first integer - no idea what this is, probably not used anyway...
53    let (next, _) = read_u64(&buffer[4..4 + mmkv_len])?;
54    let mut buffer = next;
55
56    while !buffer.is_empty() {
57        let (next, key) = read_container(buffer)?;
58        let (next, value) = read_container(next)?;
59        buffer = next;
60
61        match callback(key, value) {
62            ParseControl::Continue => continue,
63            ParseControl::Stop => break,
64        }
65    }
66
67    Ok(())
68}
69
70/// Parse a non-encrypted mmkv file to key-value pairs.
71pub fn parse(buffer: &[u8]) -> Result<HashMap<&[u8], &[u8]>, Error> {
72    let mut result = HashMap::new();
73    parse_callback(buffer, |k, v| {
74        result.insert(k, v);
75        ParseControl::Continue
76    })?;
77    Ok(result)
78}
79
80/// Parse a non-encrypted mmkv file to `HashMap<String, String>`.
81/// If the store may contain non-string values, use `parse` instead.
82pub fn parse_string_key_value_pairs(buffer: &[u8]) -> Result<HashMap<String, String>, Error> {
83    let mut result = HashMap::new();
84    let mut parse_err = Ok(());
85    parse_callback(buffer, |k, v| match read_string(v) {
86        Ok(value) => {
87            let k = String::from_utf8_lossy(k);
88            result.insert(k.into(), value);
89            ParseControl::Continue
90        }
91        Err(err) => {
92            parse_err = Err(err);
93            ParseControl::Stop
94        }
95    })
96    .and(parse_err)?;
97    Ok(result)
98}
99
100#[cfg(test)]
101mod test {
102    use std::collections::HashMap;
103
104    use super::*;
105
106    #[test]
107    fn test_read_u64() {
108        let buffer = [0xff, 0x81, 0x01, 0x00];
109        let value = read_u64(&buffer);
110        assert_eq!(value, Ok((&buffer[3..], 16639)));
111
112        let buffer = [0x81, 0xAA];
113        let value = read_u64(&buffer);
114        assert_eq!(value, Err(Error::UnexpectedEof));
115
116        let buffer = [0x80, 0x00, 0xff];
117        let value = read_u64(&buffer);
118        assert_eq!(value, Ok((&buffer[2..], 0)));
119    }
120
121    #[test]
122    fn test_read_container() {
123        let buffer = [0x03, b'A', b'B', b'C', 0];
124        let value = read_container(&buffer);
125        assert_eq!(value, Ok((&buffer[4..], &b"ABC"[..])));
126
127        let buffer = [0x00, b'A', b'B', b'C', 0];
128        let value = read_container(&buffer);
129        assert_eq!(value, Ok((&buffer[1..], &b""[..])));
130    }
131
132    #[test]
133    fn test_parse() {
134        let buffer = [
135            19, 0, 0, 0, // size of payload
136            0xff,0xff,0xff,0x07,
137            0x03, b'A', b'B', b'C', 0, //
138            0x03, b'D', b'E', b'F', //
139            0x05, 0x04, b'1', b'2', b'3', b'4',
140        ];
141        let value = parse(&buffer);
142        let mut map = HashMap::new();
143        map.insert(&b"ABC"[..], &b""[..]);
144        map.insert(&b"DEF"[..], &b"\x041234"[..]);
145        assert_eq!(value, Ok(map));
146    }
147
148    #[test]
149    fn test_parse_buffer_len() {
150        let buffer = [
151            20, 0, 0, 0, // size of payload
152        ];
153        let value = parse(&buffer);
154        assert_eq!(value, Err(Error::BufferTooSmall(24)));
155    }
156
157    #[test]
158    fn test_parse_empty() {
159        let buffer = [
160            1, 0, 0, 0, // size of payload
161            0, // padding
162            0xff, // should be ignored
163        ];
164        let value = parse(&buffer);
165        assert_eq!(value, Ok(HashMap::new()));
166    }
167}