oct 0.36.2

Octonary transcodings.
Documentation
// Copyright 2024-2026 Gabriel Bjørnager Jensen.
//
// SPDX: MIT OR Apache-2.0

//! The [`Slot`] type.

#![cfg(feature = "alloc")]

use alloc::vec::Vec;
use core::marker::PhantomData;
use core::fmt::{self, Debug, Formatter};
use oct::serdes::{Deserialise, Result, Serialise};

/// A serialisation/deserialisation slot.
///
#[cfg_attr(feature = "proc_macro", doc = include_str!("slot_example.md"))]
pub struct Slot<T: ?Sized> {
	/// The raw buffer.
	buf: Vec<u8>,

	/// The object.
	_object: PhantomData<T>,
}

impl<T: ?Sized> Slot<T> {
	/// The unit of buffer sizes.
	const SIZE_UNIT: usize = 128;

	/// Constructs a new slot.
	#[must_use]
	pub const fn new() -> Self {
		let buf = Vec::new();
		Self { buf, _object: PhantomData }
	}

	/// Constructs a slot with a minimum capacity.
	///
	/// The actual size of the slot may be larger than
	/// the provided value.
	#[must_use]
	pub fn with_capacity(mut capacity: usize) -> Self {
		capacity = capacity.next_multiple_of(Self::SIZE_UNIT);

		let buf = Vec::with_capacity(capacity);
		Self { buf, _object: PhantomData }
	}

	/// Deserialises the contained value.
	///
	/// # Errors
	///
	/// Returns an [`Err`] instance if the contained
	/// value could not be deserialised.
	pub fn read(&self) -> Result<T>
	where
		T: Deserialise + Sized,
	{
		Deserialise::deserialise(&mut self.as_bytes())
	}

	/// Serialises a value into the contained buffer.
	///
	/// The internal buffer is grown as needed.
	///
	/// # Errors
	///
	/// Returns an [`Err`] instance if the provided
	/// value could not be serialised.
	///
	/// The value of the buffer in the case of a failed
	/// serialisation is unspecified.
	pub fn write(&mut self, value: &T) -> Result<()>
	where
		T: Serialise,
	{
		self.clear();
		Serialise::serialise(value, &mut self.buf)
	}

	/// Serialises a value into the contained buffer
	/// without growing.
	///
	/// In contrast to [`write`], this method to does
	/// grow the contained buffer if it is too small.
	///
	/// [`Self::write`]
	///
	/// # Errors
	///
	/// Returns an [`Err`] instance if the provided
	/// value could not be serialised (e.g. if the
	/// contained buffer is too small.)
	///
	/// The value of the buffer in the case of a failed
	/// serialisation is unspecified.
	pub fn write_within_capacity(&mut self, value: &T) -> Result<()>
	where
		T: Serialise,
	{
		self.clear();
		Serialise::serialise(value, &mut self.buf.as_mut_slice())
	}

	/// Writes directly into the internal buffer.
	///
	/// # Errors
	///
	/// Any error returned by `op` will be forwarded.
	///
	/// # Panics
	///
	/// This method will panic if the length returned by
	/// `op` is longer than the capacity of the internal
	/// buffer.
	pub fn write_with<F: FnOnce(&mut [u8]) -> Result<usize>>(&mut self, op: F) -> Result<()>
	where
		T: Serialise,
	{
		let len = op(self.as_bytes_mut())?;
		self.set_len(len);
		Ok(())
	}

	/// Copies data from another slice into the slot.
	///
	/// The internal buffer is resized to hold the
	/// provided data.
	pub fn copy_from_slice(&mut self, data: &[u8]) {
		self.buf.resize(data.len(), 0);
		self.as_bytes_mut().copy_from_slice(data);
	}

	/// Clears the contained serialisation.
	pub fn clear(&mut self) {
		self.buf.clear()
	}

	/// Overwrites the length of the contained object.
	///
	/// # Panic
	///
	/// This method will panic if the provided length is
	/// longer than the capacity of the internal buffer.
	#[inline]
	pub fn set_len(&mut self, len: usize) {
		assert!(
			len <= self.capacity(),
			"cannot extend length beyond slot capacity",
		);

		// SAFETY: We have tested that the length is within
		// bounds.
		unsafe { self.set_len_unchecked(len) };
	}

	/// Overwrites the length of the contained object.
	///
	/// # Safety
	///
	/// The provided length may not be longer than the
	/// current capacity.
	#[inline]
	pub unsafe fn set_len_unchecked(&mut self, len: usize) {
		debug_assert!(len <= self.capacity());

		// SAFETY:
		// * Caller guarantees that the length does not
		//   point of allocated bounds.
		//
		// * We guarantee that all octets are always ini-
		//   tialised.
		unsafe { self.buf.set_len(len) };
	}

	/// Retrieves the capacity of the slot, in octets.
	#[inline]
	#[must_use]
	pub fn capacity(&self) -> usize {
		self.buf.capacity()
	}

	/// Retrieves the length of the current
	/// serialisation, measured in octets.
	#[inline(always)]
	#[must_use]
	pub fn len(&self) -> usize {
		self.buf.len()
	}

	/// Tests if the slot is empty.
	///
	/// Note that an empty slot may still contain a
	/// serialisation if the serialised type is a ZST.
	#[inline]
	#[must_use]
	pub fn is_empty(&self) -> bool {
		self.len() == 0
	}

	/// Retrieves a slice over the slot bytes (octets.)
	#[inline]
	#[must_use]
	pub fn as_bytes(&self) -> &[u8] {
		self.buf.as_slice()
	}

	/// Retrieves a mutable slice over the slot bytes
	/// (octets.)
	#[inline]
	#[must_use]
	pub fn as_bytes_mut(&mut self) -> &mut [u8] {
		self.buf.as_mut_slice()
	}

	/// Retrieves a constant pointer to the first octet.
	#[inline(always)]
	#[must_use]
	pub fn as_ptr(&self) -> *const u8 {
		self.buf.as_ptr()
	}

	/// Retrieves a mutable pointer to the first octet.
	#[inline(always)]
	#[must_use]
	pub fn as_mut_ptr(&mut self) -> *mut u8 {
		self.buf.as_mut_ptr()
	}
}

impl<T: ?Sized> Clone for Slot<T> {
	fn clone(&self) -> Self {
		let buf = self.buf.clone();
		Self { buf, _object: PhantomData }
	}
}

impl<T: ?Sized> Debug for Slot<T> {
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
		let mut debug = f.debug_tuple("Slot");
		debug.field(&self.as_bytes());
		debug.finish()
	}
}

impl<T: ?Sized> Default for Slot<T> {
	#[inline]
	fn default() -> Self {
		Self::new()
	}
}

impl<T: ?Sized> Drop for Slot<T> {
	fn drop(&mut self) {}
}