C

MiniJSON

JSONC280Rust220

C

typedef enum { JSON_NULL, JSON_BOOL, JSON_NUMBER, JSON_STRING, JSON_ARRAY, JSON_OBJECT } JsonType;
typedef struct JsonValue {
 JsonType type;
 union {
 int boolean; double number; char* string;
 struct { JsonValue** items; size_t count; } array;
 struct { JsonMember* members; size_t count; } object;
 } data;
} JsonValue;

typedataunionUBchar*

Rust

#[derive(Debug, Clone, PartialEq)]
pub enum JsonValue {
 Null, Bool(bool), Number(f64),
 String(String), Array(Vec<JsonValue>), Object(Vec<(String, JsonValue)>),
}
CRust
enum
freeDrop
JsonValue** + countVec
JsonMember* + countVec<(String, JsonValue)>

C: Data Model → Lexer → Parser → Public API
Rust: enum → &str → +Result → json_parse/json_stringify
CRust
enum + unionenum with data
calloc/freeBox/Vec + Drop
+&str + chars()
+ goto+ Result + ?
snprintf + strdup + mallocformat! + String

#[derive(Debug)]
pub enum ParseError {
 UnexpectedEof, UnexpectedChar { pos: usize, expected: String, got: char },
 InvalidNumber { pos: usize }, InvalidEscape { pos: usize, char: char },
 UnclosedString, TrailingData(usize),
}

CNULLRust

C

typedef struct { const char* input; size_t pos, len; } Lexer;
static char lexer_peek(const Lexer* lex) {
 return (lex->pos < lex->len) ? lex->input[lex->pos] : '\0';
}

Rust

struct Lexer<'a> { input: &'a str, pos: usize }
impl<'a> Lexer<'a> {
 fn peek(&self) -> Option<char> { self.input[self.pos..].chars().next() }
 fn next(&mut self) -> Option<char> {
 let c = self.peek()?;
 self.pos += c.len_utf8();
 Some(c)
 }
}

&strpeekOption’\0’UTF-8

API

pub fn json_parse(input: &str) -> Result<JsonValue, ParseError> {
 let mut lexer = Lexer::new(input);
 let value = parse_value(&mut lexer)?;
 lexer.skip_whitespace();
 if !lexer.is_done() { return Err(ParseError::TrailingData(lexer.pos())); }
 Ok(value)
}
 
pub fn json_stringify(value: &JsonValue) -> String {
 match value {
 JsonValue::Null => "null".to_string(),
 JsonValue::Bool(b) => b.to_string(),
 JsonValue::Number(n) => format!("{}", n),
 JsonValue::String(s) => stringify_string(s),
 JsonValue::Array(arr) => {
 let items: Vec<_> = arr.iter().map(json_stringify).collect();
 format!("[{}]", items.join(","))
 }
 JsonValue::Object(obj) => {
 let items: Vec<_> = obj.iter()
 .map(|(k, v)| format!("{}:{}", stringify_string(k), json_stringify(v)))
 .collect();
 format!("{{{}}}", items.join(","))
 }
 }
}

json_free——Drop traitJSON

fn parse_value(lexer: &mut Lexer) -> Result<JsonValue, ParseError> {
 lexer.skip_whitespace();
 match lexer.peek() {
 None => Err(UnexpectedEof),
 Some('"') => parse_string(lexer),
 Some('[') => parse_array(lexer),
 Some('{') => parse_object(lexer),
 Some(c) if c == '-' || c.is_ascii_digit() => parse_number(lexer),
 Some('t') | Some('f') | Some('n') => parse_keyword(lexer),
 Some(c) => Err(UnexpectedChar { pos: lexer.pos(), expected: "value".into(), got: c }),
 }
}

parse_object

Creallocgotokey_valvalmembers15Rust

members.push((key, value)); // VecVec
// Vec

impl JsonValue {
 pub fn index(&self, idx: usize) -> Option<&JsonValue> {
 match self { JsonValue::Array(arr) => arr.get(idx), _ => None }
 }
 pub fn key(&self, k: &str) -> Option<&JsonValue> {
 match self { JsonValue::Object(m) => m.iter().find(|(key,_)| key==k).map(|(_,v)| v), _ => None }
 }
 pub fn as_str(&self) -> Option<&str> {
 match self { JsonValue::String(s) => Some(s.as_str()), _ => None }
 }
 pub fn as_f64(&self) -> Option<f64> {
 match self { JsonValue::Number(n) => Some(*n), _ => None }
 }
}

C v && v->type == JSON_XXXRustmatch

  1. ****CRustAPIFFIC
  2. ****RustAPI + C + Rust
  3. ****c2rust → unsafe<5000
  4. ****CC ABI>10000

C vs Rust

CRust
~280~220
unsafe100%0
free
NULL+
#[test]

json_stringify(json_parse(input))