X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Futil%2Fser.rs;h=5b1a86a6a95f27a276f1dc221bd705f57fbf8ed4;hb=b76040718f954fa7baae42c7070e3ff6ac8add3c;hp=fe331f6bf4d748cb7f74a22607b87f0622a03589;hpb=002a5db5b07fd317545d3aa4f1a217cd40b608e2;p=rust-lightning diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index fe331f6b..5b1a86a6 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -11,14 +11,17 @@ //! as ChannelsManagers and ChannelMonitors. use prelude::*; -use std::io::{Read, Write}; +use io::{self, Read, Write}; +use io_extras::{copy, sink}; use core::hash::Hash; 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; @@ -26,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}; @@ -34,42 +38,35 @@ 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> { #[inline] - fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> { + fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> { self.0.write_all(buf) } #[inline] - fn write(&mut self, buf: &[u8]) -> Result { + fn write(&mut self, buf: &[u8]) -> Result { self.0.write_all(buf)?; Ok(buf.len()) } #[inline] - fn flush(&mut self) -> Result<(), ::std::io::Error> { + fn flush(&mut self) -> Result<(), io::Error> { Ok(()) } } @@ -77,14 +74,10 @@ impl<'a, W: Writer + 'a> Write for WriterWriteAdaptor<'a, W> { pub(crate) struct VecWriter(pub Vec); impl Writer for VecWriter { #[inline] - fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> { + fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> { self.0.extend_from_slice(buf); Ok(()) } - #[inline] - 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 @@ -92,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 @@ -119,7 +110,7 @@ impl FixedLengthReader { #[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 { @@ -129,7 +120,7 @@ impl FixedLengthReader { } impl Read for FixedLengthReader { #[inline] - fn read(&mut self, dest: &mut [u8]) -> Result { + fn read(&mut self, dest: &mut [u8]) -> Result { if self.total_bytes == self.bytes_read { Ok(0) } else { @@ -145,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 { @@ -158,7 +156,7 @@ impl ReadTrackingReader { } impl Read for ReadTrackingReader { #[inline] - fn read(&mut self, dest: &mut [u8]) -> Result { + fn read(&mut self, dest: &mut [u8]) -> Result { match self.read.read(dest) { Ok(0) => Ok(0), Ok(len) => { @@ -175,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 { @@ -185,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(); @@ -206,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 @@ -230,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 @@ -240,6 +254,13 @@ 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] @@ -247,12 +268,18 @@ impl Readable for OptionDeserWrapper { 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)) } +} /// 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> { #[inline] - fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { for ref v in self.0.iter() { v.write(writer)?; } @@ -261,15 +288,16 @@ impl<'a, T: Writeable> Writeable for VecWriteWrapper<'a, T> { } /// 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 { +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 Readable::read(&mut track_read) { - Ok(v) => { values.push(v); }, + 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, @@ -283,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)) } } @@ -303,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) @@ -370,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]) } @@ -424,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]) } } @@ -439,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}]) } } @@ -461,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) } } @@ -478,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 @@ -494,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)?; @@ -506,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) } @@ -522,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) } @@ -540,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)?; @@ -560,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()) } @@ -582,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] @@ -602,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) @@ -624,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[..]) } } @@ -639,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] @@ -659,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) } } @@ -672,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) } } @@ -685,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) } } @@ -698,7 +761,7 @@ impl Readable for PaymentSecret { } impl Writeable for Box { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + fn write(&self, w: &mut W) -> Result<(), io::Error> { T::write(&**self, w) } } @@ -710,7 +773,7 @@ impl Readable for Box { } 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) => { @@ -725,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); @@ -736,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[..]) } } @@ -751,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[..]) } } @@ -766,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(()) @@ -787,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), @@ -799,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), } @@ -817,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) } } @@ -830,7 +894,7 @@ 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) } @@ -845,9 +909,147 @@ impl Readable for (A, B, C) { } } impl Writeable for (A, B, C) { - 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)?; 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"); + } +}