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