sonic_rs/lazyvalue/
value.rs

1use std::{
2    borrow::Cow,
3    fmt::{self, Debug, Display},
4    hash::Hash,
5    str::from_utf8_unchecked,
6    sync::{
7        atomic::{AtomicPtr, Ordering},
8        Arc,
9    },
10};
11
12use faststr::FastStr;
13
14use crate::{
15    from_str, get_unchecked,
16    index::Index,
17    input::JsonSlice,
18    lazyvalue::iterator::{ArrayJsonIter, ObjectJsonIter},
19    serde::Number,
20    JsonType, JsonValueTrait, RawNumber,
21};
22
23/// LazyValue wrappers a unparsed raw JSON text. It is borrowed from the origin JSON text.
24///
25/// LazyValue can be [`get`](crate::get),  [`get_unchecked`](crate::get_unchecked) or
26/// [`deserialize`](crate::from_str) from a JSON text.
27///
28/// # Examples
29///
30/// ```
31/// use sonic_rs::{get, JsonValueTrait, LazyValue, Value};
32///
33/// // get a lazyvalue from a json, the "a"'s value will not be parsed
34/// let input = r#"{
35///  "a": "hello world",
36///  "b": true,
37///  "c": [0, 1, 2],
38///  "d": {
39///     "sonic": "rs"
40///   }
41/// }"#;
42/// let lv_a: LazyValue = get(input, &["a"]).unwrap();
43/// let lv_c: LazyValue = get(input, &["c"]).unwrap();
44///
45/// // use as_raw_xx to get the unparsed JSON text
46/// assert_eq!(lv_a.as_raw_str(), "\"hello world\"");
47/// assert_eq!(lv_c.as_raw_str(), "[0, 1, 2]");
48///
49/// // use as_xx to get the parsed value
50/// assert_eq!(lv_a.as_str().unwrap(), "hello world");
51/// assert_eq!(lv_c.as_str(), None);
52/// assert!(lv_c.is_array());
53/// ```
54///
55/// # Serde Examples
56///
57/// `LazyValue<'a>` can only be deserialized with borrowed.
58/// If need to be owned, use [`OwnedLazyValue`](crate::OwnedLazyValue).
59///
60/// ```
61/// # use sonic_rs::LazyValue;
62/// use serde::{Deserialize, Serialize};
63///
64/// #[derive(Debug, Deserialize, Serialize, PartialEq)]
65/// struct TestLazyValue<'a> {
66///     #[serde(borrow)]
67///     borrowed_lv: LazyValue<'a>,
68/// }
69///
70/// let input = r#"{ "borrowed_lv": "hello"}"#;
71///
72/// let data: TestLazyValue = sonic_rs::from_str(input).unwrap();
73/// assert_eq!(data.borrowed_lv.as_raw_str(), "\"hello\"");
74/// ```
75///
76/// # Convert to serde_json::Value
77///
78/// `LazyValue<'a>` can convert into `serde_json::Value` from bytes slice.
79///
80/// ```
81///  use sonic_rs::{pointer, JsonValueTrait};
82///
83///  let json: &str = r#"{
84///      "bool": true,
85///      "int": -1,
86///      "uint": 0,
87///      "float": 1.1,
88///      "string": "hello",
89///      "string_escape": "\"hello\"",
90///      "array": [1,2,3],
91///      "object": {"a":"aaa"},
92///      "strempty": "",
93///      "objempty": {},
94///      "arrempty": []
95///  }"#;
96///  let lazy_value = sonic_rs::get(json, pointer![].iter()).unwrap();
97///
98///  for (key, expect_value) in [
99///      ("bool", serde_json::json!(true)),
100///      ("int", serde_json::json!(-1)),
101///      ("uint", serde_json::json!(0)),
102///      ("float", serde_json::json!(1.1)),
103///      ("string", serde_json::json!("hello")),
104///      ("string_escape", serde_json::json!("\"hello\"")),
105///      ("array", serde_json::json!([1, 2, 3])),
106///      ("object", serde_json::json!({"a":"aaa"})),
107///      ("strempty", serde_json::json!("")),
108///      ("objempty", serde_json::json!({})),
109///      ("arrempty", serde_json::json!([])),
110///  ] {
111///      let value = lazy_value.get(key);
112///
113///      let trans_value =
114///          serde_json::from_slice::<serde_json::Value>(value.unwrap().as_raw_str().as_bytes())
115///              .unwrap();
116///      assert_eq!(trans_value, expect_value);
117///      println!("checked {key} with {trans_value:?}");
118///  }
119/// ```
120#[derive(Clone)]
121pub struct LazyValue<'a> {
122    // the raw slice from origin json
123    pub(crate) raw: JsonSlice<'a>,
124    pub(crate) inner: Inner,
125}
126
127pub(crate) struct Inner {
128    pub(crate) status: HasEsc,
129    pub(crate) unescaped: AtomicPtr<()>,
130}
131
132impl Inner {
133    pub(crate) fn no_escaped(&self) -> bool {
134        self.status == HasEsc::None
135    }
136
137    pub(crate) fn parse_from(&self, raw: &[u8]) -> Option<&str> {
138        let ptr = self.unescaped.load(Ordering::Acquire);
139        if !ptr.is_null() {
140            return Some(unsafe { &*(ptr as *const String) });
141        }
142
143        unsafe {
144            let parsed: String = crate::from_slice_unchecked(raw).ok()?;
145            let parsed = Arc::into_raw(Arc::new(parsed)) as *mut ();
146            match self.unescaped.compare_exchange_weak(
147                ptr,
148                parsed,
149                Ordering::AcqRel,
150                Ordering::Acquire,
151            ) {
152                Ok(_) => Some(&*(parsed as *const String)),
153                Err(e) => {
154                    Arc::decrement_strong_count(parsed);
155                    Some(&*(e as *const String))
156                }
157            }
158        }
159    }
160}
161impl Default for Inner {
162    fn default() -> Self {
163        Self {
164            status: HasEsc::None,
165            unescaped: AtomicPtr::new(std::ptr::null_mut()),
166        }
167    }
168}
169
170impl Clone for Inner {
171    fn clone(&self) -> Self {
172        let ptr = if !self.no_escaped() {
173            // possible is parsing
174            let ptr = self.unescaped.load(Ordering::Acquire);
175            if !ptr.is_null() {
176                unsafe { Arc::increment_strong_count(ptr as *const String) };
177            }
178            ptr
179        } else {
180            std::ptr::null_mut()
181        };
182        Self {
183            status: self.status,
184            unescaped: AtomicPtr::new(ptr),
185        }
186    }
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq)]
190pub(crate) enum HasEsc {
191    None,
192    Yes,
193    Possible,
194}
195
196impl Default for LazyValue<'_> {
197    fn default() -> Self {
198        Self {
199            raw: JsonSlice::Raw(&b"null"[..]),
200            inner: Inner::default(),
201        }
202    }
203}
204
205impl Drop for Inner {
206    fn drop(&mut self) {
207        if self.no_escaped() {
208            return;
209        }
210
211        let ptr = self.unescaped.load(Ordering::Acquire);
212        if !ptr.is_null() {
213            unsafe { Arc::decrement_strong_count(ptr as *const String) };
214        }
215    }
216}
217
218impl<'a> Debug for LazyValue<'a> {
219    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
220        formatter
221            .debug_struct("LazyValue")
222            .field("raw json", &format_args!("{}", &self.as_raw_str()))
223            .field("has_escaped", &self.inner.status)
224            .finish()
225    }
226}
227
228impl<'a> Display for LazyValue<'a> {
229    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
230        f.write_str(self.as_raw_str())
231    }
232}
233
234impl PartialEq for LazyValue<'_> {
235    fn eq(&self, other: &Self) -> bool {
236        self.raw.as_ref() == other.raw.as_ref()
237    }
238}
239
240impl PartialOrd for LazyValue<'_> {
241    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
242        Some(self.cmp(other))
243    }
244}
245
246impl<'a> Ord for LazyValue<'a> {
247    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
248        self.raw.as_ref().cmp(other.raw.as_ref())
249    }
250}
251
252impl<'a> Eq for LazyValue<'a> {}
253
254impl<'a> Hash for LazyValue<'a> {
255    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
256        self.raw.as_ref().hash(state)
257    }
258}
259
260impl<'a> JsonValueTrait for LazyValue<'a> {
261    type ValueType<'v>
262        = LazyValue<'v>
263    where
264        Self: 'v;
265
266    fn as_bool(&self) -> Option<bool> {
267        match self.raw.as_ref() {
268            b"true" => Some(true),
269            b"false" => Some(false),
270            _ => None,
271        }
272    }
273
274    fn as_number(&self) -> Option<Number> {
275        from_str(self.as_raw_str()).ok()
276    }
277
278    fn as_raw_number(&self) -> Option<RawNumber> {
279        from_str(self.as_raw_str()).ok()
280    }
281
282    fn as_str(&self) -> Option<&str> {
283        if !self.is_str() {
284            return None;
285        }
286
287        if self.inner.no_escaped() {
288            // remove the quotes
289            let origin = {
290                let raw = self.as_raw_str().as_bytes();
291                &raw[1..raw.len() - 1]
292            };
293            Some(unsafe { from_utf8_unchecked(origin) })
294        } else {
295            self.inner.parse_from(self.raw.as_ref())
296        }
297    }
298
299    fn get_type(&self) -> crate::JsonType {
300        match self.raw.as_ref()[0] {
301            b'-' | b'0'..=b'9' => JsonType::Number,
302            b'"' => JsonType::String,
303            b'{' => JsonType::Object,
304            b'[' => JsonType::Array,
305            b't' | b'f' => JsonType::Boolean,
306            b'n' => JsonType::Null,
307            _ => unreachable!(),
308        }
309    }
310
311    fn get<I: Index>(&self, index: I) -> Option<LazyValue<'_>> {
312        if let Some(key) = index.as_key() {
313            self.get_key(key)
314        } else if let Some(index) = index.as_index() {
315            self.get_index(index)
316        } else {
317            unreachable!("index must be key or index")
318        }
319    }
320
321    fn pointer<P: IntoIterator>(&self, path: P) -> Option<LazyValue<'_>>
322    where
323        P::Item: Index,
324    {
325        let path = path.into_iter();
326        match &self.raw {
327            // #Safety
328            // LazyValue is built with JSON validation, so we can use get_unchecked here.
329            JsonSlice::Raw(r) => unsafe { get_unchecked(*r, path).ok() },
330            JsonSlice::FastStr(f) => unsafe { get_unchecked(f, path).ok() },
331        }
332    }
333}
334
335impl<'a> LazyValue<'a> {
336    /// Export the raw JSON text as `str`.
337    ///
338    /// # Examples
339    ///
340    /// ```
341    /// use sonic_rs::{get, LazyValue};
342    ///
343    /// let lv: LazyValue = sonic_rs::get(r#"{"a": "hello world"}"#, &["a"]).unwrap();
344    /// assert_eq!(lv.as_raw_str(), "\"hello world\"");
345    /// ```
346    pub fn as_raw_str(&self) -> &str {
347        // # Safety
348        // it is validate when using to_object_iter/get ...
349        // if use `get_unchecked` unsafe apis, it must ensured by the user at first
350        unsafe { from_utf8_unchecked(self.raw.as_ref()) }
351    }
352
353    /// Export the raw JSON text as `Cow<'de, str>`.  The lifetime `'de` is the origin JSON.
354    ///
355    /// # Examples
356    ///
357    /// ```
358    /// use sonic_rs::{get, LazyValue};
359    ///
360    /// let lv: LazyValue = sonic_rs::get(r#"{"a": "hello world"}"#, &["a"]).unwrap();
361    /// assert_eq!(lv.as_raw_cow(), "\"hello world\"");
362    /// ```
363    pub fn as_raw_cow(&self) -> Cow<'a, str> {
364        match &self.raw {
365            JsonSlice::Raw(r) => Cow::Borrowed(unsafe { from_utf8_unchecked(r) }),
366            JsonSlice::FastStr(f) => Cow::Owned(f.to_string()),
367        }
368    }
369
370    /// Export the raw json text as faststr.
371    ///
372    /// # Note
373    /// If the input json is not bytes or faststr, there will be a string copy.
374    ///
375    /// # Examples
376    ///
377    /// ```
378    /// use faststr::FastStr;
379    /// use sonic_rs::LazyValue;
380    ///
381    /// let lv: LazyValue = sonic_rs::get(r#"{"a": "hello world"}"#, &["a"]).unwrap();
382    /// // will copy the raw_str into a new faststr
383    /// assert_eq!(lv.as_raw_faststr(), "\"hello world\"");
384    ///
385    /// let fs = FastStr::new(r#"{"a": "hello world"}"#);
386    /// let lv: LazyValue = sonic_rs::get(&fs, &["a"]).unwrap();
387    /// assert_eq!(lv.as_raw_faststr(), "\"hello world\""); // zero-copy
388    /// ```
389    pub fn as_raw_faststr(&self) -> FastStr {
390        match &self.raw {
391            JsonSlice::Raw(r) => unsafe { FastStr::new_u8_slice_unchecked(r) },
392            JsonSlice::FastStr(f) => f.clone(),
393        }
394    }
395
396    pub fn into_object_iter(mut self) -> Option<ObjectJsonIter<'a>> {
397        if self.is_object() {
398            Some(ObjectJsonIter::new_inner(std::mem::take(&mut self.raw)))
399        } else {
400            None
401        }
402    }
403
404    pub fn into_array_iter(mut self) -> Option<ArrayJsonIter<'a>> {
405        if self.is_array() {
406            Some(ArrayJsonIter::new_inner(std::mem::take(&mut self.raw)))
407        } else {
408            None
409        }
410    }
411
412    /// get with index from lazyvalue
413    pub(crate) fn get_index(&'a self, index: usize) -> Option<Self> {
414        let path = [index];
415        match &self.raw {
416            // #Safety
417            // LazyValue is built with JSON validation, so we can use get_unchecked here.
418            JsonSlice::Raw(r) => unsafe { get_unchecked(*r, path.iter()).ok() },
419            JsonSlice::FastStr(f) => unsafe { get_unchecked(f, path.iter()).ok() },
420        }
421    }
422
423    /// get with key from lazyvalue
424    pub(crate) fn get_key(&'a self, key: &str) -> Option<Self> {
425        let path = [key];
426        match &self.raw {
427            // #Safety
428            // LazyValue is built with JSON validation, so we can use get_unchecked here.
429            JsonSlice::Raw(r) => unsafe { get_unchecked(*r, path.iter()).ok() },
430            JsonSlice::FastStr(f) => unsafe { get_unchecked(f, path.iter()).ok() },
431        }
432    }
433
434    pub(crate) fn new(raw: JsonSlice<'a>, status: HasEsc) -> Self {
435        Self {
436            raw,
437            inner: Inner {
438                status,
439                unescaped: AtomicPtr::new(std::ptr::null_mut()),
440            },
441        }
442    }
443}
444
445#[cfg(test)]
446mod test {
447    use super::*;
448    use crate::{pointer, to_array_iter};
449
450    const TEST_JSON: &str = r#"{
451        "bool": true,
452        "int": -1,
453        "uint": 0,
454        "float": 1.1,
455        "string": "hello",
456        "string_escape": "\"hello\"",
457        "array": [1,2,3],
458        "object": {"a":"aaa"},
459        "strempty": "",
460        "objempty": {},
461        "arrempty": [],
462        "arrempty": []
463    }"#;
464
465    #[test]
466    fn test_lazyvalue_export() {
467        let f = FastStr::new(TEST_JSON);
468        let value = unsafe { get_unchecked(&f, pointer![].iter()).unwrap() };
469        assert_eq!(value.get("int").unwrap().as_raw_str(), "-1");
470        assert_eq!(
471            value.get("array").unwrap().as_raw_faststr().as_str(),
472            "[1,2,3]"
473        );
474        assert_eq!(
475            value
476                .pointer(pointer!["object", "a"])
477                .unwrap()
478                .as_raw_str()
479                .as_bytes(),
480            b"\"aaa\""
481        );
482        assert!(value.pointer(pointer!["objempty", "a"]).is_none());
483    }
484
485    #[test]
486    fn test_lazyvalue_is() {
487        let value = unsafe { get_unchecked(TEST_JSON, pointer![].iter()).unwrap() };
488        assert!(value.get("bool").is_boolean());
489        assert!(value.get("bool").is_true());
490        assert!(value.get("uint").is_u64());
491        assert!(value.get("uint").is_number());
492        assert!(value.get("int").is_i64());
493        assert!(value.get("float").is_f64());
494        assert!(value.get("string").is_str());
495        assert!(value.get("array").is_array());
496        assert!(value.get("object").is_object());
497        assert!(value.get("strempty").is_str());
498        assert!(value.get("objempty").is_object());
499        assert!(value.get("arrempty").is_array());
500    }
501
502    #[test]
503    fn test_lazyvalue_get() {
504        let value = unsafe { get_unchecked(TEST_JSON, pointer![].iter()).unwrap() };
505        assert_eq!(value.get("int").as_i64().unwrap(), -1);
506        assert_eq!(value.pointer(pointer!["array", 2]).as_u64().unwrap(), 3);
507        assert_eq!(
508            value.pointer(pointer!["object", "a"]).as_str().unwrap(),
509            "aaa"
510        );
511        assert!(value.pointer(pointer!["object", "b"]).is_none());
512        assert!(value.pointer(pointer!["object", "strempty"]).is_none());
513        assert_eq!(value.pointer(pointer!["objempty", "a"]).as_str(), None);
514        assert!(value.pointer(pointer!["arrempty", 1]).is_none());
515        assert!(value.pointer(pointer!["array", 3]).is_none());
516        assert!(value.pointer(pointer!["array", 4]).is_none());
517        assert_eq!(value.pointer(pointer!["arrempty", 1]).as_str(), None);
518        assert_eq!(value.get("string").as_str().unwrap(), "hello");
519
520        let value = unsafe { get_unchecked(TEST_JSON, pointer![].iter()).unwrap() };
521        assert_eq!(value.get("string_escape").as_str().unwrap(), "\"hello\"");
522
523        let value = unsafe { get_unchecked(TEST_JSON, pointer![].iter()).unwrap() };
524        assert!(value.get("int").as_str().is_none());
525        assert_eq!(value.get("int").as_i64(), Some(-1));
526        assert_eq!(value.get("uint").as_i64(), Some(0));
527        assert_eq!(value.get("float").as_f64(), Some(1.1));
528    }
529
530    #[test]
531    fn test_lazyvalue_cow() {
532        fn get_cow(json: &str) -> Option<Cow<'_, str>> {
533            to_array_iter(json)
534                .next()
535                .map(|val| val.unwrap().as_raw_cow())
536        }
537
538        assert_eq!(get_cow("[true]").unwrap(), "true");
539    }
540}