looklook 0.5.0

Descriptive signal synthesiser.
// Copyright 2026 Gabriel Bjørnager Jensen.
//
// This file is part of LOOKLOOK.
//
// LOOKLOOK is free software: you can redistribute
// it and/or modify it under the terms of the GNU
// Affero General Public License as published by
// the Free Software Foundation, either version 3
// of the License, or (at your option) any later
// version.
//
// LOOKLOOK is distributed in the hope that it will
// be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Af-
// fero General Public License along with LOOKLOOK.
// If not, see <https://www.gnu.org/licenses/>.

//! The [`RenderBuf`] type.

use crate::mode::SampleDepth;

/// A render buffer.
#[derive(Clone, Debug)]
pub struct RenderBuf {
	/// The sampling depth.
	depth: SampleDepth,

	/// The raw render data.
	data: Box<[u8]>,
}

impl RenderBuf {
	/// The maximum, allowed render length.
	///
	/// This value is either equal to [`isize::MAX`] or
	/// [`u64::MAX`], whichever is less. In any case, it
	/// must always be less than `u64::MAX` so that it
	/// can be indexed.
	pub const MAX_LEN: u64 = u64::try_from(isize::MAX).unwrap_or(u64::MAX);

	/// Creates a new, empty render buffer.
	///
	/// # Panics
	///
	/// This constructor will panic if the computed size
	/// of the buffer is greater than [`usize::MAX`].
	#[inline]
	#[must_use]
	pub fn new(depth: SampleDepth, len: u64) -> Self {
		let size = depth.as_u64()
			.checked_mul(len)
			.and_then(|size| usize::try_from(size).ok())
			.expect("overflow when computing render buffer size");

		let data = vec![0x00; size].into();

		Self { depth, data }
	}

	/// Gets a sample.
	#[inline]
	#[must_use]
	pub fn get(&self, index: u64) -> Option<&[u8]> {
		let start: usize = index.checked_mul(self.depth.as_u64())?
			.try_into()
			.ok()?;

		let end = start.checked_add(self.depth.as_usize())?;

		self.data.get(start..end)
	}

	/// Mutably gets a sample.
	#[inline]
	#[must_use]
	pub fn get_mut(&mut self, index: u64) -> Option<&mut [u8]> {
		let start: usize = index.checked_mul(self.depth.as_u64())?
			.try_into()
			.ok()?;

		let end = start.checked_add(self.depth.as_usize())?;

		self.data.get_mut(start..end)
	}

	/// Clears the buffer.
	#[inline]
	pub fn clear(&mut self) {
		self.data.fill(Default::default())
	}

	/// Retrieves the length of the buffer.
	#[inline]
	#[must_use]
	pub fn len(&self) -> u64 {
		self.data.len() as u64 / self.depth.as_u64()
	}

	/// Tests if the buffer is empty.
	#[inline]
	#[must_use]
	pub fn is_empty(&self) -> bool {
		self.data.is_empty()
	}

	/// Borrows the raw render data.
	#[inline]
	#[must_use]
	pub const fn data(&self) -> &[u8] {
		&self.data
	}

	/// Mutably borrows the raw render data.
	#[inline]
	#[must_use]
	pub const fn data_mut(&mut self) -> &mut [u8] {
		&mut self.data
	}
}

impl Drop for RenderBuf {
	#[inline(always)]
	fn drop(&mut self) {}
}