serde_pickle/
value.rs

1// Copyright (c) 2015-2024 Georg Brandl.  Licensed under the Apache License,
2// Version 2.0 <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>
3// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at
4// your option. This file may not be copied, modified, or distributed except
5// according to those terms.
6
7//! Python values, and serialization instances for them.
8
9use num_bigint::BigInt;
10use num_traits::{Signed, ToPrimitive};
11use std::cmp::Ordering;
12use std::collections::{BTreeMap, BTreeSet};
13use std::fmt;
14
15pub use crate::value_impls::{from_value, to_value};
16
17use crate::error::{Error, ErrorCode};
18
19/// Represents all primitive builtin Python values that can be restored by
20/// unpickling.
21///
22/// Note on integers: the distinction between the two types (short and long) is
23/// very fuzzy in Python, and they can be used interchangeably.  In Python 3,
24/// all integers are long integers, so all are pickled as such.  While decoding,
25/// we simply put all integers that fit into an i64, and use `BigInt` for the
26/// rest.
27#[derive(Clone, Debug, PartialEq)]
28pub enum Value {
29    /// None
30    None,
31    /// Boolean
32    Bool(bool),
33    /// Short integer
34    I64(i64),
35    /// Long integer (unbounded length)
36    Int(BigInt),
37    /// Float
38    F64(f64),
39    /// Bytestring
40    Bytes(Vec<u8>),
41    /// Unicode string
42    String(String),
43    /// List
44    List(Vec<Value>),
45    /// Tuple
46    Tuple(Vec<Value>),
47    /// Set
48    Set(BTreeSet<HashableValue>),
49    /// Frozen (immutable) set
50    FrozenSet(BTreeSet<HashableValue>),
51    /// Dictionary (map)
52    Dict(BTreeMap<HashableValue, Value>),
53}
54
55/// Represents all primitive builtin Python values that can be contained
56/// in a "hashable" context (i.e., as dictionary keys and set elements).
57///
58/// In Rust, the type is *not* hashable, since we use B-tree maps and sets
59/// instead of the hash variants.  To be able to put all Value instances
60/// into these B-trees, we implement a consistent ordering between all
61/// the possible types (see below).
62#[derive(Clone, Debug)]
63pub enum HashableValue {
64    /// None
65    None,
66    /// Boolean
67    Bool(bool),
68    /// Short integer
69    I64(i64),
70    /// Long integer
71    Int(BigInt),
72    /// Float
73    F64(f64),
74    /// Bytestring
75    Bytes(Vec<u8>),
76    /// Unicode string
77    String(String),
78    /// Tuple
79    Tuple(Vec<HashableValue>),
80    /// Frozen (immutable) set
81    FrozenSet(BTreeSet<HashableValue>),
82}
83
84fn values_to_hashable(values: Vec<Value>) -> Result<Vec<HashableValue>, Error> {
85    values.into_iter().map(Value::into_hashable).collect()
86}
87
88fn hashable_to_values(values: Vec<HashableValue>) -> Vec<Value> {
89    values.into_iter().map(HashableValue::into_value).collect()
90}
91
92impl Value {
93    /// Convert the value into a hashable version, if possible.  If not, return
94    /// a ValueNotHashable error.
95    pub fn into_hashable(self) -> Result<HashableValue, Error> {
96        match self {
97            Value::None => Ok(HashableValue::None),
98            Value::Bool(b) => Ok(HashableValue::Bool(b)),
99            Value::I64(i) => Ok(HashableValue::I64(i)),
100            Value::Int(i) => Ok(HashableValue::Int(i)),
101            Value::F64(f) => Ok(HashableValue::F64(f)),
102            Value::Bytes(b) => Ok(HashableValue::Bytes(b)),
103            Value::String(s) => Ok(HashableValue::String(s)),
104            Value::FrozenSet(v) => Ok(HashableValue::FrozenSet(v)),
105            Value::Tuple(v) => values_to_hashable(v).map(HashableValue::Tuple),
106            _ => Err(Error::Syntax(ErrorCode::ValueNotHashable)),
107        }
108    }
109}
110
111impl HashableValue {
112    /// Convert the value into its non-hashable version.  This always works.
113    pub fn into_value(self) -> Value {
114        match self {
115            HashableValue::None => Value::None,
116            HashableValue::Bool(b) => Value::Bool(b),
117            HashableValue::I64(i) => Value::I64(i),
118            HashableValue::Int(i) => Value::Int(i),
119            HashableValue::F64(f) => Value::F64(f),
120            HashableValue::Bytes(b) => Value::Bytes(b),
121            HashableValue::String(s) => Value::String(s),
122            HashableValue::FrozenSet(v) => Value::FrozenSet(v),
123            HashableValue::Tuple(v) => Value::Tuple(hashable_to_values(v)),
124        }
125    }
126}
127
128fn write_elements<'a, I, T>(
129    f: &mut fmt::Formatter, it: I, prefix: &'static str, suffix: &'static str, len: usize, always_comma: bool,
130) -> fmt::Result
131where
132    I: Iterator<Item = &'a T>,
133    T: fmt::Display + 'a,
134{
135    f.write_str(prefix)?;
136    for (i, item) in it.enumerate() {
137        if i < len - 1 || always_comma {
138            write!(f, "{}, ", item)?;
139        } else {
140            write!(f, "{}", item)?;
141        }
142    }
143    f.write_str(suffix)
144}
145
146impl fmt::Display for Value {
147    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148        match *self {
149            Value::None => write!(f, "None"),
150            Value::Bool(b) => write!(f, "{}", if b { "True" } else { "False" }),
151            Value::I64(i) => write!(f, "{}", i),
152            Value::Int(ref i) => write!(f, "{}", i),
153            Value::F64(v) => write!(f, "{}", v),
154            Value::Bytes(ref b) => write!(f, "b{:?}", b), //
155            Value::String(ref s) => write!(f, "{:?}", s),
156            Value::List(ref v) => write_elements(f, v.iter(), "[", "]", v.len(), false),
157            Value::Tuple(ref v) => write_elements(f, v.iter(), "(", ")", v.len(), v.len() == 1),
158            Value::FrozenSet(ref v) => write_elements(f, v.iter(), "frozenset([", "])", v.len(), false),
159            Value::Set(ref v) => {
160                if v.is_empty() {
161                    write!(f, "set()")
162                } else {
163                    write_elements(f, v.iter(), "{", "}", v.len(), false)
164                }
165            }
166            Value::Dict(ref v) => {
167                write!(f, "{{")?;
168                for (i, (key, value)) in v.iter().enumerate() {
169                    if i < v.len() - 1 {
170                        write!(f, "{}: {}, ", key, value)?;
171                    } else {
172                        write!(f, "{}: {}", key, value)?;
173                    }
174                }
175                write!(f, "}}")
176            }
177        }
178    }
179}
180
181impl fmt::Display for HashableValue {
182    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183        match *self {
184            HashableValue::None => write!(f, "None"),
185            HashableValue::Bool(b) => write!(f, "{}", if b { "True" } else { "False" }),
186            HashableValue::I64(i) => write!(f, "{}", i),
187            HashableValue::Int(ref i) => write!(f, "{}", i),
188            HashableValue::F64(v) => write!(f, "{}", v),
189            HashableValue::Bytes(ref b) => write!(f, "b{:?}", b), //
190            HashableValue::String(ref s) => write!(f, "{:?}", s),
191            HashableValue::Tuple(ref v) => write_elements(f, v.iter(), "(", ")", v.len(), v.len() == 1),
192            HashableValue::FrozenSet(ref v) => {
193                write_elements(f, v.iter(), "frozenset([", "])", v.len(), false)
194            }
195        }
196    }
197}
198
199impl PartialEq for HashableValue {
200    fn eq(&self, other: &HashableValue) -> bool {
201        self.cmp(other) == Ordering::Equal
202    }
203}
204
205impl Eq for HashableValue {}
206
207impl PartialOrd for HashableValue {
208    fn partial_cmp(&self, other: &HashableValue) -> Option<Ordering> {
209        Some(self.cmp(other))
210    }
211}
212
213/// Implement a (more or less) consistent ordering for `HashableValue`s
214/// so that they can be added to dictionaries and sets.
215///
216/// Also, like in Python, numeric values with the same value (integral or not)
217/// must compare equal.
218///
219/// For other types, we define an ordering between all types A and B so that all
220/// objects of type A are always lesser than objects of type B.  This is done
221/// similar to Python 2's ordering of different types.
222impl Ord for HashableValue {
223    fn cmp(&self, other: &HashableValue) -> Ordering {
224        use self::HashableValue::*;
225        match *self {
226            None => match *other {
227                None => Ordering::Equal,
228                _ => Ordering::Less,
229            },
230            Bool(b) => match *other {
231                None => Ordering::Greater,
232                Bool(b2) => b.cmp(&b2),
233                I64(i2) => (b as i64).cmp(&i2),
234                Int(ref bi) => BigInt::from(b as i64).cmp(bi),
235                F64(f) => float_ord(b as i64 as f64, f),
236                _ => Ordering::Less,
237            },
238            I64(i) => match *other {
239                None => Ordering::Greater,
240                Bool(b) => i.cmp(&(b as i64)),
241                I64(i2) => i.cmp(&i2),
242                Int(ref bi) => BigInt::from(i).cmp(bi),
243                F64(f) => float_ord(i as f64, f),
244                _ => Ordering::Less,
245            },
246            Int(ref bi) => match *other {
247                None => Ordering::Greater,
248                Bool(b) => bi.cmp(&BigInt::from(b as i64)),
249                I64(i) => bi.cmp(&BigInt::from(i)),
250                Int(ref bi2) => bi.cmp(bi2),
251                F64(f) => float_bigint_ord(bi, f),
252                _ => Ordering::Less,
253            },
254            F64(f) => match *other {
255                None => Ordering::Greater,
256                Bool(b) => float_ord(f, b as i64 as f64),
257                I64(i) => float_ord(f, i as f64),
258                Int(ref bi) => BigInt::from(f as i64).cmp(bi),
259                F64(f2) => float_ord(f, f2),
260                _ => Ordering::Less,
261            },
262            Bytes(ref bs) => match *other {
263                String(_) | FrozenSet(_) | Tuple(_) => Ordering::Less,
264                Bytes(ref bs2) => bs.cmp(bs2),
265                _ => Ordering::Greater,
266            },
267            String(ref s) => match *other {
268                FrozenSet(_) | Tuple(_) => Ordering::Less,
269                String(ref s2) => s.cmp(s2),
270                _ => Ordering::Greater,
271            },
272            FrozenSet(ref s) => match *other {
273                Tuple(_) => Ordering::Less,
274                FrozenSet(ref s2) => s.cmp(s2),
275                _ => Ordering::Greater,
276            },
277            Tuple(ref t) => match *other {
278                Tuple(ref t2) => t.cmp(t2),
279                _ => Ordering::Greater,
280            },
281        }
282    }
283}
284
285/// A "reasonable" total ordering for floats.
286fn float_ord(f: f64, g: f64) -> Ordering {
287    match f.partial_cmp(&g) {
288        Some(o) => o,
289        None => Ordering::Less,
290    }
291}
292
293/// Ordering between floats and big integers.
294fn float_bigint_ord(bi: &BigInt, g: f64) -> Ordering {
295    match bi.to_f64() {
296        Some(f) => float_ord(f, g),
297        None => {
298            if bi.is_positive() {
299                Ordering::Greater
300            } else {
301                Ordering::Less
302            }
303        }
304    }
305}