Skip to content

Commit

Permalink
Merge pull request #10 from carlsverre/main
Browse files Browse the repository at this point in the history
Add `bytes` feature flag
  • Loading branch information
marvin-j97 authored Nov 15, 2024
2 parents a1c05b4 + 18fda52 commit 8c4db90
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 79 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ path = "src/lib.rs"
[features]
default = []
serde = ["dep:serde"]
bytes = ["dep:bytes"]

[dependencies]
byteorder = "1.5.0"
bytes = { version = "1.8.0", optional = true }
log = "0.4.22"
min-max-heap = "1.3.0"
path-absolutize = "3.1.1"
Expand Down
177 changes: 98 additions & 79 deletions src/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,46 @@
// This source code is licensed under both the Apache 2.0 and MIT License
// (found in the LICENSE-* files in the repository)

use std::hash::Hash;
use std::sync::Arc;

/// An immutable byte slice that can be cloned without additional heap allocation
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct Slice(Arc<[u8]>);
#[cfg(not(feature = "bytes"))]
mod slice_arc;

impl Slice {
/// Construct a [`Slice`] from a byte slice.
#[must_use]
pub fn new(bytes: &[u8]) -> Self {
Self::from(bytes)
}

#[must_use]
#[doc(hidden)]
pub fn with_size(len: usize) -> Self {
// TODO: optimize this with byteview to remove the reallocation
let v = vec![0; len];
Self(v.into())
}
#[cfg(feature = "bytes")]
mod slice_bytes;

#[doc(hidden)]
pub fn from_reader<R: std::io::Read>(reader: &mut R, len: usize) -> std::io::Result<Self> {
let mut view = Self::with_size(len);
let builder = Arc::get_mut(&mut view.0).expect("we are the owner");
reader.read_exact(builder)?;
Ok(view)
}
}

impl std::borrow::Borrow<[u8]> for Slice {
fn borrow(&self) -> &[u8] {
self
}
}
use std::sync::Arc;

impl std::ops::Deref for Slice {
type Target = [u8];
#[cfg(not(feature = "bytes"))]
pub use slice_arc::Slice;
#[cfg(feature = "bytes")]
pub use slice_bytes::Slice;

fn deref(&self) -> &Self::Target {
impl AsRef<[u8]> for Slice {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl PartialEq<[u8]> for Slice {
fn eq(&self, other: &[u8]) -> bool {
self.0.as_ref() == other
impl From<&[u8]> for Slice {
fn from(value: &[u8]) -> Self {
Self::new(value)
}
}

impl PartialEq<Slice> for &[u8] {
fn eq(&self, other: &Slice) -> bool {
*self == other.0.as_ref()
impl From<&str> for Slice {
fn from(value: &str) -> Self {
Self::from(value.as_bytes())
}
}

impl PartialOrd<[u8]> for Slice {
fn partial_cmp(&self, other: &[u8]) -> Option<std::cmp::Ordering> {
self.0.as_ref().partial_cmp(other)
impl From<Arc<str>> for Slice {
fn from(value: Arc<str>) -> Self {
Self::from(&*value)
}
}

impl PartialOrd<Slice> for &[u8] {
fn partial_cmp(&self, other: &Slice) -> Option<std::cmp::Ordering> {
self.partial_cmp(&other.0.as_ref())
impl<const N: usize> From<[u8; N]> for Slice {
fn from(value: [u8; N]) -> Self {
Self::from(value.as_slice())
}
}

Expand All @@ -76,55 +50,51 @@ impl FromIterator<u8> for Slice {
where
T: IntoIterator<Item = u8>,
{
Self::from(iter.into_iter().collect::<Vec<u8>>())
Vec::from_iter(iter).into()
}
}

impl AsRef<[u8]> for Slice {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl From<&[u8]> for Slice {
fn from(value: &[u8]) -> Self {
Self(value.into())
}
}
impl std::ops::Deref for Slice {
type Target = [u8];

impl From<Arc<[u8]>> for Slice {
fn from(value: Arc<[u8]>) -> Self {
Self(value)
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}

impl From<Vec<u8>> for Slice {
fn from(value: Vec<u8>) -> Self {
Self(value.into())
impl std::borrow::Borrow<[u8]> for Slice {
fn borrow(&self) -> &[u8] {
self
}
}

impl From<&str> for Slice {
fn from(value: &str) -> Self {
Self::from(value.as_bytes())
impl<T> PartialEq<T> for Slice
where
T: AsRef<[u8]>,
{
fn eq(&self, other: &T) -> bool {
self.as_ref() == other.as_ref()
}
}

impl From<String> for Slice {
fn from(value: String) -> Self {
Self::from(value.as_bytes())
impl PartialEq<Slice> for &[u8] {
fn eq(&self, other: &Slice) -> bool {
*self == other.as_ref()
}
}

impl From<Arc<str>> for Slice {
fn from(value: Arc<str>) -> Self {
Self::from(&*value)
impl<T> PartialOrd<T> for Slice
where
T: AsRef<[u8]>,
{
fn partial_cmp(&self, other: &T) -> Option<std::cmp::Ordering> {
self.as_ref().partial_cmp(other.as_ref())
}
}

impl<const N: usize> From<[u8; N]> for Slice {
fn from(value: [u8; N]) -> Self {
Self::from(value.as_slice())
impl PartialOrd<Slice> for &[u8] {
fn partial_cmp(&self, other: &Slice) -> Option<std::cmp::Ordering> {
(*self).partial_cmp(other.as_ref())
}
}

Expand Down Expand Up @@ -171,3 +141,52 @@ mod serde {
}
}
}

#[cfg(test)]
mod tests {
use super::Slice;
use std::{fmt::Debug, sync::Arc};

fn assert_slice_handles<T>(v: T)
where
T: Clone + Debug,
Slice: From<T> + PartialEq<T> + PartialOrd<T>,
{
// verify slice arc roundtrips
let slice: Slice = v.clone().into();
assert_eq!(slice, v, "slice_arc: {slice:?}, v: {v:?}");
assert!(slice >= v, "slice_arc: {slice:?}, v: {v:?}");
}

/// This test verifies that we can create a `Slice` from various types and compare a `Slice` with them.
#[test]
fn test_slice_instantiation() {
// - &[u8]
assert_slice_handles::<&[u8]>(&[1, 2, 3, 4]);
// - Arc<u8>
assert_slice_handles::<Arc<[u8]>>(Arc::new([1, 2, 3, 4]));
// - Vec<u8>
assert_slice_handles::<Vec<u8>>(vec![1, 2, 3, 4]);
// - &str
assert_slice_handles::<&str>("hello");
// - String
assert_slice_handles::<String>("hello".to_string());
// - [u8; N]
assert_slice_handles::<[u8; 4]>([1, 2, 3, 4]);

// Special case for these types
// - Iterator<Item = u8>
let slice = Slice::from_iter(vec![1, 2, 3, 4]);
assert_eq!(slice, vec![1, 2, 3, 4]);

// - Arc<str>
let arc_str: Arc<str> = Arc::from("hello");
let slice = Slice::from(arc_str.clone());
assert_eq!(slice.as_ref(), arc_str.as_bytes());

// - io::Read
let reader = std::io::Cursor::new(vec![1, 2, 3, 4]);
let slice = Slice::from_reader(&mut reader.clone(), 4).expect("read");
assert_eq!(slice, vec![1, 2, 3, 4]);
}
}
54 changes: 54 additions & 0 deletions src/slice/slice_arc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) 2024-present, fjall-rs
// This source code is licensed under both the Apache 2.0 and MIT License
// (found in the LICENSE-* files in the repository)

use std::sync::Arc;

/// An immutable byte slice that can be cloned without additional heap allocation
#[derive(Debug, Clone, Eq, Hash, Ord)]
pub struct Slice(pub(super) Arc<[u8]>);

impl Slice {
/// Construct a [`Slice`] from a byte slice.
#[must_use]
pub fn new(bytes: &[u8]) -> Self {
Self(bytes.into())
}

#[must_use]
#[doc(hidden)]
pub fn with_size(len: usize) -> Self {
// TODO: optimize this with byteview to remove the reallocation
let v = vec![0; len];
Self(v.into())
}

#[doc(hidden)]
pub fn from_reader<R: std::io::Read>(reader: &mut R, len: usize) -> std::io::Result<Self> {
let mut view = Self::with_size(len);
let builder = Arc::get_mut(&mut view.0).expect("we are the owner");
reader.read_exact(builder)?;
Ok(view)
}
}

// Arc::from<Vec<T>> is specialized
impl From<Vec<u8>> for Slice {
fn from(value: Vec<u8>) -> Self {
Self(Arc::from(value))
}
}

// Arc::from<Vec<T>> is specialized
impl From<String> for Slice {
fn from(value: String) -> Self {
Self(Arc::from(value.into_bytes()))
}
}

// direct conversion
impl From<Arc<[u8]>> for Slice {
fn from(value: Arc<[u8]>) -> Self {
Self(value)
}
}
59 changes: 59 additions & 0 deletions src/slice/slice_bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) 2024-present, fjall-rs
// This source code is licensed under both the Apache 2.0 and MIT License
// (found in the LICENSE-* files in the repository)

use std::sync::Arc;

use bytes::{Bytes, BytesMut};

/// An immutable byte slice that can be cloned without additional heap allocation
#[derive(Debug, Clone, Eq, Hash, Ord)]
pub struct Slice(pub(super) Bytes);

impl Slice {
/// Construct a [`Slice`] from a byte slice.
#[must_use]
pub fn new(bytes: &[u8]) -> Self {
Self(Bytes::copy_from_slice(bytes))
}

#[doc(hidden)]
pub fn from_reader<R: std::io::Read>(reader: &mut R, len: usize) -> std::io::Result<Self> {
let mut builder = BytesMut::zeroed(len);
reader.read_exact(&mut builder)?;
Ok(builder.freeze().into())
}
}

impl From<Bytes> for Slice {
fn from(value: Bytes) -> Self {
Self(value)
}
}

impl From<Slice> for Bytes {
fn from(value: Slice) -> Self {
value.0
}
}

// Bytes::from<Vec<u8>> is zero-copy optimized
impl From<Vec<u8>> for Slice {
fn from(value: Vec<u8>) -> Self {
Self(Bytes::from(value))
}
}

// Bytes::from<String> is zero-copy optimized
impl From<String> for Slice {
fn from(value: String) -> Self {
Self(Bytes::from(value))
}
}

// Needed because slice_arc specializes this impl
impl From<Arc<[u8]>> for Slice {
fn from(value: Arc<[u8]>) -> Self {
Self::new(value.as_ref())
}
}

0 comments on commit 8c4db90

Please sign in to comment.