content 0.1.0

Trait for content-adressable types
Documentation
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

use content::Content;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Write, Result, Error, ErrorKind};
use hash::NewHash;
use backend::Backend;

/// Storage implementations for common std things

impl<T> Content for Option<T> where T: Content {
	fn to_content(
		&self,
		sink: &mut Write,
		backend: &mut Backend,
	) -> Result<()> {
		match *self {
			Some(ref t) => {
				try!(sink.write(&[1]));
				t.to_content(sink, backend)
			},
			None => sink.write_all(&[0])
		}
	}
	fn from_content(
		source: &mut Read,
		newhash: &NewHash,
	) -> Result<Self> {
		let mut byte = [0];
		try!(source.read_exact(&mut byte));
		match byte[0] {
			0 => Ok(None),
			1 => Ok(Some(try!(T::from_content(source, newhash)))),
			_ => Err(Error::new(
				ErrorKind::Other, "Invalid Option<T> encoding!")
			),
		}
	}
}

// Box just needs to pass through the writers and readers
// of T, wrapping the read value in a new Box.
//
// This means that Box<T> and T have an identical content
// hash, differing only on the type level!
impl<T> Content for Box<T> where T: Content {
	fn to_content(
		&self,
		sink: &mut Write,
		backend: &mut Backend,
	) -> Result<()> {
		(self as &T).to_content(sink, backend)
	}
	fn from_content(
		source: &mut Read,
		newhash: &NewHash,
	) -> Result<Self> {
		Ok(Box::new(try!(T::from_content(source, newhash))))
	}
}

impl Content for u8 {
	fn to_content(&self, sink: &mut Write, _: &mut Backend) -> Result<()> {
		sink.write_all(&[*self])
	}
	fn from_content(source: &mut Read, _: &NewHash) -> Result<Self> {
		let b = &mut [0u8];
		try!(source.read_exact(b));
		Ok(b[0])
	}
}

macro_rules! number {
	( $t:ty: $read:ident, $write:ident ) => {
		impl Content for $t {
			fn to_content(
				&self,
				sink: &mut Write,
				_: &mut Backend,
			) -> Result<()> {
				sink.$write::<BigEndian>(*self)
			}
			fn from_content(
				source: &mut Read,
				_: &NewHash,
			) -> Result<Self> {
				source.$read::<BigEndian>()
			}
		}
	}
}

number!(u64: read_u64, write_u64);
number!(u32: read_u32, write_u32);
number!(u16: read_u16, write_u16);

#[cfg(test)]
mod tests {
	use test_common;
	use content::Content;
	use std::fmt::Debug;

	fn put_get<T: Content + Debug + PartialEq>(t: T) {
		let mut store = test_common::store();
		let hash = store.put(&t).unwrap();
		assert_eq!(store.get(&hash).unwrap(), t);
	}

	#[test]
	fn std() {
		put_get(Some(46u64));
		put_get(None as Option<u64>);
		put_get(38u64);
	}

}