oct 0.34.0

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

//! The [`Slot`] type.

use crate::serdes::{Deserialise, Serialise};

use alloc::vec::Vec;
use core::marker::PhantomData;
use core::fmt::{self, Debug, Formatter};
use std::io;

/// A serialisation/deserialisation slot.
///
/// This type wraps a raw octet buffer
///
#[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) -> io::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) -> io::Result<()>
	where
		T: Serialise,
	{
		self.clear();
		Serialise::serialise(value, &mut self.buf)
	}

	/// 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]) -> io::Result<usize>>(&mut self, op: F) -> io::Result<()>
	where
		T: Serialise,
	{
		let len = op(self.as_bytes_mut())?;
		self.set_len(len);
		Ok(())
	}

	/// Copies data from a slice.
	///
	/// 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",
		);

		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());

		unsafe { self.buf.set_len(len) };
	}

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

	/// Retrieves the length of the current
	/// in octets.
	#[inline(always)]
	#[must_use]
	pub const 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 const fn is_empty(&self) -> bool {
		self.len() == 0
	}

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

	/// Retrieves a mutable slice over the slot bytes
	/// (octets.)
	#[inline]
	#[must_use]
	pub const 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 const fn as_ptr(&self) -> *const u8 {
		self.buf.as_ptr()
	}

	/// Retrieves a mutable pointer to the first octet.
	#[inline(always)]
	#[must_use]
	pub const 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 {
		Debug::fmt(&self.as_bytes(), f)
	}
}

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

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