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
472#[cfg(test)]
473mod tests {
474 use super::*;
475
476 #[test]
477 fn test_aligned_buffer_creation() {
478 let buffer = AlignedBuffer::<f32>::new(100, 64)
479 .expect("Failed to create aligned buffer with valid parameters");
480 assert_eq!(buffer.len(), 100);
481 assert_eq!(buffer.alignment(), 64);
482 assert!(!buffer.is_empty());
483
484 let ptr = buffer.as_ptr();
486 assert_eq!((ptr as usize) % 64, 0);
487 }
488
489 #[test]
490 fn test_aligned_buffer_zeros() {
491 let buffer = AlignedBuffer::<f32>::zeros(100, 64)
492 .expect("Failed to create zero-initialized aligned buffer");
493 for val in buffer.as_slice() {
494 assert_eq!(*val, 0.0);
495 }
496 }
497
498 #[test]
499 fn test_aligned_buffer_copy() {
500 let mut buffer =
501 AlignedBuffer::<f32>::new(10, 64).expect("Failed to create aligned buffer");
502 let data: Vec<f32> = (0..10).map(|i| i as f32).collect();
503
504 buffer
505 .copy_from_slice(&data)
506 .expect("Failed to copy data to aligned buffer");
507
508 for (i, val) in buffer.as_slice().iter().enumerate() {
509 assert_eq!(*val, i as f32);
510 }
511 }
512
513 #[test]
514 fn test_strided_view() {
515 let mut buffer =
516 AlignedBuffer::<f32>::new(10, 64).expect("Failed to create aligned buffer");
517 let data: Vec<f32> = (0..10).map(|i| i as f32).collect();
518 buffer
519 .copy_from_slice(&data)
520 .expect("Failed to copy data to buffer");
521
522 let view = buffer
523 .strided_view(2)
524 .expect("Failed to create strided view");
525 assert_eq!(view.len(), 5);
526
527 let values: Vec<f32> = view.iter().copied().collect();
528 assert_eq!(values, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
529 }
530
531 #[test]
532 fn test_tiled_buffer() {
533 let buffer =
534 TiledBuffer::<f32>::new(100, 100, 32, 32).expect("Failed to create tiled buffer");
535 assert_eq!(buffer.width(), 100);
536 assert_eq!(buffer.height(), 100);
537
538 let tile_count = buffer.tiles().count();
539 assert_eq!(tile_count, 16);
541 }
542
543 #[test]
544 fn test_tile_dimensions() {
545 let buffer =
546 TiledBuffer::<f32>::new(100, 100, 32, 32).expect("Failed to create tiled buffer");
547 let tiles: Vec<Tile> = buffer.tiles().collect();
548
549 assert_eq!(tiles[0].x, 0);
551 assert_eq!(tiles[0].y, 0);
552 assert_eq!(tiles[0].width, 32);
553 assert_eq!(tiles[0].height, 32);
554
555 let last = &tiles[15];
557 assert_eq!(last.x, 96);
558 assert_eq!(last.y, 96);
559 assert_eq!(last.width, 4); assert_eq!(last.height, 4);
561 }
562
563 #[test]
564 fn test_invalid_alignment() {
565 let result = AlignedBuffer::<f32>::new(100, 63);
567 assert!(result.is_err());
568
569 let result = AlignedBuffer::<f32>::new(100, 1);
571 assert!(result.is_err());
572 }
573
574 #[test]
575 fn test_zero_capacity() {
576 let result = AlignedBuffer::<f32>::new(0, 64);
577 assert!(result.is_err());
578 }
579}