use core::{fmt, ops};
macro_rules! wire_int {
(
$(#[$meta:meta])*
$name:ident, $native:ty, $size:literal, $canonical:literal
) => {
$(#[$meta])*
#[derive(Clone, Copy, PartialEq, Eq, Default)]
#[repr(transparent)]
pub struct $name([u8; $size]);
const _: () = assert!(core::mem::size_of::<$name>() == $size);
const _: () = assert!(core::mem::align_of::<$name>() == 1);
impl $name {
pub const ZERO: Self = Self([0u8; $size]);
pub const MAX: Self = Self(<$native>::MAX.to_le_bytes());
pub const MIN: Self = Self(<$native>::MIN.to_le_bytes());
#[inline(always)]
pub const fn new(v: $native) -> Self {
Self(v.to_le_bytes())
}
#[inline(always)]
pub const fn get(self) -> $native {
<$native>::from_le_bytes(self.0)
}
#[inline(always)]
pub fn set(&mut self, v: $native) {
self.0 = v.to_le_bytes();
}
#[inline(always)]
pub fn checked_add_assign(
&mut self,
rhs: $native,
) -> ::core::result::Result<(), ::hopper_runtime::ProgramError> {
let next = self
.get()
.checked_add(rhs)
.ok_or(::hopper_runtime::ProgramError::ArithmeticOverflow)?;
self.set(next);
Ok(())
}
#[inline(always)]
pub fn add_assign_checked(
&mut self,
rhs: $native,
) -> ::core::result::Result<(), ::hopper_runtime::ProgramError> {
self.checked_add_assign(rhs)
}
#[inline(always)]
pub fn checked_sub_assign(
&mut self,
rhs: $native,
) -> ::core::result::Result<(), ::hopper_runtime::ProgramError> {
let next = self
.get()
.checked_sub(rhs)
.ok_or(::hopper_runtime::ProgramError::ArithmeticOverflow)?;
self.set(next);
Ok(())
}
#[inline(always)]
pub fn sub_assign_checked(
&mut self,
rhs: $native,
) -> ::core::result::Result<(), ::hopper_runtime::ProgramError> {
self.checked_sub_assign(rhs)
}
#[inline(always)]
pub fn checked_mul_assign(
&mut self,
rhs: $native,
) -> ::core::result::Result<(), ::hopper_runtime::ProgramError> {
let next = self
.get()
.checked_mul(rhs)
.ok_or(::hopper_runtime::ProgramError::ArithmeticOverflow)?;
self.set(next);
Ok(())
}
#[inline(always)]
pub fn mul_assign_checked(
&mut self,
rhs: $native,
) -> ::core::result::Result<(), ::hopper_runtime::ProgramError> {
self.checked_mul_assign(rhs)
}
#[inline(always)]
pub const fn as_bytes(&self) -> &[u8; $size] {
&self.0
}
#[inline(always)]
pub fn as_bytes_mut(&mut self) -> &mut [u8; $size] {
&mut self.0
}
}
impl From<$native> for $name {
#[inline(always)]
fn from(v: $native) -> Self {
Self::new(v)
}
}
impl From<$name> for $native {
#[inline(always)]
fn from(w: $name) -> Self {
w.get()
}
}
impl ops::Add<$native> for $name {
type Output = Self;
#[inline(always)]
fn add(self, rhs: $native) -> Self::Output {
Self::new(self.get() + rhs)
}
}
impl ops::Add<Self> for $name {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self::Output {
Self::new(self.get() + rhs.get())
}
}
impl ops::AddAssign<$native> for $name {
#[inline(always)]
fn add_assign(&mut self, rhs: $native) {
self.set(self.get() + rhs);
}
}
impl ops::AddAssign<Self> for $name {
#[inline(always)]
fn add_assign(&mut self, rhs: Self) {
self.set(self.get() + rhs.get());
}
}
impl ops::Sub<$native> for $name {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: $native) -> Self::Output {
Self::new(self.get() - rhs)
}
}
impl ops::Sub<Self> for $name {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
Self::new(self.get() - rhs.get())
}
}
impl ops::SubAssign<$native> for $name {
#[inline(always)]
fn sub_assign(&mut self, rhs: $native) {
self.set(self.get() - rhs);
}
}
impl ops::SubAssign<Self> for $name {
#[inline(always)]
fn sub_assign(&mut self, rhs: Self) {
self.set(self.get() - rhs.get());
}
}
impl ops::Mul<$native> for $name {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: $native) -> Self::Output {
Self::new(self.get() * rhs)
}
}
impl ops::Mul<Self> for $name {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: Self) -> Self::Output {
Self::new(self.get() * rhs.get())
}
}
impl ops::MulAssign<$native> for $name {
#[inline(always)]
fn mul_assign(&mut self, rhs: $native) {
self.set(self.get() * rhs);
}
}
impl ops::MulAssign<Self> for $name {
#[inline(always)]
fn mul_assign(&mut self, rhs: Self) {
self.set(self.get() * rhs.get());
}
}
impl PartialOrd for $name {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for $name {
#[inline(always)]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.get().cmp(&other.get())
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}({})", stringify!($name), self.get())
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.get())
}
}
unsafe impl crate::abi::WireType for $name {
const WIRE_SIZE: usize = $size;
const CANONICAL_NAME: &'static str = $canonical;
}
#[cfg(feature = "hopper-native-backend")]
unsafe impl ::hopper_runtime::__hopper_native::bytemuck::Zeroable for $name {}
#[cfg(feature = "hopper-native-backend")]
unsafe impl ::hopper_runtime::__hopper_native::bytemuck::Pod for $name {}
unsafe impl crate::account::Pod for $name {}
unsafe impl ::hopper_runtime::__sealed::HopperZeroCopySealed for $name {}
impl crate::account::FixedLayout for $name {
const SIZE: usize = $size;
}
};
}
wire_int!(
WireU16, u16, 2, "u16"
);
wire_int!(
WireU32, u32, 4, "u32"
);
wire_int!(
WireU64, u64, 8, "u64"
);
wire_int!(
WireU128, u128, 16, "u128"
);
wire_int!(
WireI16, i16, 2, "i16"
);
wire_int!(
WireI32, i32, 4, "i32"
);
wire_int!(
WireI64, i64, 8, "i64"
);
wire_int!(
WireI128, i128, 16, "i128"
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wire_u64_roundtrip() {
let w = WireU64::new(0xDEAD_BEEF_CAFE_BABE);
assert_eq!(w.get(), 0xDEAD_BEEF_CAFE_BABE);
}
#[test]
fn wire_i64_negative() {
let w = WireI64::new(-42);
assert_eq!(w.get(), -42);
}
#[test]
fn wire_u64_checked_assign_helpers() {
let mut w = WireU64::new(10);
w.checked_add_assign(5).unwrap();
assert_eq!(w.get(), 15);
w.checked_sub_assign(3).unwrap();
assert_eq!(w.get(), 12);
w.checked_mul_assign(2).unwrap();
assert_eq!(w.get(), 24);
}
#[test]
fn wire_u64_assignment_operators_accept_native_rhs() {
let mut w = WireU64::new(10);
w += 5;
assert_eq!(w.get(), 15);
w -= 3;
assert_eq!(w.get(), 12);
w *= 2;
assert_eq!(w.get(), 24);
assert_eq!((w + 1).get(), 25);
assert_eq!((w - 4).get(), 20);
assert_eq!((w * 3).get(), 72);
}
#[test]
fn wire_assignment_operators_accept_wire_rhs() {
let mut w = WireI64::new(10);
w += WireI64::new(-3);
assert_eq!(w.get(), 7);
w -= WireI64::new(2);
assert_eq!(w.get(), 5);
w *= WireI64::new(-2);
assert_eq!(w.get(), -10);
}
#[test]
fn wire_u64_checked_assign_overflow_is_reported() {
let mut w = WireU64::new(u64::MAX);
assert_eq!(
w.checked_add_assign(1),
Err(::hopper_runtime::ProgramError::ArithmeticOverflow)
);
assert_eq!(w.get(), u64::MAX);
}
#[test]
fn wire_ordering() {
let a = WireU32::new(10);
let b = WireU32::new(20);
assert!(a < b);
}
#[test]
fn wire_default_is_zero() {
let w = WireU64::default();
assert_eq!(w.get(), 0);
}
}