From 2f1e72c91e5cc104c998c23f4a57b94d797104f3 Mon Sep 17 00:00:00 2001 From: TimLuq Date: Sat, 3 Aug 2024 11:09:25 +0200 Subject: [PATCH] support tagged/authed pointers after union change + bytes v1.7.1 --- Cargo.toml | 2 +- README.md | 18 +- src/byte_chunk.rs | 1 - src/bytedata.rs | 420 ++++++++++++++++-------------------- src/bytes_1/bytedata.rs | 37 +++- src/bytes_1/mod.rs | 7 + src/bytes_1/shared_bytes.rs | 11 + src/lib.rs | 2 - src/serde_1/bytedata.rs | 3 +- src/serde_1/stringdata.rs | 9 +- src/shared_bytes.rs | 16 +- src/shared_bytes_builder.rs | 3 +- src/std.rs | 4 - src/test/bytes_1.rs | 24 +-- 14 files changed, 267 insertions(+), 290 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dfd6692..eb7e660 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ authors = ["TimLuq"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bytes_1 = { package = "bytes", version = "^1.6.1", optional = true, default-features = false } +bytes_1 = { package = "bytes", version = "^1.7.1", optional = true, default-features = false } http-body_04 = { package = "http-body", version = "0.4.5", optional = true } http-body_1 = { package = "http-body", version = "1", optional = true } http_02 = { package = "http", version = "0.2.4", optional = true } diff --git a/README.md b/README.md index c7599a0..a5bf2ac 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A library for working with byte slices in a `const`-friendly manner. +Provides the main types `ByteData` and `StringData` which are wrappers around byte sequences and string sequences respectively. + When the `alloc` feature is enabled this crate provides `SharedBytes` which operates like `Arc<[u8]>` but with a representation that allows for `const` references to the bytes and zero-copy subslicing. The `SharedBytes` type is then also added as one additional representation of `ByteData`. @@ -10,11 +12,13 @@ The `SharedBytes` type is then also added as one additional representation of `B ### alloc Enables runtime allocation of byte arrays on the heap. -This allows for dynamic allocation of byte arrays which are exposed as `SharedBytes` and can be wrapped as `ByteData::Shared(_)`. +This allows for dynamic allocation of byte arrays which are exposed as `SharedBytes` and can be wrapped using `ByteData::from_shared`. ### chunk -Enables runtime representation of small byte arrays inline as `ByteData::Chunk(_)`. +This feature is now built-in and activating it does nothing. + +Previously enabled runtime representation of small byte arrays inline as `ByteChunk` which is one representation of `ByteData`. This allows for optimized storage of small byte arrays that are less than or equal to 14 bytes in size. ### macros @@ -25,17 +29,17 @@ These macros allow for concatenation of static byte arrays and strings that are ### bytes_1 -Enables integration with the `bytes` crate (version `>=1.6.1, <2`). +Enables integration with the `bytes` crate (version `>=1.7.1, <2`). This allows for conversion between `SharedBytes` and `bytes::Bytes` types. -Where possible no bytes will be cloned, which means that `ByteData::Static(_)` will map to `bytes::Bytes::from_static`, +Where possible no bytes will be cloned, which means that `ByteData::from_static` will map to `bytes::Bytes::from_static`, and that `>::from` will return a `bytes::Bytes` object that is just a wrapper and still share the bytes as normal without any copying. -There is, however, a possibility that the internal vtable or structure of `bytes::Bytes` changes in the future, in which case the zero-copy may break or segfault. +There is, however, a possibility that the internal vtable or structure of `bytes::Bytes` changes in the future or results in a different ordered ABI, in which case the zero-copy may break or segfault. If this happens you can enable the feature `bytes_1_safe` which will always cause the bytes to be cloned when converting to and from `bytes::Bytes` without the use of any internal structures. ### bytes_1_safe -Enables integration with the `bytes` crate (version `>=1.6.1, <2`) in a safe manner. +Enables integration with the `bytes` crate (version `>=1.7.1, <2`) in a safe manner. This will cause the bytes to always be cloned when converting to and from `bytes::Bytes`. For zero-copy conversion between `SharedBytes` and `bytes::Bytes`, use the `bytes_1` feature instead - unless it is broken. @@ -54,7 +58,7 @@ The trait `http_body::Body` is then implemented for `ByteData` and `SharedBytes` ### queue -Enables the `ByteQueue` type which is a queue of `ByteData` objects that can be pushed to and popped from. +Enables the `ByteQueue`/`StringQueue` types which are queues of `ByteData`/`StringData` objects that can be pushed to and popped from. Unless the `alloc` feature is enabled, the queue will be limited to a maximum size of 8 elements. ### nom_7 diff --git a/src/byte_chunk.rs b/src/byte_chunk.rs index 7b3a932..f1a5140 100644 --- a/src/byte_chunk.rs +++ b/src/byte_chunk.rs @@ -1,7 +1,6 @@ use core::{ops::RangeBounds, slice::SliceIndex}; /// A chunk of bytes that is 14 bytes or less. -#[cfg_attr(docsrs, doc(cfg(feature = "chunk")))] #[derive(Clone, Copy)] pub struct ByteChunk { /// The length of the chunk. diff --git a/src/bytedata.rs b/src/bytedata.rs index e72579f..cc5e7bd 100644 --- a/src/bytedata.rs +++ b/src/bytedata.rs @@ -22,10 +22,9 @@ unsafe impl<'a> Sync for ByteSlice<'a> {} impl<'a> ByteSlice<'a> { #[inline] - const fn new(data: &[u8]) -> Self { - let len = data.len() as u64; - #[cfg(target_pointer_width = "64")] - let len = len.to_be(); + const fn new(data: &[u8], is_static: bool) -> Self { + let mask = if is_static { 0b1000_1111 } else { 0b0000_1111 }; + let len = (((data.len() as u64) << 8) | mask).to_le(); ByteSlice { addr: data.as_ptr(), len, @@ -34,21 +33,20 @@ impl<'a> ByteSlice<'a> { } #[inline] - const fn is_empty(&self) -> bool { - self.len() == 0 + pub(crate) const fn is_static(&self) -> bool { + let p = unsafe { core::mem::transmute_copy::(self) }; + (p & 0b1000_0000) != 0 } - #[cfg(target_pointer_width = "64")] #[inline] - const fn len(&self) -> usize { - let a = self.len.to_be() & 0x00FF_FFFF_FFFF_FFFF; - a as usize + pub(crate) const fn is_empty(&self) -> bool { + self.len() == 0 } - #[cfg(not(target_pointer_width = "64"))] #[inline] - const fn len(&self) -> usize { - self.len as usize + pub(crate) const fn len(&self) -> usize { + let a = self.len.to_le() >> 8; + a as usize } #[inline] @@ -56,37 +54,74 @@ impl<'a> ByteSlice<'a> { let len = self.len(); unsafe { core::slice::from_raw_parts(self.addr, len) } } + + #[allow(dead_code)] + #[inline] + pub(crate) const fn as_static(&self) -> Option<&'static [u8]> { + if self.is_static() { + Some(unsafe { core::mem::transmute::<&[u8], &'static [u8]>(self.as_slice()) }) + } else { + None + } + } +} + +impl ByteSlice<'static> { + #[inline] + fn make_static(&mut self) { + let p = self as *mut Self as *mut u8; + unsafe { *p |= 0b1000_0000 }; + } } #[repr(C)] #[derive(Clone, Copy)] pub(crate) struct DataKind { pub(crate) kind: u8, - data: T, + pub(crate) data: T, } -pub(crate) const KIND_EMPTY: u8 = 0; -pub(crate) const KIND_STATIC: u8 = 1; -pub(crate) const KIND_BORROWED: u8 = 2; -#[cfg(feature = "chunk")] -pub(crate) const KIND_CHUNK: u8 = 3; -#[cfg(feature = "alloc")] -pub(crate) const KIND_SHARED: u8 = 4; +impl DataKind { + #[inline] + pub(crate) const fn kind(&self) -> Kind { + let base_kind = self.kind & 0b0000_1111; + if base_kind == 0b0000_0111 { + return Kind::Chunk; + } + if base_kind == 0b0000_1111 { + return Kind::Slice; + } + + #[cfg(feature = "alloc")] + if (base_kind & 0b0000_0011) == 0b0000_0000 { + return Kind::Shared; + } + #[cfg(not(feature = "alloc"))] + if (base_kind & 0b0000_0011) == 0b0000_0000 { + panic!("alloc feature is not enabled, so no path should trigger this"); + } + + panic!("no path should trigger this"); + } +} + +pub(crate) enum Kind { + Chunk, + Slice, + #[cfg(feature = "alloc")] + Shared, +} + +const KIND_CHUNK_MASK: u8 = 0b0000_0111; type WrappedChunk = DataKind; /// A container of bytes that can be either static, borrowed, or shared. pub union ByteData<'a> { - /// Placeholder for the data kind. - pub(crate) kind: DataKind<[u8; 15]>, - /// A static byte slice. - pub(crate) static_slice: ByteSlice<'static>, - /// A borrowed byte slice. - pub(crate) borrowed_slice: ByteSlice<'a>, - #[cfg(feature = "chunk")] - #[cfg_attr(docsrs, doc(cfg(feature = "chunk")))] /// A chunk of bytes that is 14 bytes or less. pub(crate) chunk: WrappedChunk, + /// A byte slice. + pub(crate) slice: ByteSlice<'a>, #[cfg(feature = "alloc")] /// A shared byte slice. #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] @@ -95,38 +130,37 @@ pub union ByteData<'a> { impl<'a> Clone for ByteData<'a> { fn clone(&self) -> Self { - match unsafe { self.kind }.kind { - KIND_EMPTY => Self::empty(), - KIND_STATIC => Self { - static_slice: unsafe { self.static_slice }, - }, - KIND_BORROWED => Self { - borrowed_slice: unsafe { self.borrowed_slice }, - }, - #[cfg(feature = "chunk")] - KIND_CHUNK => Self { + match unsafe { self.chunk.kind() } { + Kind::Chunk => Self { chunk: unsafe { self.chunk }, }, + Kind::Slice => Self { + slice: unsafe { self.slice }, + }, #[cfg(feature = "alloc")] - KIND_SHARED => Self { + Kind::Shared => Self { shared: core::mem::ManuallyDrop::new(unsafe { SharedBytes::clone(&self.shared) }), }, - _ => unreachable!(), } } } +const fn empty_chunk() -> WrappedChunk { + WrappedChunk { + kind: KIND_CHUNK_MASK, + data: unsafe { core::mem::zeroed() }, + } +} + impl<'a> Drop for ByteData<'a> { fn drop(&mut self) { - match unsafe { self.kind }.kind { - KIND_EMPTY | KIND_STATIC | KIND_BORROWED => (), - #[cfg(feature = "chunk")] - KIND_CHUNK => (), + match unsafe { self.chunk.kind() } { + Kind::Chunk | Kind::Slice => (), #[cfg(feature = "alloc")] - KIND_SHARED => unsafe { + Kind::Shared => unsafe { core::ptr::drop_in_place(&mut self.shared); + self.chunk = empty_chunk(); }, - _ => unreachable!(), } } } @@ -141,16 +175,12 @@ impl<'a> ByteData<'a> { /// Creates a `ByteData` from a slice of bytes. #[inline] pub const fn from_static(dat: &'static [u8]) -> Self { - let mut a = Self { - static_slice: ByteSlice::new(dat), - }; - a.kind.kind = KIND_STATIC; - a + Self { + slice: ByteSlice::new(dat, true), + } } - #[cfg(feature = "chunk")] /// Creates a `ByteData` from a slice of bytes. The slice must be 14 bytes or less. If the slice is larger, this will panic. - #[cfg_attr(docsrs, doc(cfg(feature = "chunk")))] #[inline] pub const fn from_chunk_slice(dat: &[u8]) -> Self { if dat.is_empty() { @@ -158,29 +188,25 @@ impl<'a> ByteData<'a> { } else { Self { chunk: WrappedChunk { - kind: KIND_CHUNK, + kind: KIND_CHUNK_MASK, data: crate::byte_chunk::ByteChunk::from_slice(dat), }, } } } - #[cfg(feature = "chunk")] /// Creates a `ByteData` from a single byte. - #[cfg_attr(docsrs, doc(cfg(feature = "chunk")))] #[inline] pub const fn from_byte(b0: u8) -> Self { Self { chunk: WrappedChunk { - kind: KIND_CHUNK, + kind: KIND_CHUNK_MASK, data: crate::byte_chunk::ByteChunk::from_byte(b0), }, } } - #[cfg(feature = "chunk")] /// Creates a `ByteData` from an array of bytes. The array must be 14 bytes or less. If the array is larger, this will panic. - #[cfg_attr(docsrs, doc(cfg(feature = "chunk")))] #[inline] pub const fn from_chunk(dat: &[u8; L]) -> Self { if L == 0 { @@ -188,7 +214,7 @@ impl<'a> ByteData<'a> { } else { Self { chunk: WrappedChunk { - kind: KIND_CHUNK, + kind: KIND_CHUNK_MASK, data: crate::byte_chunk::ByteChunk::from_array(dat), }, } @@ -199,13 +225,13 @@ impl<'a> ByteData<'a> { #[inline] pub const fn from_borrowed(dat: &'a [u8]) -> Self { if dat.is_empty() { - Self::empty() + Self { + chunk: empty_chunk(), + } } else { - let mut a = Self { - borrowed_slice: ByteSlice::new(dat), - }; - a.kind.kind = KIND_BORROWED; - a + Self { + slice: ByteSlice::new(dat, false), + } } } @@ -214,11 +240,9 @@ impl<'a> ByteData<'a> { #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] #[inline] pub const fn from_shared(dat: SharedBytes) -> Self { - let mut a = Self { + Self { shared: core::mem::ManuallyDrop::new(dat), - }; - a.kind.kind = KIND_SHARED; - a + } } #[cfg(feature = "alloc")] @@ -229,20 +253,17 @@ impl<'a> ByteData<'a> { if dat.is_empty() { return Self::empty(); } - #[cfg(feature = "chunk")] if dat.len() <= 14 { return Self { chunk: WrappedChunk { - kind: KIND_CHUNK, + kind: KIND_CHUNK_MASK, data: crate::byte_chunk::ByteChunk::from_slice(dat.as_slice()), }, }; } - let mut a = Self { + Self { shared: core::mem::ManuallyDrop::new(dat.into()), - }; - a.kind.kind = KIND_SHARED; - a + } } #[cfg(feature = "alloc")] @@ -267,58 +288,46 @@ impl<'a> ByteData<'a> { /// Returns the underlying byte slice. pub const fn as_slice(&self) -> &[u8] { - match unsafe { self.kind }.kind { - KIND_EMPTY => &[], - KIND_STATIC => unsafe { self.static_slice.as_slice() }, - KIND_BORROWED => unsafe { self.borrowed_slice.as_slice() }, - #[cfg(feature = "chunk")] - KIND_CHUNK => unsafe { self.chunk.data.as_slice() }, + match unsafe { self.chunk.kind() } { + Kind::Chunk => unsafe { self.chunk.data.as_slice() }, + Kind::Slice => unsafe { self.slice.as_slice() }, #[cfg(feature = "alloc")] - KIND_SHARED => unsafe { + Kind::Shared => unsafe { core::mem::transmute::<&core::mem::ManuallyDrop, &SharedBytes>( &self.shared, ) } .as_slice(), - _ => unreachable!(), } } /// Returns the length of the underlying byte slice. pub const fn len(&self) -> usize { - match unsafe { self.kind }.kind { - KIND_EMPTY => 0, - KIND_STATIC => unsafe { self.static_slice.len() }, - KIND_BORROWED => unsafe { self.borrowed_slice.len() }, - #[cfg(feature = "chunk")] - KIND_CHUNK => unsafe { self.chunk.data.len() }, + match unsafe { self.chunk.kind() } { + Kind::Chunk => unsafe { self.chunk.data.len() }, + Kind::Slice => unsafe { self.slice.len() }, #[cfg(feature = "alloc")] - KIND_SHARED => unsafe { + Kind::Shared => unsafe { core::mem::transmute::<&core::mem::ManuallyDrop, &SharedBytes>( &self.shared, ) } .len(), - _ => unreachable!(), } } /// Returns `true` if the underlying byte slice is empty. pub const fn is_empty(&self) -> bool { - match unsafe { self.kind }.kind { - KIND_EMPTY => true, - KIND_STATIC => unsafe { self.static_slice.is_empty() }, - KIND_BORROWED => unsafe { self.borrowed_slice.is_empty() }, - #[cfg(feature = "chunk")] - KIND_CHUNK => unsafe { self.chunk.data.is_empty() }, + match unsafe { self.chunk.kind() } { + Kind::Chunk => unsafe { self.chunk.data.is_empty() }, + Kind::Slice => unsafe { self.slice.is_empty() }, #[cfg(feature = "alloc")] - KIND_SHARED => unsafe { + Kind::Shared => unsafe { core::mem::transmute::<&core::mem::ManuallyDrop, &SharedBytes>( &self.shared, ) } .is_empty(), - _ => unreachable!(), } } @@ -351,42 +360,28 @@ impl<'a> ByteData<'a> { &self, range: R, ) -> Self { - match unsafe { self.kind }.kind { - KIND_EMPTY => { - match range.start_bound() { - core::ops::Bound::Unbounded | core::ops::Bound::Included(&0) => (), - _ => panic!("ByteData::sliced: range out of bounds"), - } - match range.end_bound() { - core::ops::Bound::Unbounded - | core::ops::Bound::Included(&0) - | core::ops::Bound::Excluded(&1) => (), - _ => panic!("ByteData::sliced: range out of bounds"), + match unsafe { self.chunk.kind() } { + Kind::Chunk => Self::from_chunk_slice(unsafe { &self.chunk.data.as_slice()[range] }), + Kind::Slice => { + let a = unsafe { &self.slice }; + Self { + slice: ByteSlice::new(&a.as_slice()[range], a.is_static()), } - return Self::empty(); } - KIND_STATIC => Self::from_static(unsafe { &self.static_slice.as_slice()[range] }), - KIND_BORROWED => Self::from_borrowed(unsafe { &self.borrowed_slice.as_slice()[range] }), - #[cfg(feature = "chunk")] - KIND_CHUNK => Self::from_chunk_slice(unsafe { &self.chunk.data.as_slice()[range] }), #[cfg(feature = "alloc")] - KIND_SHARED => { + Kind::Shared => { let dat = unsafe { core::mem::transmute::<&core::mem::ManuallyDrop, &SharedBytes>( &self.shared, ) }; let dat = dat.sliced_range(range); - if dat.is_empty() { - return Self::empty(); - } - #[cfg(feature = "chunk")] if dat.len() <= 14 { - return Self::from_chunk_slice(dat.as_slice()); + Self::from_chunk_slice(dat.as_slice()) + } else { + Self::from_shared(dat) } - Self::from_shared(dat) } - _ => unreachable!(), } } @@ -395,37 +390,18 @@ impl<'a> ByteData<'a> { mut self, range: R, ) -> Self { - match unsafe { self.kind }.kind { - KIND_EMPTY => { - match range.start_bound() { - core::ops::Bound::Unbounded | core::ops::Bound::Included(&0) => (), - _ => panic!("ByteData::into_sliced: range out of bounds"), - } - match range.end_bound() { - core::ops::Bound::Unbounded - | core::ops::Bound::Included(&0) - | core::ops::Bound::Excluded(&1) => (), - _ => panic!("ByteData::into_sliced: range out of bounds"), - } - return Self::empty(); - } - KIND_STATIC => { - unsafe { self.static_slice = ByteSlice::new(&self.static_slice.as_slice()[range]) }; - self - } - KIND_BORROWED => { - unsafe { - self.borrowed_slice = ByteSlice::new(&self.borrowed_slice.as_slice()[range]) - }; + match unsafe { self.chunk.kind() } { + Kind::Chunk => { + unsafe { self.chunk.data.make_sliced(range) }; self } - #[cfg(feature = "chunk")] - KIND_CHUNK => { - unsafe { self.chunk.data.make_sliced(range) }; + Kind::Slice => { + let a = unsafe { &mut self.slice }; + *a = ByteSlice::new(&a.as_slice()[range], a.is_static()); self } #[cfg(feature = "alloc")] - KIND_SHARED => { + Kind::Shared => { let dat = unsafe { core::mem::transmute::< &mut core::mem::ManuallyDrop, @@ -433,22 +409,15 @@ impl<'a> ByteData<'a> { >(&mut self.shared) }; dat.make_sliced_range(range); - if dat.is_empty() { - unsafe { core::ptr::drop_in_place(dat) }; - core::mem::forget(self); - return Self::empty(); - } - #[cfg(feature = "chunk")] if dat.len() <= 14 { let r = Self::from_chunk_slice(dat.as_slice()); unsafe { core::ptr::drop_in_place(dat) }; core::mem::forget(self); - return r; + r + } else { + self } - unsafe { self.kind }.kind = KIND_SHARED; - self } - _ => unreachable!(), } } @@ -457,33 +426,16 @@ impl<'a> ByteData<'a> { &'_ mut self, range: R, ) { - match unsafe { self.kind }.kind { - KIND_EMPTY => { - match range.start_bound() { - core::ops::Bound::Unbounded | core::ops::Bound::Included(&0) => (), - _ => panic!("ByteData::into_sliced: range out of bounds"), - } - match range.end_bound() { - core::ops::Bound::Unbounded - | core::ops::Bound::Included(&0) - | core::ops::Bound::Excluded(&1) => (), - _ => panic!("ByteData::into_sliced: range out of bounds"), - } - } - KIND_STATIC => { - unsafe { self.static_slice = ByteSlice::new(&self.static_slice.as_slice()[range]) }; - } - KIND_BORROWED => { - unsafe { - self.borrowed_slice = ByteSlice::new(&self.borrowed_slice.as_slice()[range]) - }; - } - #[cfg(feature = "chunk")] - KIND_CHUNK => { + match unsafe { self.chunk.kind() } { + Kind::Chunk => { unsafe { self.chunk.data.make_sliced(range) }; } + Kind::Slice => { + let a = unsafe { &mut self.slice }; + *a = ByteSlice::new(&a.as_slice()[range], a.is_static()); + } #[cfg(feature = "alloc")] - KIND_SHARED => { + Kind::Shared => { let dat = unsafe { core::mem::transmute::< &mut core::mem::ManuallyDrop, @@ -491,24 +443,17 @@ impl<'a> ByteData<'a> { >(&mut self.shared) }; dat.make_sliced_range(range); - if dat.is_empty() { - unsafe { core::ptr::drop_in_place(dat) }; - self.kind.kind = KIND_EMPTY; - } - #[cfg(feature = "chunk")] if dat.len() <= 14 { let r = crate::ByteChunk::from_slice(dat.as_slice()); unsafe { core::ptr::drop_in_place(dat); self.chunk = DataKind { - kind: KIND_CHUNK, + kind: KIND_CHUNK_MASK, data: r, }; } } - unsafe { self.kind }.kind = KIND_SHARED; } - _ => unreachable!(), } } @@ -516,19 +461,29 @@ impl<'a> ByteData<'a> { /// Transform any borrowed data into shared data. This is useful when you wish to change the lifetime of the data. #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] pub fn into_shared<'s>(mut self) -> ByteData<'s> { - match unsafe { self.kind }.kind { - KIND_EMPTY | KIND_STATIC | KIND_SHARED => unsafe { + match unsafe { self.chunk.kind() } { + Kind::Chunk | Kind::Shared => unsafe { core::mem::transmute::(self) }, - KIND_BORROWED => { - let r = SharedBytes::from_slice(unsafe { self.borrowed_slice.as_slice() }); - self.shared = core::mem::ManuallyDrop::new(r); - unsafe { self.kind }.kind = KIND_SHARED; - unsafe { core::mem::transmute::(self) } + Kind::Slice => { + let a = unsafe { &self.slice }; + if a.is_static() { + unsafe { core::mem::transmute::(self) } + } else if a.len() <= 14 { + let r = crate::byte_chunk::ByteChunk::from_slice(a.as_slice()); + core::mem::forget(self); + ByteData { + chunk: DataKind { + kind: KIND_CHUNK_MASK, + data: r, + }, + } + } else { + let r = SharedBytes::from_slice(a.as_slice()); + self.shared = core::mem::ManuallyDrop::new(r); + unsafe { core::mem::transmute::(self) } + } } - #[cfg(feature = "chunk")] - KIND_CHUNK => unsafe { core::mem::transmute::(self) }, - _ => unreachable!(), } } @@ -541,39 +496,38 @@ impl<'a> ByteData<'a> { mut self, range: R, ) -> ByteData<'s> { - match unsafe { self.kind }.kind { - KIND_EMPTY | KIND_STATIC | KIND_SHARED => { - self.make_sliced(range); - unsafe { core::mem::transmute::(self) } - } - KIND_BORROWED => { - let dat = unsafe { self.borrowed_slice.as_slice() }; - let r = &dat[range]; - if r.is_empty() { - core::mem::forget(self); - return ByteData::empty(); - } + match unsafe { self.chunk.kind() } { + Kind::Chunk => unsafe { + self.chunk.data.make_sliced(range); + core::mem::transmute::(self) + }, + Kind::Shared => unsafe { + (*self.shared).make_sliced_range(range); + core::mem::transmute::(self) + }, + Kind::Slice => { + let a = unsafe { &self.slice }; + let r = &a.as_slice()[range]; if r.len() <= 14 { let r = crate::byte_chunk::ByteChunk::from_slice(r); core::mem::forget(self); return ByteData { chunk: DataKind { - kind: KIND_CHUNK, + kind: KIND_CHUNK_MASK, data: r, }, }; } + if a.is_static() { + core::mem::forget(self); + return ByteData { + slice: ByteSlice::new(r, true), + }; + } let r = SharedBytes::from_slice(r); self.shared = core::mem::ManuallyDrop::new(r); - unsafe { self.kind }.kind = KIND_SHARED; unsafe { core::mem::transmute::(self) } } - #[cfg(feature = "chunk")] - KIND_CHUNK => { - unsafe { self.chunk.data.make_sliced(range) }; - unsafe { core::mem::transmute::(self) } - } - _ => unreachable!(), } } @@ -591,13 +545,14 @@ impl<'a> ByteData<'a> { /// Consume the `ByteData` until the byte condition is triggered. pub fn take_while bool>(&mut self, mut f: F) -> ByteData<'a> { let mut i = 0; - while i < self.len() && f(self[i]) { + let a = self.as_slice(); + while i < a.len() && f(a[i]) { i += 1; } if i == 0 { return ByteData::empty(); } - if i == self.len() { + if i == a.len() { return core::mem::replace(self, ByteData::empty()); } let a = self.sliced(0..i); @@ -673,11 +628,11 @@ impl<'a> ByteData<'a> { } impl ByteData<'static> { - /// Returns a `ByteData` with the given range of bytes. + /// Forces any borrowed slice to be marked as static. #[inline] pub fn statically_borrowed(mut self) -> ByteData<'static> { - if unsafe { self.kind }.kind == KIND_BORROWED { - self.kind.kind = KIND_STATIC; + if matches!(unsafe { self.chunk.kind() }, Kind::Slice) { + unsafe { self.slice.make_static() }; } unsafe { core::mem::transmute::(self) } } @@ -709,14 +664,11 @@ impl<'a> From<&'a [u8]> for ByteData<'a> { impl<'a> From for ByteData<'a> { #[inline] fn from(dat: SharedBytes) -> Self { - if dat.is_empty() { - return Self::empty(); - } - #[cfg(feature = "chunk")] if dat.len() <= 14 { - return Self::from_chunk_slice(&dat); + Self::from_chunk_slice(&dat) + } else { + Self::from_shared(dat) } - Self::from_shared(dat) } } diff --git a/src/bytes_1/bytedata.rs b/src/bytes_1/bytedata.rs index 89bca6d..1dcc42e 100644 --- a/src/bytes_1/bytedata.rs +++ b/src/bytes_1/bytedata.rs @@ -5,16 +5,37 @@ use crate::ByteData; #[cfg_attr(docsrs, doc(cfg(feature = "bytes_1")))] impl From> for bytes::Bytes { fn from(dat: ByteData) -> Self { - match unsafe { dat.kind }.kind { - crate::bytedata::KIND_STATIC => { - bytes::Bytes::from_static(unsafe { dat.static_slice }.as_slice()) + match unsafe { dat.chunk.kind() } { + crate::bytedata::Kind::Slice => { + let a = unsafe { &dat.slice }; + if a.is_empty() { + core::mem::forget(dat); + return bytes::Bytes::new(); + } + if let Some(a) = a.as_static() { + let r = bytes::Bytes::from_static(a); + core::mem::forget(dat); + return r; + } + let r = bytes::Bytes::copy_from_slice(a.as_slice()); + core::mem::forget(dat); + r + } + crate::bytedata::Kind::Chunk => { + let l = unsafe { dat.chunk.data.len }; + if l == 0 { + core::mem::forget(dat); + return bytes::Bytes::new(); + } + let r = bytes::Bytes::copy_from_slice(unsafe { dat.chunk.data.as_slice() }); + core::mem::forget(dat); + r } - crate::bytedata::KIND_BORROWED => bytes::Bytes::copy_from_slice(dat.as_slice()), - #[cfg(feature = "chunk")] - crate::bytedata::KIND_CHUNK => bytes::Bytes::copy_from_slice(dat.as_slice()), #[cfg(feature = "alloc")] - crate::bytedata::KIND_SHARED => dat.into(), - _ => unreachable!(), + crate::bytedata::Kind::Shared => { + let s = unsafe { core::mem::transmute::(dat) }; + s.into() + } } } } diff --git a/src/bytes_1/mod.rs b/src/bytes_1/mod.rs index 686f182..aed73a3 100644 --- a/src/bytes_1/mod.rs +++ b/src/bytes_1/mod.rs @@ -37,8 +37,15 @@ impl SBytes { /// A struct containing (hopefully) the same ABI as the hidden `Vtable` from the `bytes` crate. #[allow(dead_code)] struct SBytesVtable { + /// fn(data, ptr, len) pub clone: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> bytes::Bytes, + /// fn(data, ptr, len) + /// + /// takes `Bytes` to value pub to_vec: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> alloc::vec::Vec, + pub to_mut: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> bytes::BytesMut, + /// fn(data) pub is_unique: unsafe fn(&AtomicPtr<()>) -> bool, + /// fn(data, ptr, len) pub drop: unsafe fn(&mut AtomicPtr<()>, *const u8, usize), } diff --git a/src/bytes_1/shared_bytes.rs b/src/bytes_1/shared_bytes.rs index f775a52..291741c 100644 --- a/src/bytes_1/shared_bytes.rs +++ b/src/bytes_1/shared_bytes.rs @@ -34,6 +34,17 @@ static SHARED_BYTES_BVT: super::SBytesVtable = super::SBytesVtable { } vec }, + to_mut: |_data, ptr, len| { + if len == 0 { + return bytes::BytesMut::new(); + } + let mut vec = bytes::BytesMut::with_capacity(len); + unsafe { + core::ptr::copy_nonoverlapping(ptr, vec.as_mut_ptr(), len); + vec.set_len(len); + } + vec + }, is_unique: |data| unsafe { let p = data.load(Ordering::Relaxed); let meta = &*(p as *const crate::SharedBytesMeta); diff --git a/src/lib.rs b/src/lib.rs index b98d57d..d00ac2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,9 +59,7 @@ mod macros; #[allow(unused_imports)] pub use self::macros::*; -#[cfg(feature = "chunk")] mod byte_chunk; -#[cfg(feature = "chunk")] pub use byte_chunk::ByteChunk; #[cfg(feature = "queue")] diff --git a/src/serde_1/bytedata.rs b/src/serde_1/bytedata.rs index f8b1992..9362f51 100644 --- a/src/serde_1/bytedata.rs +++ b/src/serde_1/bytedata.rs @@ -29,8 +29,7 @@ impl<'de> serde::de::Deserialize<'de> for ByteData<'de> { } fn visit_bytes(self, v: &[u8]) -> Result { - #[cfg(feature = "chunk")] - if v.len() <= 14 { + if v.len() <= crate::ByteChunk::LEN { return Ok(ByteData::from_chunk_slice(v)); } #[cfg(feature = "alloc")] diff --git a/src/serde_1/stringdata.rs b/src/serde_1/stringdata.rs index 4e2e011..0303beb 100644 --- a/src/serde_1/stringdata.rs +++ b/src/serde_1/stringdata.rs @@ -28,18 +28,17 @@ impl<'de> serde::de::Deserialize<'de> for StringData<'de> { Ok(StringData::from_borrowed(v)) } - fn visit_str(self, _v: &str) -> Result { - #[cfg(feature = "chunk")] - if _v.len() <= 14 { + fn visit_str(self, v: &str) -> Result { + if v.len() <= crate::ByteChunk::LEN { return Ok(unsafe { StringData::from_bytedata_unchecked(crate::ByteData::from_chunk_slice( - _v.as_bytes(), + v.as_bytes(), )) }); } #[cfg(feature = "alloc")] { - Ok(StringData::from_borrowed(_v).into_shared()) + Ok(StringData::from_borrowed(v).into_shared()) } #[cfg(not(feature = "alloc"))] { diff --git a/src/shared_bytes.rs b/src/shared_bytes.rs index 7cc8215..d2584de 100644 --- a/src/shared_bytes.rs +++ b/src/shared_bytes.rs @@ -87,16 +87,9 @@ impl SharedBytes { Self::EMPTY } - #[cfg(target_pointer_width = "64")] #[inline] pub(crate) const fn dat(&self) -> *const u8 { - (self.dat_addr.to_be() & 0x00FF_FFFF_FFFF_FFFF) as usize as *const u8 - } - - #[cfg(not(target_pointer_width = "64"))] - #[inline] - pub(crate) const fn dat(&self) -> *const u8 { - (self.dat_addr as usize) as *const u8 + self.dat_addr.to_le() as usize as *const u8 } /// Creates a `SharedBytes` from a slice of bytes. @@ -136,8 +129,7 @@ impl SharedBytes { ptr }; let dat_addr = ptr as usize as u64; - #[cfg(target_pointer_width = "64")] - let dat_addr = dat_addr.to_be(); + let dat_addr = dat_addr.to_le(); Self { len, off: off as u32, @@ -406,11 +398,11 @@ impl From for SharedBytes { impl<'a> From> for SharedBytes { #[inline] fn from(dat: crate::ByteData<'a>) -> Self { - if unsafe { dat.kind }.kind == crate::bytedata::KIND_SHARED { + if matches!(unsafe { dat.chunk.kind() }, crate::bytedata::Kind::Shared) { return unsafe { core::mem::transmute::(dat) }; } let a = Self::from_slice(dat.as_slice()); - core::mem::forget(dat); + core::mem::drop(dat); a } } diff --git a/src/shared_bytes_builder.rs b/src/shared_bytes_builder.rs index c5793b4..7f1899a 100644 --- a/src/shared_bytes_builder.rs +++ b/src/shared_bytes_builder.rs @@ -266,8 +266,7 @@ impl SharedBytesBuilder { const INIT: SharedBytesMeta = SharedBytesMeta::new().with_refcount(1); unsafe { (dat as *mut SharedBytesMeta).write(INIT.with_align(align).with_len(len)) }; let dat_addr = dat as usize as u64; - #[cfg(target_pointer_width = "64")] - let dat_addr = dat_addr.to_be(); + let dat_addr = dat_addr.to_le(); SharedBytes { len: off - data_off, off: data_off, diff --git a/src/std.rs b/src/std.rs index 6937156..23237fa 100644 --- a/src/std.rs +++ b/src/std.rs @@ -319,10 +319,6 @@ impl<'a> BufRead for crate::ByteQueue<'a> { impl<'a> Write for crate::ByteQueue<'a> { fn write(&mut self, buf: &[u8]) -> std::io::Result { let len = buf.len(); - if len == 0 { - return Ok(0); - } - #[cfg(feature = "chunk")] if len <= crate::ByteChunk::LEN { self.push_back(ByteData::from_chunk_slice(buf)); return Ok(len); diff --git a/src/test/bytes_1.rs b/src/test/bytes_1.rs index 8c696ed..c0b002e 100644 --- a/src/test/bytes_1.rs +++ b/src/test/bytes_1.rs @@ -5,13 +5,13 @@ fn test_bytedata_bytes_1_static() { let s0 = crate::bytedata::ByteData::from(s0); #[cfg(not(feature = "bytes_1_safe"))] assert!(matches!( - unsafe { s0.kind }.kind, - crate::bytedata::KIND_STATIC + unsafe { s0.chunk.kind() }, + crate::bytedata::Kind::Slice )); #[cfg(feature = "bytes_1_safe")] assert!(matches!( - unsafe { s0.kind }.kind, - crate::bytedata::KIND_SHARED + unsafe { s0.chunk.kind() }, + crate::bytedata::Kind::Shared )); assert_eq!(s0, b"hello world".as_slice()); } @@ -22,8 +22,8 @@ fn test_bytedata_bytes_1_borrowed() { let s0: ::bytes_1::Bytes = ::bytes_1::Bytes::copy_from_slice(b"hello world"); let s0 = crate::bytedata::ByteData::from(s0); assert!(matches!( - unsafe { s0.kind }.kind, - crate::bytedata::KIND_SHARED + unsafe { s0.chunk.kind() }, + crate::bytedata::Kind::Shared )); assert_eq!(s0, b"hello world".as_slice()); } @@ -35,8 +35,8 @@ fn test_bytedata_bytes_1_boxed() { ::bytes_1::Bytes::from(alloc::boxed::Box::<[u8]>::from(b"hello world".as_slice())); let s0 = crate::bytedata::ByteData::from(s0); assert!(matches!( - unsafe { s0.kind }.kind, - crate::bytedata::KIND_SHARED + unsafe { s0.chunk.kind() }, + crate::bytedata::Kind::Shared )); assert_eq!(s0, b"hello world".as_slice()); } @@ -48,8 +48,8 @@ fn test_bytedata_bytes_1_vec_exact() { ::bytes_1::Bytes::from(alloc::vec::Vec::::from(b"hello world".as_slice())); let s0 = crate::bytedata::ByteData::from(s0); assert!(matches!( - unsafe { s0.kind }.kind, - crate::bytedata::KIND_SHARED + unsafe { s0.chunk.kind() }, + crate::bytedata::Kind::Shared )); assert_eq!(s0, b"hello world".as_slice()); } @@ -62,8 +62,8 @@ fn test_bytedata_bytes_1_vec_extra() { let s0: ::bytes_1::Bytes = ::bytes_1::Bytes::from(s0); let s0 = crate::bytedata::ByteData::from(s0); assert!(matches!( - unsafe { s0.kind }.kind, - crate::bytedata::KIND_SHARED + unsafe { s0.chunk.kind() }, + crate::bytedata::Kind::Shared )); assert_eq!(s0, b"hello world".as_slice()); }