Skip to content
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
454 changes: 0 additions & 454 deletions slice-codec/src/buffer/slice.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) ZeroC, Inc.

use crate::buffer::InputSource;
use crate::decoder::Decoder;
use super::decoder::Decoder;
use crate::input_source::InputSource;
use crate::Result;

/// TODO
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) ZeroC, Inc.

use crate::buffer::InputSource;
use crate::decode_from::DecodeFrom;
use super::decode_from::DecodeFrom;
use crate::input_source::InputSource;
use crate::Result;
use core::ops::{Deref, DerefMut};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (c) ZeroC, Inc.

use crate::buffer::InputSource;
use crate::decode_from::*;
use crate::decoder::Decoder;
use super::decode_from::*;
use super::decoder::Decoder;
use crate::input_source::InputSource;
use crate::{Error, InvalidDataErrorKind, Result};

// We only support `String`, `Vec`, and `BTreeMap` if the `alloc` crate is available through the `alloc` feature flag.
Expand Down
7 changes: 7 additions & 0 deletions slice-codec/src/decoding/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) ZeroC, Inc.

pub mod decode_from;
pub mod decoder;

// This module is private because it doesn't export any types, just implementations.
mod implementations;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) ZeroC, Inc.

use crate::buffer::OutputTarget;
use crate::encoder::Encoder;
use super::encoder::Encoder;
use crate::output_target::OutputTarget;
use crate::Result;

/// TODO
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) ZeroC, Inc.

use crate::buffer::OutputTarget;
use crate::encode_into::EncodeInto;
use super::encode_into::EncodeInto;
use crate::output_target::OutputTarget;
use crate::Result;
use core::ops::{Deref, DerefMut};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (c) ZeroC, Inc.

use crate::buffer::OutputTarget;
use crate::encode_into::*;
use crate::encoder::Encoder;
use super::encode_into::*;
use super::encoder::Encoder;
use crate::output_target::OutputTarget;
use crate::{Error, InvalidDataErrorKind, Result, VARINT62_MAX, VARINT62_MIN, VARUINT62_MAX, VARUINT62_MIN};

// We only support 'owned' sequence/dictionary types if the `alloc` crate is available through the `alloc` feature flag.
Expand Down
7 changes: 7 additions & 0 deletions slice-codec/src/encoding/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) ZeroC, Inc.

pub mod encode_into;
pub mod encoder;

// This module is private because it doesn't export any types, just implementations.
mod implementations;
2 changes: 1 addition & 1 deletion slice-codec/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub enum ErrorKind {

/// A buffer reservation did not fit within its buffer.
/// This error represents a serious problem in the implementation, or intentional tampering by callers.
/// See [`write_bytes_exact_into_reserved`](crate::buffer::OutputTarget::write_bytes_into_reserved_exact).
/// See [`write_bytes_exact_into_reserved`](crate::output_target::OutputTarget::write_bytes_into_reserved_exact).
InvalidReservation {
/// The length of the buffer.
buffer_len: usize,
Expand Down
39 changes: 39 additions & 0 deletions slice-codec/src/input_source/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) ZeroC, Inc.

//! TODO maybe write a comment explaining this module?

mod slice_input_source;
pub use slice_input_source::*;

use crate::Result;

/// A trait for types that can be read from by a [Slice decoder](crate::decoder::Decoder).
pub trait InputSource {
/// Returns the number of unread bytes currently remaining in the source.
fn remaining(&self) -> usize;

/// Returns the next byte available from this source without consuming it.
///
/// If there are no more bytes available from this source, an [`UnexpectedEob`] error is returned instead.
///
/// [`UnexpectedEob`]: crate::ErrorKind::UnexpectedEob
fn peek_byte(&mut self) -> Result<u8>;

/// Returns the next byte available from this source, and advances past it (consuming it).
///
/// If there are no more bytes available from this source, an [`UnexpectedEob`] error is returned instead.
///
/// [`UnexpectedEob`]: crate::ErrorKind::UnexpectedEob
fn read_byte(&mut self) -> Result<u8>;

// TODO remove these functions after adding `advance_by` and the low-level required API functions.
fn read_bytes_exact<const N: usize>(&mut self) -> Result<&[u8; N]>;
fn read_byte_slice_exact(&mut self, count: usize) -> Result<&[u8]>;

/// Reads bytes from this source into the provided buffer, and advances past them (consuming them).
///
/// This function reads exactly `dest.len()`-many bytes, or if it's unable to, returns an error instead.
/// If such an error occurs, no guarantees are made about how many bytes were read from the source, except that it
/// is less than `dest.len()`.
fn read_bytes_into_exact(&mut self, dest: &mut [u8]) -> Result<()>;
}
204 changes: 204 additions & 0 deletions slice-codec/src/input_source/slice_input_source.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// Copyright (c) ZeroC, Inc.

//! TODO maybe write a comment explaining this module?

use super::*;
use crate::{ErrorKind, Result};
use core::borrow::Borrow;
use core::{debug_assert, debug_assert_eq};

/// A wrapper around a `&[u8]` that implements [`InputSource`].
#[derive(Debug)]
pub struct SliceInputSource<'a> {
/// The underlying buffer that this type wraps.
buffer: &'a [u8],
/// Tracks the current position in the buffer that is being read from.
pos: usize,
}

impl<'a> SliceInputSource<'a> {
/// Checks whether there are at least `requested` unread bytes left in the buffer.
/// If there are, this returns `Ok`, and if there aren't this returns an [`ErrorKind::UnexpectedEob`] error.
///
/// This function is only used internally to ensure a particular read operation is safe to attempt.
fn does_buffer_have_at_least(&self, requested: usize) -> Result<()> {
let remaining = self.remaining();
if remaining < requested {
let error = ErrorKind::UnexpectedEob { requested, remaining };
Err(error.into())
} else {
Ok(())
}
}
}

impl InputSource for SliceInputSource<'_> {
fn remaining(&self) -> usize {
self.buffer.len() - self.pos
}

fn peek_byte(&mut self) -> Result<u8> {
self.does_buffer_have_at_least(1)?;

// SAFETY: the necessary bounds checking is performed by the above function call.
unsafe {
debug_assert!(self.buffer.get(self.pos).is_some());
Ok(*self.buffer.get_unchecked(self.pos))
}
}

fn read_byte(&mut self) -> Result<u8> {
let byte = self.peek_byte()?;
self.pos += 1;
Ok(byte)
}

fn read_bytes_exact<const N: usize>(&mut self) -> Result<&[u8; N]> {
let byte_slice = self.read_byte_slice_exact(N)?;

// SAFETY: `read_byte_slice_exact` is guaranteed to return exactly 'N' bytes, which means it's safe to
// convert, since `&[u8; N]` has the same layout as an `&[u8]` over 'N' bytes.
let byte_array = unsafe {
debug_assert_eq!(byte_slice.len(), N);
byte_slice.try_into().unwrap_unchecked()
};

Ok(byte_array)
}

fn read_byte_slice_exact(&mut self, count: usize) -> Result<&[u8]> {
self.does_buffer_have_at_least(count)?;

// SAFETY: the necessary bounds checking is performed by the above function call.
let byte_slice = unsafe {
let end = self.pos + count;
debug_assert!(self.buffer.get(self.pos..end).is_some());
self.buffer.get_unchecked(self.pos..end)
};
self.pos += count;
Ok(byte_slice)
}

fn read_bytes_into_exact(&mut self, dst: &mut [u8]) -> Result<()> {
let src = self.read_byte_slice_exact(dst.len())?;

// SAFETY: `read_byte_slice_exact` is guaranteed to return exactly `dst.len()` bytes, so there is enough space
// in `dst` to write these bytes, and we know the slices cannot overlap because `dst` is mutably borrowed,
// which guarantees exclusive access.
unsafe {
debug_assert_eq!(src.len(), dst.len());
core::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), dst.len());
Ok(())
}
}
}

impl<'a, T> From<&'a T> for SliceInputSource<'a>
where
T: Borrow<[u8]> + ?Sized,
{
/// Creates a new [`SliceInputSource`] that wraps the provided buffer.
fn from(value: &'a T) -> Self {
Self {
buffer: value.borrow(),
pos: 0,
}
}
}

// Allows users to create a [`Decoder`] directly from a slice,
// without needing to construct an intermediate [`SliceInputSource`].
impl<'a, T> From<T> for crate::decoding::decoder::Decoder<SliceInputSource<'a>>
where
T: Into<SliceInputSource<'a>>,
{
fn from(value: T) -> Self {
crate::decoding::decoder::Decoder::new(value.into())
}
}

#[cfg(test)]
mod tests {
use super::*;

/// Verifies that [`does_buffer_have_at_least`] returns the correct number of remaining bytes in the buffer
/// when the remaining bytes number are greater than or equal to the number of requested bytes.
#[test]
fn does_buffer_has_at_least_returns_ok() {
// Arrange
let buffer = [115, 108, 105, 99, 101];
let source = SliceInputSource::from(&buffer);

// Act
let result = source.does_buffer_have_at_least(5);

// Assert
assert!(result.is_ok());
}

/// Verifies that [`does_buffer_have_at_least`] returns an error when the remaining bytes number are less than
/// the number of requested bytes.
#[test]
fn does_buffer_have_at_least_returns_error() {
// Arrange
let source = SliceInputSource::from(&[115, 108, 105, 99, 101]);

// Act
let result = source.does_buffer_have_at_least(6);

// Assert
assert!(result.is_err());
assert!(matches!(result.unwrap_err().kind(), ErrorKind::UnexpectedEob {
requested: 6,
remaining: 5
}));
}

/// Verifies that [`peek_byte`] returns the correct byte from the buffer without consuming it.
#[test]
fn peek_byte_returns_correct_byte() {
// Arrange
let mut source = SliceInputSource::from(&[115, 108, 105, 99, 101]);

// Act
let result = source.peek_byte();

// Assert
assert!(result.is_ok());
assert_eq!(result.unwrap(), 115);
assert_eq!(source.pos, 0);
assert_eq!(source.remaining(), 5);
}

/// Verifies that [`read_byte`] returns the correct byte from the buffer and consumes it.
#[test]
fn read_byte_returns_correct_byte() {
// Arrange
let mut source = SliceInputSource::from(&[115, 108, 105, 99, 101]);

// Act
let result = source.read_byte();

// Assert
assert!(result.is_ok());
assert_eq!(result.unwrap(), 115);
assert_eq!(source.pos, 1);
assert_eq!(source.remaining(), 4);
}

/// Verifies that [`read_bytes_exact`] returns the correct number of bytes from the buffer and consumes them.
#[test]
fn read_bytes_exact_returns_correct_bytes() {
// Arrange
let mut source = SliceInputSource::from(&[115, 108, 105, 99, 101]);

// Act
let result = source.read_bytes_exact::<3>();

// Assert
assert!(result.is_ok());
assert_eq!(result.unwrap(), &[115, 108, 105]);
assert_eq!(source.pos, 3);
assert_eq!(source.remaining(), 2);
}
}
16 changes: 7 additions & 9 deletions slice-codec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,16 @@ extern crate alloc;
#[cfg(feature = "std")]
extern crate std;

// These modules are private because they don't export any types, just implementations.
pub mod input_source;
pub mod output_target;

mod decoding;
mod encoding;

pub mod buffer;
pub mod decode_from;
pub mod decoder;
pub mod encode_into;
pub mod encoder;

// Re-export the contents of the `error` module directly into the crate root, so they're easier to reference.
mod error;
Comment thread
InsertCreativityHere marked this conversation as resolved.

// Re-export the contents of some modules directly into the crate root, so they're easier to reference.
pub use decoding::*;
pub use encoding::*;
pub use error::*;

/// The smallest value that can be represented as a `varint32`.
Expand Down
Loading