File size: 5,452 Bytes
f7ccd08
 
 
32b4893
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// SPDX-License-Identifier: LicenseRef-Sovereign-Leviathan-SL-AGPL3-001
// Licensing: see LICENSE in the repository root.

//! Buffer management with generation counters and reference tracking
//!
//! Each buffer has:
//! - Unique 64-bit ID
//! - Generation counter (increments on allocation/materialization)
//! - Shared Arc-based ownership
//! - Reference count for exclusivity checking

use crate::error::{HardwareError, Result};
use std::sync::Arc;

/// Unique 64-bit buffer identifier
pub type BufferId = u64;

/// Generation counter for stale descriptor detection
pub type Generation = u64;

/// Hardware buffer with generation tracking
#[derive(Clone)]
pub struct Buffer {
    /// Physical storage (shared ownership)
    pub data: Arc<Vec<f32>>,

    /// Current generation counter
    pub generation: Generation,

    /// Unique buffer ID
    pub id: BufferId,

    /// True if buffer is the canonical owner (used for COW decisions)
    pub is_owner: bool,
}

impl Buffer {
    /// Create new buffer with fresh generation counter
    ///
    /// **Governance**: B_own=1 (exclusive), generation=1
    pub fn allocate(id: BufferId, size: usize) -> Result<Self> {
        if size == 0 {
            return Err(HardwareError::ZeroElementShape);
        }

        let data = Arc::new(vec![0.0; size]);

        Ok(Buffer {
            data,
            generation: 1,
            id,
            is_owner: true,
        })
    }

    /// Strong reference count (number of Arc holders)
    #[inline]
    pub fn strong_count(&self) -> usize {
        Arc::strong_count(&self.data)
    }

    /// Check if this buffer has exclusive ownership
    ///
    /// Returns true only if strong_count == 1 AND is_owner flag is set
    #[inline]
    pub fn is_exclusive(&self) -> bool {
        self.is_owner && self.strong_count() == 1
    }

    /// Create a shared view (does not copy data)
    ///
    /// **Governance**: B_own → 0, generation unchanged
    pub fn create_view(&self) -> Self {
        Buffer {
            data: Arc::clone(&self.data),
            generation: self.generation,
            id: self.id,
            is_owner: false, // Shared view
        }
    }

    /// Increment generation counter for new materialization
    ///
    /// **State Transition**: (gen, ownership) → (gen+1, exclusive=1)
    pub fn next_generation(&self) -> Generation {
        self.generation.saturating_add(1)
    }

    /// Validate that descriptor generation matches buffer generation
    ///
    /// **Safety**: Rejects use-after-free by stale descriptor
    pub fn validate_generation(&self, descriptor_gen: Generation) -> Result<()> {
        if descriptor_gen != self.generation {
            return Err(HardwareError::StaleGeneration {
                buffer_id: self.id,
                expected: self.generation,
                actual: descriptor_gen,
            });
        }
        Ok(())
    }

    /// Check mutability: exclusive ownership + non-shared
    pub fn check_mutable(&self) -> Result<()> {
        if !self.is_exclusive() {
            return Err(HardwareError::SharedTensorMutation);
        }
        Ok(())
    }

    /// Total buffer capacity in elements
    #[inline]
    pub fn capacity(&self) -> usize {
        self.data.len()
    }

    /// Direct element access (unchecked - use with bounds validation)
    #[inline]
    pub fn get_unchecked(&self, index: usize) -> f32 {
        self.data[index]
    }

    /// Direct element write (unchecked - use with bounds/mutability validation)
    #[inline]
    pub fn set_unchecked(&self, index: usize, value: f32) {
        // SAFETY: We use unsafe to bypass Arc immutability for exclusive buffers
        // The caller must ensure:
        // 1. is_exclusive() is true
        // 2. index is in bounds
        unsafe {
            let ptr = self.data.as_ptr() as *mut f32;
            *ptr.add(index) = value;
        }
    }
}

/// Buffer handle with metadata for efficient lookups
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BufferHandle {
    pub id: BufferId,
    pub generation: Generation,
}

impl BufferHandle {
    /// Create handle from buffer
    pub fn from_buffer(buf: &Buffer) -> Self {
        BufferHandle {
            id: buf.id,
            generation: buf.generation,
        }
    }

    /// Validate handle against current buffer state
    pub fn validate(&self, buffer: &Buffer) -> Result<()> {
        if self.id != buffer.id {
            return Err(HardwareError::BufferNotFound(self.id));
        }
        buffer.validate_generation(self.generation)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_allocate_sets_generation() {
        let buf = Buffer::allocate(1, 100).unwrap();
        assert_eq!(buf.generation, 1);
        assert!(buf.is_exclusive());
    }

    #[test]
    fn test_view_shares_data_not_ownership() {
        let buf = Buffer::allocate(1, 100).unwrap();
        let view = buf.create_view();

        assert_eq!(buf.strong_count(), 2);
        assert!(!view.is_owner);
        assert!(buf.is_owner);
        assert!(!view.is_exclusive());
    }

    #[test]
    fn test_generation_validation() {
        let buf = Buffer::allocate(1, 100).unwrap();
        assert!(buf.validate_generation(1).is_ok());
        assert!(buf.validate_generation(2).is_err());
    }

    #[test]
    fn test_zero_size_allocation_fails() {
        assert!(Buffer::allocate(1, 0).is_err());
    }
}