1use crate::core::*;
2use crate::core::to_sml;
3use crate::value::*;
4use std::collections::BTreeMap;
5use std::os::raw::{c_char, c_int};
10use std::ptr;
11
12fn cstr(s: &str) -> *mut c_char {
13 let c = std::ffi::CString::new(s).unwrap_or_default();
14 c.into_raw()
15}
16
17#[cfg_attr(edge2024, unsafe(no_mangle))]
19#[cfg_attr(not(edge2024), no_mangle)]
20pub extern "C" fn sml_parse(text: *const c_char) -> *mut c_char {
21 if text.is_null() {
22 return ptr::null_mut();
23 }
24 let t = unsafe { std::ffi::CStr::from_ptr(text) }.to_string_lossy().into_owned();
25 match parse(&t) {
26 Ok(v) => cstr(&jsonify(&v)),
27 Err(_) => ptr::null_mut(),
28 }
29}
30
31#[cfg_attr(edge2024, unsafe(no_mangle))]
33#[cfg_attr(not(edge2024), no_mangle)]
34pub extern "C" fn sml_dump(json: *const c_char) -> *mut c_char {
35 if json.is_null() {
36 return ptr::null_mut();
37 }
38 let j = unsafe { std::ffi::CStr::from_ptr(json) }.to_string_lossy().into_owned();
39 match json_to_value(&j) {
40 Some(v) => cstr(&to_sml(&v)),
41 None => ptr::null_mut(),
42 }
43}
44
45#[cfg_attr(edge2024, unsafe(no_mangle))]
51#[cfg_attr(not(edge2024), no_mangle)]
52pub unsafe extern "C" fn sml_free_str(p: *mut c_char) {
53 if !p.is_null() {
54 drop(unsafe { std::ffi::CString::from_raw(p) });
55 }
56}
57
58#[cfg_attr(edge2024, unsafe(no_mangle))]
64#[cfg_attr(not(edge2024), no_mangle)]
65pub extern "C" fn sml_version() -> *const c_char {
66 concat!("sml ", env!("CARGO_PKG_VERSION"), "\0").as_ptr() as *const c_char
67}
68
69fn parse_opts_json(opts: &str) -> Result<(Vec<Feature>, Vec<(String, String)>, Vec<Version>), String> {
83 let mut features: Vec<Feature> = Vec::new();
84 let mut env: Vec<(String, String)> = Vec::new();
85 let mut allow: Vec<Version> = Vec::new();
86 if opts.trim().is_empty() {
87 return Ok((features, env, allow));
88 }
89 let b = opts.as_bytes();
91 let mut i = 0usize;
92 let len = b.len();
93 while i < len && b[i] != b'{' { i += 1; }
95 if i >= len { return Err("opts 不是 JSON object".into()); }
96 i += 1; loop {
98 while i < len && (b[i] == b' ' || b[i] == b'\t' || b[i] == b'\n' || b[i] == b'\r' || b[i] == b',') { i += 1; }
100 if i >= len || b[i] == b'}' { break; }
101 if b[i] != b'"' { return Err("opts key 须为字符串".into()); }
103 i += 1;
104 let ks = i;
105 while i < len && b[i] != b'"' { i += 1; }
106 let key = std::str::from_utf8(&b[ks..i]).map_err(|_| "opts key 非法 UTF-8".to_string())?.to_string();
107 i += 1; while i < len && (b[i] == b' ' || b[i] == b':' || b[i] == b'\t') { i += 1; }
109 match key.as_str() {
110 "features" | "allow" => {
111 if i >= len || b[i] != b'[' { return Err(format!("opts.{key} 须为数组")); }
113 i += 1;
114 loop {
115 while i < len && (b[i] == b' ' || b[i] == b'\t' || b[i] == b'\n' || b[i] == b'\r' || b[i] == b',') { i += 1; }
116 if i < len && b[i] == b']' { i += 1; break; }
117 if i >= len || b[i] != b'"' { return Err(format!("opts.{key} 元素须为字符串")); }
118 i += 1;
119 let vs = i;
120 while i < len && b[i] != b'"' { i += 1; }
121 let val = std::str::from_utf8(&b[vs..i]).map_err(|_| "opts 值非法 UTF-8".to_string())?.to_string();
122 i += 1;
123 if key == "features" {
124 features.push(Feature::from_name(&val).ok_or_else(|| format!("未知特性 {val}"))?);
125 } else {
126 allow.push(Version::from_word(&val).ok_or_else(|| format!("未知版本 {val}"))?);
127 }
128 }
129 }
130 "env" => {
131 if i >= len || b[i] != b'{' { return Err("opts.env 须为 object".into()); }
132 i += 1;
133 loop {
134 while i < len && (b[i] == b' ' || b[i] == b'\t' || b[i] == b'\n' || b[i] == b'\r' || b[i] == b',') { i += 1; }
135 if i < len && b[i] == b'}' { i += 1; break; }
136 if i >= len || b[i] != b'"' { return Err("opts.env key 须为字符串".into()); }
137 i += 1;
138 let ks = i;
139 while i < len && b[i] != b'"' { i += 1; }
140 let ek = std::str::from_utf8(&b[ks..i]).map_err(|_| "opts.env key 非法".to_string())?.to_string();
141 i += 1;
142 while i < len && (b[i] == b' ' || b[i] == b':' || b[i] == b'\t') { i += 1; }
143 if i >= len || b[i] != b'"' { return Err("opts.env value 须为字符串".into()); }
144 i += 1;
145 let vs = i;
146 while i < len && b[i] != b'"' { i += 1; }
147 let ev = std::str::from_utf8(&b[vs..i]).map_err(|_| "opts.env value 非法".to_string())?.to_string();
148 i += 1;
149 env.push((ek, ev));
150 }
151 }
152 _ => {
153 let mut depth = 0i32;
155 loop {
156 if i >= len { break; }
157 match b[i] {
158 b'"' => { i += 1; while i < len && b[i] != b'"' { if b[i] == b'\\' { i += 2; } else { i += 1; } } i += 1; }
159 b'{' | b'[' => { depth += 1; i += 1; }
160 b'}' | b']' => { depth -= 1; i += 1; if depth <= 0 { break; } }
161 _ => { i += 1; }
162 }
163 }
164 }
165 }
166 }
167 Ok((features, env, allow))
168}
169
170#[allow(unused_unsafe)]
181#[cfg_attr(edge2024, unsafe(no_mangle))]
182#[cfg_attr(not(edge2024), no_mangle)]
183pub extern "C" fn sml_parse_ex(text: *const c_char, opts: *const c_char) -> *mut c_char {
184 if text.is_null() {
185 return ptr::null_mut();
186 }
187 let t = unsafe { std::ffi::CStr::from_ptr(text) }.to_string_lossy().into_owned();
188 let opts_str = if opts.is_null() {
189 String::new()
190 } else {
191 unsafe { std::ffi::CStr::from_ptr(opts) }.to_string_lossy().into_owned()
192 };
193 let (feats, env, allow) = match parse_opts_json(&opts_str) {
194 Ok(x) => x,
195 Err(_) => return ptr::null_mut(),
196 };
197 let prev: Vec<(String, Option<String>)> = env
199 .iter()
200 .map(|(k, _)| (k.clone(), std::env::var(k).ok()))
201 .collect();
202 for (k, v) in &env {
203 unsafe { std::env::set_var(k, v) };
204 }
205 let result = (|| {
206 let mut allowed = FeatureSet::all();
208 for f in &feats {
209 allowed = allowed.with(*f);
210 }
211 let val = parse_with_features(&t, allowed).map(|(v, _)| v)?;
212 if !allow.is_empty() {
213 let declared = strip_version(&t).ok().and_then(|(_, d)| d);
214 if let Some(d) = declared {
215 if !allow.contains(&d) {
216 return Err(format!("文档声明版本 {} 不在 allow 范围", d.name()));
217 }
218 }
219 }
220 Ok(jsonify(&val))
221 })();
222 for (k, v) in &prev {
224 match v {
225 Some(old) => unsafe { std::env::set_var(k, old) },
226 None => unsafe { std::env::remove_var(k) },
227 }
228 }
229 match result {
230 Ok(s) => cstr(&s),
231 Err(_) => ptr::null_mut(),
232 }
233}
234
235#[cfg_attr(edge2024, unsafe(no_mangle))]
238#[cfg_attr(not(edge2024), no_mangle)]
239pub extern "C" fn sml_parse_file(path: *const c_char) -> *mut c_char {
240 if path.is_null() {
241 return ptr::null_mut();
242 }
243 let p = unsafe { std::ffi::CStr::from_ptr(path) }.to_string_lossy().into_owned();
244 match parse_file(&p) {
245 Ok(v) => cstr(&jsonify(&v)),
246 Err(_) => ptr::null_mut(),
247 }
248}
249
250#[cfg_attr(edge2024, unsafe(no_mangle))]
253#[cfg_attr(not(edge2024), no_mangle)]
254pub extern "C" fn sml_features() -> *mut c_char {
255 let names: Vec<&str> = FEATURES.iter().map(|(n, _)| *n).collect();
256 let body = names
257 .iter()
258 .map(|n| format!("\"{}\"", n))
259 .collect::<Vec<_>>()
260 .join(",");
261 cstr(&format!("[{}]", body))
262}
263
264use std::os::raw::{c_uint, c_ulonglong};
280
281#[repr(C)]
283#[derive(Clone, Copy)]
284pub enum CSmlErrc {
285 Ok = 0,
286 Syntax = 1,
287 FeatureDisabled = 2,
288 VersionMismatch = 3,
289 Contract = 4,
290 IncludeLoop = 5,
291 Io = 6,
292 Utf8 = 7,
293 Internal = 8,
294}
295
296#[repr(C)]
300pub struct CSmlError {
301 pub code: c_int,
302 pub line: c_int,
303 pub column: c_int,
304 pub position: usize,
305 pub source: [c_char; 128],
306 pub text: [c_char; 256],
307}
308
309impl CSmlError {
310 unsafe fn fill(out: *mut CSmlError, code: CSmlErrc, msg: &str, source: &str) {
315 if out.is_null() {
316 return;
317 }
318 let e = &mut *out;
319 e.code = code as c_int;
320 e.line = 0;
321 e.column = 0;
322 e.position = 0;
323 e.source = [0; 128];
324 e.text = [0; 256];
325 copy_cstr(&mut e.source, source);
326 copy_cstr(&mut e.text, msg);
327
328 if let Some(l) = extract_line(msg) {
330 e.line = l;
331 }
332 }
333}
334
335fn copy_cstr(dst: &mut [c_char], s: &str) {
337 if dst.is_empty() {
338 return;
339 }
340 let bytes = s.as_bytes();
341 let n = bytes.len().min(dst.len() - 1);
342 for i in 0..n {
343 dst[i] = bytes[i] as c_char;
344 }
345 dst[n] = 0;
346}
347
348fn extract_line(msg: &str) -> Option<c_int> {
350 for pat in ["第 ", "line "] {
351 if let Some(idx) = msg.find(pat) {
352 let rest = &msg[idx + pat.len()..];
353 let digits: String = rest.chars().take_while(|c| c.is_ascii_digit()).collect();
354 if let Ok(n) = digits.parse::<i32>() {
355 if n > 0 {
356 return Some(n);
357 }
358 }
359 }
360 }
361 None
362}
363
364#[repr(transparent)]
367pub struct CSmlValue(Value);
368
369fn classify(err: &str) -> CSmlErrc {
371 if err.contains("include") && (err.contains("循环") || err.contains("loop")) {
372 CSmlErrc::IncludeLoop
373 } else if err.contains("特性") || err.contains("feature") {
374 CSmlErrc::FeatureDisabled
375 } else if err.contains("版本") || err.contains("version") {
376 CSmlErrc::VersionMismatch
377 } else if err.contains("契约") || err.contains("contract") {
378 CSmlErrc::Contract
379 } else if err.contains("读取失败") || err.contains("IO") {
380 CSmlErrc::Io
381 } else {
382 CSmlErrc::Syntax
383 }
384}
385
386fn feature_set_from_flags(flags: c_uint) -> FeatureSet {
391 if flags == 0 {
392 return FeatureSet::baseline();
393 }
394 let mut s = FeatureSet::none();
395 for (i, (_, f)) in FEATURES.iter().enumerate() {
396 if i >= 32 {
397 break;
398 }
399 if flags & (1u32 << i) != 0 {
400 s = s.with(*f);
401 }
402 }
403 s
404}
405
406#[cfg_attr(edge2024, unsafe(no_mangle))]
411#[cfg_attr(not(edge2024), no_mangle)]
412pub unsafe extern "C" fn sml_loads(
413 text: *const c_char,
414 flags: c_uint,
415 err: *mut CSmlError,
416) -> *mut CSmlValue {
417 if text.is_null() {
418 CSmlError::fill(err, CSmlErrc::Internal, "sml_loads: text is NULL", "<string>");
419 return ptr::null_mut();
420 }
421 let t = std::ffi::CStr::from_ptr(text).to_string_lossy().into_owned();
422 let allowed = feature_set_from_flags(flags);
423 match parse_with_features(&t, allowed) {
424 Ok((v, _)) => Box::into_raw(Box::new(CSmlValue(v))),
425 Err(e) => {
426 CSmlError::fill(err, classify(&e), &e, "<string>");
427 ptr::null_mut()
428 }
429 }
430}
431
432#[cfg_attr(edge2024, unsafe(no_mangle))]
437#[cfg_attr(not(edge2024), no_mangle)]
438pub unsafe extern "C" fn sml_load_file(
439 path: *const c_char,
440 flags: c_uint,
441 err: *mut CSmlError,
442) -> *mut CSmlValue {
443 if path.is_null() {
444 CSmlError::fill(err, CSmlErrc::Internal, "sml_load_file: path is NULL", "<file>");
445 return ptr::null_mut();
446 }
447 let p = std::ffi::CStr::from_ptr(path).to_string_lossy().into_owned();
448 let _ = flags; match parse_file(&p) {
450 Ok(v) => Box::into_raw(Box::new(CSmlValue(v))),
451 Err(e) => {
452 CSmlError::fill(err, classify(&e), &e, &p);
453 ptr::null_mut()
454 }
455 }
456}
457
458#[cfg_attr(edge2024, unsafe(no_mangle))]
463#[cfg_attr(not(edge2024), no_mangle)]
464pub unsafe extern "C" fn sml_free(v: *mut CSmlValue) {
465 if !v.is_null() {
466 drop(Box::from_raw(v));
467 }
468}
469
470#[cfg_attr(edge2024, unsafe(no_mangle))]
472#[cfg_attr(not(edge2024), no_mangle)]
473pub unsafe extern "C" fn sml_typeof(v: *const CSmlValue) -> c_int {
474 if v.is_null() {
475 return -1;
476 }
477 let inner = &(*(v as *const Value));
478 match inner {
479 Value::Null => 0,
480 Value::Bool(_) => 1,
481 Value::Int(_) => 2,
482 Value::Float(_) => 3,
483 Value::Str(_) => 4,
484 Value::Array(_) => 5,
485 Value::Object(_) => 6,
486 }
487}
488
489#[cfg_attr(edge2024, unsafe(no_mangle))]
491#[cfg_attr(not(edge2024), no_mangle)]
492pub unsafe extern "C" fn sml_get(
493 v: *const CSmlValue,
494 key: *const c_char,
495) -> *const CSmlValue {
496 if v.is_null() || key.is_null() {
497 return ptr::null();
498 }
499 let inner = &(*(v as *const Value));
500 let k = std::ffi::CStr::from_ptr(key).to_string_lossy();
501 match inner {
502 Value::Object(m) => m
503 .get(k.as_ref())
504 .map(|x| x as *const Value as *const CSmlValue)
505 .unwrap_or(ptr::null()),
506 _ => ptr::null(),
507 }
508}
509
510#[cfg_attr(edge2024, unsafe(no_mangle))]
512#[cfg_attr(not(edge2024), no_mangle)]
513pub unsafe extern "C" fn sml_get_path(
514 v: *const CSmlValue,
515 path: *const c_char,
516) -> *const CSmlValue {
517 if v.is_null() || path.is_null() {
518 return ptr::null();
519 }
520 let p = std::ffi::CStr::from_ptr(path).to_string_lossy();
521 let mut cur: *const CSmlValue = v;
522 for seg in p.split('.') {
523 if seg.is_empty() {
524 continue;
525 }
526 let c_seg = match std::ffi::CString::new(seg) {
527 Ok(c) => c,
528 Err(_) => return ptr::null(),
529 };
530 let next = sml_get(cur, c_seg.as_ptr());
531 if next.is_null() {
532 return ptr::null();
533 }
534 cur = next;
535 }
536 cur
537}
538
539#[cfg_attr(edge2024, unsafe(no_mangle))]
541#[cfg_attr(not(edge2024), no_mangle)]
542pub unsafe extern "C" fn sml_at(v: *const CSmlValue, idx: usize) -> *const CSmlValue {
543 if v.is_null() {
544 return ptr::null();
545 }
546 let inner = &(*(v as *const Value));
547 match inner {
548 Value::Array(a) => a
549 .get(idx)
550 .map(|x| x as *const Value as *const CSmlValue)
551 .unwrap_or(ptr::null()),
552 _ => ptr::null(),
553 }
554}
555
556#[cfg_attr(edge2024, unsafe(no_mangle))]
558#[cfg_attr(not(edge2024), no_mangle)]
559pub unsafe extern "C" fn sml_size(v: *const CSmlValue) -> usize {
560 if v.is_null() {
561 return 0;
562 }
563 match &(*(v as *const Value)) {
564 Value::Array(a) => a.len(),
565 Value::Object(m) => m.len(),
566 _ => 0,
567 }
568}
569
570#[cfg_attr(edge2024, unsafe(no_mangle))]
572#[cfg_attr(not(edge2024), no_mangle)]
573pub unsafe extern "C" fn sml_str_copy(
574 v: *const CSmlValue,
575 buf: *mut c_char,
576 buflen: usize,
577) -> usize {
578 if v.is_null() {
579 return 0;
580 }
581 let s = match &(*(v as *const Value)) {
582 Value::Str(s) => s.as_str(),
583 _ => return 0,
584 };
585 let need = s.len();
586 if buf.is_null() || buflen == 0 {
587 return need;
588 }
589 let n = need.min(buflen - 1);
590 let src = s.as_bytes();
591 for i in 0..n {
592 *buf.add(i) = src[i] as c_char;
593 }
594 *buf.add(n) = 0;
595 need
596}
597
598#[cfg_attr(edge2024, unsafe(no_mangle))]
600#[cfg_attr(not(edge2024), no_mangle)]
601pub unsafe extern "C" fn sml_str_dup(v: *const CSmlValue) -> *mut c_char {
602 if v.is_null() {
603 return ptr::null_mut();
604 }
605 match &(*(v as *const Value)) {
606 Value::Str(s) => cstr(s),
607 _ => ptr::null_mut(),
608 }
609}
610
611#[cfg_attr(edge2024, unsafe(no_mangle))]
613#[cfg_attr(not(edge2024), no_mangle)]
614pub unsafe extern "C" fn sml_int_value(v: *const CSmlValue) -> i64 {
615 if v.is_null() {
616 return 0;
617 }
618 match &(*(v as *const Value)) {
619 Value::Int(i) => *i,
620 Value::Float(f) => *f as i64,
621 _ => 0,
622 }
623}
624
625#[cfg_attr(edge2024, unsafe(no_mangle))]
627#[cfg_attr(not(edge2024), no_mangle)]
628pub unsafe extern "C" fn sml_real_value(v: *const CSmlValue) -> f64 {
629 if v.is_null() {
630 return 0.0;
631 }
632 match &(*(v as *const Value)) {
633 Value::Float(f) => *f,
634 Value::Int(i) => *i as f64,
635 _ => 0.0,
636 }
637}
638
639#[cfg_attr(edge2024, unsafe(no_mangle))]
641#[cfg_attr(not(edge2024), no_mangle)]
642pub unsafe extern "C" fn sml_bool_value(v: *const CSmlValue) -> c_int {
643 if v.is_null() {
644 return 0;
645 }
646 match &(*(v as *const Value)) {
647 Value::Bool(b) => {
648 if *b {
649 1
650 } else {
651 0
652 }
653 }
654 _ => 0,
655 }
656}
657
658#[cfg_attr(edge2024, unsafe(no_mangle))]
662#[cfg_attr(not(edge2024), no_mangle)]
663pub unsafe extern "C" fn sml_str_in(
664 v: *const CSmlValue,
665 path: *const c_char,
666) -> *mut c_char {
667 let node = sml_get_path(v, path);
668 if node.is_null() {
669 return ptr::null_mut();
670 }
671 sml_str_dup(node)
672}
673
674#[cfg_attr(edge2024, unsafe(no_mangle))]
676#[cfg_attr(not(edge2024), no_mangle)]
677pub unsafe extern "C" fn sml_int_in(
678 v: *const CSmlValue,
679 path: *const c_char,
680 ok: *mut c_int,
681) -> i64 {
682 let node = sml_get_path(v, path);
683 if node.is_null() {
684 if !ok.is_null() {
685 *ok = 0;
686 }
687 return 0;
688 }
689 let is_int = sml_typeof(node) == 2;
690 if !ok.is_null() {
691 *ok = if is_int { 1 } else { 0 };
692 }
693 sml_int_value(node)
694}
695
696#[cfg_attr(edge2024, unsafe(no_mangle))]
698#[cfg_attr(not(edge2024), no_mangle)]
699pub unsafe extern "C" fn sml_bool_in(
700 v: *const CSmlValue,
701 path: *const c_char,
702 ok: *mut c_int,
703) -> c_int {
704 let node = sml_get_path(v, path);
705 if node.is_null() {
706 if !ok.is_null() {
707 *ok = 0;
708 }
709 return 0;
710 }
711 let is_bool = sml_typeof(node) == 1;
712 if !ok.is_null() {
713 *ok = if is_bool { 1 } else { 0 };
714 }
715 sml_bool_value(node)
716}
717
718#[cfg_attr(edge2024, unsafe(no_mangle))]
720#[cfg_attr(not(edge2024), no_mangle)]
721pub unsafe extern "C" fn sml_dumps(v: *const CSmlValue, _flags: c_uint) -> *mut c_char {
722 if v.is_null() {
723 return ptr::null_mut();
724 }
725 cstr(&to_sml(&(*(v as *const Value))))
726}
727
728#[cfg_attr(edge2024, unsafe(no_mangle))]
734#[cfg_attr(not(edge2024), no_mangle)]
735pub extern "C" fn sml_feature_name(bit: c_uint) -> *const c_char {
736 let s: &'static str = match bit {
737 0 => "bareword-string\0",
738 1 => "include\0",
739 2 => "env\0",
740 3 => "contract\0",
741 4 => "fragment\0",
742 5 => "top-level-array\0",
743 6 => "namespace\0",
744 7 => "implicit-ns\0",
745 8 => "multi-include\0",
746 9 => "glob-include\0",
747 10 => "regex-include\0",
748 11 => "ext-rewrite\0",
749 _ => return ptr::null(),
750 };
751 s.as_ptr() as *const c_char
752}
753
754#[cfg_attr(edge2024, unsafe(no_mangle))]
756#[cfg_attr(not(edge2024), no_mangle)]
757pub extern "C" fn sml_features_mask() -> c_uint {
758 let mut m = 0u32;
759 for (i, _) in FEATURES.iter().enumerate() {
760 if i >= 32 {
761 break;
762 }
763 m |= 1u32 << i;
764 }
765 m
766}
767
768#[cfg_attr(edge2024, unsafe(no_mangle))]
770#[cfg_attr(not(edge2024), no_mangle)]
771pub extern "C" fn sml_version_str() -> *mut c_char {
772 cstr(env!("CARGO_PKG_VERSION"))
773}
774
775#[allow(dead_code)]
777type _CUnsignedLongLong = c_ulonglong;
778
779pub(crate) fn jsonify(v: &Value) -> String {
784 fn esc(s: &str) -> String {
785 s.replace('\\', "\\\\").replace('"', "\\\"")
786 }
787 match v {
788 Value::Null => "null".into(),
789 Value::Bool(b) => b.to_string(),
790 Value::Int(i) => i.to_string(),
791 Value::Float(f) => f.to_string(),
792 Value::Str(s) => format!("\"{}\"", esc(s)),
793 Value::Array(a) => {
794 let parts: Vec<String> = a.iter().map(jsonify).collect();
795 format!("[{}]", parts.join(","))
796 }
797 Value::Object(m) => {
798 let parts: Vec<String> = m
799 .iter()
800 .map(|(k, val)| format!("\"{}\":{}", esc(k), jsonify(val)))
801 .collect();
802 format!("{{{}}}", parts.join(","))
803 }
804 }
805}
806
807pub(crate) fn json_to_value(s: &str) -> Option<Value> {
808 let bytes = s.as_bytes();
809 let mut i = 0;
810 let _n = bytes.len();
811 let mut skip_ws = |b: &[u8], i: &mut usize| {
812 while *i < b.len() && matches!(b[*i], b' ' | b'\t' | b'\n' | b'\r') {
813 *i += 1;
814 }
815 };
816 let mut parse_str = |b: &[u8], i: &mut usize| -> Option<String> {
817 skip_ws(b, i);
818 if *i >= b.len() || b[*i] != b'"' {
819 return None;
820 }
821 *i += 1;
822 let mut out = String::new();
823 while *i < b.len() {
824 let c = b[*i];
825 if c == b'"' {
826 *i += 1;
827 return Some(out);
828 }
829 if c == b'\\' && *i + 1 < b.len() {
830 *i += 1;
831 let e = b[*i];
832 out.push(match e {
833 b'n' => '\n',
834 b't' => '\t',
835 b'r' => '\r',
836 b'"' => '"',
837 b'\\' => '\\',
838 _ => e as char,
839 });
840 } else {
841 out.push(c as char);
842 }
843 *i += 1;
844 }
845 None
846 };
847 fn parse_val_impl(
848 b: &[u8],
849 i: &mut usize,
850 s: &str,
851 parse_str: &dyn Fn(&[u8], &mut usize) -> Option<String>,
852 ) -> Option<Value> {
853 let mut skip_ws = |b: &[u8], i: &mut usize| {
854 while *i < b.len() && matches!(b[*i], b' ' | b'\t' | b'\n' | b'\r') {
855 *i += 1;
856 }
857 };
858 skip_ws(b, i);
859 if *i >= b.len() {
860 return None;
861 }
862 match b[*i] {
863 b'{' => {
864 *i += 1;
865 let mut m = BTreeMap::new();
866 skip_ws(b, i);
867 if *i < b.len() && b[*i] == b'}' {
868 *i += 1;
869 return Some(Value::Object(m));
870 }
871 loop {
872 skip_ws(b, i);
873 let k = parse_str(b, i)?;
874 skip_ws(b, i);
875 if *i < b.len() && b[*i] == b':' {
876 *i += 1;
877 }
878 let v = parse_val_impl(b, i, s, parse_str)?;
879 m.insert(k, v);
880 skip_ws(b, i);
881 if *i < b.len() && b[*i] == b',' {
882 *i += 1;
883 } else if *i < b.len() && b[*i] == b'}' {
884 *i += 1;
885 break;
886 }
887 }
888 Some(Value::Object(m))
889 }
890 b'[' => {
891 *i += 1;
892 let mut a = Vec::new();
893 skip_ws(b, i);
894 if *i < b.len() && b[*i] == b']' {
895 *i += 1;
896 return Some(Value::Array(a));
897 }
898 loop {
899 a.push(parse_val_impl(b, i, s, parse_str)?);
900 skip_ws(b, i);
901 if *i < b.len() && b[*i] == b',' {
902 *i += 1;
903 } else if *i < b.len() && b[*i] == b']' {
904 *i += 1;
905 break;
906 }
907 }
908 Some(Value::Array(a))
909 }
910 b'"' => parse_str(b, i).map(Value::Str),
911 b't' => {
912 if s[*i..].starts_with("true") {
913 *i += 4;
914 Some(Value::Bool(true))
915 } else {
916 None
917 }
918 }
919 b'f' => {
920 if s[*i..].starts_with("false") {
921 *i += 5;
922 Some(Value::Bool(false))
923 } else {
924 None
925 }
926 }
927 b'n' => {
928 if s[*i..].starts_with("null") {
929 *i += 4;
930 Some(Value::Null)
931 } else {
932 None
933 }
934 }
935 _ => {
936 let start = *i;
937 while *i < b.len()
938 && (b[*i].is_ascii_digit()
939 || matches!(b[*i], b'-' | b'+' | b'.' | b'e' | b'E'))
940 {
941 *i += 1;
942 }
943 let tok = s[start..*i].to_string();
944 if let Ok(iv) = tok.parse::<i64>() {
945 Some(Value::Int(iv))
946 } else if let Ok(fv) = tok.parse::<f64>() {
947 Some(Value::Float(fv))
948 } else {
949 None
950 }
951 }
952 }
953 }
954 parse_val_impl(bytes, &mut i, s, &parse_str)
955}
956