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)>),
}| C | Rust | |
|---|---|---|
| enum | ||
| free | Drop | |
| JsonValue** + count | Vec | |
| JsonMember* + count | Vec<(String, JsonValue)> |
C: Data Model → Lexer → Parser → Public API
Rust: enum → &str → +Result → json_parse/json_stringify
| C | Rust | |
|---|---|---|
| enum + union | enum with data | |
| calloc/free | Box/Vec + Drop | |
| + | &str + chars() | |
| + goto | + Result + ? | |
| snprintf + strdup + malloc | format! + 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
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
// Vecimpl 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
- ****CRustAPIFFIC
- ****RustAPI + C + Rust
- ****c2rust → unsafe<5000
- ****CC ABI>10000
C vs Rust
| C | Rust | |
|---|---|---|
| ~280 | ~220 | |
| unsafe | 100% | 0 |
| free | ||
| NULL | + | |
| #[test] |
json_stringify(json_parse(input))