use std::{mem, str};
use num_traits::ToPrimitive;
use traits::{Intern, Resolve, Len, SymbolId};
use {ErrorKind, Result};
use sym::{self, Symbol};
#[doc(hidden)]
pub trait Pack: Sized + PartialOrd {
fn is_inlined(&self) -> bool {
*self >= Self::msb_mask()
}
fn msb_mask() -> Self;
fn pack(s: &str) -> Option<Self>;
fn get_packed_ref(&self) -> Option<&str>;
}
macro_rules! msb_mask {
($T: tt, $N: expr) => ( (1 as $T) << ($N * 8 - 1) );
}
#[test]
fn test_msb_mask() {
assert_eq!(1 << 7, msb_mask!(u8, 1));
assert_eq!(1 << 15, msb_mask!(u16, 2));
assert_eq!(1 << 31, msb_mask!(u32, 4));
assert_eq!(1u64 << 63, msb_mask!(u64, 8));
}
macro_rules! impl_pack {
($T: tt, $N: expr) => {
impl Pack for $T {
fn msb_mask() -> Self {
msb_mask!($T, $N)
}
#[cfg(target_endian = "little")]
fn pack(s: &str) -> Option<Self> {
if s.len() >= $N { return None; }
let mut bytes = [0u8; $N];
bytes[0..s.len()].copy_from_slice(s.as_ref());
bytes[$N - 1] = s.len() as u8 | 0x80;
Some(unsafe { mem::transmute(bytes) })
}
#[cfg(target_endian = "big")]
fn pack(s: &str) -> Option<Self> {
if s.len() >= $N { return None; }
let mut bytes = [0u8; $N];
bytes[1..(s.len() + 1)].copy_from_slice(s.as_ref());
bytes[0] = s.len() as u8 | 0x80;
Some(unsafe { mem::transmute(bytes) })
}
#[cfg(target_endian = "little")]
fn get_packed_ref(&self) -> Option<&str> {
if ! self.is_inlined() { return None; }
unsafe {
let bytes: &[u8; $N] = mem::transmute(self);
let len = (bytes[$N - 1] & ! 0x80) as usize;
Some(str::from_utf8_unchecked(&bytes[0..len]))
}
}
#[cfg(target_endian = "big")]
fn get_packed_ref(&self) -> Option<&str> {
if ! self.is_inlined() { return None; }
unsafe {
let bytes: &[u8; $N] = mem::transmute(self);
let len = (bytes[0] & ! 0x80) as usize;
match str::from_utf8_unchecked(&bytes[1..(len + 1)]) {
Ok(s) => Some(s),
Err(_) => None
}
}
}
}
}
}
impl_pack!(u16, 2);
impl_pack!(u32, 4);
impl_pack!(u64, 8);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Sym<S> {
wrapped: S
}
impl<S> sym::Symbol for Sym<S>
where S: sym::Symbol
{
type Id = S::Id;
#[cfg(debug_assertions)]
fn pool_id(&self) -> ::sym::PoolId {
self.wrapped.pool_id()
}
fn id(&self) -> Self::Id { self.wrapped.id() }
fn id_ref(&self) -> &Self::Id { self.wrapped.id_ref() }
#[cfg(not(debug_assertions))]
fn create(id: Self::Id) -> Self {
Sym{wrapped: <S as ::sym::Symbol>::create(id)}
}
#[cfg(debug_assertions)]
fn create(id: Self::Id, pool_id: ::sym::PoolId) -> Self {
Sym{wrapped: <S as ::sym::Symbol>::create(id, pool_id)}
}
}
impl<S> From<S> for Sym<S> {
fn from(s: S) -> Self {
Sym{wrapped: s}
}
}
#[derive(Copy, Clone, Debug)]
pub struct Inline<W> {
wrapped: W
}
impl<W> Inline<W> {
pub fn new() -> Self
where W: Default
{
Default::default()
}
}
impl<W> Default for Inline<W>
where W: Default
{
fn default() -> Self {
Inline{wrapped: Default::default()}
}
}
impl<W> From<W> for Inline<W> {
fn from(w: W) -> Self {
Inline{wrapped: w}
}
}
impl<W> Len for Inline<W>
where W: Len + ::sym::Pool,
<<W as sym::Pool>::Symbol as sym::Symbol>::Id: Pack + ToPrimitive
{
fn len(&self) -> usize {
(&self.wrapped).len()
}
fn is_empty(&self) -> bool {
(&self.wrapped).is_empty()
}
fn is_full(&self) -> bool {
(&self.wrapped).len() >= <<<W as sym::Pool>::Symbol as sym::Symbol>::Id as Pack>::msb_mask().to_usize().unwrap()
}
}
impl<W> ::sym::Pool for Inline<W>
where W: sym::Pool,
<<W as sym::Pool>::Symbol as sym::Symbol>::Id: Pack,
{
type Symbol = W::Symbol;
#[cfg(debug_assertions)]
fn id(&self) -> ::sym::PoolId {
self.wrapped.id()
}
fn create_symbol(&self, id: <<W as sym::Pool>::Symbol as ::sym::Symbol>::Id) -> Self::Symbol {
<W as sym::Pool>::create_symbol(&self.wrapped, id).into()
}
}
macro_rules! impl_intern {
($($mutt: tt)*) => {
impl<'a, W, WS> Intern for &'a $($mutt)* Inline<W>
where W: Len + sym::Pool<Symbol=WS>,
&'a $($mutt)* W: Intern<Input=str,Symbol=<W as sym::Pool>::Symbol>,
WS: sym::Symbol,
WS::Id: Pack
{
type Input = str;
type Symbol = Sym<WS>;
fn intern(self, s: &Self::Input) -> Result<Self::Symbol> {
match WS::Id::pack(s) {
Some(id) => Ok(Sym{wrapped: self.wrapped.create_symbol(id)}),
None => {
if self.is_full() {
Err(ErrorKind::PoolOverflow.into())
} else {
match self.wrapped.intern(s) {
Ok(b) => Ok(b.into()),
Err(e) => Err(e)
}
}
}
}
}
}
}
}
impl_intern!();
impl_intern!(mut);
impl<'a, W, WS> Resolve for &'a Inline<W>
where for<'b> &'b W: Resolve<Input=WS, Output=&'b str>,
WS: 'a + sym::Symbol,
WS::Id: Pack + SymbolId,
{
type Input = &'a Sym<WS>;
type Output = &'a str;
fn resolve(self, symbol: Self::Input) -> Result<Self::Output>
{
match symbol.id_ref().get_packed_ref() {
Some(s) => Ok(s),
None => self.wrapped.resolve(symbol.wrapped)
}
}
}
#[cfg(test)]
mod tests {
use super::{Inline, Pack};
use sym::Symbol;
use traits::{Intern, Resolve, Len};
#[test]
fn inlined_values_do_not_affect_size() {
let mut pool = Inline::<::basic::Pool<str,u16>>::new();
assert!(pool.is_empty());
let x = pool.intern("x").expect("failed to intern single-character string");
assert_eq!(0, pool.len());
assert!(x.id().is_inlined());
assert_eq!(Ok("x"), pool.resolve(&x));
let xy = pool.intern("xy").expect("failed to intern two-character string");
assert_eq!(1, pool.len());
assert!(! xy.id().is_inlined());
assert_eq!(Ok("xy"), pool.resolve(&xy));
}
#[cfg(feature="composition-tests")]
#[test]
fn can_stack_inliners() {
let mut pool = Inline::<Inline<::basic::Pool<str,u16>>>::new();
let xy = pool.intern("xy").expect("failed to intern two-character string");
assert_eq!(Ok("xy"), pool.resolve(&xy));
}
}