#![allow(unsafe_code)]
use std::alloc::{self, Layout};
use std::ptr::NonNull;
use std::slice;
use crate::error::{OxiGdalError, Result};
pub struct AlignedBuffer<T> {
ptr: NonNull<T>,
len: usize,
align: usize,
layout: Layout,
}
impl<T> AlignedBuffer<T> {
pub fn new(capacity: usize, align: usize) -> Result<Self> {
if !align.is_power_of_two() {
return Err(OxiGdalError::InvalidParameter {
parameter: "align",
message: "Alignment must be a power of 2".to_string(),
});
}
if align < std::mem::align_of::<T>() {
return Err(OxiGdalError::InvalidParameter {
parameter: "align",
message: format!(
"Alignment {} is less than natural alignment of {}",
align,
std::mem::align_of::<T>()
),
});
}
if capacity == 0 {
return Err(OxiGdalError::InvalidParameter {
parameter: "capacity",
message: "Capacity must be greater than 0".to_string(),
});
}
let size = capacity
.checked_mul(std::mem::size_of::<T>())
.ok_or_else(|| OxiGdalError::InvalidParameter {
parameter: "capacity",
message: "Capacity overflow".to_string(),
})?;
let layout = Layout::from_size_align(size, align).map_err(|e| OxiGdalError::Internal {
message: format!("Invalid layout: {e}"),
})?;
let ptr = unsafe { alloc::alloc(layout) };
let ptr = NonNull::new(ptr)
.ok_or_else(|| OxiGdalError::Internal {
message: "Failed to allocate aligned buffer".to_string(),
})?
.cast::<T>();
Ok(Self {
ptr,
len: capacity,
align,
layout,
})
}
pub fn zeros(capacity: usize, align: usize) -> Result<Self>
where
T: Default + Copy,
{
let buffer = Self::new(capacity, align)?;
unsafe {
std::ptr::write_bytes(buffer.ptr.as_ptr(), 0, capacity);
}
Ok(buffer)
}
#[must_use]
pub const fn len(&self) -> usize {
self.len
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub const fn alignment(&self) -> usize {
self.align
}
#[must_use]
pub fn as_ptr(&self) -> *const T {
self.ptr.as_ptr()
}
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut T {
self.ptr.as_ptr()
}
#[must_use]
pub fn as_slice(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
pub fn copy_from_slice(&mut self, src: &[T]) -> Result<()>
where
T: Copy,
{
if src.len() != self.len {
return Err(OxiGdalError::InvalidParameter {
parameter: "src",
message: format!(
"Source length {} doesn't match buffer capacity {}",
src.len(),
self.len
),
});
}
self.as_mut_slice().copy_from_slice(src);
Ok(())
}
pub fn strided_view(&self, stride: usize) -> Result<StridedView<'_, T>> {
if stride == 0 {
return Err(OxiGdalError::InvalidParameter {
parameter: "stride",
message: "Stride must be greater than 0".to_string(),
});
}
Ok(StridedView {
buffer: self.as_slice(),
stride,
})
}
}
impl<T> Drop for AlignedBuffer<T> {
fn drop(&mut self) {
unsafe {
alloc::dealloc(self.ptr.as_ptr().cast::<u8>(), self.layout);
}
}
}
unsafe impl<T: Send> Send for AlignedBuffer<T> {}
unsafe impl<T: Sync> Sync for AlignedBuffer<T> {}
pub struct StridedView<'a, T> {
buffer: &'a [T],
stride: usize,
}
impl<T> StridedView<'_, T> {
#[must_use]
pub fn len(&self) -> usize {
self.buffer.len().div_ceil(self.stride)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
#[must_use]
pub fn get(&self, index: usize) -> Option<&T> {
let offset = index * self.stride;
self.buffer.get(offset)
}
#[must_use]
pub fn iter(&self) -> StridedIterator<'_, T> {
StridedIterator {
buffer: self.buffer,
stride: self.stride,
index: 0,
}
}
}
pub struct StridedIterator<'a, T> {
buffer: &'a [T],
stride: usize,
index: usize,
}
impl<'a, T> Iterator for StridedIterator<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
let offset = self.index * self.stride;
if offset < self.buffer.len() {
self.index += 1;
Some(&self.buffer[offset])
} else {
None
}
}
}
pub struct TiledBuffer<T> {
buffer: AlignedBuffer<T>,
width: usize,
height: usize,
tile_width: usize,
tile_height: usize,
}
impl<T: Default + Copy> TiledBuffer<T> {
pub fn new(width: usize, height: usize, tile_width: usize, tile_height: usize) -> Result<Self> {
if tile_width == 0 || tile_height == 0 {
return Err(OxiGdalError::InvalidParameter {
parameter: "tile_size",
message: "Tile dimensions must be greater than 0".to_string(),
});
}
let capacity = width
.checked_mul(height)
.ok_or_else(|| OxiGdalError::Internal {
message: "Buffer size overflow".to_string(),
})?;
let buffer = AlignedBuffer::zeros(capacity, 64)?;
Ok(Self {
buffer,
width,
height,
tile_width,
tile_height,
})
}
#[must_use]
pub const fn width(&self) -> usize {
self.width
}
#[must_use]
pub const fn height(&self) -> usize {
self.height
}
#[must_use]
pub fn tiles(&self) -> TileIterator<'_, T> {
TileIterator {
buffer: &self.buffer,
width: self.width,
height: self.height,
tile_width: self.tile_width,
tile_height: self.tile_height,
current_x: 0,
current_y: 0,
}
}
#[must_use]
pub const fn buffer(&self) -> &AlignedBuffer<T> {
&self.buffer
}
}
pub struct TileIterator<'a, T> {
#[allow(dead_code)]
buffer: &'a AlignedBuffer<T>,
width: usize,
height: usize,
tile_width: usize,
tile_height: usize,
current_x: usize,
current_y: usize,
}
pub struct Tile {
pub x: usize,
pub y: usize,
pub width: usize,
pub height: usize,
}
impl<T> Iterator for TileIterator<'_, T> {
type Item = Tile;
fn next(&mut self) -> Option<Self::Item> {
if self.current_y >= self.height {
return None;
}
let tile = Tile {
x: self.current_x,
y: self.current_y,
width: self.tile_width.min(self.width - self.current_x),
height: self.tile_height.min(self.height - self.current_y),
};
self.current_x += self.tile_width;
if self.current_x >= self.width {
self.current_x = 0;
self.current_y += self.tile_height;
}
Some(tile)
}
}
pub struct ArenaTile<'a> {
pub x: usize,
pub y: usize,
pub width: usize,
pub height: usize,
pub data: &'a [u8],
}
pub struct TileIteratorArena<'a> {
src: &'a [u8],
row_stride: usize,
bytes_per_pixel: usize,
width: usize,
height: usize,
tile_width: usize,
tile_height: usize,
current_x: usize,
current_y: usize,
arena: &'a crate::memory::arena::Arena,
}
impl<'a> TileIteratorArena<'a> {
#[must_use]
pub fn new(
src: &'a [u8],
width: usize,
height: usize,
bytes_per_pixel: usize,
tile_width: usize,
tile_height: usize,
arena: &'a crate::memory::arena::Arena,
) -> Self {
assert!(bytes_per_pixel > 0, "bytes_per_pixel must be > 0");
assert!(tile_width > 0, "tile_width must be > 0");
assert!(tile_height > 0, "tile_height must be > 0");
Self {
src,
row_stride: width * bytes_per_pixel,
bytes_per_pixel,
width,
height,
tile_width,
tile_height,
current_x: 0,
current_y: 0,
arena,
}
}
}
impl<'a> Iterator for TileIteratorArena<'a> {
type Item = crate::error::Result<ArenaTile<'a>>;
fn next(&mut self) -> Option<Self::Item> {
if self.current_y >= self.height {
return None;
}
let tile_x = self.current_x;
let tile_y = self.current_y;
let tw = self.tile_width.min(self.width - tile_x);
let th = self.tile_height.min(self.height - tile_y);
let tile_bytes = tw * th * self.bytes_per_pixel;
let dest: &mut [u8] = match self.arena.allocate_slice(tile_bytes) {
Ok(s) => s,
Err(e) => return Some(Err(e)),
};
for row in 0..th {
let src_row = tile_y + row;
let src_start = src_row * self.row_stride + tile_x * self.bytes_per_pixel;
let dst_start = row * tw * self.bytes_per_pixel;
let copy_len = tw * self.bytes_per_pixel;
dest[dst_start..dst_start + copy_len]
.copy_from_slice(&self.src[src_start..src_start + copy_len]);
}
self.current_x += self.tile_width;
if self.current_x >= self.width {
self.current_x = 0;
self.current_y += self.tile_height;
}
Some(Ok(ArenaTile {
x: tile_x,
y: tile_y,
width: tw,
height: th,
data: unsafe { core::slice::from_raw_parts(dest.as_ptr(), dest.len()) },
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_aligned_buffer_creation() {
let buffer = AlignedBuffer::<f32>::new(100, 64)
.expect("Failed to create aligned buffer with valid parameters");
assert_eq!(buffer.len(), 100);
assert_eq!(buffer.alignment(), 64);
assert!(!buffer.is_empty());
let ptr = buffer.as_ptr();
assert_eq!((ptr as usize) % 64, 0);
}
#[test]
fn test_aligned_buffer_zeros() {
let buffer = AlignedBuffer::<f32>::zeros(100, 64)
.expect("Failed to create zero-initialized aligned buffer");
for val in buffer.as_slice() {
assert_eq!(*val, 0.0);
}
}
#[test]
fn test_aligned_buffer_copy() {
let mut buffer =
AlignedBuffer::<f32>::new(10, 64).expect("Failed to create aligned buffer");
let data: Vec<f32> = (0..10).map(|i| i as f32).collect();
buffer
.copy_from_slice(&data)
.expect("Failed to copy data to aligned buffer");
for (i, val) in buffer.as_slice().iter().enumerate() {
assert_eq!(*val, i as f32);
}
}
#[test]
fn test_strided_view() {
let mut buffer =
AlignedBuffer::<f32>::new(10, 64).expect("Failed to create aligned buffer");
let data: Vec<f32> = (0..10).map(|i| i as f32).collect();
buffer
.copy_from_slice(&data)
.expect("Failed to copy data to buffer");
let view = buffer
.strided_view(2)
.expect("Failed to create strided view");
assert_eq!(view.len(), 5);
let values: Vec<f32> = view.iter().copied().collect();
assert_eq!(values, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
}
#[test]
fn test_tiled_buffer() {
let buffer =
TiledBuffer::<f32>::new(100, 100, 32, 32).expect("Failed to create tiled buffer");
assert_eq!(buffer.width(), 100);
assert_eq!(buffer.height(), 100);
let tile_count = buffer.tiles().count();
assert_eq!(tile_count, 16);
}
#[test]
fn test_tile_dimensions() {
let buffer =
TiledBuffer::<f32>::new(100, 100, 32, 32).expect("Failed to create tiled buffer");
let tiles: Vec<Tile> = buffer.tiles().collect();
assert_eq!(tiles[0].x, 0);
assert_eq!(tiles[0].y, 0);
assert_eq!(tiles[0].width, 32);
assert_eq!(tiles[0].height, 32);
let last = &tiles[15];
assert_eq!(last.x, 96);
assert_eq!(last.y, 96);
assert_eq!(last.width, 4); assert_eq!(last.height, 4);
}
#[test]
fn test_invalid_alignment() {
let result = AlignedBuffer::<f32>::new(100, 63);
assert!(result.is_err());
let result = AlignedBuffer::<f32>::new(100, 1);
assert!(result.is_err());
}
#[test]
fn test_zero_capacity() {
let result = AlignedBuffer::<f32>::new(0, 64);
assert!(result.is_err());
}
#[test]
fn test_arena_tile_iterator_yields_arena_tiles() {
let src: Vec<u8> = (0..16u8).collect();
let arena = crate::memory::arena::Arena::with_capacity(4096).expect("arena creation");
let mut it = TileIteratorArena::new(&src, 4, 4, 1, 2, 2, &arena);
let tile = it.next().expect("first tile").expect("no error");
assert_eq!(tile.x, 0);
assert_eq!(tile.y, 0);
assert_eq!(tile.width, 2);
assert_eq!(tile.height, 2);
assert_eq!(tile.data, &[0, 1, 4, 5]);
let tile2 = it.next().expect("second tile").expect("no error");
assert_eq!(tile2.x, 2);
assert_eq!(tile2.y, 0);
assert_eq!(tile2.data, &[2, 3, 6, 7]);
let total = TileIteratorArena::new(&src, 4, 4, 1, 2, 2, &arena).count();
assert_eq!(total, 4);
}
#[test]
fn test_arena_tile_iterator_drops_with_arena() {
let src: Vec<u8> = vec![0u8; 16];
let arena = crate::memory::arena::Arena::with_capacity(8192).expect("arena creation");
{
let it = TileIteratorArena::new(&src, 4, 4, 1, 2, 2, &arena);
let tiles: Vec<_> = it.collect();
assert_eq!(tiles.len(), 4);
for t in &tiles {
let tile = t.as_ref().expect("no error");
assert_eq!(tile.data.len(), 4);
}
}
arena.reset();
assert_eq!(arena.usage(), 0);
}
}