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
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//! [`Integer`](../struct.Integer.html) (un)packing.

use std::os::raw::c_int;
use crate::ruby;

/// A type whose bytes can be directly used as a word when (un)packing an
/// [`Integer`](../struct.Integer.html).
pub unsafe trait Word: Copy {
    /// Whether the type is a signed integer.
    const IS_SIGNED: bool;

    /// `Self` instantiated as 0.
    const ZERO: Self;
}

macro_rules! impl_word {
    ($signed:expr => $($t:ty)+) => { $(
        unsafe impl Word for $t {
            const IS_SIGNED: bool = $signed;

            const ZERO: Self = 0;
        }
    )+ }
}

impl_word! { false => usize u128 u64 u32 u16 u8 }
impl_word! { true  => isize i128 i64 i32 i16 i8 }

/// Options to use when (un)packing.
#[derive(Clone, Copy, Debug)]
pub struct Options {
    pub(super) byte_order: Order,
    pub(super) word_order: Order,
    pub(super) is_negative: bool,
}

impl Default for Options {
    #[inline]
    fn default() -> Self {
        Options {
            word_order: Order::Least,

            #[cfg(target_endian = "little")]
            byte_order: Order::Least,

            #[cfg(target_endian = "big")]
            byte_order: Order::Most,

            is_negative: false,
        }
    }
}

impl Options {
    #[inline]
    pub(super) fn flags(self) -> c_int {
        use ruby::integer_flags::*;

        let byte_order = match self.byte_order {
            Order::Least => PACK_LSBYTE_FIRST,
            Order::Most  => PACK_MSBYTE_FIRST,
        };
        let word_order = match self.word_order {
            Order::Least => PACK_LSWORD_FIRST,
            Order::Most  => PACK_MSWORD_FIRST,
        };

        word_order | byte_order
    }

    /// Returns a new instance for big-endian byte order.
    #[inline]
    pub fn big_endian() -> Self {
        Self::default().byte_order(Order::Most)
    }

    /// Returns a new instance for little-endian byte order.
    #[inline]
    pub fn little_endian() -> Self {
        Self::default().byte_order(Order::Least)
    }

    /// Sets the [endianness](https://en.wikipedia.org/wiki/Endianness) for each
    /// word.
    ///
    /// The default is the platform's native byte order:
    #[cfg_attr(target_endian = "little", doc = "**little-endian**.")]
    #[cfg_attr(target_endian = "big",    doc = "**big-endian**.")]
    #[inline]
    #[must_use]
    pub fn byte_order(mut self, order: Order) -> Self {
        self.byte_order = order;
        self
    }

    /// Sets the order in which words should be packed.
    ///
    /// The default is least-significant first.
    #[inline]
    #[must_use]
    pub fn word_order(mut self, order: Order) -> Self {
        self.word_order = order;
        self
    }

    /// Makes the `Integer` instance negative. This is only used when unpacking.
    #[inline]
    pub fn is_negative(mut self) -> Self {
        self.is_negative = true;
        self
    }
}

/// An order for arranging words and the bytes of those words when calling
/// [`pack_using`](../struct.Integer.html#method.pack_using).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Order {
    /// Least-significant first.
    Least,
    /// Most-significant first.
    Most,
}

/// The sign of an [`Integer`](../struct.Integer.html) value returned after
/// [`pack`](../struct.Integer.html#method.pack)ing one into a buffer.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Sign {
    /// Packing resulted in a value equal to 0.
    Zero,
    /// Packing resulted in a positive value.
    Positive {
        /// An overflow occurred when packing an
        /// [`Integer`](../struct.Integer.html) into a buffer.
        did_overflow: bool,
    },
    /// Packing resulted in a negative value.
    Negative {
        /// An overflow occurred when packing an
        /// [`Integer`](../struct.Integer.html) into a buffer.
        did_overflow: bool,
    },
}

impl Sign {
    /// Returns whether an overflow occurred when packing an
    /// [`Integer`](../struct.Integer.html) into a buffer.
    #[inline]
    pub fn did_overflow(&self) -> bool {
        use Sign::*;
        match *self {
            Zero => false,
            Positive { did_overflow } |
            Negative { did_overflow } => did_overflow,
        }
    }

    /// Returns whether the sign is negative.
    #[inline]
    pub fn is_negative(&self) -> bool {
        if let Sign::Negative { .. } = *self {
            true
        } else {
            false
        }
    }
}