use std::{fmt, marker::PhantomData};
pub trait Bytes: Copy + 'static {
const LENGTH: usize;
unsafe fn read_bytes(bytes: &[u8]) -> Self;
unsafe fn write_bytes(self, bytes: &mut [u8]);
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Memory {
bytes: Vec<u8>,
}
#[derive(Debug, Default, Eq, PartialEq)]
pub struct MemoryBuilder {
bytes: Vec<u8>,
}
impl MemoryBuilder {
pub fn new() -> Self {
Self { bytes: vec![] }
}
pub fn finish(self) -> Memory {
Memory { bytes: self.bytes }
}
}
pub struct Pointer<T> {
offset: usize,
phantom: PhantomData<T>,
}
impl<T> Pointer<T>
where
T: Bytes,
{
pub fn new(builder: &mut MemoryBuilder, val: T) -> Self {
let offset = builder.bytes.len();
builder.bytes.extend((0..T::LENGTH).map(|_| 0));
unsafe {
val.write_bytes(
builder
.bytes
.get_unchecked_mut(offset..(offset + T::LENGTH)),
)
}
Self {
offset,
phantom: PhantomData,
}
}
#[inline]
pub fn get(self, memory: &Memory) -> T {
unsafe {
T::read_bytes(
memory
.bytes
.get_unchecked(self.offset..(self.offset + T::LENGTH)),
)
}
}
#[inline]
pub fn set(self, memory: &mut Memory, val: T) {
unsafe {
val.write_bytes(
memory
.bytes
.get_unchecked_mut(self.offset..(self.offset + T::LENGTH)),
);
}
}
#[inline]
pub fn update(self, memory: &mut Memory, f: impl FnOnce(T) -> T) {
self.set(memory, f(self.get(memory)));
}
}
impl<T> Clone for Pointer<T> {
fn clone(&self) -> Self {
Self {
offset: self.offset,
phantom: PhantomData,
}
}
}
impl<T> Copy for Pointer<T> {}
impl<T> fmt::Debug for Pointer<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Pointer")
.field("offset", &self.offset)
.finish()
}
}
impl<T> Eq for Pointer<T> {}
impl<T> PartialEq for Pointer<T> {
fn eq(&self, other: &Self) -> bool {
self.offset == other.offset
}
}
pub struct ArrayPointer<T> {
offset: usize,
len: usize,
phantom: PhantomData<T>,
}
impl<T> ArrayPointer<T>
where
T: Bytes,
{
pub fn new(builder: &mut MemoryBuilder, vals: &[T]) -> Self {
let offset = builder.bytes.len();
builder
.bytes
.extend((0..(T::LENGTH * vals.len())).map(|_| 0));
let mut val_offset = offset;
for val in vals.iter() {
unsafe {
val.write_bytes(
builder
.bytes
.get_unchecked_mut(val_offset..(val_offset + T::LENGTH)),
);
}
val_offset += T::LENGTH;
}
Self {
offset,
len: vals.len(),
phantom: PhantomData,
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn get(&self, memory: &Memory, i: usize) -> T {
assert!(i < self.len, "array index out of bounds");
let mem_offset = self.offset + i * T::LENGTH;
unsafe {
T::read_bytes(
memory
.bytes
.get_unchecked(mem_offset..(mem_offset + T::LENGTH)),
)
}
}
#[inline]
pub fn set(&self, memory: &mut Memory, i: usize, val: T) {
assert!(i < self.len, "array index out of bounds");
let mem_offset = self.offset + i * T::LENGTH;
unsafe {
val.write_bytes(
memory
.bytes
.get_unchecked_mut(mem_offset..(mem_offset + T::LENGTH)),
);
}
}
#[inline]
pub fn update(&self, memory: &mut Memory, i: usize, f: impl FnOnce(T) -> T) {
self.set(memory, i, f(self.get(memory, i)));
}
#[inline]
pub fn swap(&self, memory: &mut Memory, i: usize, j: usize) {
let temp_i = self.get(memory, i);
self.set(memory, i, self.get(memory, j));
self.set(memory, j, temp_i);
}
}
impl<T> Clone for ArrayPointer<T> {
fn clone(&self) -> Self {
Self {
offset: self.offset,
len: self.len,
phantom: PhantomData,
}
}
}
impl<T> Copy for ArrayPointer<T> {}
impl<T> fmt::Debug for ArrayPointer<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ArrayPointer")
.field("offset", &self.offset)
.field("len", &self.len)
.finish()
}
}
impl<T> Eq for ArrayPointer<T> {}
impl<T> PartialEq for ArrayPointer<T> {
fn eq(&self, other: &Self) -> bool {
self.offset == other.offset && self.len == other.len
}
}
macro_rules! impl_bytes_primitive {
( $( $T:ty ),* , ) => {
$(
impl Bytes for $T {
const LENGTH: usize = std::mem::size_of::<$T>();
#[inline]
unsafe fn read_bytes(bytes: &[u8]) -> $T {
let byte_array = *(bytes.as_ptr() as *const [u8; std::mem::size_of::<$T>()]);
std::mem::transmute::<[u8; std::mem::size_of::<$T>()], $T>(byte_array)
}
#[inline]
unsafe fn write_bytes(self, bytes: &mut [u8]) {
let byte_array = std::mem::transmute::<$T, [u8; std::mem::size_of::<$T>()]>(self);
bytes.copy_from_slice(&byte_array);
}
}
)*
}
}
impl_bytes_primitive! {
i8, i16, i32, i64, i128, isize,
u8, u16, u32, u64, u128, usize,
f32, f64,
char,
bool,
(),
}
#[cfg(test)]
mod tests {
use super::*;
const TRIALS: usize = 100;
const SEED: [u8; 32] = [42; 32];
macro_rules! test_bytes {
( $( [ $T:ty, $test_fn:ident ], )* ) => {
mod read_write_bytes {
use super::*;
$(
#[test]
fn $test_fn() {
use rand::{Rng, SeedableRng, rngs::StdRng};
let mut rng = StdRng::from_seed(SEED);
for _ in 0..TRIALS {
let val = rng.gen::<$T>();
let mut bytes = [0u8; std::mem::size_of::<$T>()];
unsafe {
val.write_bytes(&mut bytes);
}
assert_eq!(unsafe { <$T as Bytes>::read_bytes(&bytes) }, val);
}
}
)*
}
};
}
test_bytes!(
[i8, i8],
[i16, i16],
[i32, i32],
[i64, i64],
[i128, i128],
[isize, isize],
[u8, u8],
[u16, u16],
[u32, u32],
[u64, u64],
[u128, u128],
[usize, usize],
[f32, f32],
[f64, f64],
[char, char],
[bool, bool],
[(), unit],
);
mod pointer {
use super::*;
#[test]
fn debug() {
let mut builder = MemoryBuilder::new();
let offset_0 = Pointer::new(&mut builder, 0_u64);
let offset_8 = Pointer::new(&mut builder, false);
assert_eq!(format!("{:?}", offset_0), "Pointer { offset: 0 }");
assert_eq!(format!("{:?}", offset_8), "Pointer { offset: 8 }");
}
#[test]
fn clone_eq() {
let mut builder = MemoryBuilder::new();
let pointer = Pointer::new(&mut builder, 'B');
assert_eq!(pointer, pointer.clone());
}
#[test]
fn get_set_update() {
let mut builder = MemoryBuilder::new();
let pointer = Pointer::new(&mut builder, 5);
let mut memory = builder.finish();
assert_eq!(pointer.get(&memory), 5);
pointer.set(&mut memory, 6);
assert_eq!(pointer.get(&memory), 6);
pointer.update(&mut memory, |x| x - 1);
assert_eq!(pointer.get(&memory), 5);
}
}
mod array_pointer {
use super::*;
#[test]
fn debug() {
let mut builder = MemoryBuilder::new();
let offset_0 = ArrayPointer::new(&mut builder, &[0u64; 8]);
let offset_64 = ArrayPointer::new(&mut builder, &[false]);
assert_eq!(
format!("{:?}", offset_0),
"ArrayPointer { offset: 0, len: 8 }"
);
assert_eq!(
format!("{:?}", offset_64),
"ArrayPointer { offset: 64, len: 1 }"
);
}
#[test]
fn clone_eq() {
let mut builder = MemoryBuilder::new();
let pointer = ArrayPointer::new(&mut builder, &['R', 'U', 'S', 'T']);
assert_eq!(pointer, pointer.clone());
}
#[test]
fn empty() {
let mut builder = MemoryBuilder::new();
let empty = ArrayPointer::<char>::new(&mut builder, &[]);
let not_empty = ArrayPointer::new(&mut builder, &['a', 'b', 'c']);
assert!(empty.is_empty());
assert!(empty.len() == 0);
assert!(!not_empty.is_empty());
assert!(not_empty.len() != 0);
}
#[test]
fn get_set_update() {
let values = [1, 3, 5, 7];
let mut builder = MemoryBuilder::new();
let pointer = ArrayPointer::new(&mut builder, &values);
let mut memory = builder.finish();
for i in 0..4 {
assert_eq!(pointer.get(&memory, i), values[i]);
pointer.set(&mut memory, i, values[i] + 1);
assert_eq!(pointer.get(&memory, i), values[i] + 1);
pointer.update(&mut memory, i, |x| x - 1);
assert_eq!(pointer.get(&memory, i), values[i]);
}
}
#[test]
fn swap() {
let mut builder = MemoryBuilder::new();
let pointer = ArrayPointer::new(&mut builder, &['a', 'z']);
let mut memory = builder.finish();
assert_eq!(pointer.get(&memory, 0), 'a');
assert_eq!(pointer.get(&memory, 1), 'z');
pointer.swap(&mut memory, 0, 1);
assert_eq!(pointer.get(&memory, 0), 'z');
assert_eq!(pointer.get(&memory, 1), 'a');
}
}
}