lil_json/
lib.rs

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