1#![allow(unsafe_code)]
16
17use std::alloc::{self, Layout};
59use std::ptr::NonNull;
60use std::slice;
61
62use crate::error::{OxiGdalError, Result};
63
64pub struct AlignedBuffer<T> {
70 ptr: NonNull<T>,
72 len: usize,
74 align: usize,
76 layout: Layout,
78}
79
80impl<T> AlignedBuffer<T> {
81 pub fn new(capacity: usize, align: usize) -> Result<Self> {
95 if !align.is_power_of_two() {
96 return Err(OxiGdalError::InvalidParameter {
97 parameter: "align",
98 message: "Alignment must be a power of 2".to_string(),
99 });
100 }
101
102 if align < std::mem::align_of::<T>() {
103 return Err(OxiGdalError::InvalidParameter {
104 parameter: "align",
105 message: format!(
106 "Alignment {} is less than natural alignment of {}",
107 align,
108 std::mem::align_of::<T>()
109 ),
110 });
111 }
112
113 if capacity == 0 {
114 return Err(OxiGdalError::InvalidParameter {
115 parameter: "capacity",
116 message: "Capacity must be greater than 0".to_string(),
117 });
118 }
119
120 let size = capacity
121 .checked_mul(std::mem::size_of::<T>())
122 .ok_or_else(|| OxiGdalError::InvalidParameter {
123 parameter: "capacity",
124 message: "Capacity overflow".to_string(),
125 })?;
126
127 let layout = Layout::from_size_align(size, align).map_err(|e| OxiGdalError::Internal {
128 message: format!("Invalid layout: {e}"),
129 })?;
130
131 let ptr = unsafe { alloc::alloc(layout) };
133
134 let ptr = NonNull::new(ptr)
135 .ok_or_else(|| OxiGdalError::Internal {
136 message: "Failed to allocate aligned buffer".to_string(),
137 })?
138 .cast::<T>();
139
140 Ok(Self {
141 ptr,
142 len: capacity,
143 align,
144 layout,
145 })
146 }
147
148 pub fn zeros(capacity: usize, align: usize) -> Result<Self>
159 where
160 T: Default + Copy,
161 {
162 let buffer = Self::new(capacity, align)?;
163
164 unsafe {
166 std::ptr::write_bytes(buffer.ptr.as_ptr(), 0, capacity);
167 }
168
169 Ok(buffer)
170 }
171
172 #[must_use]
174 pub const fn len(&self) -> usize {
175 self.len
176 }
177
178 #[must_use]
180 pub const fn is_empty(&self) -> bool {
181 self.len == 0
182 }
183
184 #[must_use]
186 pub const fn alignment(&self) -> usize {
187 self.align
188 }
189
190 #[must_use]
192 pub fn as_ptr(&self) -> *const T {
193 self.ptr.as_ptr()
194 }
195
196 #[must_use]
198 pub fn as_mut_ptr(&mut self) -> *mut T {
199 self.ptr.as_ptr()
200 }
201
202 #[must_use]
204 pub fn as_slice(&self) -> &[T] {
205 unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
207 }
208
209 #[must_use]
211 pub fn as_mut_slice(&mut self) -> &mut [T] {
212 unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
214 }
215
216 pub fn copy_from_slice(&mut self, src: &[T]) -> Result<()>
222 where
223 T: Copy,
224 {
225 if src.len() != self.len {
226 return Err(OxiGdalError::InvalidParameter {
227 parameter: "src",
228 message: format!(
229 "Source length {} doesn't match buffer capacity {}",
230 src.len(),
231 self.len
232 ),
233 });
234 }
235
236 self.as_mut_slice().copy_from_slice(src);
237 Ok(())
238 }
239
240 pub fn strided_view(&self, stride: usize) -> Result<StridedView<'_, T>> {
252 if stride == 0 {
253 return Err(OxiGdalError::InvalidParameter {
254 parameter: "stride",
255 message: "Stride must be greater than 0".to_string(),
256 });
257 }
258
259 Ok(StridedView {
260 buffer: self.as_slice(),
261 stride,
262 })
263 }
264}
265
266impl<T> Drop for AlignedBuffer<T> {
267 fn drop(&mut self) {
268 unsafe {
270 alloc::dealloc(self.ptr.as_ptr().cast::<u8>(), self.layout);
271 }
272 }
273}
274
275unsafe impl<T: Send> Send for AlignedBuffer<T> {}
277
278unsafe impl<T: Sync> Sync for AlignedBuffer<T> {}
280
281pub struct StridedView<'a, T> {
283 buffer: &'a [T],
284 stride: usize,
285}
286
287impl<T> StridedView<'_, T> {
288 #[must_use]
290 pub fn len(&self) -> usize {
291 self.buffer.len().div_ceil(self.stride)
292 }
293
294 #[must_use]
296 pub fn is_empty(&self) -> bool {
297 self.buffer.is_empty()
298 }
299
300 #[must_use]
302 pub fn get(&self, index: usize) -> Option<&T> {
303 let offset = index * self.stride;
304 self.buffer.get(offset)
305 }
306
307 #[must_use]
309 pub fn iter(&self) -> StridedIterator<'_, T> {
310 StridedIterator {
311 buffer: self.buffer,
312 stride: self.stride,
313 index: 0,
314 }
315 }
316}
317
318pub struct StridedIterator<'a, T> {
320 buffer: &'a [T],
321 stride: usize,
322 index: usize,
323}
324
325impl<'a, T> Iterator for StridedIterator<'a, T> {
326 type Item = &'a T;
327
328 fn next(&mut self) -> Option<Self::Item> {
329 let offset = self.index * self.stride;
330 if offset < self.buffer.len() {
331 self.index += 1;
332 Some(&self.buffer[offset])
333 } else {
334 None
335 }
336 }
337}
338
339pub struct TiledBuffer<T> {
344 buffer: AlignedBuffer<T>,
345 width: usize,
346 height: usize,
347 tile_width: usize,
348 tile_height: usize,
349}
350
351impl<T: Default + Copy> TiledBuffer<T> {
352 pub fn new(width: usize, height: usize, tile_width: usize, tile_height: usize) -> Result<Self> {
365 if tile_width == 0 || tile_height == 0 {
366 return Err(OxiGdalError::InvalidParameter {
367 parameter: "tile_size",
368 message: "Tile dimensions must be greater than 0".to_string(),
369 });
370 }
371
372 let capacity = width
373 .checked_mul(height)
374 .ok_or_else(|| OxiGdalError::Internal {
375 message: "Buffer size overflow".to_string(),
376 })?;
377
378 let buffer = AlignedBuffer::zeros(capacity, 64)?;
379
380 Ok(Self {
381 buffer,
382 width,
383 height,
384 tile_width,
385 tile_height,
386 })
387 }
388
389 #[must_use]
391 pub const fn width(&self) -> usize {
392 self.width
393 }
394
395 #[must_use]
397 pub const fn height(&self) -> usize {
398 self.height
399 }
400
401 #[must_use]
403 pub fn tiles(&self) -> TileIterator<'_, T> {
404 TileIterator {
405 buffer: &self.buffer,
406 width: self.width,
407 height: self.height,
408 tile_width: self.tile_width,
409 tile_height: self.tile_height,
410 current_x: 0,
411 current_y: 0,
412 }
413 }
414
415 #[must_use]
417 pub const fn buffer(&self) -> &AlignedBuffer<T> {
418 &self.buffer
419 }
420}
421
422pub struct TileIterator<'a, T> {
424 #[allow(dead_code)]
425 buffer: &'a AlignedBuffer<T>,
426 width: usize,
427 height: usize,
428 tile_width: usize,
429 tile_height: usize,
430 current_x: usize,
431 current_y: usize,
432}
433
434pub struct Tile {
436 pub x: usize,
438 pub y: usize,
440 pub width: usize,
442 pub height: usize,
444}
445
446impl<T> Iterator for TileIterator<'_, T> {
447 type Item = Tile;
448
449 fn next(&mut self) -> Option<Self::Item> {
450 if self.current_y >= self.height {
451 return None;
452 }
453
454 let tile = Tile {
455 x: self.current_x,
456 y: self.current_y,
457 width: self.tile_width.min(self.width - self.current_x),
458 height: self.tile_height.min(self.height - self.current_y),
459 };
460
461 self.current_x += self.tile_width;
463 if self.current_x >= self.width {
464 self.current_x = 0;
465 self.current_y += self.tile_height;
466 }
467
468 Some(tile)
469 }
470}
471
472pub struct ArenaTile<'a> {
479 pub x: usize,
481 pub y: usize,
483 pub width: usize,
485 pub height: usize,
487 pub data: &'a [u8],
489}
490
491pub struct TileIteratorArena<'a> {
499 src: &'a [u8],
501 row_stride: usize,
503 bytes_per_pixel: usize,
505 width: usize,
507 height: usize,
509 tile_width: usize,
511 tile_height: usize,
513 current_x: usize,
515 current_y: usize,
517 arena: &'a crate::memory::arena::Arena,
519}
520
521impl<'a> TileIteratorArena<'a> {
522 #[must_use]
538 pub fn new(
539 src: &'a [u8],
540 width: usize,
541 height: usize,
542 bytes_per_pixel: usize,
543 tile_width: usize,
544 tile_height: usize,
545 arena: &'a crate::memory::arena::Arena,
546 ) -> Self {
547 assert!(bytes_per_pixel > 0, "bytes_per_pixel must be > 0");
548 assert!(tile_width > 0, "tile_width must be > 0");
549 assert!(tile_height > 0, "tile_height must be > 0");
550 Self {
551 src,
552 row_stride: width * bytes_per_pixel,
553 bytes_per_pixel,
554 width,
555 height,
556 tile_width,
557 tile_height,
558 current_x: 0,
559 current_y: 0,
560 arena,
561 }
562 }
563}
564
565impl<'a> Iterator for TileIteratorArena<'a> {
566 type Item = crate::error::Result<ArenaTile<'a>>;
567
568 fn next(&mut self) -> Option<Self::Item> {
569 if self.current_y >= self.height {
570 return None;
571 }
572
573 let tile_x = self.current_x;
574 let tile_y = self.current_y;
575 let tw = self.tile_width.min(self.width - tile_x);
576 let th = self.tile_height.min(self.height - tile_y);
577 let tile_bytes = tw * th * self.bytes_per_pixel;
578
579 let dest: &mut [u8] = match self.arena.allocate_slice(tile_bytes) {
581 Ok(s) => s,
582 Err(e) => return Some(Err(e)),
583 };
584
585 for row in 0..th {
587 let src_row = tile_y + row;
588 let src_start = src_row * self.row_stride + tile_x * self.bytes_per_pixel;
589 let dst_start = row * tw * self.bytes_per_pixel;
590 let copy_len = tw * self.bytes_per_pixel;
591 dest[dst_start..dst_start + copy_len]
592 .copy_from_slice(&self.src[src_start..src_start + copy_len]);
593 }
594
595 self.current_x += self.tile_width;
597 if self.current_x >= self.width {
598 self.current_x = 0;
599 self.current_y += self.tile_height;
600 }
601
602 Some(Ok(ArenaTile {
603 x: tile_x,
604 y: tile_y,
605 width: tw,
606 height: th,
607 data: unsafe { core::slice::from_raw_parts(dest.as_ptr(), dest.len()) },
610 }))
611 }
612}
613
614#[cfg(test)]
617mod tests {
618 use super::*;
619
620 #[test]
621 fn test_aligned_buffer_creation() {
622 let buffer = AlignedBuffer::<f32>::new(100, 64)
623 .expect("Failed to create aligned buffer with valid parameters");
624 assert_eq!(buffer.len(), 100);
625 assert_eq!(buffer.alignment(), 64);
626 assert!(!buffer.is_empty());
627
628 let ptr = buffer.as_ptr();
630 assert_eq!((ptr as usize) % 64, 0);
631 }
632
633 #[test]
634 fn test_aligned_buffer_zeros() {
635 let buffer = AlignedBuffer::<f32>::zeros(100, 64)
636 .expect("Failed to create zero-initialized aligned buffer");
637 for val in buffer.as_slice() {
638 assert_eq!(*val, 0.0);
639 }
640 }
641
642 #[test]
643 fn test_aligned_buffer_copy() {
644 let mut buffer =
645 AlignedBuffer::<f32>::new(10, 64).expect("Failed to create aligned buffer");
646 let data: Vec<f32> = (0..10).map(|i| i as f32).collect();
647
648 buffer
649 .copy_from_slice(&data)
650 .expect("Failed to copy data to aligned buffer");
651
652 for (i, val) in buffer.as_slice().iter().enumerate() {
653 assert_eq!(*val, i as f32);
654 }
655 }
656
657 #[test]
658 fn test_strided_view() {
659 let mut buffer =
660 AlignedBuffer::<f32>::new(10, 64).expect("Failed to create aligned buffer");
661 let data: Vec<f32> = (0..10).map(|i| i as f32).collect();
662 buffer
663 .copy_from_slice(&data)
664 .expect("Failed to copy data to buffer");
665
666 let view = buffer
667 .strided_view(2)
668 .expect("Failed to create strided view");
669 assert_eq!(view.len(), 5);
670
671 let values: Vec<f32> = view.iter().copied().collect();
672 assert_eq!(values, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
673 }
674
675 #[test]
676 fn test_tiled_buffer() {
677 let buffer =
678 TiledBuffer::<f32>::new(100, 100, 32, 32).expect("Failed to create tiled buffer");
679 assert_eq!(buffer.width(), 100);
680 assert_eq!(buffer.height(), 100);
681
682 let tile_count = buffer.tiles().count();
683 assert_eq!(tile_count, 16);
685 }
686
687 #[test]
688 fn test_tile_dimensions() {
689 let buffer =
690 TiledBuffer::<f32>::new(100, 100, 32, 32).expect("Failed to create tiled buffer");
691 let tiles: Vec<Tile> = buffer.tiles().collect();
692
693 assert_eq!(tiles[0].x, 0);
695 assert_eq!(tiles[0].y, 0);
696 assert_eq!(tiles[0].width, 32);
697 assert_eq!(tiles[0].height, 32);
698
699 let last = &tiles[15];
701 assert_eq!(last.x, 96);
702 assert_eq!(last.y, 96);
703 assert_eq!(last.width, 4); assert_eq!(last.height, 4);
705 }
706
707 #[test]
708 fn test_invalid_alignment() {
709 let result = AlignedBuffer::<f32>::new(100, 63);
711 assert!(result.is_err());
712
713 let result = AlignedBuffer::<f32>::new(100, 1);
715 assert!(result.is_err());
716 }
717
718 #[test]
719 fn test_zero_capacity() {
720 let result = AlignedBuffer::<f32>::new(0, 64);
721 assert!(result.is_err());
722 }
723
724 #[test]
725 fn test_arena_tile_iterator_yields_arena_tiles() {
726 let src: Vec<u8> = (0..16u8).collect();
728 let arena = crate::memory::arena::Arena::with_capacity(4096).expect("arena creation");
729 let mut it = TileIteratorArena::new(&src, 4, 4, 1, 2, 2, &arena);
730
731 let tile = it.next().expect("first tile").expect("no error");
732 assert_eq!(tile.x, 0);
733 assert_eq!(tile.y, 0);
734 assert_eq!(tile.width, 2);
735 assert_eq!(tile.height, 2);
736 assert_eq!(tile.data, &[0, 1, 4, 5]);
738
739 let tile2 = it.next().expect("second tile").expect("no error");
740 assert_eq!(tile2.x, 2);
741 assert_eq!(tile2.y, 0);
742 assert_eq!(tile2.data, &[2, 3, 6, 7]);
744
745 let total = TileIteratorArena::new(&src, 4, 4, 1, 2, 2, &arena).count();
746 assert_eq!(total, 4);
747 }
748
749 #[test]
750 fn test_arena_tile_iterator_drops_with_arena() {
751 let src: Vec<u8> = vec![0u8; 16];
753 let arena = crate::memory::arena::Arena::with_capacity(8192).expect("arena creation");
754 {
755 let it = TileIteratorArena::new(&src, 4, 4, 1, 2, 2, &arena);
756 let tiles: Vec<_> = it.collect();
757 assert_eq!(tiles.len(), 4);
758 for t in &tiles {
760 let tile = t.as_ref().expect("no error");
761 assert_eq!(tile.data.len(), 4);
762 }
763 }
764 arena.reset();
766 assert_eq!(arena.usage(), 0);
767 }
768}