lil_json/
lib.rs

1#![no_std]
2
3use embedded_io::Write;
4use numtoa::base10;
5
6#[derive(Debug,PartialEq,Eq,Clone,Copy)]
7pub enum JsonValue<'a> {
8    String(&'a str),
9    Boolean(bool),
10    Number(i64),
11}
12
13#[derive(Debug,PartialEq,Eq,Clone,Copy)]
14pub struct JsonField<'a,'b> {
15    pub key: &'a str,
16    pub value: JsonValue<'b>,
17}
18
19impl <'a,'b> JsonField<'a,'b> {
20    pub fn new(key: &'a str, value: JsonValue<'b>) -> Self {
21        JsonField { key, value }
22    }
23
24    pub fn new_string(key: &'a str, value: &'b str) -> Self {
25        Self::new(key, JsonValue::String(value))
26    }
27
28    pub fn new_number(key: &'a str, value: i64) -> Self {
29        Self::new(key, JsonValue::Number(value))
30    }
31
32    pub fn new_boolean(key: &'a str, value: bool) -> Self {
33        Self::new(key, JsonValue::Boolean(value))
34    }
35}
36
37#[derive(Debug)]
38pub struct JsonObject<'a,const MAX_FIELDS: usize> {
39    fields: [JsonField<'a,'a>; MAX_FIELDS],
40    num_fields: usize,
41}
42
43#[derive(Debug)]
44pub enum JsonParseFailure {
45    Incomplete,
46    TooManyFields,
47    InvalidStructure,
48    InvalidStringField,
49    InvalidNumericField,
50    InvalidBooleanField,
51}
52
53pub const EMPTY_FIELD: JsonField<'static,'static> = JsonField{ key: "", value: JsonValue::String("")};
54
55impl <'a,'b> Default for JsonField<'a,'b> {
56    fn default() -> Self {
57        EMPTY_FIELD
58    }
59}
60
61impl<'a,const MAX_FIELDS: usize> Default for JsonObject<'a,MAX_FIELDS> {
62    fn default() -> Self {
63        Self::new()
64    }
65}
66
67impl <'a,const MAX_FIELDS: usize> JsonObject<'a,MAX_FIELDS> {
68
69    pub const fn new() -> Self {
70        JsonObject { fields: [EMPTY_FIELD; MAX_FIELDS], num_fields: 0 }
71    }
72
73    pub const fn as_slice(&self) -> &[JsonField<'a,'a>] {
74        self.fields.split_at(self.num_fields).0
75    }
76
77    pub const fn as_mut_slice(&mut self) -> &mut [JsonField<'a,'a>] {
78        self.fields.split_at_mut(self.num_fields).0
79    }
80
81    pub const fn push<'x: 'a,'y: 'a>(&mut self, field: JsonField<'x,'y>) -> Result<(),JsonField<'x,'y>> {
82        if self.num_fields == MAX_FIELDS {
83            return Err(field);
84        }
85        self.fields[self.num_fields] = field;
86        self.num_fields += 1;
87        Ok(())
88    }
89
90    pub fn pop(&mut self) -> Option<JsonField<'a,'a>> {
91        if self.num_fields == 0 {
92            return None;
93        }
94        let field =  core::mem::take(&mut self.fields[self.num_fields]);
95        self.num_fields -= 1;
96        Some(field)
97    }
98
99    pub const fn push_field(&mut self, key: &'a str, value: JsonValue<'a>) -> Result<(),()> {
100        if self.num_fields == MAX_FIELDS {
101            return Err(());
102        }
103        self.fields[self.num_fields] = JsonField { key, value: value };
104        self.num_fields += 1;
105        Ok(())
106    }
107
108    pub fn parse_from(data: &'a [u8]) -> Result<(usize,Self),JsonParseFailure> {
109        parse_json_map(data)
110    }
111
112    pub fn serialize_blocking<T: Write>(&self, output: T) -> Result<usize,T::Error> {
113        write_json_map(output, self.as_slice())
114    }
115
116}
117
118
119fn skip_json_string(index: &mut usize, data: &[u8]) -> Result<(),JsonParseFailure> {
120    let mut last_char_escape = false;
121    while *index < data.len() {
122        if data[*index] == b'\\' && !last_char_escape {
123            last_char_escape = true;
124        } else if data[*index] == b'"' && !last_char_escape {
125            return Ok(());
126        } else if !data[*index].is_ascii() {
127            return Err(JsonParseFailure::InvalidStringField);
128        } else {
129            last_char_escape = false
130        }
131        *index += 1;
132    }
133    Err(JsonParseFailure::Incomplete)
134}
135
136fn skip_json_numeric(index: &mut usize, data: &[u8]) -> Result<(),JsonParseFailure> {
137    while *index < data.len() && data[*index] <= b'9' && data[*index] >= b'0' {
138        *index += 1;
139    }
140    if *index == data.len() {
141        Err(JsonParseFailure::Incomplete)
142    } else if data[*index].is_ascii_whitespace() || data[*index] == b',' || data[*index] == b'}' {
143        Ok(())
144    } else {
145        Err(JsonParseFailure::InvalidNumericField)
146    }
147}
148
149fn skip_json_boolean(index: &mut usize, data: &[u8], value: bool) -> Result<(),JsonParseFailure> {
150    let start = *index;
151    let target = if value { "true" } else { "false" };
152    while (*index - start) < target.len() {
153        if *index >= data.len() {
154            return Err(JsonParseFailure::Incomplete)
155        }
156        if data[*index] != target.as_bytes()[*index-start] {
157            return Err(JsonParseFailure::InvalidBooleanField);
158        }
159        *index += 1;
160    }
161    Ok(())
162}
163
164fn skip_whitespace(index: &mut usize, data: &[u8]) -> Result<(),JsonParseFailure> {
165    while *index < data.len() && data[*index].is_ascii_whitespace() {
166        *index += 1;
167    }
168    if *index == data.len() {
169        Err(JsonParseFailure::Incomplete)
170    } else {
171        Ok(())
172    }
173}
174
175fn parse_json_map<'a,const MAX_FIELDS: usize>(data: &'a [u8]) -> Result<(usize,JsonObject<'a, MAX_FIELDS>),JsonParseFailure> {
176    let mut ret = JsonObject::default();
177    let mut current_index = 0;
178    let mut map_entry_needs_comma = false;
179    skip_whitespace(&mut current_index, data)?;
180    if data[current_index] != b'{' {
181        return Err(JsonParseFailure::InvalidStructure);
182    }
183    let _map_start_index = current_index;
184    current_index += 1;
185    while current_index < data.len()  {
186        skip_whitespace(&mut current_index, data)?;
187        if data[current_index] == b'}' {
188            return Ok((current_index+1,ret))
189        } else if map_entry_needs_comma  {
190            if data[current_index] != b',' {
191                return Err(JsonParseFailure::InvalidStructure);
192            }
193            current_index += 1;
194            map_entry_needs_comma = false;
195        } else {
196            map_entry_needs_comma = true;
197            let key_start_quote_index = current_index;
198            current_index += 1;
199            skip_json_string(&mut current_index, data)?;
200            let key_end_quote_index = current_index;
201            let string_key = core::str::from_utf8(&data[key_start_quote_index+1..key_end_quote_index]).expect("skipped json object key string");
202            current_index += 1;
203            skip_whitespace(&mut current_index, data)?;
204            if data[current_index] != b':' {
205                return Err(JsonParseFailure::InvalidStructure);
206            }
207            current_index += 1;
208            skip_whitespace(&mut current_index, data)?;
209
210            if data[current_index] == b'"' {
211                let value_start_quote_index = current_index;
212                current_index += 1;
213                skip_json_string(&mut current_index, data)?;
214                let value_end_quote_index = current_index;
215                current_index += 1;
216                let string_value = core::str::from_utf8(&data[value_start_quote_index+1..value_end_quote_index]).expect("skipped json object value string");
217                if let Err(()) = ret.push_field(string_key, JsonValue::String(string_value)) {
218                    return Err(JsonParseFailure::TooManyFields);
219                }
220
221            } else if data[current_index] == b't' || data[current_index] == b'f' {
222                let expect_true = data[current_index] == b't';
223                skip_json_boolean(&mut current_index, data, expect_true)?;
224                if let Err(()) = ret.push_field(string_key, JsonValue::Boolean(expect_true)) {
225                    return Err(JsonParseFailure::TooManyFields);
226                }
227            } else if (data[current_index] >= b'0' && data[current_index] < b'9') || data[current_index] == b'-' {
228                let numeric_start_index = current_index;
229                current_index += 1;
230                skip_json_numeric(&mut current_index, data)?;
231                let numeric_after_index = current_index;
232                let numeric_string = core::str::from_utf8(&data[numeric_start_index..numeric_after_index]).expect("skipped numeric digits");
233                let numeric_value: i64 = match numeric_string.parse() {
234                    Ok(i) => i,
235                    Err(_) => return Err(JsonParseFailure::InvalidNumericField),
236                };
237                if let Err(()) = ret.push_field(string_key, JsonValue::Number(numeric_value)) {
238                    return Err(JsonParseFailure::TooManyFields);
239                }
240            } else {
241                return Err(JsonParseFailure::InvalidStructure);
242            }
243        }
244    }
245    Err(JsonParseFailure::Incomplete)
246}
247
248fn tracked_write<T: Write>(mut output: T, counter: &mut usize, data: &str) -> Result<(), T::Error> {
249    output.write_all(data.as_bytes())?;
250    *counter += data.len();
251    Ok(())
252}
253
254fn write_escaped_json_string<T: Write>(mut output: T, counter: &mut usize, data: &str) -> Result<(), T::Error> {
255    tracked_write(&mut output, &mut *counter, "\"")?;
256    for field_character in data.chars() {
257        if field_character == '"' {
258            tracked_write(&mut output, &mut *counter, unsafe { core::str::from_utf8_unchecked(&[b'\\', field_character as u8]) })?;
259        } else {
260            tracked_write(&mut output, &mut *counter, unsafe { core::str::from_utf8_unchecked(&[field_character as u8]) })?;
261        }
262    }
263    tracked_write(&mut output, &mut *counter, "\"")?;
264    Ok(())
265}
266
267pub(crate) fn write_json_map<T: Write>(mut output: T, fields: &[JsonField]) -> Result<usize, T::Error> {
268    let mut ret = 0;
269    tracked_write(&mut output,&mut ret , "{")?;
270    let mut field_needs_comma = false;
271    for field in fields.iter() {
272        if field_needs_comma {
273            tracked_write(&mut output,&mut ret , ",")?;
274        } else {
275            field_needs_comma = true;
276        }
277        write_escaped_json_string(&mut output, &mut ret , field.key)?;
278        tracked_write(&mut output, &mut ret , ":")?;
279        match field.value {
280            JsonValue::String(s) => {
281                write_escaped_json_string(&mut output, &mut ret , s)?;
282            },
283            JsonValue::Boolean(false) => {
284                tracked_write(&mut output,&mut ret , "false")?;
285            },
286            JsonValue::Boolean(true) => {
287                tracked_write(&mut output,&mut ret , "true")?;
288            },
289            JsonValue::Number(n) => {
290                tracked_write(&mut output,&mut ret , &base10::i64(n))?;
291            },
292        }
293    }
294    tracked_write(&mut output, &mut ret , "}")?;
295    Ok(ret)
296}
297
298
299#[cfg(test)]
300mod tests {
301    use super::*;
302
303    #[test]
304    fn test_serialize_object_empty() {
305        let mut buffer = [0_u8; 1000];
306        let test_map = JsonObject::<0>::default();
307        let n = test_map.serialize_blocking(buffer.as_mut_slice()).unwrap();
308        assert_eq!(b"{}", buffer.split_at(n).0)
309    }
310
311    #[test]
312    fn test_serialize_object_simple() {
313        let mut buffer = [0_u8; 1000];
314        let mut test_map = JsonObject::<50>::default();
315        test_map.push_field("sub", JsonValue::String("1234567890")).unwrap();
316        test_map.push_field("name", JsonValue::String("John Doe")).unwrap();
317        test_map.push_field("iat", JsonValue::Number(1516239022)).unwrap();
318        test_map.push_field("something", JsonValue::Boolean(false)).unwrap();
319        let n = test_map.serialize_blocking(buffer.as_mut_slice()).unwrap();
320        assert_eq!(b"{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"iat\":1516239022,\"something\":false}", buffer.split_at(n).0)
321    }
322
323    #[test]
324    fn test_parse_object_success_empty() {
325        let data = b"{}";
326        let (n,test_map) = JsonObject::<0>::parse_from(data).unwrap();
327        assert_eq!(data.len(),n);
328        assert!(test_map.as_slice().is_empty());
329    }
330
331    #[test]
332    fn test_parse_object_success_simple() {
333        let data = b"{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"iat\":1516239022,\"something\":false}";
334        let (n,test_map) = JsonObject::<50>::parse_from(data).unwrap();
335        assert_eq!(data.len(),n);
336        let test_fields = test_map.as_slice();
337        assert_eq!(4, test_fields.len());
338        assert_eq!(JsonField { key: "sub", value: JsonValue::String("1234567890")}, test_fields[0]);
339        assert_eq!(JsonField { key: "name", value: JsonValue::String("John Doe")}, test_fields[1]);
340        assert_eq!(JsonField { key: "iat", value: JsonValue::Number(1516239022)}, test_fields[2]);
341        assert_eq!(JsonField { key: "something", value: JsonValue::Boolean(false)}, test_fields[3]);
342    }
343
344    #[test]
345    fn test_parse_object_failure_incomplete_simple() {
346        match JsonObject::<50>::parse_from(b"{") {
347            Err(JsonParseFailure::Incomplete) => {},
348            other => panic!("{:?}", other)
349        }
350    }
351
352    #[test]
353    fn test_parse_object_failure_incomplete_brace() {
354        match JsonObject::<50>::parse_from(b"{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"iat\":1516239022,\"something\":false") {
355            Err(JsonParseFailure::Incomplete) => {},
356            other => panic!("{:?}", other)
357        }
358    }
359
360        #[test]
361    fn test_parse_object_failure_too_many_fields() {
362        match JsonObject::<0>::parse_from(b"{\"some\":\"thing\"}") {
363            Err(JsonParseFailure::TooManyFields) => {},
364            other => panic!("{:?}", other)
365        }
366    }
367
368}