1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// Stringly-Typed JSON Library for Rust
// Written in 2018 by
//   Andrew Poelstra <apoelstra@wpsoftware.net>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # Unsafe functionality to support detecting our de/serializer without a modern compiler supporting specialization
//!
//! The goal of this detection is to have a `Json` object be able to pass a
//! pointer to itself to a `Serializer` or `Deserializer` which is trying
//! to transform it to/from some serialized form. The serde API gives us no
//! ability to pass pointers directly like this, so we will instead smuggle
//! it in an unrelated `usize` parameter which is guaranteed to be large
//! enough to hold a pointer.
//!
//! Since this is inherently extremely unsafe, and if triggered by accident
//! could cause some random piece of memory to be dereferenced, we need to
//! ensure that we are *certain* in our `Serializer`/`Deserializer` code
//! that we are actually dealing with a `Json` object that knows how to set
//! this up.
//!

use std::fmt;
use serde::{de, ser};

/// Trait that distinguishes sentinels from non-sentinels. The trick is
/// that it needs to be implemented for all fmt::Display types so that
/// it will be accessible from our `ser::Error::custom` method.
pub trait IsSentinel {
    fn is_sentinel(&self) -> bool;
}

/// Object that, when constructed, will return `true` for `is_sentinel`,
/// which it does using a creative `Display` implementation.
struct Sentinel;

pub static SENTINEL_STR: &'static str = "$$$STRASON$$$SENTINEL$$$";

impl fmt::Display for Sentinel {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(SENTINEL_STR)
    }
}

impl<T: fmt::Display> IsSentinel for T {
    fn is_sentinel(&self) -> bool {
        struct DummyWrite(bool);
        impl fmt::Write for DummyWrite {
            fn write_str(&mut self, x: &str) -> fmt::Result {
                if x.as_ptr() == SENTINEL_STR.as_ptr() {
                    self.0 = true;
                }
                Ok(())
            }
            fn write_char(&mut self, _: char) -> fmt::Result { Ok(()) }
            fn write_fmt(&mut self, _: fmt::Arguments) -> fmt::Result { Ok(()) }
        }

        let mut check = DummyWrite(false);
        let _ = fmt::write(&mut check, format_args!("{}", self));
        check.0
    }
}

/// Function that detects our `Error` type, which uses 
pub fn detect_our_error_type_de<E: de::Error>() -> bool {
    let err = E::custom(Sentinel);
    err.description().as_ptr() == SENTINEL_STR.as_ptr()
}

/// Function that detects our `Error` type, which uses 
pub fn detect_our_error_type_ser<E: ser::Error>() -> bool {
    let err = E::custom(Sentinel);
    err.description().as_ptr() == SENTINEL_STR.as_ptr()
}