X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Futil%2Fser.rs;h=5b1a86a6a95f27a276f1dc221bd705f57fbf8ed4;hb=82474d356403d6b91c6b8ad1937e942f53f809e8;hp=61cd1e60de0ae0a57691fa8b5b858c766c451523;hpb=c632fffc3ddb98da0ba918a77317fdda504116f3;p=rust-lightning diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 61cd1e60..5b1a86a6 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -11,15 +11,17 @@ //! as ChannelsManagers and ChannelMonitors. use prelude::*; -use std::io::{Read, Write}; -use std::collections::HashMap; +use io::{self, Read, Write}; +use io_extras::{copy, sink}; use core::hash::Hash; -use std::sync::Mutex; +use sync::Mutex; use core::cmp; +use core::convert::TryFrom; +use core::ops::Deref; -use bitcoin::secp256k1::Signature; -use bitcoin::secp256k1::key::{PublicKey, SecretKey}; +use bitcoin::secp256k1::{PublicKey, SecretKey}; use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE}; +use bitcoin::secp256k1::ecdsa::Signature; use bitcoin::blockdata::script::Script; use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut}; use bitcoin::consensus; @@ -27,6 +29,7 @@ use bitcoin::consensus::Encodable; use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hash_types::{Txid, BlockHash}; use core::marker::Sized; +use core::time::Duration; use ln::msgs::DecodeError; use ln::{PaymentPreimage, PaymentHash, PaymentSecret}; @@ -35,52 +38,46 @@ use util::byte_utils::{be48_to_array, slice_to_be48}; /// serialization buffer size pub const MAX_BUF_SIZE: usize = 64 * 1024; -/// A trait that is similar to std::io::Write but has one extra function which can be used to size -/// buffers being written into. -/// An impl is provided for any type that also impls std::io::Write which simply ignores size -/// hints. +/// A simplified version of std::io::Write that exists largely for backwards compatibility. +/// An impl is provided for any type that also impls std::io::Write. /// /// (C-not exported) as we only export serialization to/from byte arrays instead pub trait Writer { /// Writes the given buf out. See std::io::Write::write_all for more - fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error>; - /// Hints that data of the given size is about the be written. This may not always be called - /// prior to data being written and may be safely ignored. - fn size_hint(&mut self, size: usize); + fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error>; } impl Writer for W { #[inline] - fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> { - ::write_all(self, buf) + fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> { + ::write_all(self, buf) } - #[inline] - fn size_hint(&mut self, _size: usize) { } } pub(crate) struct WriterWriteAdaptor<'a, W: Writer + 'a>(pub &'a mut W); impl<'a, W: Writer + 'a> Write for WriterWriteAdaptor<'a, W> { - fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> { self.0.write_all(buf) } - fn write(&mut self, buf: &[u8]) -> Result { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { self.0.write_all(buf)?; Ok(buf.len()) } - fn flush(&mut self) -> Result<(), ::std::io::Error> { + #[inline] + fn flush(&mut self) -> Result<(), io::Error> { Ok(()) } } pub(crate) struct VecWriter(pub Vec); impl Writer for VecWriter { - fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> { self.0.extend_from_slice(buf); Ok(()) } - fn size_hint(&mut self, size: usize) { - self.0.reserve_exact(size); - } } /// Writer that only tracks the amount of data written - useful if you need to calculate the length @@ -88,12 +85,10 @@ impl Writer for VecWriter { pub(crate) struct LengthCalculatingWriter(pub usize); impl Writer for LengthCalculatingWriter { #[inline] - fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> { + fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> { self.0 += buf.len(); Ok(()) } - #[inline] - fn size_hint(&mut self, _size: usize) {} } /// Essentially std::io::Take but a bit simpler and with a method to walk the underlying stream @@ -108,12 +103,14 @@ impl FixedLengthReader { Self { read, bytes_read: 0, total_bytes } } + #[inline] pub fn bytes_remain(&mut self) -> bool { self.bytes_read != self.total_bytes } + #[inline] pub fn eat_remaining(&mut self) -> Result<(), DecodeError> { - ::std::io::copy(self, &mut ::std::io::sink()).unwrap(); + copy(self, &mut sink()).unwrap(); if self.bytes_read != self.total_bytes { Err(DecodeError::ShortRead) } else { @@ -122,7 +119,8 @@ impl FixedLengthReader { } } impl Read for FixedLengthReader { - fn read(&mut self, dest: &mut [u8]) -> Result { + #[inline] + fn read(&mut self, dest: &mut [u8]) -> Result { if self.total_bytes == self.bytes_read { Ok(0) } else { @@ -138,6 +136,13 @@ impl Read for FixedLengthReader { } } +impl LengthRead for FixedLengthReader { + #[inline] + fn total_bytes(&self) -> u64 { + self.total_bytes + } +} + /// A Read which tracks whether any bytes have been read at all. This allows us to distinguish /// between "EOF reached before we started" and "EOF reached mid-read". pub(crate) struct ReadTrackingReader { @@ -150,7 +155,8 @@ impl ReadTrackingReader { } } impl Read for ReadTrackingReader { - fn read(&mut self, dest: &mut [u8]) -> Result { + #[inline] + fn read(&mut self, dest: &mut [u8]) -> Result { match self.read.read(dest) { Ok(0) => Ok(0), Ok(len) => { @@ -167,7 +173,7 @@ impl Read for ReadTrackingReader { /// (C-not exported) as we only export serialization to/from byte arrays instead pub trait Writeable { /// Writes self out to the given Writer - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error>; + fn write(&self, writer: &mut W) -> Result<(), io::Error>; /// Writes self out to a Vec fn encode(&self) -> Vec { @@ -177,6 +183,7 @@ pub trait Writeable { } /// Writes self out to a Vec + #[cfg(test)] fn encode_with_len(&self) -> Vec { let mut msg = VecWriter(Vec::new()); 0u16.write(&mut msg).unwrap(); @@ -198,7 +205,7 @@ pub trait Writeable { } impl<'a, T: Writeable> Writeable for &'a T { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { (*self).write(writer) } + fn write(&self, writer: &mut W) -> Result<(), io::Error> { (*self).write(writer) } } /// A trait that various rust-lightning types implement allowing them to be read in from a Read @@ -222,6 +229,21 @@ pub trait ReadableArgs

fn read(reader: &mut R, params: P) -> Result; } +/// A std::io::Read that also provides the total bytes available to read. +pub(crate) trait LengthRead: Read { + /// The total number of bytes available to read. + fn total_bytes(&self) -> u64; +} + +/// A trait that various higher-level rust-lightning types implement allowing them to be read in +/// from a Read given some additional set of arguments which is required to deserialize, requiring +/// the implementer to provide the total length of the read. +pub(crate) trait LengthReadableArgs

where Self: Sized +{ + /// Reads a Self in from the given LengthRead + fn read(reader: &mut R, params: P) -> Result; +} + /// A trait that various rust-lightning types implement allowing them to (maybe) be read in from a Read /// /// (C-not exported) as we only export serialization to/from byte arrays instead @@ -232,33 +254,53 @@ pub trait MaybeReadable fn read(reader: &mut R) -> Result, DecodeError>; } +impl MaybeReadable for T { + #[inline] + fn read(reader: &mut R) -> Result, DecodeError> { + Ok(Some(Readable::read(reader)?)) + } +} + pub(crate) struct OptionDeserWrapper(pub Option); impl Readable for OptionDeserWrapper { + #[inline] fn read(reader: &mut R) -> Result { Ok(Self(Some(Readable::read(reader)?))) } } +/// When handling default_values, we want to map the default-value T directly +/// to a OptionDeserWrapper in a way that works for `field: T = t;` as +/// well. Thus, we assume `Into for T` does nothing and use that. +impl From for OptionDeserWrapper { + fn from(t: T) -> OptionDeserWrapper { OptionDeserWrapper(Some(t)) } +} -const MAX_ALLOC_SIZE: u64 = 64*1024; - +/// Wrapper to write each element of a Vec with no length prefix pub(crate) struct VecWriteWrapper<'a, T: Writeable>(pub &'a Vec); impl<'a, T: Writeable> Writeable for VecWriteWrapper<'a, T> { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { - (self.0.len() as u64).write(writer)?; + #[inline] + fn write(&self, writer: &mut W) -> Result<(), io::Error> { for ref v in self.0.iter() { v.write(writer)?; } Ok(()) } } -pub(crate) struct VecReadWrapper(pub Vec); -impl Readable for VecReadWrapper { - fn read(reader: &mut R) -> Result { - let count: u64 = Readable::read(reader)?; - let mut values = Vec::with_capacity(cmp::min(count, MAX_ALLOC_SIZE / (core::mem::size_of::() as u64)) as usize); - for _ in 0..count { - match Readable::read(reader) { - Ok(v) => { values.push(v); }, + +/// Wrapper to read elements from a given stream until it reaches the end of the stream. +pub(crate) struct VecReadWrapper(pub Vec); +impl Readable for VecReadWrapper { + #[inline] + fn read(mut reader: &mut R) -> Result { + let mut values = Vec::new(); + loop { + let mut track_read = ReadTrackingReader::new(&mut reader); + match MaybeReadable::read(&mut track_read) { + Ok(Some(v)) => { values.push(v); }, + Ok(None) => { }, + // If we failed to read any bytes at all, we reached the end of our TLV + // stream and have simply exhausted all entries. + Err(ref e) if e == &DecodeError::ShortRead && !track_read.have_read => break, Err(e) => return Err(e), } } @@ -269,7 +311,7 @@ impl Readable for VecReadWrapper { pub(crate) struct U48(pub u64); impl Writeable for U48 { #[inline] - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { writer.write_all(&be48_to_array(self.0)) } } @@ -289,10 +331,10 @@ impl Readable for U48 { /// encoded in several different ways, which we must check for at deserialization-time. Thus, if /// you're looking for an example of a variable-length integer to use for your own project, move /// along, this is a rather poor design. -pub(crate) struct BigSize(pub u64); +pub struct BigSize(pub u64); impl Writeable for BigSize { #[inline] - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { match self.0 { 0...0xFC => { (self.0 as u8).write(writer) @@ -356,13 +398,13 @@ macro_rules! impl_writeable_primitive { ($val_type:ty, $len: expr) => { impl Writeable for $val_type { #[inline] - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { writer.write_all(&self.to_be_bytes()) } } impl Writeable for HighZeroBytesDroppedVarInt<$val_type> { #[inline] - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { // Skip any full leading 0 bytes when writing (in BE): writer.write_all(&self.0.to_be_bytes()[(self.0.leading_zeros()/8) as usize..$len]) } @@ -410,7 +452,7 @@ impl_writeable_primitive!(u16, 2); impl Writeable for u8 { #[inline] - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { writer.write_all(&[*self]) } } @@ -425,7 +467,7 @@ impl Readable for u8 { impl Writeable for bool { #[inline] - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { writer.write_all(&[if *self {1} else {0}]) } } @@ -447,7 +489,7 @@ macro_rules! impl_array { impl Writeable for [u8; $size] { #[inline] - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { w.write_all(self) } } @@ -464,10 +506,9 @@ macro_rules! impl_array { ); } -//TODO: performance issue with [u8; size] with impl_array!() impl_array!(3); // for rgb impl_array!(4); // for IPv4 -impl_array!(10); // for OnionV2 +impl_array!(12); // for OnionV2 impl_array!(16); // for IPv6 impl_array!(32); // for channel id & hmac impl_array!(PUBLIC_KEY_SIZE); // for PublicKey @@ -480,7 +521,7 @@ impl Writeable for HashMap V: Writeable { #[inline] - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { (self.len() as u16).write(w)?; for (key, value) in self.iter() { key.write(w)?; @@ -492,14 +533,50 @@ impl Writeable for HashMap impl Readable for HashMap where K: Readable + Eq + Hash, - V: Readable + V: MaybeReadable { #[inline] fn read(r: &mut R) -> Result { let len: u16 = Readable::read(r)?; let mut ret = HashMap::with_capacity(len as usize); for _ in 0..len { - ret.insert(K::read(r)?, V::read(r)?); + let k = K::read(r)?; + let v_opt = V::read(r)?; + if let Some(v) = v_opt { + if ret.insert(k, v).is_some() { + return Err(DecodeError::InvalidValue); + } + } + } + Ok(ret) + } +} + +// HashSet +impl Writeable for HashSet +where T: Writeable + Eq + Hash +{ + #[inline] + fn write(&self, w: &mut W) -> Result<(), io::Error> { + (self.len() as u16).write(w)?; + for item in self.iter() { + item.write(w)?; + } + Ok(()) + } +} + +impl Readable for HashSet +where T: Readable + Eq + Hash +{ + #[inline] + fn read(r: &mut R) -> Result { + let len: u16 = Readable::read(r)?; + let mut ret = HashSet::with_capacity(len as usize); + for _ in 0..len { + if !ret.insert(T::read(r)?) { + return Err(DecodeError::InvalidValue) + } } Ok(ret) } @@ -508,7 +585,7 @@ impl Readable for HashMap // Vectors impl Writeable for Vec { #[inline] - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { (self.len() as u16).write(w)?; w.write_all(&self) } @@ -526,7 +603,7 @@ impl Readable for Vec { } impl Writeable for Vec { #[inline] - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { (self.len() as u16).write(w)?; for e in self.iter() { e.write(w)?; @@ -546,13 +623,13 @@ impl Readable for Vec { return Err(DecodeError::BadLengthDescriptor); } let mut ret = Vec::with_capacity(len as usize); - for _ in 0..len { ret.push(Signature::read(r)?); } + for _ in 0..len { ret.push(Readable::read(r)?); } Ok(ret) } } impl Writeable for Script { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { (self.len() as u16).write(w)?; w.write_all(self.as_bytes()) } @@ -568,7 +645,7 @@ impl Readable for Script { } impl Writeable for PublicKey { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { self.serialize().write(w) } #[inline] @@ -588,7 +665,7 @@ impl Readable for PublicKey { } impl Writeable for SecretKey { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { let mut ser = [0; SECRET_KEY_SIZE]; ser.copy_from_slice(&self[..]); ser.write(w) @@ -610,7 +687,7 @@ impl Readable for SecretKey { } impl Writeable for Sha256dHash { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { w.write_all(&self[..]) } } @@ -625,7 +702,7 @@ impl Readable for Sha256dHash { } impl Writeable for Signature { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { self.serialize_compact().write(w) } #[inline] @@ -645,7 +722,7 @@ impl Readable for Signature { } impl Writeable for PaymentPreimage { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { self.0.write(w) } } @@ -658,7 +735,7 @@ impl Readable for PaymentPreimage { } impl Writeable for PaymentHash { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { self.0.write(w) } } @@ -671,7 +748,7 @@ impl Readable for PaymentHash { } impl Writeable for PaymentSecret { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { self.0.write(w) } } @@ -683,8 +760,20 @@ impl Readable for PaymentSecret { } } +impl Writeable for Box { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + T::write(&**self, w) + } +} + +impl Readable for Box { + fn read(r: &mut R) -> Result { + Ok(Box::new(Readable::read(r)?)) + } +} + impl Writeable for Option { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { match *self { None => 0u8.write(w)?, Some(ref data) => { @@ -699,7 +788,8 @@ impl Writeable for Option { impl Readable for Option { fn read(r: &mut R) -> Result { - match BigSize::read(r)?.0 { + let len: BigSize = Readable::read(r)?; + match len.0 { 0 => Ok(None), len => { let mut reader = FixedLengthReader::new(r, len - 1); @@ -710,7 +800,7 @@ impl Readable for Option } impl Writeable for Txid { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { w.write_all(&self[..]) } } @@ -725,7 +815,7 @@ impl Readable for Txid { } impl Writeable for BlockHash { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { w.write_all(&self[..]) } } @@ -740,7 +830,7 @@ impl Readable for BlockHash { } impl Writeable for OutPoint { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { self.txid.write(w)?; self.vout.write(w)?; Ok(()) @@ -761,7 +851,7 @@ impl Readable for OutPoint { macro_rules! impl_consensus_ser { ($bitcoin_type: ty) => { impl Writeable for $bitcoin_type { - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { match self.consensus_encode(WriterWriteAdaptor(writer)) { Ok(_) => Ok(()), Err(e) => Err(e), @@ -773,7 +863,7 @@ macro_rules! impl_consensus_ser { fn read(r: &mut R) -> Result { match consensus::encode::Decodable::consensus_decode(r) { Ok(t) => Ok(t), - Err(consensus::encode::Error::Io(ref e)) if e.kind() == ::std::io::ErrorKind::UnexpectedEof => Err(DecodeError::ShortRead), + Err(consensus::encode::Error::Io(ref e)) if e.kind() == io::ErrorKind::UnexpectedEof => Err(DecodeError::ShortRead), Err(consensus::encode::Error::Io(e)) => Err(DecodeError::Io(e.kind())), Err(_) => Err(DecodeError::InvalidValue), } @@ -791,7 +881,7 @@ impl Readable for Mutex { } } impl Writeable for Mutex { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { self.lock().unwrap().write(w) } } @@ -804,8 +894,162 @@ impl Readable for (A, B) { } } impl Writeable for (A, B) { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { self.0.write(w)?; self.1.write(w) } } + +impl Readable for (A, B, C) { + fn read(r: &mut R) -> Result { + let a: A = Readable::read(r)?; + let b: B = Readable::read(r)?; + let c: C = Readable::read(r)?; + Ok((a, b, c)) + } +} +impl Writeable for (A, B, C) { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.0.write(w)?; + self.1.write(w)?; + self.2.write(w) + } +} + +impl Writeable for () { + fn write(&self, _: &mut W) -> Result<(), io::Error> { + Ok(()) + } +} +impl Readable for () { + fn read(_r: &mut R) -> Result { + Ok(()) + } +} + +impl Writeable for String { + #[inline] + fn write(&self, w: &mut W) -> Result<(), io::Error> { + (self.len() as u16).write(w)?; + w.write_all(self.as_bytes()) + } +} +impl Readable for String { + #[inline] + fn read(r: &mut R) -> Result { + let v: Vec = Readable::read(r)?; + let ret = String::from_utf8(v).map_err(|_| DecodeError::InvalidValue)?; + Ok(ret) + } +} + +/// Represents a hostname for serialization purposes. +/// Only the character set and length will be validated. +/// The character set consists of ASCII alphanumeric characters, hyphens, and periods. +/// Its length is guaranteed to be representable by a single byte. +/// This serialization is used by BOLT 7 hostnames. +#[derive(Clone, Debug, PartialEq)] +pub struct Hostname(String); +impl Hostname { + /// Returns the length of the hostname. + pub fn len(&self) -> u8 { + (&self.0).len() as u8 + } +} +impl Deref for Hostname { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl From for String { + fn from(hostname: Hostname) -> Self { + hostname.0 + } +} +impl TryFrom> for Hostname { + type Error = (); + + fn try_from(bytes: Vec) -> Result { + if let Ok(s) = String::from_utf8(bytes) { + Hostname::try_from(s) + } else { + Err(()) + } + } +} +impl TryFrom for Hostname { + type Error = (); + + fn try_from(s: String) -> Result { + if s.len() <= 255 && s.chars().all(|c| + c.is_ascii_alphanumeric() || + c == '.' || + c == '-' + ) { + Ok(Hostname(s)) + } else { + Err(()) + } + } +} +impl Writeable for Hostname { + #[inline] + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.len().write(w)?; + w.write_all(self.as_bytes()) + } +} +impl Readable for Hostname { + #[inline] + fn read(r: &mut R) -> Result { + let len: u8 = Readable::read(r)?; + let mut vec = Vec::with_capacity(len.into()); + vec.resize(len.into(), 0); + r.read_exact(&mut vec)?; + Hostname::try_from(vec).map_err(|_| DecodeError::InvalidValue) + } +} + +impl Writeable for Duration { + #[inline] + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.as_secs().write(w)?; + self.subsec_nanos().write(w) + } +} +impl Readable for Duration { + #[inline] + fn read(r: &mut R) -> Result { + let secs = Readable::read(r)?; + let nanos = Readable::read(r)?; + Ok(Duration::new(secs, nanos)) + } +} + +#[cfg(test)] +mod tests { + use core::convert::TryFrom; + use util::ser::{Readable, Hostname, Writeable}; + + #[test] + fn hostname_conversion() { + assert_eq!(Hostname::try_from(String::from("a-test.com")).unwrap().as_str(), "a-test.com"); + + assert!(Hostname::try_from(String::from("\"")).is_err()); + assert!(Hostname::try_from(String::from("$")).is_err()); + assert!(Hostname::try_from(String::from("⚡")).is_err()); + let mut large_vec = Vec::with_capacity(256); + large_vec.resize(256, b'A'); + assert!(Hostname::try_from(String::from_utf8(large_vec).unwrap()).is_err()); + } + + #[test] + fn hostname_serialization() { + let hostname = Hostname::try_from(String::from("test")).unwrap(); + let mut buf: Vec = Vec::new(); + hostname.write(&mut buf).unwrap(); + assert_eq!(Hostname::read(&mut buf.as_slice()).unwrap().as_str(), "test"); + } +}