iota_ledger_nano/api/
packable.rs

1// Copyright 2020 IOTA Stiftung
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
4// the License. You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
9// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and limitations under the License.
11
12use thiserror::Error;
13
14pub use std::io::{Read, Write};
15
16use std::str;
17
18#[derive(Debug, Error)]
19#[allow(dead_code)] // to have "green" files ;-)
20pub enum Error {
21    #[error("I/O error happened: {0}.")]
22    Io(#[from] std::io::Error),
23    #[error("Invalid variant read.")]
24    InvalidVariant,
25    #[error("Invalid Utf8 string read.")]
26    InvalidUtf8String,
27    #[error("Invalid version read.")]
28    InvalidVersion,
29    #[error("Invalid type read.")]
30    InvalidType,
31    #[error("Invalid announced len.")]
32    InvalidAnnouncedLen,
33    #[error("String too long.")]
34    StringTooLong,
35}
36
37pub trait Packable {
38    fn packed_len(&self) -> usize;
39
40    fn pack<W: Write>(&self, buf: &mut W) -> Result<(), Error>;
41
42    fn unpack<R: Read>(buf: &mut R) -> Result<Self, Error>
43    where
44        Self: Sized;
45}
46
47impl Packable for () {
48    fn packed_len(&self) -> usize {
49        0
50    }
51
52    fn pack<W: Write>(&self, _buf: &mut W) -> Result<(), Error> {
53        Ok(())
54    }
55
56    fn unpack<R: Read>(_buf: &mut R) -> Result<Self, Error>
57    where
58        Self: Sized,
59    {
60        Ok(())
61    }
62}
63
64macro_rules! impl_packable_for_num {
65    ($ty:ident) => {
66        impl Packable for $ty {
67            fn packed_len(&self) -> usize {
68                std::mem::size_of::<$ty>()
69            }
70
71            fn pack<W: Write>(&self, buf: &mut W) -> Result<(), Error> {
72                buf.write_all(self.to_le_bytes().as_ref())?;
73
74                Ok(())
75            }
76
77            fn unpack<R: Read>(buf: &mut R) -> Result<Self, Error> {
78                let mut bytes = [0; std::mem::size_of::<$ty>()];
79                buf.read_exact(&mut bytes)?;
80                Ok($ty::from_le_bytes(bytes))
81            }
82        }
83    };
84}
85
86impl Packable for String {
87    fn packed_len(&self) -> usize {
88        0u8.packed_len() + self.chars().count()
89    }
90
91    fn pack<W: Write>(&self, buf: &mut W) -> Result<(), Error> {
92        if self.chars().count() > 255 {
93            return Err(Error::StringTooLong);
94        }
95        let bytes = self.clone().into_bytes();
96        (bytes.len() as u8).pack(buf)?;
97        buf.write_all(&bytes)?;
98        Ok(())
99    }
100
101    fn unpack<R: Read>(buf: &mut R) -> Result<Self, Error>
102    where
103        Self: Sized,
104    {
105        let l = u8::unpack(buf)?;
106        let mut v: Vec<u8> = Vec::new();
107        for _ in 0..l {
108            v.push(u8::unpack(buf)?);
109        }
110        match str::from_utf8(v.as_ref()) {
111            Ok(v) => Ok(String::from(v)),
112            Err(_) => Err(Error::InvalidUtf8String),
113        }
114    }
115}
116
117impl_packable_for_num!(i8);
118impl_packable_for_num!(u8);
119impl_packable_for_num!(i16);
120impl_packable_for_num!(u16);
121impl_packable_for_num!(i32);
122impl_packable_for_num!(u32);
123impl_packable_for_num!(i64);
124impl_packable_for_num!(u64);
125impl_packable_for_num!(i128);
126impl_packable_for_num!(u128);