looklook 0.9.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 ver-
// sion.
//
// LOOKLOOK is distributed in the hope that it will be useful, but WITHOUT ANY WAR-
// RANTY; 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 Affero General Public License along
// with LOOKLOOK. If not, see <https://www.gnu.org/licenses/>.

//! The [`Word`] type.

use crate::isa::Byte;

use std::fmt::{self, Debug, Display, Formatter};

/// A `16`-bit word value.
#[repr(transparent)]
#[derive(Copy)]
#[derive_const(
	Clone,
	Default,
	Eq,
	Ord,
	PartialEq,
	PartialOrd,
)]
pub struct Word(u16);

impl Word {
	/// The amount of bits in a word.
	pub const BITS: u32 = 16;

	/// Constructs a word from a [`u16`] value.
	#[inline(always)]
	#[must_use]
	pub const fn from_u16(value: u16) -> Self {
		Self(value)
	}

	/// Constructs a word from an [`i16`] value.
	#[inline]
	#[must_use]
	pub const fn from_i16(value: i16) -> Self {
		Self(value.cast_unsigned())
	}

	/// Converts a big-endia word to the host endian.
	#[inline]
	#[must_use]
	pub const fn from_be(value: Self) -> Self {
		Self(u16::from_be(value.0))
	}

	/// Reinterprets host-endian bytes as a word.
	#[inline]
	#[must_use]
	pub const fn from_ne_bytes(bytes: [u8; size_of::<Self>()]) -> Self {
		Self(u16::from_ne_bytes(bytes))
	}

	/// Converts big-endian bytes to a host-endian word.
	#[inline]
	#[must_use]
	pub const fn from_be_bytes(bytes: [u8; size_of::<Self>()]) -> Self {
		Self(u16::from_be_bytes(bytes))
	}

	/// Interprets the word as a [`u16`] value.
	#[inline(always)]
	#[must_use]
	pub const fn as_u16(self) -> u16 {
		self.0
	}

	/// Interprets the word as a [`i16`] value.
	#[inline]
	#[must_use]
	pub const fn as_i16(self) -> i16 {
		self.0.cast_signed()
	}

	/// Converts a host-endian word to big-endian.
	#[inline]
	#[must_use]
	pub const fn to_be(self) -> Self {
		Self(self.0.to_be())
	}

	/// Reinterprets the word as host-endian bytes.
	#[inline]
	#[must_use]
	pub const fn to_ne_bytes(self) -> [u8; size_of::<Self>()] {
		self.0.to_ne_bytes()
	}

	/// Converts the host-endian word to big-endian bytes.
	#[inline]
	#[must_use]
	pub const fn to_be_bytes(self) -> [u8; size_of::<Self>()] {
		self.0.to_be_bytes()
	}
}

impl Debug for Word {
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
		let prefix = match self.as_u16().max(1).ilog(16) {
			0 => "$...",
			1 => "$..",
			2 => "$.",
			3 => "$",

			_ => {
				unreachable!();
			}
		};

		write!(f, "{prefix}{:x}", self.as_u16())
	}
}

impl Display for Word {
	#[inline]
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
		Debug::fmt(self, f)
	}
}

const impl From<Byte> for Word {
	#[inline]
	fn from(value: Byte) -> Self {
		Self::from_u16(value.as_u8().into())
	}
}

const impl From<i16> for Word {
	#[inline(always)]
	fn from(value: i16) -> Self {
		Self::from_i16(value)
	}
}

const impl From<i8> for Word {
	#[inline]
	fn from(value: i8) -> Self {
		Self::from_i16(value.into())
	}
}

const impl From<u16> for Word {
	#[inline(always)]
	fn from(value: u16) -> Self {
		Self::from_u16(value)
	}
}

const impl From<u8> for Word {
	#[inline]
	fn from(value: u8) -> Self {
		Self::from_u16(value.into())
	}
}