Skip to content

Add bytes feature flag #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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())
}
}