py27_marshal/
lib.rs

1// Ported from <https://github.com/python/cpython/blob/master/Python/marshal.c>
2use bitflags::bitflags;
3pub use bstr;
4use bstr::{BStr, BString};
5use num_bigint::BigInt;
6use num_complex::Complex;
7use num_derive::{FromPrimitive, ToPrimitive};
8use std::{
9    collections::{HashMap, HashSet},
10    convert::TryFrom,
11    fmt,
12    hash::{Hash, Hasher},
13    iter::FromIterator,
14    sync::{Arc, RwLock},
15};
16
17pub mod read;
18//pub mod write;
19
20/// `Arc` = immutable
21/// `ArcRwLock` = mutable
22pub type ArcRwLock<T> = Arc<RwLock<T>>;
23
24#[derive(FromPrimitive, ToPrimitive, Debug, Copy, Clone)]
25#[repr(u8)]
26pub enum Type {
27    Null          = b'0',
28    None          = b'N',
29    False         = b'F',
30    True          = b'T',
31    StopIter      = b'S',
32    Ellipsis      = b'.',
33    Int           = b'i',
34    Int64         = b'I',
35    Float         = b'f',
36    BinaryFloat   = b'g',
37    Complex       = b'x',
38    BinaryComplex = b'y',
39    Long          = b'l',
40    String        = b's',
41    Interned      = b't',
42    StringRef     = b'R',
43    Tuple         = b'(',
44    List          = b'[',
45    Dict          = b'{',
46    Code          = b'c',
47    Unicode       = b'u',
48    Unknown       = b'?',
49    Set           = b'<',
50    FrozenSet     = b'>',
51    /// Not a real type
52    Bytes         = 0x0,
53}
54impl Type {
55    const FLAG_REF: u8 = b'\x80';
56}
57
58pub(crate) struct Depth(Arc<()>);
59impl Depth {
60    const MAX: usize = 900;
61
62    #[must_use]
63    pub fn new() -> Self {
64        Self(Arc::new(()))
65    }
66
67    pub fn try_clone(&self) -> Option<Self> {
68        if Arc::strong_count(&self.0) > Self::MAX {
69            None
70        } else {
71            Some(Self(self.0.clone()))
72        }
73    }
74}
75impl fmt::Debug for Depth {
76    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
77        f.debug_tuple("Depth")
78            .field(&Arc::strong_count(&self.0))
79            .finish()
80    }
81}
82
83bitflags! {
84    pub struct CodeFlags: u32 {
85        const OPTIMIZED                   = 0x1;
86        const NEWLOCALS                   = 0x2;
87        const VARARGS                     = 0x4;
88        const VARKEYWORDS                 = 0x8;
89        const NESTED                     = 0x10;
90        const GENERATOR                  = 0x20;
91        const NOFREE                     = 0x40;
92        const COROUTINE                  = 0x80;
93        const ITERABLE_COROUTINE        = 0x100;
94        const ASYNC_GENERATOR           = 0x200;
95        // TODO: old versions
96        const GENERATOR_ALLOWED        = 0x1000;
97        const FUTURE_DIVISION          = 0x2000;
98        const FUTURE_ABSOLUTE_IMPORT   = 0x4000;
99        const FUTURE_WITH_STATEMENT    = 0x8000;
100        const FUTURE_PRINT_FUNCTION   = 0x10000;
101        const FUTURE_UNICODE_LITERALS = 0x20000;
102        const FUTURE_BARRY_AS_BDFL    = 0x40000;
103        const FUTURE_GENERATOR_STOP   = 0x80000;
104        #[allow(clippy::unreadable_literal)]
105        const FUTURE_ANNOTATIONS     = 0x100000;
106    }
107}
108
109#[rustfmt::skip]
110#[derive(Clone, Debug)]
111pub struct Code {
112    pub argcount:        u32,
113    pub nlocals:         u32,
114    pub stacksize:       u32,
115    pub flags:           CodeFlags,
116    pub code:            Arc<Vec<u8>>,
117    pub consts:          Arc<Vec<Obj>>,
118    pub names:           Vec<Arc<BString>>,
119    pub varnames:        Vec<Arc<BString>>,
120    pub freevars:        Vec<Arc<BString>>,
121    pub cellvars:        Vec<Arc<BString>>,
122    pub filename:        Arc<BString>,
123    pub name:            Arc<BString>,
124    pub firstlineno:     u32,
125    pub lnotab:          Arc<Vec<u8>>,
126}
127
128#[rustfmt::skip]
129#[derive(Clone)]
130pub enum Obj {
131    None,
132    StopIteration,
133    Ellipsis,
134    Bool     (bool),
135    Long     (Arc<BigInt>),
136    Float    (f64),
137    Complex  (Complex<f64>),
138    Bytes    (Arc<Vec<u8>>),
139    String   (Arc<BString>),
140    Tuple    (Arc<Vec<Obj>>),
141    List     (ArcRwLock<Vec<Obj>>),
142    Dict     (ArcRwLock<HashMap<ObjHashable, Obj>>),
143    Set      (ArcRwLock<HashSet<ObjHashable>>),
144    FrozenSet(Arc<HashSet<ObjHashable>>),
145    Code     (Arc<Code>),
146    // etc.
147}
148macro_rules! define_extract {
149    ($extract_fn:ident($variant:ident) -> ()) => {
150        define_extract! { $extract_fn -> () { $variant => () } }
151    };
152    ($extract_fn:ident($variant:ident) -> Arc<$ret:ty>) => {
153        define_extract! { $extract_fn -> Arc<$ret> { $variant(x) => x } }
154    };
155    ($extract_fn:ident($variant:ident) -> ArcRwLock<$ret:ty>) => {
156        define_extract! { $extract_fn -> ArcRwLock<$ret> { $variant(x) => x } }
157    };
158    ($extract_fn:ident($variant:ident) -> $ret:ty) => {
159        define_extract! { $extract_fn -> $ret { $variant(x) => x } }
160    };
161    ($extract_fn:ident -> $ret:ty { $variant:ident$(($($pat:pat),+))? => $expr:expr }) => {
162        /// # Errors
163        /// Returns a reference to self if extraction fails
164        pub fn $extract_fn(self) -> Result<$ret, Self> {
165            if let Self::$variant$(($($pat),+))? = self {
166                Ok($expr)
167            } else {
168                Err(self)
169            }
170        }
171    }
172}
173macro_rules! define_is {
174    ($is_fn:ident($variant:ident$(($($pat:pat),+))?)) => {
175        /// # Errors
176        /// Returns a reference to self if extraction fails
177        #[must_use]
178        pub fn $is_fn(&self) -> bool {
179            if let Self::$variant$(($($pat),+))? = self {
180                true
181            } else {
182                false
183            }
184        }
185    }
186}
187impl Obj {
188    define_extract! { extract_none          (None)          -> ()                                    }
189    define_extract! { extract_stop_iteration(StopIteration) -> ()                                    }
190    define_extract! { extract_bool          (Bool)          -> bool                                  }
191    define_extract! { extract_long          (Long)          -> Arc<BigInt>                           }
192    define_extract! { extract_float         (Float)         -> f64                                   }
193    define_extract! { extract_bytes         (String)        -> Arc<BString>                          }
194    define_extract! { extract_string        (String)        -> Arc<BString>                          }
195    define_extract! { extract_tuple         (Tuple)         -> Arc<Vec<Self>>                        }
196    define_extract! { extract_list          (List)          -> ArcRwLock<Vec<Self>>                  }
197    define_extract! { extract_dict          (Dict)          -> ArcRwLock<HashMap<ObjHashable, Self>> }
198    define_extract! { extract_set           (Set)           -> ArcRwLock<HashSet<ObjHashable>>       }
199    define_extract! { extract_frozenset     (FrozenSet)     -> Arc<HashSet<ObjHashable>>             }
200    define_extract! { extract_code          (Code)          -> Arc<Code>                             }
201
202    define_is! { is_none          (None)          }
203    define_is! { is_stop_iteration(StopIteration) }
204    define_is! { is_bool          (Bool(_))       }
205    define_is! { is_long          (Long(_))       }
206    define_is! { is_float         (Float(_))      }
207    define_is! { is_bytes         (Bytes(_))      }
208    define_is! { is_string        (String(_))     }
209    define_is! { is_tuple         (Tuple(_))      }
210    define_is! { is_list          (List(_))       }
211    define_is! { is_dict          (Dict(_))       }
212    define_is! { is_set           (Set(_))        }
213    define_is! { is_frozenset     (FrozenSet(_))  }
214    define_is! { is_code          (Code(_))       }
215}
216/// Should mostly match Python's repr
217///
218/// # Float, Complex
219/// - Uses `float('...')` instead of `...` for nan, inf, and -inf.
220/// - Uses Rust's float-to-decimal conversion.
221///
222/// # Bytes, String
223/// - Always uses double-quotes
224/// - Escapes both kinds of quotes
225///
226/// # Code
227/// - Uses named arguments for readability
228/// - lnotab is formatted as bytes(...) with a list of integers, instead of a bytes literal
229impl fmt::Debug for Obj {
230    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
231        match self {
232            Self::None => write!(f, "None"),
233            Self::StopIteration => write!(f, "StopIteration"),
234            Self::Ellipsis => write!(f, "Ellipsis"),
235            Self::Bool(true) => write!(f, "True"),
236            Self::Bool(false) => write!(f, "False"),
237            Self::Long(x) => write!(f, "{}", x),
238            &Self::Float(x) => python_float_repr_full(f, x),
239            &Self::Complex(x) => python_complex_repr(f, x),
240            Self::Bytes(x) => python_bytes_repr(f, x),
241            Self::String(x) => python_string_repr(f, x.as_ref().as_ref()),
242            Self::Tuple(x) => python_tuple_repr(f, x),
243            Self::List(x) => f.debug_list().entries(x.read().unwrap().iter()).finish(),
244            Self::Dict(x) => f.debug_map().entries(x.read().unwrap().iter()).finish(),
245            Self::Set(x) => f.debug_set().entries(x.read().unwrap().iter()).finish(),
246            Self::FrozenSet(x) => python_frozenset_repr(f, x),
247            Self::Code(x) => python_code_repr(f, x),
248        }
249    }
250}
251
252impl TryFrom<&ObjHashable> for Obj {
253    type Error = ObjHashable;
254
255    fn try_from(orig: &ObjHashable) -> Result<Self, ObjHashable> {
256        match orig {
257            ObjHashable::None => Ok(Self::None),
258            ObjHashable::StopIteration => Ok(Self::StopIteration),
259            ObjHashable::Ellipsis => Ok(Self::Ellipsis),
260            ObjHashable::Bool(x) => Ok(Self::Bool(*x)),
261            ObjHashable::Long(x) => Ok(Self::Long(Arc::clone(x))),
262            ObjHashable::Float(x) => Ok(Self::Float(x.0)),
263            ObjHashable::Complex(Complex { re, im }) => Ok(Self::Complex(Complex {
264                re: re.0,
265                im: im.0,
266            })),
267            ObjHashable::String(x) => Ok(Self::String(Arc::clone(x))),
268            ObjHashable::Tuple(x) => Ok(Self::Tuple(Arc::new(
269                x.iter()
270                    .map(Self::try_from)
271                    .collect::<Result<Vec<Self>, ObjHashable>>()?,
272            ))),
273            ObjHashable::FrozenSet(x) => Ok(Self::FrozenSet(Arc::new(
274                x.0.iter().cloned().collect::<HashSet<ObjHashable>>(),
275            ))),
276        }
277    }
278}
279
280impl Obj {
281    pub fn typ(&self) -> Type {
282        match self {
283            Self::None => Type::None,
284            Self::StopIteration => Type::StopIter,
285            Self::Ellipsis => Type::Ellipsis,
286            Self::Bool(true) => Type::True,
287            Self::Bool(false) => Type::False,
288            Self::Long(x) => Type::Long,
289            &Self::Float(x) => Type::Float,
290            &Self::Complex(x) => Type::Complex,
291            Self::Bytes(x) => Type::Bytes,
292            Self::String(x) => Type::String,
293            Self::Tuple(x) => Type::Tuple,
294            Self::List(x) => Type::List,
295            Self::Dict(x) => Type::Dict,
296            Self::Set(x) => Type::Set,
297            Self::FrozenSet(x) => Type::FrozenSet,
298            Self::Code(x) => Type::Code,
299        }
300    }
301}
302
303fn python_float_repr_full(f: &mut fmt::Formatter, x: f64) -> fmt::Result {
304    python_float_repr_core(f, x)?;
305    if x.fract() == 0. {
306        write!(f, ".0")?;
307    };
308    Ok(())
309}
310fn python_float_repr_core(f: &mut fmt::Formatter, x: f64) -> fmt::Result {
311    if x.is_nan() {
312        write!(f, "float('nan')")
313    } else if x.is_infinite() {
314        if x.is_sign_positive() {
315            write!(f, "float('inf')")
316        } else {
317            write!(f, "-float('inf')")
318        }
319    } else {
320        // properly handle -0.0
321        if x.is_sign_negative() {
322            write!(f, "-")?;
323        }
324        write!(f, "{}", x.abs())
325    }
326}
327fn python_complex_repr(f: &mut fmt::Formatter, x: Complex<f64>) -> fmt::Result {
328    if x.re == 0. && x.re.is_sign_positive() {
329        python_float_repr_core(f, x.im)?;
330        write!(f, "j")?;
331    } else {
332        write!(f, "(")?;
333        python_float_repr_core(f, x.re)?;
334        if x.im >= 0. || x.im.is_nan() {
335            write!(f, "+")?;
336        }
337        python_float_repr_core(f, x.im)?;
338        write!(f, "j)")?;
339    };
340    Ok(())
341}
342fn python_bytes_repr(f: &mut fmt::Formatter, x: &[u8]) -> fmt::Result {
343    write!(f, "b\"")?;
344    for &byte in x.iter() {
345        match byte {
346            b'\t' => write!(f, "\\t")?,
347            b'\n' => write!(f, "\\n")?,
348            b'\r' => write!(f, "\\r")?,
349            b'\'' | b'"' | b'\\' => write!(f, "\\{}", char::from(byte))?,
350            b' '..=b'~' => write!(f, "{}", char::from(byte))?,
351            _ => write!(f, "\\x{:02x}", byte)?,
352        }
353    }
354    write!(f, "\"")?;
355    Ok(())
356}
357fn python_string_repr(f: &mut fmt::Formatter, x: &BStr) -> fmt::Result {
358    let original = format!("{:?}", x);
359    let mut last_end = 0;
360    // Note: the behavior is arbitrary if there are improper escapes.
361    for (start, _) in original.match_indices("\\u{") {
362        f.write_str(&original[last_end..start])?;
363        let len = original[start..].find('}').ok_or(fmt::Error)? + 1;
364        let end = start + len;
365        match len - 4 {
366            0..=2 => write!(f, "\\x{:0>2}", &original[start + 3..end - 1])?,
367            3..=4 => write!(f, "\\u{:0>4}", &original[start + 3..end - 1])?,
368            5..=8 => write!(f, "\\U{:0>8}", &original[start + 3..end - 1])?,
369            _ => panic!("Internal error: length of unicode escape = {} > 8", len),
370        }
371        last_end = end;
372    }
373    f.write_str(&original[last_end..])?;
374    Ok(())
375}
376fn python_tuple_repr(f: &mut fmt::Formatter, x: &[Obj]) -> fmt::Result {
377    if x.is_empty() {
378        f.write_str("()") // Otherwise this would get formatted into an empty string
379    } else {
380        let mut debug_tuple = f.debug_tuple("");
381        for o in x.iter() {
382            debug_tuple.field(&o);
383        }
384        debug_tuple.finish()
385    }
386}
387fn python_frozenset_repr(f: &mut fmt::Formatter, x: &HashSet<ObjHashable>) -> fmt::Result {
388    f.write_str("frozenset(")?;
389    if !x.is_empty() {
390        f.debug_set().entries(x.iter()).finish()?;
391    }
392    f.write_str(")")?;
393    Ok(())
394}
395fn python_code_repr(f: &mut fmt::Formatter, x: &Code) -> fmt::Result {
396    write!(f, "code(argcount={:?}, nlocals={:?}, stacksize={:?}, flags={:?}, code={:?}, consts={:?}, names={:?}, varnames={:?}, freevars={:?}, cellvars={:?}, filename={:?}, name={:?}, firstlineno={:?}, lnotab=bytes({:?}))", x.argcount, x.nlocals, x.stacksize, x.flags, Obj::Bytes(Arc::clone(&x.code)), x.consts, x.names, x.varnames, x.freevars, x.cellvars, x.filename, x.name, x.firstlineno, &x.lnotab)
397}
398/// This is a f64 wrapper suitable for use as a key in a (Hash)Map, since NaNs compare equal to
399/// each other, so it can implement Eq and Hash. `HashF64(-0.0) == HashF64(0.0)`.
400#[derive(Clone, Debug)]
401pub struct HashF64(f64);
402impl PartialEq for HashF64 {
403    fn eq(&self, other: &Self) -> bool {
404        self.0 == other.0 || (self.0.is_nan() && other.0.is_nan())
405    }
406}
407impl Eq for HashF64 {}
408impl Hash for HashF64 {
409    fn hash<H: Hasher>(&self, state: &mut H) {
410        if self.0.is_nan() {
411            // Multiple NaN values exist
412            state.write_u8(0);
413        } else if self.0 == 0.0 {
414            // 0.0 == -0.0
415            state.write_u8(1);
416        } else {
417            state.write_u64(self.0.to_bits()); // This should be fine, since all the dupes should be accounted for.
418        }
419    }
420}
421
422#[derive(Debug)]
423pub struct HashableHashSet<T>(HashSet<T>);
424impl<T> Hash for HashableHashSet<T>
425where
426    T: Hash,
427{
428    fn hash<H: Hasher>(&self, state: &mut H) {
429        let mut xor: u64 = 0;
430        let hasher = std::collections::hash_map::DefaultHasher::new();
431        for value in &self.0 {
432            let mut hasher_clone = hasher.clone();
433            value.hash(&mut hasher_clone);
434            xor ^= hasher_clone.finish();
435        }
436        state.write_u64(xor);
437    }
438}
439impl<T> PartialEq for HashableHashSet<T>
440where
441    T: Eq + Hash,
442{
443    fn eq(&self, other: &Self) -> bool {
444        self.0 == other.0
445    }
446}
447impl<T> Eq for HashableHashSet<T> where T: Eq + Hash {}
448impl<T> FromIterator<T> for HashableHashSet<T>
449where
450    T: Eq + Hash,
451{
452    fn from_iter<I>(iter: I) -> Self
453    where
454        I: IntoIterator<Item = T>,
455    {
456        Self(iter.into_iter().collect())
457    }
458}
459
460#[derive(PartialEq, Eq, Hash, Clone)]
461pub enum ObjHashable {
462    None,
463    StopIteration,
464    Ellipsis,
465    Bool(bool),
466    Long(Arc<BigInt>),
467    Float(HashF64),
468    Complex(Complex<HashF64>),
469    String(Arc<BString>),
470    Tuple(Arc<Vec<ObjHashable>>),
471    FrozenSet(Arc<HashableHashSet<ObjHashable>>),
472    // etc.
473}
474impl TryFrom<&Obj> for ObjHashable {
475    type Error = Obj;
476
477    fn try_from(orig: &Obj) -> Result<Self, Obj> {
478        match orig {
479            Obj::None => Ok(Self::None),
480            Obj::StopIteration => Ok(Self::StopIteration),
481            Obj::Ellipsis => Ok(Self::Ellipsis),
482            Obj::Bool(x) => Ok(Self::Bool(*x)),
483            Obj::Long(x) => Ok(Self::Long(Arc::clone(x))),
484            Obj::Float(x) => Ok(Self::Float(HashF64(*x))),
485            Obj::Complex(Complex { re, im }) => Ok(Self::Complex(Complex {
486                re: HashF64(*re),
487                im: HashF64(*im),
488            })),
489            Obj::String(x) => Ok(Self::String(Arc::clone(x))),
490            Obj::Tuple(x) => Ok(Self::Tuple(Arc::new(
491                x.iter()
492                    .map(Self::try_from)
493                    .collect::<Result<Vec<Self>, Obj>>()?,
494            ))),
495            Obj::FrozenSet(x) => Ok(Self::FrozenSet(Arc::new(
496                x.iter().cloned().collect::<HashableHashSet<Self>>(),
497            ))),
498            x => Err(x.clone()),
499        }
500    }
501}
502impl fmt::Debug for ObjHashable {
503    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
504        match self {
505            Self::None => write!(f, "None"),
506            Self::StopIteration => write!(f, "StopIteration"),
507            Self::Ellipsis => write!(f, "Ellipsis"),
508            Self::Bool(true) => write!(f, "True"),
509            Self::Bool(false) => write!(f, "False"),
510            Self::Long(x) => write!(f, "{}", x),
511            Self::Float(x) => python_float_repr_full(f, x.0),
512            Self::Complex(x) => python_complex_repr(
513                f,
514                Complex {
515                    re: x.re.0,
516                    im: x.im.0,
517                },
518            ),
519            Self::String(x) => python_string_repr(f, x.as_ref().as_ref()),
520            Self::Tuple(x) => python_tuple_hashable_repr(f, x),
521            Self::FrozenSet(x) => python_frozenset_repr(f, &x.0),
522        }
523    }
524}
525fn python_tuple_hashable_repr(f: &mut fmt::Formatter, x: &[ObjHashable]) -> fmt::Result {
526    if x.is_empty() {
527        f.write_str("()") // Otherwise this would get formatted into an empty string
528    } else {
529        let mut debug_tuple = f.debug_tuple("");
530        for o in x.iter() {
531            debug_tuple.field(&o);
532        }
533        debug_tuple.finish()
534    }
535}
536
537#[cfg(test)]
538mod test {
539    use super::{Code, CodeFlags, Obj, ObjHashable};
540    use bstr::{BString, ByteSlice};
541    use num_bigint::BigInt;
542    use num_complex::Complex;
543    use std::{
544        collections::{HashMap, HashSet},
545        sync::{Arc, RwLock},
546    };
547
548    #[test]
549    fn test_debug_repr() {
550        assert_eq!(format!("{:?}", Obj::None), "None");
551        assert_eq!(format!("{:?}", Obj::StopIteration), "StopIteration");
552        assert_eq!(format!("{:?}", Obj::Ellipsis), "Ellipsis");
553        assert_eq!(format!("{:?}", Obj::Bool(true)), "True");
554        assert_eq!(format!("{:?}", Obj::Bool(false)), "False");
555        assert_eq!(
556            format!("{:?}", Obj::Long(Arc::new(BigInt::from(-123)))),
557            "-123"
558        );
559        assert_eq!(format!("{:?}", Obj::Tuple(Arc::new(vec![]))), "()");
560        assert_eq!(
561            format!("{:?}", Obj::Tuple(Arc::new(vec![Obj::Bool(true)]))),
562            "(True,)"
563        );
564        assert_eq!(
565            format!(
566                "{:?}",
567                Obj::Tuple(Arc::new(vec![Obj::Bool(true), Obj::None]))
568            ),
569            "(True, None)"
570        );
571        assert_eq!(
572            format!(
573                "{:?}",
574                Obj::List(Arc::new(RwLock::new(vec![Obj::Bool(true)])))
575            ),
576            "[True]"
577        );
578        assert_eq!(
579            format!(
580                "{:?}",
581                Obj::Dict(Arc::new(RwLock::new(
582                    vec![(
583                        ObjHashable::Bool(true),
584                        Obj::Bytes(Arc::new(Vec::from(b"a" as &[u8])))
585                    )]
586                    .into_iter()
587                    .collect::<HashMap<_, _>>()
588                )))
589            ),
590            "{True: b\"a\"}"
591        );
592        assert_eq!(
593            format!(
594                "{:?}",
595                Obj::Set(Arc::new(RwLock::new(
596                    vec![ObjHashable::Bool(true)]
597                        .into_iter()
598                        .collect::<HashSet<_>>()
599                )))
600            ),
601            "{True}"
602        );
603        assert_eq!(
604            format!(
605                "{:?}",
606                Obj::FrozenSet(Arc::new(
607                    vec![ObjHashable::Bool(true)]
608                        .into_iter()
609                        .collect::<HashSet<_>>()
610                ))
611            ),
612            "frozenset({True})"
613        );
614        assert_eq!(format!("{:?}", Obj::Code(Arc::new(Code {
615            argcount: 0,
616            nlocals: 3,
617            stacksize: 4,
618            flags: CodeFlags::NESTED | CodeFlags::COROUTINE,
619             code: Arc::new(Vec::from(b"abc" as &[u8])),
620            consts: Arc::new(vec![Obj::Bool(true)]),
621            names: vec![],
622            varnames: vec![Arc::new(BString::from("a"))],
623            freevars: vec![Arc::new(BString::from("b")), Arc::new(BString::from("c"))],
624            cellvars: vec![Arc::new(BString::from("de"))],
625            filename: Arc::new(BString::from("xyz.py")),
626            name: Arc::new(BString::from("fgh")),
627            firstlineno: 5,
628            lnotab: Arc::new(vec![255, 0, 45, 127, 0, 73]),
629        }))), "code(argcount=0, nlocals=3, stacksize=4, flags=NESTED | COROUTINE, code=b\"abc\", consts=[True], names=[], varnames=[\"a\"], freevars=[\"b\", \"c\"], cellvars=[\"de\"], filename=\"xyz.py\", name=\"fgh\", firstlineno=5, lnotab=bytes([255, 0, 45, 127, 0, 73]))");
630    }
631
632    #[test]
633    fn test_float_debug_repr() {
634        assert_eq!(format!("{:?}", Obj::Float(1.23)), "1.23");
635        assert_eq!(format!("{:?}", Obj::Float(f64::NAN)), "float('nan')");
636        assert_eq!(format!("{:?}", Obj::Float(f64::INFINITY)), "float('inf')");
637        assert_eq!(format!("{:?}", Obj::Float(-f64::INFINITY)), "-float('inf')");
638        assert_eq!(format!("{:?}", Obj::Float(0.0)), "0.0");
639        assert_eq!(format!("{:?}", Obj::Float(-0.0)), "-0.0");
640    }
641
642    #[test]
643    fn test_complex_debug_repr() {
644        assert_eq!(
645            format!("{:?}", Obj::Complex(Complex { re: 2., im: 1. })),
646            "(2+1j)"
647        );
648        assert_eq!(
649            format!("{:?}", Obj::Complex(Complex { re: 0., im: 1. })),
650            "1j"
651        );
652        assert_eq!(
653            format!("{:?}", Obj::Complex(Complex { re: 2., im: 0. })),
654            "(2+0j)"
655        );
656        assert_eq!(
657            format!("{:?}", Obj::Complex(Complex { re: 0., im: 0. })),
658            "0j"
659        );
660        assert_eq!(
661            format!("{:?}", Obj::Complex(Complex { re: -2., im: 1. })),
662            "(-2+1j)"
663        );
664        assert_eq!(
665            format!("{:?}", Obj::Complex(Complex { re: -2., im: 0. })),
666            "(-2+0j)"
667        );
668        assert_eq!(
669            format!("{:?}", Obj::Complex(Complex { re: 2., im: -1. })),
670            "(2-1j)"
671        );
672        assert_eq!(
673            format!("{:?}", Obj::Complex(Complex { re: 0., im: -1. })),
674            "-1j"
675        );
676        assert_eq!(
677            format!("{:?}", Obj::Complex(Complex { re: -2., im: -1. })),
678            "(-2-1j)"
679        );
680        assert_eq!(
681            format!("{:?}", Obj::Complex(Complex { re: 0., im: -1. })),
682            "-1j"
683        );
684        assert_eq!(
685            format!("{:?}", Obj::Complex(Complex { re: -2., im: 0. })),
686            "(-2+0j)"
687        );
688        assert_eq!(
689            format!("{:?}", Obj::Complex(Complex { re: -0., im: 1. })),
690            "(-0+1j)"
691        );
692        assert_eq!(
693            format!("{:?}", Obj::Complex(Complex { re: -0., im: -1. })),
694            "(-0-1j)"
695        );
696    }
697
698    #[test]
699    fn test_bytes_string_debug_repr() {
700        assert_eq!(format!("{:?}", Obj::Bytes(Arc::new(Vec::from(
701                            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe" as &[u8]
702                            )))),
703        "b\"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !\\\"#$%&\\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\""
704        );
705        assert_eq!(format!("{:?}", Obj::String(Arc::new(BString::from(
706                            "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f")))),
707                            "\"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !\\\"#$%&\\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\"");
708    }
709}
710
711mod utils {
712    use num_bigint::{BigUint, Sign};
713    use num_traits::Zero;
714    use std::cmp::Ordering;
715
716    /// Based on `_PyLong_AsByteArray` in <https://github.com/python/cpython/blob/master/Objects/longobject.c>
717    #[allow(clippy::cast_possible_truncation)]
718    pub fn biguint_from_pylong_digits(digits: &[u16]) -> BigUint {
719        if digits.is_empty() {
720            return BigUint::zero();
721        };
722        assert!(digits[digits.len() - 1] != 0);
723        let mut accum: u64 = 0;
724        let mut accumbits: u8 = 0;
725        let mut p = Vec::<u32>::new();
726        for (i, &thisdigit) in digits.iter().enumerate() {
727            accum |= u64::from(thisdigit) << accumbits;
728            accumbits += if i == digits.len() - 1 {
729                16 - (thisdigit.leading_zeros() as u8)
730            } else {
731                15
732            };
733
734            // Modified to get u32s instead of u8s.
735            while accumbits >= 32 {
736                p.push(accum as u32);
737                accumbits -= 32;
738                accum >>= 32;
739            }
740        }
741        assert!(accumbits < 32);
742        if accumbits > 0 {
743            p.push(accum as u32);
744        }
745        BigUint::new(p)
746    }
747
748    pub fn sign_of<T: Ord + Zero>(x: &T) -> Sign {
749        match x.cmp(&T::zero()) {
750            Ordering::Less => Sign::Minus,
751            Ordering::Equal => Sign::NoSign,
752            Ordering::Greater => Sign::Plus,
753        }
754    }
755
756    #[cfg(test)]
757    mod test {
758        use super::biguint_from_pylong_digits;
759        use num_bigint::BigUint;
760
761        #[allow(clippy::inconsistent_digit_grouping)]
762        #[test]
763        fn test_biguint_from_pylong_digits() {
764            assert_eq!(
765                biguint_from_pylong_digits(&[
766                    0b000_1101_1100_0100,
767                    0b110_1101_0010_0100,
768                    0b001_0000_1001_1101
769                ]),
770                BigUint::from(0b001_0000_1001_1101_110_1101_0010_0100_000_1101_1100_0100_u64)
771            );
772        }
773    }
774}