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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! macros which are only useful inside of this crate.

/// Implement `SerHex` for a unsigned integer with a size equivalent
/// to `$bytes`.  Currently just offloads conversion to the appropriately
/// sized byte-array logic, and then does a endianness-aware transmute to
/// the target type.  TODO: benchmark this and determine if it is worth
/// writing a custom impl instead.
macro_rules! impl_serhex_uint {
    ($type: ty, $bytes: expr) => {
        impl_serhex_seq!($type, $bytes);
        impl<C> $crate::SerHex<C> for $type
        where
            C: $crate::HexConf,
        {
            type Error = $crate::types::Error;
            fn into_hex_raw<D>(&self, mut dst: D) -> ::std::result::Result<(), Self::Error>
            where
                D: ::std::io::Write,
            {
                let bytes: [u8; $bytes] = unsafe { ::std::mem::transmute(self.to_be()) };
                into_hex_bytearray!(bytes, dst, $bytes)?;
                Ok(())
            }
            fn from_hex_raw<S>(src: S) -> ::std::result::Result<Self, Self::Error>
            where
                S: AsRef<[u8]>,
            {
                let rslt: ::std::result::Result<[u8; $bytes], Self::Error> =
                    from_hex_bytearray!(src, $bytes);
                match rslt {
                    Ok(buf) => {
                        let val: $type = unsafe { ::std::mem::transmute(buf) };
                        Ok(Self::from_be(val))
                    }
                    Err(e) => Err(e),
                }
            }
        }
    };
}

/// implement `SerHexSeq` for an array of elements which implement
/// `SerHexSeq`.
macro_rules! impl_serhex_seq_array {
    ($conf:ty,$len:expr) => {
        impl<T, E> $crate::SerHexSeq<$conf> for [T; $len]
        where
            E: From<$crate::types::Error> + ::std::error::Error,
            T: $crate::SerHexSeq<$conf>
                + $crate::SerHex<$crate::Strict, Error = E>
                + $crate::SerHex<$crate::StrictPfx, Error = E>
                + $crate::SerHex<$crate::StrictCap, Error = E>
                + $crate::SerHex<$crate::StrictCapPfx, Error = E>,
        {
            fn size() -> usize {
                <T as $crate::SerHexSeq<$conf>>::size() * $len
            }
        }
    };
}

/// generate a blanket impl for a strict variant of `SerHex` for
/// an any array of `[T;$len]` where `T` meets the trait bound:
/// `SerHex<Strict> + SerHex<StrictCap>`.  this macro is invoked
/// by the `impl_serhex_strict_array` macro for all `Strict`
/// variants, so prefer that macro over calling this one directly.
/// *TODO*: the `from_hex_raw` impl generated by this macro takes
/// twice as long for the worst `Err` case as it does for the `Ok`
/// case.  This needs to be updated.
macro_rules! impl_serhex_strictconf_array {
    ($conf:ty,$len:expr) => {
        impl_serhex_seq_array!($conf, $len);

        impl<T, E> $crate::SerHex<$conf> for [T; $len]
        where
            E: From<$crate::types::Error> + ::std::error::Error,
            T: $crate::SerHex<$crate::Strict, Error = E>
                + $crate::SerHex<$crate::StrictPfx, Error = E>
                + $crate::SerHex<$crate::StrictCap, Error = E>
                + $crate::SerHex<$crate::StrictCapPfx, Error = E>,
        {
            type Error = E;

            fn into_hex_raw<D>(&self, mut dst: D) -> Result<(), Self::Error>
            where
                D: io::Write,
            {
                let src: &[T] = self.as_ref();
                debug_assert!(src.len() == $len);
                let mut items = src.iter();
                // first element is serialized with `$conf` to allow prefixing if specified.
                // plus this has the handy side-effect of preventing impls of `Compact` variants,
                // since they are not part of the constraints on `T`.
                match items.next() {
                    Some(itm) => <T as $crate::SerHex<$conf>>::into_hex_raw(itm, &mut dst)?,
                    None => {
                        // should only happen in the `[T;0]` case.
                        debug_assert!($len == 0);
                        return Ok(());
                    }
                }
                if <$conf as $crate::HexConf>::withcap() {
                    for itm in items {
                        <T as SerHex<$crate::StrictCap>>::into_hex_raw(&itm, &mut dst)?;
                    }
                } else {
                    for itm in items {
                        <T as SerHex<$crate::Strict>>::into_hex_raw(&itm, &mut dst)?;
                    }
                }
                Ok(())
            }

            fn from_hex_raw<S>(src: S) -> Result<Self, Self::Error>
            where
                S: AsRef<[u8]>,
            {
                let raw: &[u8] = src.as_ref();
                let hex = if <$conf as $crate::HexConf>::withpfx() {
                    let pfx = "0x".as_bytes();
                    if raw.starts_with(pfx) {
                        &raw[2..]
                    } else {
                        raw
                    }
                } else {
                    raw
                };
                // get iterator over chunks of expected size.  the underlying
                // `SerHex<Strict>` implementation must raise an appropriate
                // error if chunks are not of the proper size.
                let chunks = hex.chunks(hex.len() / $len);
                let values =
                    chunks.filter_map(
                        |chunk| match <T as $crate::SerHex<$crate::Strict>>::from_hex(chunk) {
                            Ok(val) => Some(val),
                            Err(_) => None,
                        },
                    );
                match array_init::from_iter(values) {
                    Some(rslt) => Ok(rslt),
                    None => {
                        // TODO: currently just repeats above process until
                        // error is found.  this is a workaround for the fact
                        // that `aray_init::from_iter` consumes the iterator.
                        // need to find a better workaround.
                        let chunks = hex.chunks(hex.len() / $len);
                        let mut errors = chunks.filter_map(|chunk| {
                            match <T as $crate::SerHex<$crate::Strict>>::from_hex(chunk) {
                                Ok(_) => None,
                                Err(e) => Some(e),
                            }
                        });
                        if let Some(err) = errors.next() {
                            Err(err.into())
                        } else {
                            let expect = $len;
                            let actual = hex.len() / $len;
                            let inner = $crate::types::ParseHexError::Size { expect, actual };
                            let error = $crate::types::Error::from(inner);
                            Err(error.into())
                        }
                    }
                }
            }
        }
    };
}

/// generate blanket impls for all strict variants of `SerHex` for
/// an any array of `[T;$len]` where `T: SerHex`.  We only implement
/// strict variants since the implementation assumes no framing/separators
/// (meaning that separation must be knowable by offset).  If you would
/// like to Serialize/Deserialize to arrays of non-strict hex, this is
/// best handled elsewhere; `SerHex` is intended to operate on types
/// representable as contiguous hex.
macro_rules! impl_serhex_strict_array {
    ( $($len: expr),+ ) => {
        $(
            impl_serhex_strictconf_array!($crate::Strict,$len);
            impl_serhex_strictconf_array!($crate::StrictPfx,$len);
            impl_serhex_strictconf_array!($crate::StrictCap,$len);
            impl_serhex_strictconf_array!($crate::StrictCapPfx,$len);
        )+
    }
}