-
Carl Lerche authoredCarl Lerche authored
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>>());
}