Skip to content
Snippets Groups Projects
bytes.rs 6.38 KiB
use {ByteBuf, SmallByteStr};
use traits::{Buf, ByteStr, ToBytes};
use std::{fmt, mem, ops, ptr};
use std::any::{Any, TypeId};

const INLINE: usize = 1;

/// A specialized `ByteStr` box.
pub struct Bytes {
    vtable: usize,
    data: *mut (),
}

impl Bytes {
    pub fn from_slice(bytes: &[u8]) -> Bytes {
        SmallByteStr::from_slice(bytes)
            .map(|small| Bytes::of(small))
            .unwrap_or_else(|| ByteBuf::from_slice(bytes).to_bytes())
    }

    pub fn of<B: ByteStr>(bytes: B) -> Bytes {
        unsafe {
            if inline::<B>() {
                let mut vtable;
                let mut data;

                {
                    let obj: &ByteStrPriv = &bytes;
                    let obj: TraitObject = mem::transmute(obj);
                    let ptr: *const *mut () = mem::transmute(obj.data);

                    data = *ptr;
                    vtable = obj.vtable;
                }

                // Prevent drop from being called
                mem::forget(bytes);

                Bytes {
                    vtable: vtable as usize | INLINE,
                    data: data,
                }
            } else {
                let obj: Box<ByteStrPriv> = Box::new(bytes);
                let obj: TraitObject = mem::transmute(obj);

                Bytes {
                    vtable: obj.vtable as usize,
                    data: obj.data,
                }
            }
        }
    }

    pub fn empty() -> Bytes {
        Bytes::of(SmallByteStr::zero())
    }

    /// If the underlying `ByteStr` is of type `B`, returns a reference to it
    /// otherwise None.
    pub fn downcast_ref<'a, B: ByteStr>(&'a self) -> Option<&'a B> {
        if TypeId::of::<B>() == self.obj().get_type_id() {
            unsafe {
                if inline::<B>() {
                    return Some(mem::transmute(&self.data));
                } else {
                    return Some(mem::transmute(self.data));
                }
            }
        }

        None
    }

    /// If the underlying `ByteStr` is of type `B`, returns the unwraped value,
    /// otherwise, returns the original `Bytes` as `Err`.
    pub fn try_unwrap<B: ByteStr>(self) -> Result<B, Bytes> {
        if TypeId::of::<B>() == self.obj().get_type_id() {
            unsafe {
                // Underlying ByteStr value is of the correct type. Unwrap it
                let mut ret;

                if inline::<B>() {
                    // The value is inline, read directly from the pointer
                    ret = ptr::read(mem::transmute(&self.data));
                } else {
                    ret = ptr::read(mem::transmute(self.data));
                }

                mem::forget(self);
                Ok(ret)
            }
        } else {
            Err(self)
        }
    }

    fn obj(&self) -> &ByteStrPriv {
        unsafe {
            let obj = if self.is_inline() {
                TraitObject {
                    data: mem::transmute(&self.data),
                    vtable: mem::transmute(self.vtable - 1),
                }
            } else {
                TraitObject {
                    data: self.data,
                    vtable: mem::transmute(self.vtable),
                }
            };

            mem::transmute(obj)
        }
    }

    fn obj_mut(&mut self) -> &mut ByteStrPriv {
        unsafe { mem::transmute(self.obj()) }
    }

    fn is_inline(&self) -> bool {
        (self.vtable & INLINE) == INLINE
    }
}

fn inline<B: ByteStr>() -> bool {
    mem::size_of::<B>() <= 2 * mem::size_of::<usize>()
}

impl ByteStr for Bytes {

    type Buf = Box<Buf+'static>;

    fn buf(&self) -> Box<Buf+'static> {
        self.obj().buf()
    }

    fn concat<B: ByteStr>(&self, other: &B) -> Bytes {
        self.obj().concat(&Bytes::of(other.clone()))
    }
    fn len(&self) -> usize {
        self.obj().len()
    }

    fn slice(&self, begin: usize, end: usize) -> Bytes {
        self.obj().slice(begin, end)
    }

    fn split_at(&self, mid: usize) -> (Bytes, Bytes) {
        self.obj().split_at(mid)
    }
}

impl ToBytes for Bytes {
    fn to_bytes(self) -> Bytes {
        self
    }
}

impl ops::Index<usize> for Bytes {
    type Output = u8;

    fn index(&self, index: usize) -> &u8 {
        self.obj().index(index)
    }
}

impl fmt::Debug for Bytes {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        super::debug(self, "Bytes", fmt)
    }
}

impl Clone for Bytes {
    fn clone(&self) -> Bytes {
        self.obj().clone()
    }
}

impl Drop for Bytes {
    fn drop(&mut self) {
        unsafe {
            if self.is_inline() {
                let obj = self.obj_mut();
                obj.drop();
            } else {
                let _: Box<ByteStrPriv> =
                    mem::transmute(self.obj());
            }
        }
    }
}

unsafe impl Send for Bytes { }
unsafe impl Sync for Bytes { }

trait ByteStrPriv {

    fn buf(&self) -> Box<Buf+'static>;

    fn clone(&self) -> Bytes;

    fn concat(&self, other: &Bytes) -> Bytes;

    fn drop(&mut self);

    fn get_type_id(&self) -> TypeId;

    fn index(&self, index: usize) -> &u8;
    fn len(&self) -> usize;

    fn slice(&self, begin: usize, end: usize) -> Bytes;

    fn split_at(&self, mid: usize) -> (Bytes, Bytes);
}

impl<B: ByteStr> ByteStrPriv for B {

    fn buf(&self) -> Box<Buf+'static> {
        Box::new(self.buf())
    }

    fn clone(&self) -> Bytes {
        Bytes::of(self.clone())
    }

    fn concat(&self, other: &Bytes) -> Bytes {
        self.concat(other)
    }

    fn drop(&mut self) {
        unsafe {
            ptr::read(mem::transmute(self))
        }
    }

    fn get_type_id(&self) -> TypeId {
        TypeId::of::<B>()
    }

    fn index(&self, index: usize) -> &u8 {
        ops::Index::index(self, index)
    }

    fn len(&self) -> usize {
        self.len()
    }

    fn slice(&self, begin: usize, end: usize) -> Bytes {
        self.slice(begin, end)
    }

    fn split_at(&self, mid: usize) -> (Bytes, Bytes) {
        self.split_at(mid)
    }
}

// TODO: Figure out how to not depend on the memory layout of trait objects
// Blocked: rust-lang/rust#24050
#[repr(C)]
struct TraitObject {
    data: *mut (),
    vtable: *mut (),
}

#[test]
pub fn test_size_of() {
    // TODO: One day, there shouldn't be a drop flag
    let ptr_size = mem::size_of::<usize>();
    let expect = ptr_size * 3;

    assert_eq!(expect, mem::size_of::<Bytes>());
    assert_eq!(expect + ptr_size, mem::size_of::<Option<Bytes>>());
}