ruby_marshal/
lib.rs

1mod convert;
2mod dump;
3mod load;
4mod value_arena;
5
6pub use self::convert::BTreeMapFromValueError;
7pub use self::convert::DisplayByteString;
8pub use self::convert::FromValue;
9pub use self::convert::FromValueContext;
10pub use self::convert::FromValueError;
11pub use self::convert::HashMapFromValueError;
12pub use self::convert::IntoValue;
13pub use self::convert::IntoValueError;
14pub use self::dump::dump;
15pub use self::load::load;
16pub use self::value_arena::ArrayValue;
17pub use self::value_arena::BoolValue;
18pub use self::value_arena::FixnumValue;
19pub use self::value_arena::HashValue;
20pub use self::value_arena::NilValue;
21pub use self::value_arena::ObjectValue;
22pub use self::value_arena::StringValue;
23pub use self::value_arena::SymbolValue;
24pub use self::value_arena::TypedValueHandle;
25pub use self::value_arena::UserDefinedValue;
26pub use self::value_arena::Value;
27pub use self::value_arena::ValueArena;
28pub use self::value_arena::ValueHandle;
29pub use self::value_arena::ValueKind;
30
31const MAJOR_VERSION: u8 = 4;
32const MINOR_VERSION: u8 = 8;
33
34const VALUE_KIND_NIL: u8 = b'0';
35const VALUE_KIND_TRUE: u8 = b'T';
36const VALUE_KIND_FALSE: u8 = b'F';
37const VALUE_KIND_FIXNUM: u8 = b'i';
38const VALUE_KIND_SYMBOL: u8 = b':';
39const VALUE_KIND_SYMBOL_LINK: u8 = b';';
40const VALUE_KIND_OBJECT_LINK: u8 = b'@';
41const VALUE_KIND_INSTANCE_VARIABLES: u8 = b'I';
42const VALUE_KIND_ARRAY: u8 = b'[';
43const VALUE_KIND_HASH: u8 = b'{';
44const VALUE_KIND_HASH_DEFAULT: u8 = b'}';
45const VALUE_KIND_OBJECT: u8 = b'o';
46const VALUE_KIND_STRING: u8 = b'"';
47const VALUE_KIND_USER_DEFINED: u8 = b'u';
48
49/// The library error type
50#[derive(Debug)]
51pub enum Error {
52    /// Invalid version
53    InvalidVersion {
54        /// The major version
55        major: u8,
56
57        /// The minor version
58        minor: u8,
59    },
60
61    /// An I/O Error
62    Io { error: std::io::Error },
63
64    /// An invalid value kind was encountered
65    InvalidValueKind { kind: u8 },
66
67    /// A value handle was invalid
68    InvalidValueHandle {
69        /// The invalid value handle
70        handle: ValueHandle,
71    },
72
73    /// The fixnum size is too large
74    InvalidFixnumSize { size: u8 },
75
76    /// The Fixnum is not a valid usize
77    FixnumInvalidUSize { error: std::num::TryFromIntError },
78
79    /// The usize is not a valid Fixnum
80    USizeInvalidFixnum { error: std::num::TryFromIntError },
81
82    /// Missing a symbol link
83    MissingSymbolLink { index: usize },
84
85    /// Missing an object link
86    MissingObjectLink { index: usize },
87
88    /// Unexpected Value Kind
89    UnexpectedValueKind { expected: u8, actual: u8 },
90
91    /// The value is not an object
92    NotAnObject,
93
94    /// There was a duplicate instance variable
95    DuplicateInstanceVariable {
96        /// The duplicated variable
97        name: Vec<u8>,
98    },
99}
100
101impl std::fmt::Display for Error {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        match self {
104            Self::InvalidVersion { major, minor } => write!(f, "invalid version {major}.{minor}"),
105            Self::Io { .. } => write!(f, "I/O error"),
106            Self::InvalidValueKind { kind } => write!(f, "invalid value kind {kind}"),
107            Self::InvalidValueHandle { .. } => write!(f, "invalid value handle"),
108            Self::InvalidFixnumSize { size } => write!(f, "invalid fixnum size {size}"),
109            Self::FixnumInvalidUSize { .. } => write!(f, "fixnum is not a valid usize"),
110            Self::USizeInvalidFixnum { .. } => write!(f, "usize is not a valid Fixnum"),
111            Self::MissingSymbolLink { index } => write!(f, "missing symbol link {index}"),
112            Self::MissingObjectLink { index } => write!(f, "missing object link {index}"),
113            Self::UnexpectedValueKind { expected, actual } => write!(
114                f,
115                "unexpected value kind, expected {expected} but got {actual}"
116            ),
117            Self::NotAnObject => write!(f, "not an object"),
118            Self::DuplicateInstanceVariable { name } => {
119                write!(f, "duplicate instance variable \"{name:?}\"")
120            }
121        }
122    }
123}
124
125impl std::error::Error for Error {
126    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
127        match self {
128            Self::Io { error } => Some(error),
129            Self::FixnumInvalidUSize { error } => Some(error),
130            Self::USizeInvalidFixnum { error } => Some(error),
131            _ => None,
132        }
133    }
134}
135
136impl From<std::io::Error> for Error {
137    fn from(error: std::io::Error) -> Self {
138        Error::Io { error }
139    }
140}
141
142#[cfg(test)]
143mod test {
144    use super::*;
145    use std::io::Read;
146
147    #[test]
148    fn kitchen_sink() {
149        for entry in std::fs::read_dir("test_data").expect("failed to read \"test_data\"") {
150            let entry = entry.expect("failed to read entry");
151            let file_type = entry.file_type().expect("failed to get file type");
152            if file_type.is_dir() {
153                continue;
154            }
155            let entry_path = entry.path();
156
157            let data = std::fs::read(&entry_path).expect("failed to read entry");
158            let mut data_reader = std::io::Cursor::new(&data);
159
160            let value_arena = load(&mut data_reader).expect("failed to load");
161
162            let mut new_data = Vec::new();
163            dump(&mut new_data, &value_arena).expect("failed to dump");
164
165            let read_end_result = data_reader.read(&mut [0]);
166            let is_eof = matches!(read_end_result, Ok(0));
167            assert!(is_eof);
168            assert!(data == new_data, "{data:?} != {new_data:?}");
169        }
170    }
171
172    #[test]
173    fn duped_symbol() {
174        let mut value_arena = ValueArena::new();
175
176        let symbol_1 = value_arena.create_symbol("symbol".into()).into();
177        let symbol_2 = value_arena.create_symbol("symbol".into()).into();
178
179        let deduped_array = value_arena.create_array(vec![symbol_1, symbol_1]);
180        let duped_array = value_arena.create_array(vec![symbol_1, symbol_2]);
181
182        let mut deduped_data = Vec::new();
183        let mut duped_data = Vec::new();
184
185        value_arena.replace_root(deduped_array);
186        dump(&mut deduped_data, &value_arena).expect("failed to dump");
187
188        value_arena.replace_root(duped_array);
189        dump(&mut duped_data, &value_arena).expect("failed to dump");
190
191        assert!(deduped_data == duped_data);
192    }
193}