Skip to main content

spatialrust_core/
buffer.rs

1use crate::{DType, PointField, SpatialError, SpatialResult};
2
3/// Typed column buffer for one point field.
4#[derive(Clone, Debug, PartialEq)]
5pub enum PointBuffer {
6    /// `f32` column.
7    F32(Vec<f32>),
8    /// `f64` column.
9    F64(Vec<f64>),
10    /// `u8` column.
11    U8(Vec<u8>),
12    /// `u16` column.
13    U16(Vec<u16>),
14    /// `u32` column.
15    U32(Vec<u32>),
16    /// `i32` column.
17    I32(Vec<i32>),
18}
19
20impl PointBuffer {
21    /// Creates an empty buffer for the given dtype.
22    #[must_use]
23    pub fn with_capacity(dtype: DType, capacity: usize) -> Self {
24        match dtype {
25            DType::F32 | DType::F16 => Self::F32(Vec::with_capacity(capacity)),
26            DType::F64 => Self::F64(Vec::with_capacity(capacity)),
27            DType::U8 => Self::U8(Vec::with_capacity(capacity)),
28            DType::U16 => Self::U16(Vec::with_capacity(capacity)),
29            DType::U32 => Self::U32(Vec::with_capacity(capacity)),
30            DType::I32 => Self::I32(Vec::with_capacity(capacity)),
31        }
32    }
33
34    /// Creates a buffer from an existing `f32` vector.
35    #[must_use]
36    pub fn from_f32(values: Vec<f32>) -> Self {
37        Self::F32(values)
38    }
39
40    /// Returns the number of elements in the buffer.
41    #[must_use]
42    pub fn len(&self) -> usize {
43        match self {
44            Self::F32(values) => values.len(),
45            Self::F64(values) => values.len(),
46            Self::U8(values) => values.len(),
47            Self::U16(values) => values.len(),
48            Self::U32(values) => values.len(),
49            Self::I32(values) => values.len(),
50        }
51    }
52
53    /// Returns whether the buffer is empty.
54    #[must_use]
55    pub fn is_empty(&self) -> bool {
56        self.len() == 0
57    }
58
59    /// Returns the buffer dtype.
60    #[must_use]
61    pub fn dtype(&self) -> DType {
62        match self {
63            Self::F32(_) => DType::F32,
64            Self::F64(_) => DType::F64,
65            Self::U8(_) => DType::U8,
66            Self::U16(_) => DType::U16,
67            Self::U32(_) => DType::U32,
68            Self::I32(_) => DType::I32,
69        }
70    }
71
72    /// Resizes the buffer to the requested number of elements.
73    pub fn resize(&mut self, len: usize, field: &PointField) -> SpatialResult<()> {
74        if field.dtype != self.dtype() && !(field.dtype == DType::F16 && self.dtype() == DType::F32)
75        {
76            return Err(SpatialError::UnsupportedDType(field.dtype));
77        }
78        match self {
79            Self::F32(values) => values.resize(len, 0.0),
80            Self::F64(values) => values.resize(len, 0.0),
81            Self::U8(values) => values.resize(len, 0),
82            Self::U16(values) => values.resize(len, 0),
83            Self::U32(values) => values.resize(len, 0),
84            Self::I32(values) => values.resize(len, 0),
85        }
86        Ok(())
87    }
88
89    /// Returns immutable access to an `f32` column.
90    pub fn as_f32(&self) -> SpatialResult<&[f32]> {
91        match self {
92            Self::F32(values) => Ok(values),
93            _ => Err(SpatialError::UnsupportedDType(self.dtype())),
94        }
95    }
96
97    /// Returns mutable access to an `f32` column.
98    pub fn as_f32_mut(&mut self) -> SpatialResult<&mut Vec<f32>> {
99        match self {
100            Self::F32(values) => Ok(values),
101            _ => Err(SpatialError::UnsupportedDType(self.dtype())),
102        }
103    }
104
105    /// Appends one `f32` value.
106    pub fn push_f32(&mut self, value: f32) -> SpatialResult<()> {
107        self.as_f32_mut()?.push(value);
108        Ok(())
109    }
110
111    /// Appends one `f64` value.
112    pub fn push_f64(&mut self, value: f64) -> SpatialResult<()> {
113        match self {
114            Self::F64(values) => {
115                values.push(value);
116                Ok(())
117            }
118            _ => Err(SpatialError::UnsupportedDType(self.dtype())),
119        }
120    }
121
122    /// Appends one `u8` value.
123    pub fn push_u8(&mut self, value: u8) -> SpatialResult<()> {
124        match self {
125            Self::U8(values) => {
126                values.push(value);
127                Ok(())
128            }
129            _ => Err(SpatialError::UnsupportedDType(self.dtype())),
130        }
131    }
132
133    /// Appends one `u16` value.
134    pub fn push_u16(&mut self, value: u16) -> SpatialResult<()> {
135        match self {
136            Self::U16(values) => {
137                values.push(value);
138                Ok(())
139            }
140            _ => Err(SpatialError::UnsupportedDType(self.dtype())),
141        }
142    }
143
144    /// Appends one `i32` value.
145    pub fn push_i32(&mut self, value: i32) -> SpatialResult<()> {
146        match self {
147            Self::I32(values) => {
148                values.push(value);
149                Ok(())
150            }
151            _ => Err(SpatialError::UnsupportedDType(self.dtype())),
152        }
153    }
154}
155
156/// Collection of column buffers keyed by field name.
157#[derive(Clone, Debug, Default, PartialEq)]
158pub struct PointBufferSet {
159    buffers: Vec<(String, PointBuffer)>,
160}
161
162impl PointBufferSet {
163    /// Creates an empty buffer set.
164    #[must_use]
165    pub fn new() -> Self {
166        Self::default()
167    }
168
169    /// Inserts or replaces a named buffer.
170    pub fn insert(&mut self, name: impl Into<String>, buffer: PointBuffer) {
171        let name = name.into();
172        if let Some((_, existing)) = self.buffers.iter_mut().find(|(key, _)| key == &name) {
173            *existing = buffer;
174        } else {
175            self.buffers.push((name, buffer));
176        }
177    }
178
179    /// Returns a buffer by field name.
180    #[must_use]
181    pub fn get(&self, name: &str) -> Option<&PointBuffer> {
182        self.buffers.iter().find(|(key, _)| key == name).map(|(_, buffer)| buffer)
183    }
184
185    /// Returns a mutable buffer by field name.
186    pub fn get_mut(&mut self, name: &str) -> Option<&mut PointBuffer> {
187        self.buffers.iter_mut().find(|(key, _)| key == name).map(|(_, buffer)| buffer)
188    }
189
190    /// Returns all named buffers.
191    pub fn iter(&self) -> impl Iterator<Item = (&str, &PointBuffer)> {
192        self.buffers.iter().map(|(name, buffer)| (name.as_str(), buffer))
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::{PointBuffer, PointBufferSet};
199    use crate::{DType, PointField};
200
201    #[test]
202    fn resize_f32_buffer() {
203        let field = PointField::scalar("x", crate::FieldSemantic::PositionX, DType::F32);
204        let mut buffer = PointBuffer::with_capacity(DType::F32, 4);
205        buffer.resize(3, &field).unwrap();
206        assert_eq!(buffer.len(), 3);
207    }
208
209    #[test]
210    fn buffer_set_lookup() {
211        let mut set = PointBufferSet::new();
212        set.insert("x", PointBuffer::from_f32(vec![1.0, 2.0]));
213        assert_eq!(set.get("x").unwrap().len(), 2);
214    }
215}