lil_json/
lib.rs

1#![no_std]
2
3use embedded_io::Write;
4use numtoa::base10;
5
6/// terminal (non-nested) JSON types
7#[derive(Debug,PartialEq,Eq,Clone,Copy)]
8pub enum JsonValue<'a> {
9    /// a JSON string - it will be automatically escaped
10    String(&'a str),
11    /// a JSON boolean
12    Boolean(bool),
13    /// a JSON number
14    Number(i64),
15}
16
17/// a field within a JSON object
18#[derive(Debug,PartialEq,Eq,Clone,Copy)]
19pub struct JsonField<'a,'b> {
20    pub key: &'a str,
21    pub value: JsonValue<'b>,
22}
23
24impl <'a,'b> JsonField<'a,'b> {
25    /// create a new JSON object field with the given key & value
26    pub fn new(key: &'a str, value: JsonValue<'b>) -> Self {
27        JsonField { key, value }
28    }
29    /// convenience helper to create a new JSON object string field
30    pub fn new_string(key: &'a str, value: &'b str) -> Self {
31        Self::new(key, JsonValue::String(value))
32    }
33    /// convenience helper to create a new JSON object number field
34    pub fn new_number(key: &'a str, value: i64) -> Self {
35        Self::new(key, JsonValue::Number(value))
36    }
37    /// convenience helper to create a new JSON object boolean field
38    pub fn new_boolean(key: &'a str, value: bool) -> Self {
39        Self::new(key, JsonValue::Boolean(value))
40    }
41}
42
43/// a JSON Object (rfc8259) that wraps a mutable or immutable buffer of object fields. The easiest way to use it is through the ArrayJsonObject type alias, however you can use JsonObject directly to wrap your own buffer like a heap allocated Vec
44#[derive(Debug)]
45pub struct JsonObject<Fields> {
46    fields: Fields,
47    num_fields: usize,
48}
49
50/// the various reasons parsing JSON can fail
51#[derive(Debug)]
52pub enum JsonParseFailure {
53    /// there was no error, but the data slice is incomplete
54    Incomplete,
55    /// there was no error, but there were more fields than the provided field buffer could hold
56    TooManyFields,
57    /// there was an error in the JSON structure of the data
58    InvalidStructure,
59    /// an invalid JSON string was encountered
60    InvalidStringField,
61    /// an invalid JSON number was encountered
62    InvalidNumericField,
63    /// an invalid JSON boolean was encountered
64    InvalidBooleanField,
65}
66
67/// a default JSON field with static lifetime
68pub const EMPTY_FIELD: JsonField<'static,'static> = JsonField{ key: "", value: JsonValue::Number(0)};
69
70impl <'a,'b> Default for JsonField<'a,'b> {
71    fn default() -> Self {
72        EMPTY_FIELD
73    }
74}
75
76impl<'a,T: FieldBuffer<'a> + Default + ?Sized> Default for JsonObject<T> {
77    fn default() -> Self {
78        JsonObject { fields: T::default(), num_fields: 0 }
79    }
80}
81
82/// this trait is automatically implemented for all types that implement AsRef<[JsonField<'data,'data>]>
83pub trait FieldBuffer<'data>: AsRef<[JsonField<'data,'data>]> {
84
85    /// convenience one-liner to wrap an immutable reference to a collection of JsonFields with a JsonObject
86    fn into_json_object(self) -> JsonObject<Self> where Self: Sized {
87        let len = self.as_ref().len();
88        JsonObject::wrap(self, len)
89    }
90    
91    /// convenience one-liner to wrap an immutable reference to a collection of JsonFields with a JsonObject
92    fn as_json_object(&self) -> JsonObject<&Self> {
93        JsonObject::wrap(self, self.as_ref().len())
94    }
95
96}
97/// this trait is automatically implemented for all types that implement AsMut<[JsonField<'data,'data>]>
98pub trait FieldBufferMut<'a>: FieldBuffer<'a> +  AsMut<[JsonField<'a,'a>]> {
99
100    /// convenience one-liner to wrap a mutable reference to a collection of JsonFields with a JsonObject
101    fn as_json_object_mut(&mut self) -> JsonObject<&mut Self> {
102        let len = self.as_ref().len();
103        JsonObject::wrap(self, len)
104    }
105
106}
107
108impl <'a,T: AsRef<[JsonField<'a,'a>]>> FieldBuffer<'a> for T {}
109impl <'a,T: FieldBuffer<'a> + AsMut<[JsonField<'a,'a>]>> FieldBufferMut<'a> for T {}
110
111impl <'a,T: FieldBuffer<'a>> JsonObject<T> {
112    pub const fn wrap(fields: T, num_fields: usize) -> Self {
113        JsonObject { fields, num_fields }
114    }
115
116    pub const fn len(&self) -> usize {
117        self.num_fields
118    }
119
120    pub fn capacity(&self) -> usize {
121        self.fields.as_ref().len()
122    }
123
124    pub fn fields(&self) -> &[JsonField<'a,'a>] {
125        self.fields.as_ref().split_at(self.num_fields).0
126    }
127
128    pub fn serialize<Output: Write>(&self, output: Output) -> Result<usize,Output::Error> {
129        serialize_json_object(output, self.fields().as_ref())
130    }
131}
132
133impl<'a,T: FieldBuffer<'a>> From<T> for JsonObject<T> {
134    fn from(field_buffer: T) -> Self {
135        let num_headers = field_buffer.as_ref().len();
136        JsonObject::wrap(field_buffer, num_headers)
137    }
138}
139
140impl <'a,T: FieldBufferMut<'a>> JsonObject<T> {
141
142    /// get a mutable reference to the initialized fields
143    pub fn fields_mut(&mut self) -> &mut [JsonField<'a,'a>] {
144        self.fields.as_mut().split_at_mut(self.num_fields).0
145    }
146
147    /// attempt to push a new field - returns the field if there is not enough space
148    pub fn push<'x: 'a,'y: 'a>(&mut self, field: JsonField<'x,'y>) -> Result<(),JsonField<'x,'y>> {
149        if self.num_fields == self.fields.as_ref().len(){
150            return Err(field);
151        }
152        self.fields.as_mut()[self.num_fields] = field;
153        self.num_fields += 1;
154        Ok(())
155    }
156
157    /// attempt to pop an existing field - returns None if there are no initialized fields
158    pub fn pop(&mut self) -> Option<JsonField<'a,'a>> {
159        if self.num_fields == 0 {
160            return None;
161        }
162        self.num_fields -= 1;
163        Some(core::mem::take(&mut self.fields.as_mut()[self.num_fields+1]))
164    }
165
166    /// convenience helper to create and push a new field
167    pub fn push_field<'x: 'a,'y: 'a>(&mut self, key: &'x str, value: JsonValue<'y>) -> Result<(),()> {
168        if self.num_fields == self.fields.as_ref().len(){
169            return Err(());
170        }
171        self.fields.as_mut()[self.num_fields] = JsonField { key, value };
172        self.num_fields += 1;
173        Ok(())
174    }
175
176    /// attempt to parse a JSON object from the provided data slice and write its fields into this JsonObject - returns a tuple of (num bytes consumed, num fields parsed) on success
177    pub fn parse(&mut self, data: &'a [u8]) -> Result<(usize,usize),JsonParseFailure> {
178        let (data_end, parsed_fields) = parse_json_object(data, self.fields.as_mut())?;
179        let new_num_fields = parsed_fields.len();
180        self.num_fields = new_num_fields;
181        Ok((data_end,new_num_fields))
182    }
183
184}
185
186
187/// ArrayJsonObject is a type alias for a JsonObject that wraps an array. It is has some additional functionality compared to a normal JsonObject.
188pub type ArrayJsonObject<'a,const N: usize> = JsonObject<[JsonField<'a,'a>; N]>;
189
190impl<'a,const N: usize> ArrayJsonObject<'a,N> {
191
192    /// convenience method to wrap a JsonObject over an array
193    pub const fn new() -> Self {
194        JsonObject::wrap([EMPTY_FIELD; N], 0)
195    }
196
197    /// similar to JsonObject::push but supports const contexts & only returns a reference
198    pub const fn push_const(&mut self, key: &'a str, value: JsonValue<'a>) -> Result<(),()> {
199        if self.num_fields == N {
200            return Err(());
201        }
202        self.fields[self.num_fields] = JsonField { key, value: value };
203        self.num_fields += 1;
204        Ok(())
205    }
206
207    /// similar to JsonObject::pop but supports const contexts
208    pub const fn pop_const(&mut self) -> Option<&JsonField<'a,'a>> {
209        match self.fields_const().split_last() {
210            None => return None,
211            Some((split,_remaining)) => return Some(split),
212        }
213    }
214
215    /// convenience method to automatically create an ArrayJsonObject if object parsing is successful
216    pub fn new_parsed(data: &'a [u8]) -> Result<(usize,Self),JsonParseFailure> {
217        let mut ret = Self::new();
218        let (data_end,_num_headers) = ret.parse(data)?;
219        Ok((data_end,ret))
220    }
221
222    /// same as JsonObject::fields but supports const contexts
223    pub const fn fields_const(&self) -> &[JsonField<'a,'a>] {
224        self.fields.split_at(self.num_fields).0
225    }
226
227    /// same as JsonObject::fields_mut but supports const contexts
228    pub const fn fields_mut_const(&mut self) -> &mut [JsonField<'a,'a>] {
229        self.fields.split_at_mut(self.num_fields).0
230    }
231
232}
233
234/// the core function that powers parsing in the JsonObject API. It attempts to parse the fields of a json object from the provided data slice into the provided field buffer, then return (data bytes consumed, parsed field slice) on success.
235pub fn parse_json_object<'data,'field_buffer>(data: &'data [u8], field_buffer: &'field_buffer mut [JsonField<'data,'data>]) -> Result<(usize,&'field_buffer[JsonField<'data,'data>]),JsonParseFailure> {
236    let mut current_data_index = 0;
237    let mut current_field_index = 0;
238    let mut map_entry_needs_comma = false;
239    skip_whitespace(&mut current_data_index, data)?;
240    if data[current_data_index] != b'{' {
241        return Err(JsonParseFailure::InvalidStructure);
242    }
243    let _map_start_index = current_data_index;
244    current_data_index += 1;
245    while current_data_index < data.len()  {
246        skip_whitespace(&mut current_data_index, data)?;
247        if data[current_data_index] == b'}' {
248            return Ok((current_data_index+1,field_buffer.split_at(current_field_index).0))
249        } else if map_entry_needs_comma  {
250            if data[current_data_index] != b',' {
251                return Err(JsonParseFailure::InvalidStructure);
252            }
253            current_data_index += 1;
254            map_entry_needs_comma = false;
255        } else {
256            map_entry_needs_comma = true;
257            let key_start_quote_index = current_data_index;
258            current_data_index += 1;
259            skip_json_string(&mut current_data_index, data)?;
260            let key_end_quote_index = current_data_index;
261            let string_key = core::str::from_utf8(&data[key_start_quote_index+1..key_end_quote_index]).expect("skipped json object key string");
262            current_data_index += 1;
263            skip_whitespace(&mut current_data_index, data)?;
264            if data[current_data_index] != b':' {
265                return Err(JsonParseFailure::InvalidStructure);
266            }
267            current_data_index += 1;
268            skip_whitespace(&mut current_data_index, data)?;
269
270            if data[current_data_index] == b'"' {
271                let value_start_quote_index = current_data_index;
272                current_data_index += 1;
273                skip_json_string(&mut current_data_index, data)?;
274                let value_end_quote_index = current_data_index;
275                current_data_index += 1;
276                let string_value = core::str::from_utf8(&data[value_start_quote_index+1..value_end_quote_index]).expect("skipped json object value string");
277                if current_field_index >= field_buffer.len() {
278                    return Err(JsonParseFailure::TooManyFields);
279                }
280                field_buffer[current_field_index] = JsonField::new(string_key, JsonValue::String(string_value));
281                current_field_index += 1;
282            } else if data[current_data_index] == b't' || data[current_data_index] == b'f' {
283                let expect_true = data[current_data_index] == b't';
284                skip_json_boolean(&mut current_data_index, data, expect_true)?;
285                if current_field_index >= field_buffer.len() {
286                    return Err(JsonParseFailure::TooManyFields);
287                }
288                field_buffer[current_field_index] = JsonField::new(string_key, JsonValue::Boolean(expect_true));
289                current_field_index += 1;
290            } else if (data[current_data_index] >= b'0' && data[current_data_index] < b'9') || data[current_data_index] == b'-' {
291                let numeric_start_index = current_data_index;
292                current_data_index += 1;
293                skip_json_numeric(&mut current_data_index, data)?;
294                let numeric_after_index = current_data_index;
295                let numeric_string = core::str::from_utf8(&data[numeric_start_index..numeric_after_index]).expect("skipped numeric digits");
296                let numeric_value: i64 = match numeric_string.parse() {
297                    Ok(i) => i,
298                    Err(_) => return Err(JsonParseFailure::InvalidNumericField),
299                };
300                if current_field_index >= field_buffer.len() {
301                    return Err(JsonParseFailure::TooManyFields);
302                }
303                field_buffer[current_field_index] = JsonField::new(string_key, JsonValue::Number(numeric_value));
304                current_field_index += 1;
305            } else {
306                return Err(JsonParseFailure::InvalidStructure);
307            }
308        }
309    }
310    Err(JsonParseFailure::Incomplete)
311}
312
313fn skip_json_string(index: &mut usize, data: &[u8]) -> Result<(),JsonParseFailure> {
314    let mut last_char_escape = false;
315    while *index < data.len() {
316        if data[*index] == b'\\' && !last_char_escape {
317            last_char_escape = true;
318        } else if data[*index] == b'"' && !last_char_escape {
319            return Ok(());
320        } else if !data[*index].is_ascii() {
321            return Err(JsonParseFailure::InvalidStringField);
322        } else {
323            last_char_escape = false
324        }
325        *index += 1;
326    }
327    Err(JsonParseFailure::Incomplete)
328}
329
330fn skip_json_numeric(index: &mut usize, data: &[u8]) -> Result<(),JsonParseFailure> {
331    while *index < data.len() && data[*index] <= b'9' && data[*index] >= b'0' {
332        *index += 1;
333    }
334    if *index == data.len() {
335        Err(JsonParseFailure::Incomplete)
336    } else if data[*index].is_ascii_whitespace() || data[*index] == b',' || data[*index] == b'}' {
337        Ok(())
338    } else {
339        Err(JsonParseFailure::InvalidNumericField)
340    }
341}
342
343fn skip_json_boolean(index: &mut usize, data: &[u8], value: bool) -> Result<(),JsonParseFailure> {
344    let start = *index;
345    let target = if value { "true" } else { "false" };
346    while (*index - start) < target.len() {
347        if *index >= data.len() {
348            return Err(JsonParseFailure::Incomplete)
349        }
350        if data[*index] != target.as_bytes()[*index-start] {
351            return Err(JsonParseFailure::InvalidBooleanField);
352        }
353        *index += 1;
354    }
355    Ok(())
356}
357
358fn skip_whitespace(index: &mut usize, data: &[u8]) -> Result<(),JsonParseFailure> {
359    while *index < data.len() && data[*index].is_ascii_whitespace() {
360        *index += 1;
361    }
362    if *index == data.len() {
363        Err(JsonParseFailure::Incomplete)
364    } else {
365        Ok(())
366    }
367}
368
369/// the core function that powers serialization in the JsonObject API. It attempts to serialize the provided fields as a JSON object into the provided output, & returns the number of bytes written on success.
370pub fn serialize_json_object<
371'data,
372Output: Write,
373>(
374    mut output: Output,
375    fields: &[JsonField<'data,'data>],
376) -> Result<usize, Output::Error> {
377    let mut ret = 0;
378    tracked_write(&mut output,&mut ret , "{")?;
379    let mut field_needs_comma = false;
380    for field in fields.as_ref().iter() {
381        if field_needs_comma {
382            tracked_write(&mut output,&mut ret , ",")?;
383        } else {
384            field_needs_comma = true;
385        }
386        write_escaped_json_string(&mut output, &mut ret , field.key)?;
387        tracked_write(&mut output, &mut ret , ":")?;
388        match field.value {
389            JsonValue::String(s) => {
390                write_escaped_json_string(&mut output, &mut ret , s)?;
391            },
392            JsonValue::Boolean(false) => {
393                tracked_write(&mut output,&mut ret , "false")?;
394            },
395            JsonValue::Boolean(true) => {
396                tracked_write(&mut output,&mut ret , "true")?;
397            },
398            JsonValue::Number(n) => {
399                tracked_write(&mut output,&mut ret , &base10::i64(n))?;
400            },
401        }
402    }
403    tracked_write(&mut output, &mut ret , "}")?;
404    Ok(ret)
405}
406
407
408fn tracked_write<T: Write>(mut output: T, counter: &mut usize, data: &str) -> Result<(), T::Error> {
409    output.write_all(data.as_bytes())?;
410    *counter += data.len();
411    Ok(())
412}
413
414fn write_escaped_json_string<T: Write>(mut output: T, counter: &mut usize, data: &str) -> Result<(), T::Error> {
415    tracked_write(&mut output, &mut *counter, "\"")?;
416    for field_character in data.chars() {
417        if field_character == '"' {
418            tracked_write(&mut output, &mut *counter, unsafe { core::str::from_utf8_unchecked(&[b'\\', field_character as u8]) })?;
419        } else {
420            tracked_write(&mut output, &mut *counter, unsafe { core::str::from_utf8_unchecked(&[field_character as u8]) })?;
421        }
422    }
423    tracked_write(&mut output, &mut *counter, "\"")?;
424    Ok(())
425}
426
427#[cfg(test)]
428mod tests {
429
430    use super::*;
431
432    #[test]
433    fn test_serialize_object_empty() {
434        let mut buffer = [0_u8; 1000];
435        let test_map = ArrayJsonObject::<50>::new();
436        let n = test_map.serialize(buffer.as_mut_slice()).unwrap();
437        assert_eq!(b"{}", buffer.split_at(n).0)
438    }
439
440    #[test]
441    fn test_serialize_object_simple() {
442        let mut buffer = [0_u8; 1000];
443        let mut test_map = ArrayJsonObject::<50>::new();
444        test_map.push_field("sub", JsonValue::String("1234567890")).unwrap();
445        test_map.push_field("name", JsonValue::String("John Doe")).unwrap();
446        test_map.push_field("iat", JsonValue::Number(1516239022)).unwrap();
447        test_map.push_field("something", JsonValue::Boolean(false)).unwrap();
448        let n = test_map.serialize(buffer.as_mut_slice()).unwrap();
449        assert_eq!(b"{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"iat\":1516239022,\"something\":false}", buffer.split_at(n).0)
450    }
451
452    #[test]
453    fn test_parse_object_success_empty() {
454        let (bytes_consumed,json_object) = ArrayJsonObject::<0>::new_parsed(b"{}").unwrap();
455        assert!(json_object.fields().is_empty());
456        assert_eq!(bytes_consumed, 2);
457    }
458
459    #[test]
460    fn test_parse_object_success_simple() {
461        let data = b"{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"iat\":1516239022,\"something\":false}";
462        let (data_end,json_object) = ArrayJsonObject::<50>::new_parsed(data).unwrap();
463        let test_fields = json_object.fields();
464        assert_eq!(4, test_fields.len());
465        assert_eq!(JsonField { key: "sub", value: JsonValue::String("1234567890")}, test_fields[0]);
466        assert_eq!(JsonField { key: "name", value: JsonValue::String("John Doe")}, test_fields[1]);
467        assert_eq!(JsonField { key: "iat", value: JsonValue::Number(1516239022)}, test_fields[2]);
468        assert_eq!(JsonField { key: "something", value: JsonValue::Boolean(false)}, test_fields[3]);
469    }
470
471    #[test]
472    fn test_parse_object_failure_incomplete_simple() {
473        match ArrayJsonObject::<50>::new_parsed(b"{") {
474            Err(JsonParseFailure::Incomplete) => {},
475            _ => panic!("incomplete json")
476        }
477    }
478
479    #[test]
480    fn test_parse_object_failure_incomplete_brace() {
481        match ArrayJsonObject::<50>::new_parsed(b"{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"iat\":1516239022,\"something\":false") {
482            Err(JsonParseFailure::Incomplete) => {},
483            other => panic!("{:?}", other)
484        }
485    }
486
487    #[test]
488    fn test_parse_object_failure_too_many_fields() {
489        match ArrayJsonObject::<0>::new_parsed(b"{\"some\":\"thing\"}") {
490            Err(JsonParseFailure::TooManyFields) => {},
491            other => panic!("{:?}", other)
492        }
493    }
494
495}