Skip to main content

simdjson_sys/
lib.rs

1#[cxx::bridge(namespace = "simdjson_sys::dom")]
2pub mod dom_ffi {
3    struct TapeView<'a> {
4        tape: &'a [u64],
5        string_buf: &'a [u8],
6    }
7
8    unsafe extern "C++" {
9        include!("simdjson.h");
10        include!("simdjson_dom_bridge.h");
11
12        #[namespace = "simdjson::dom"]
13        type parser;
14
15        fn parser_new(max_capacity: usize) -> UniquePtr<parser>;
16        /// Parse a document, optionally copying it into padded storage.
17        ///
18        /// # Safety
19        ///
20        /// With `realloc_if_needed = false`, the allocation must provide 64
21        /// initialized readable bytes beyond `json` for the duration of the call.
22        // CXX-generated declarations trigger this lint despite the docs above.
23        #[allow(clippy::missing_safety_doc)]
24        unsafe fn parser_parse(
25            parser: Pin<&mut parser>,
26            json: &[u8],
27            realloc_if_needed: bool,
28        ) -> i32;
29        /// Borrow the initialized tape and string buffer.
30        ///
31        /// # Safety
32        ///
33        /// The last parse must have succeeded, with no subsequent mutation.
34        #[allow(clippy::missing_safety_doc)]
35        unsafe fn parser_get_tape_view(parser: &parser) -> TapeView<'_>;
36    }
37}
38
39pub const SIMDJSON_PADDING: usize = 64;
40pub const SIMDJSON_MAXSIZE_BYTES: usize = 0xFFFFFFFF;
41pub const DEFAULT_BATCH_SIZE: usize = 1000000;
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn parser_parses_string_and_exposes_tape_view() {
49        let mut parser = dom_ffi::parser_new(SIMDJSON_MAXSIZE_BYTES);
50        let json = String::from(r#"{"answer":42,"message":"ok"}"#);
51
52        // SAFETY: realloc=true lets simdjson own the padded input.
53        assert_eq!(
54            unsafe { dom_ffi::parser_parse(parser.pin_mut(), json.as_bytes(), true) },
55            0
56        );
57
58        // SAFETY: the parse above succeeded.
59        let view = unsafe { dom_ffi::parser_get_tape_view(parser.as_ref().unwrap()) };
60        assert!(!view.tape.is_empty());
61        assert_eq!(view.tape[0] >> 56, u64::from(b'r'));
62        assert!(!view.string_buf.is_empty());
63    }
64    #[test]
65    fn string_view_contains_only_initialized_prefix() {
66        let mut parser = dom_ffi::parser_new(SIMDJSON_MAXSIZE_BYTES);
67        for (json, expected) in [
68            (r#"["longer string", "x"]"#, 24),
69            // This integer's payload has a string tag in its high byte.
70            ("[2449958197289549824]", 0),
71            (r#""x""#, 6),
72            ("null", 0),
73        ] {
74            // SAFETY: realloc=true copies input into padded storage.
75            assert_eq!(
76                unsafe { dom_ffi::parser_parse(parser.pin_mut(), json.as_bytes(), true) },
77                0
78            );
79            // SAFETY: the last parse succeeded and no mutation intervened.
80            let view = unsafe { dom_ffi::parser_get_tape_view(parser.as_ref().unwrap()) };
81            assert_eq!(view.string_buf.len(), expected, "{json}");
82        }
83    }
84}