Skip to main content

agg_rust/
span_allocator.rs

1//! Span color buffer allocator.
2//!
3//! Port of `agg_span_allocator.h` — manages a reusable buffer for span
4//! generators to write per-pixel colors into during rendering.
5
6// ============================================================================
7// SpanAllocator
8// ============================================================================
9
10/// Reusable color buffer for span generators.
11///
12/// Allocates and reuses a buffer of color values. The buffer grows as
13/// needed (aligned to 256 elements) but never shrinks, avoiding
14/// repeated allocations during rendering.
15///
16/// Port of C++ `span_allocator<ColorT>`.
17pub struct SpanAllocator<C> {
18    span: Vec<C>,
19}
20
21impl<C: Default + Clone> SpanAllocator<C> {
22    pub fn new() -> Self {
23        Self { span: Vec::new() }
24    }
25
26    /// Allocate (or reuse) a buffer of at least `span_len` elements.
27    ///
28    /// Returns a mutable slice of exactly `span_len` elements.
29    /// The buffer may be larger internally (aligned to 256).
30    pub fn allocate(&mut self, span_len: usize) -> &mut [C] {
31        if span_len > self.span.len() {
32            // Align to 256 elements to reduce reallocations
33            let new_size = ((span_len + 255) >> 8) << 8;
34            self.span.resize(new_size, C::default());
35        }
36        &mut self.span[..span_len]
37    }
38
39    pub fn span(&mut self) -> &mut [C] {
40        &mut self.span
41    }
42
43    pub fn max_span_len(&self) -> usize {
44        self.span.len()
45    }
46}
47
48impl<C: Default + Clone> Default for SpanAllocator<C> {
49    fn default() -> Self {
50        Self::new()
51    }
52}
53
54// ============================================================================
55// Tests
56// ============================================================================
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn test_new_empty() {
64        let alloc = SpanAllocator::<u8>::new();
65        assert_eq!(alloc.max_span_len(), 0);
66    }
67
68    #[test]
69    fn test_allocate_grows() {
70        let mut alloc = SpanAllocator::<u32>::new();
71        let span = alloc.allocate(10);
72        assert_eq!(span.len(), 10);
73        // Internal buffer should be aligned to 256
74        assert_eq!(alloc.max_span_len(), 256);
75    }
76
77    #[test]
78    fn test_allocate_reuses() {
79        let mut alloc = SpanAllocator::<u32>::new();
80        alloc.allocate(100);
81        assert_eq!(alloc.max_span_len(), 256);
82        // Smaller allocation reuses existing buffer
83        let span = alloc.allocate(50);
84        assert_eq!(span.len(), 50);
85        assert_eq!(alloc.max_span_len(), 256);
86    }
87
88    #[test]
89    fn test_allocate_alignment() {
90        let mut alloc = SpanAllocator::<u8>::new();
91        alloc.allocate(257);
92        assert_eq!(alloc.max_span_len(), 512);
93    }
94
95    #[test]
96    fn test_span_accessor() {
97        let mut alloc = SpanAllocator::<u32>::new();
98        alloc.allocate(10);
99        let span = alloc.span();
100        assert!(span.len() >= 10);
101    }
102}