german-str-borrow 0.1.0

German/Umbra-style strings. Data is allocated, borrowed, static, or inlined
Documentation
use crate::long_str::LongBStr;
use crate::short_str::ShortBStr;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::mem;
use std::mem::ManuallyDrop;
use std::ops::Deref;

#[repr(transparent)]
pub struct GermanBStr<'a>(BStrInner<'a>);
pub use long_str::StrAllocator;

mod long_str;
mod short_str;

#[repr(C)]
union BStrInner<'a> {
    head: BStrHead,
    short: ShortBStr,
    long: ManuallyDrop<LongBStr<'a>>,
}

#[derive(Clone, Copy, Eq, PartialEq)]
#[repr(C)]
struct BStrHead {
    len: u32,
    head: [u8; 4],
}

impl GermanBStr<'_> {
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

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

    fn head_bytes(&self) -> &[u8; 4] {
        &self.head().head
    }

    fn head(&self) -> &BStrHead {
        unsafe { &self.0.head }
    }
}

impl Deref for GermanBStr<'_> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        if self.len() <= 12 {
            unsafe { &self.0.short }
        } else {
            unsafe { &self.0.long }
        }
    }
}

macro_rules! define_constructor {
    ($name:ident,$ty:ty) => {
        pub fn $name(data: $ty) -> Self {
            if data.len() <= 12 {
                GermanBStr(BStrInner {
                    short: ShortBStr::new(data),
                })
            } else {
                GermanBStr(BStrInner {
                    long: ManuallyDrop::new(LongBStr::$name(data)),
                })
            }
        }
    };
}

impl<'a> GermanBStr<'a> {
    define_constructor!(new_static, &'static [u8]);
    define_constructor!(new_borrowed, &'a [u8]);
    define_constructor!(new_boxed, &[u8]);

    pub fn reallocate_borrowed<'b>(mut self, dst: impl StrAllocator<'b>) -> GermanBStr<'b> {
        unsafe {
            if self.len() <= 12 {
                GermanBStr(BStrInner {
                    short: self.0.short,
                })
            } else {
                let long = ManuallyDrop::take(&mut self.0.long);
                mem::forget(self);
                GermanBStr(BStrInner {
                    long: ManuallyDrop::new(long.reallocate_borrowed(dst)),
                })
            }
        }
    }
}

impl PartialOrd for GermanBStr<'_> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for GermanBStr<'_> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.head_bytes()
            .cmp(other.head_bytes())
            .then_with(|| <[u8]>::cmp(self, other))
    }
}

impl PartialEq for GermanBStr<'_> {
    fn eq(&self, other: &Self) -> bool {
        self.head() == other.head() && <[u8]>::eq(self, &**other)
    }
}

impl Eq for GermanBStr<'_> {}

impl Hash for GermanBStr<'_> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.deref().hash(state)
    }
}

impl Drop for GermanBStr<'_> {
    fn drop(&mut self) {
        if self.len() > 12 {
            unsafe { ManuallyDrop::drop(&mut self.0.long) }
        }
    }
}