use core::{fmt, fmt::Debug};
use std::{
borrow::Cow,
hash::{Hash, Hasher},
marker::PhantomData,
mem::ManuallyDrop,
};
use crate::errors::SetBytesError;
#[derive(Eq, PartialOrd, Ord)]
pub struct Bytes<'a> {
data: BytesInner,
_lt: PhantomData<&'a [u8]>,
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum BytesInner {
Borrowed(*const u8, u32),
Owned(*mut u8, u32),
}
impl<'a> PartialEq<str> for Bytes<'a> {
#[inline]
fn eq(&self, other: &str) -> bool {
self == other.as_bytes()
}
}
impl<'a> PartialEq<[u8]> for Bytes<'a> {
#[inline]
fn eq(&self, other: &[u8]) -> bool {
self.as_bytes() == other
}
}
impl<'a> PartialEq for Bytes<'a> {
#[inline]
fn eq(&self, other: &Self) -> bool {
let this = self.as_bytes();
let that = other.as_bytes();
this == that
}
}
impl<'a> Hash for Bytes<'a> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
let this = self.as_bytes();
this.hash(state);
}
}
impl<'a> Clone for Bytes<'a> {
fn clone(&self) -> Self {
match &self.data {
BytesInner::Borrowed(data, len) => {
Bytes::from(unsafe { compact_bytes_to_slice(*data, *len) })
}
BytesInner::Owned(data, len) => {
let (ptr, len) = unsafe { clone_compact_bytes_parts(*data, *len) };
Bytes {
data: BytesInner::Owned(ptr, len),
_lt: PhantomData,
}
}
}
}
}
impl<'a> From<&'a str> for Bytes<'a> {
#[inline]
fn from(s: &'a str) -> Self {
<Self as From<&'a [u8]>>::from(s.as_bytes())
}
}
impl<'a> From<&'a [u8]> for Bytes<'a> {
#[inline]
fn from(s: &'a [u8]) -> Self {
Bytes {
data: BytesInner::Borrowed(s.as_ptr(), s.len() as u32),
_lt: PhantomData,
}
}
}
impl TryFrom<String> for Bytes<'static> {
type Error = SetBytesError;
#[inline]
fn try_from(s: String) -> Result<Self, Self::Error> {
let mut bytes = Bytes::new();
bytes.set(s)?;
Ok(bytes)
}
}
#[inline]
unsafe fn compact_bytes_to_slice<'a>(ptr: *const u8, l: u32) -> &'a [u8] {
std::slice::from_raw_parts(ptr, l as usize)
}
unsafe fn boxed_slice_into_compact_parts(slice: Box<[u8]>) -> (*mut u8, u32) {
let mut slice = ManuallyDrop::new(slice);
let len = slice.len();
let ptr = slice.as_mut_ptr();
(ptr, len as u32)
}
#[inline]
unsafe fn clone_compact_bytes_parts(ptr: *mut u8, len: u32) -> (*mut u8, u32) {
let slice = compact_bytes_to_slice(ptr, len).to_vec().into_boxed_slice();
boxed_slice_into_compact_parts(slice)
}
impl<'a> Debug for Bytes<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Bytes").field(&self.as_utf8_str()).finish()
}
}
impl<'a> Bytes<'a> {
#[inline]
pub fn new() -> Self {
Self {
data: BytesInner::Borrowed("".as_bytes().as_ptr(), 0),
_lt: PhantomData,
}
}
#[inline]
pub fn as_utf8_str(&self) -> Cow<'_, str> {
String::from_utf8_lossy(self.as_bytes())
}
#[inline]
pub fn try_as_utf8_str(&self) -> Option<&str> {
std::str::from_utf8(self.as_bytes()).ok()
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
match &self.data {
BytesInner::Borrowed(b, l) => unsafe { compact_bytes_to_slice(*b, *l) },
BytesInner::Owned(o, l) => unsafe { compact_bytes_to_slice(*o, *l) },
}
}
#[inline]
pub fn as_bytes_borrowed(&self) -> Option<&'a [u8]> {
match &self.data {
BytesInner::Borrowed(b, l) => Some(unsafe { compact_bytes_to_slice(*b, *l) }),
_ => None,
}
}
#[inline]
pub fn as_ptr(&self) -> *const u8 {
match &self.data {
BytesInner::Borrowed(b, _) => *b,
BytesInner::Owned(o, _) => *o,
}
}
pub fn set<B: IntoOwnedBytes>(&mut self, data: B) -> Result<Option<Box<[u8]>>, SetBytesError> {
const MAX: usize = u32::MAX as usize;
let data = <B as IntoOwnedBytes>::into_bytes(data);
if data.len() > MAX {
return Err(SetBytesError::LengthOverflow);
}
Ok(unsafe { self.set_unchecked(data) })
}
#[inline]
pub unsafe fn set_unchecked<B: IntoOwnedBytes>(&mut self, data: B) -> Option<Box<[u8]>> {
let data = <B as IntoOwnedBytes>::into_bytes(data);
let (ptr, len) = boxed_slice_into_compact_parts(data);
let bytes = BytesInner::Owned(ptr, len);
let old = std::mem::replace(&mut self.data, bytes);
let old = ManuallyDrop::new(old);
match &*old {
BytesInner::Borrowed(_, _) => None,
BytesInner::Owned(ptr, len) => {
let len = *len as usize;
Some(Vec::from_raw_parts(*ptr, len, len).into_boxed_slice())
}
}
}
}
mod private {
pub trait Sealed {}
}
pub trait IntoOwnedBytes: private::Sealed {
fn into_bytes(self) -> Box<[u8]>;
}
macro_rules! impl_into_owned_bytes_trivial {
($($t:ty),*) => {
$(
impl private::Sealed for $t {}
impl IntoOwnedBytes for $t {
#[inline]
fn into_bytes(self) -> Box<[u8]> {
self.into()
}
}
)*
};
}
impl_into_owned_bytes_trivial!(Box<[u8]>, &[u8], Vec<u8>);
impl private::Sealed for &str {}
impl IntoOwnedBytes for &str {
#[inline]
fn into_bytes(self) -> Box<[u8]> {
self.as_bytes().into()
}
}
impl private::Sealed for String {}
impl IntoOwnedBytes for String {
#[inline]
fn into_bytes(self) -> Box<[u8]> {
self.into_bytes().into()
}
}
impl Drop for BytesInner {
fn drop(&mut self) {
if let BytesInner::Owned(ptr, len) = self {
let ptr = *ptr;
let len = *len as usize;
unsafe { drop(Vec::from_raw_parts(ptr, len, len).into_boxed_slice()) };
}
}
}