Skip to main content

antecedent_data/
buffer.rs

1//! Contiguous f64 column buffers — owned or foreign-backed.
2//!
3//! Foreign buffers never expose Arrow types; the owner is type-erased.
4//! Unsafe code here is limited to pointer→slice projection under an owner
5//! lifetime contract.
6//!
7//! SPDX-License-Identifier: MIT OR Apache-2.0
8
9#![allow(unsafe_code)]
10
11use std::ops::Deref;
12use std::sync::Arc;
13
14/// Contiguous `f64` values for a column.
15#[derive(Clone, Debug)]
16pub struct F64Buffer {
17    inner: F64BufferInner,
18}
19
20#[derive(Clone, Debug)]
21enum F64BufferInner {
22    Owned(Arc<[f64]>),
23    Foreign(ForeignF64Buffer),
24}
25
26/// Foreign-backed f64 slice kept alive by an opaque owner.
27#[derive(Clone, Debug)]
28pub struct ForeignF64Buffer {
29    owner: Arc<dyn ForeignBufferOwner>,
30    ptr: *const f64,
31    len: usize,
32}
33
34impl Drop for ForeignF64Buffer {
35    fn drop(&mut self) {
36        // Touch owner so clippy/rustc know it is semantically used for lifetime.
37        let _ = Arc::as_ptr(&self.owner);
38    }
39}
40
41/// Type-erased owner that keeps foreign memory valid.
42pub trait ForeignBufferOwner: Send + Sync + std::fmt::Debug {}
43
44impl<T: Send + Sync + std::fmt::Debug + 'static> ForeignBufferOwner for T {}
45
46// SAFETY: `ptr` is valid for `len` elements while `owner` is alive; owner is Send+Sync.
47unsafe impl Send for ForeignF64Buffer {}
48unsafe impl Sync for ForeignF64Buffer {}
49
50impl ForeignF64Buffer {
51    /// Construct from a validated pointer and length.
52    ///
53    /// # Safety
54    ///
55    /// - `ptr` must be valid for reads of `len` `f64` values for the lifetime of `owner`.
56    /// - The pointed-to memory must be properly aligned for `f64`.
57    /// - No mutable aliases may exist while this buffer (or clones) is live.
58    #[must_use]
59    pub unsafe fn new(owner: Arc<dyn ForeignBufferOwner>, ptr: *const f64, len: usize) -> Self {
60        Self { owner, ptr, len }
61    }
62
63    /// Borrow as a slice.
64    #[must_use]
65    pub fn as_slice(&self) -> &[f64] {
66        if self.len == 0 {
67            return &[];
68        }
69        // SAFETY: constructor contract — ptr valid for len while owner alive.
70        unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
71    }
72
73    /// Length.
74    #[must_use]
75    pub const fn len(&self) -> usize {
76        self.len
77    }
78
79    /// Whether empty.
80    #[must_use]
81    pub const fn is_empty(&self) -> bool {
82        self.len == 0
83    }
84}
85
86impl F64Buffer {
87    /// Owned buffer.
88    #[must_use]
89    pub fn owned(values: impl Into<Arc<[f64]>>) -> Self {
90        Self { inner: F64BufferInner::Owned(values.into()) }
91    }
92
93    /// Foreign-backed buffer.
94    #[must_use]
95    pub fn foreign(buf: ForeignF64Buffer) -> Self {
96        Self { inner: F64BufferInner::Foreign(buf) }
97    }
98
99    /// Whether this buffer borrows foreign memory.
100    #[must_use]
101    pub const fn is_foreign(&self) -> bool {
102        matches!(self.inner, F64BufferInner::Foreign(_))
103    }
104
105    /// Byte length of the logical values.
106    #[must_use]
107    pub fn nbytes(&self) -> u64 {
108        (self.len() * core::mem::size_of::<f64>()) as u64
109    }
110
111    /// Borrow as slice.
112    #[must_use]
113    pub fn as_slice(&self) -> &[f64] {
114        match &self.inner {
115            F64BufferInner::Owned(v) => v.as_ref(),
116            F64BufferInner::Foreign(f) => f.as_slice(),
117        }
118    }
119
120    /// Length.
121    #[must_use]
122    pub fn len(&self) -> usize {
123        match &self.inner {
124            F64BufferInner::Owned(v) => v.len(),
125            F64BufferInner::Foreign(f) => f.len(),
126        }
127    }
128
129    /// Whether empty.
130    #[must_use]
131    pub fn is_empty(&self) -> bool {
132        self.len() == 0
133    }
134}
135
136impl Deref for F64Buffer {
137    type Target = [f64];
138
139    fn deref(&self) -> &[f64] {
140        self.as_slice()
141    }
142}
143
144impl From<Arc<[f64]>> for F64Buffer {
145    fn from(value: Arc<[f64]>) -> Self {
146        Self::owned(value)
147    }
148}
149
150impl From<Vec<f64>> for F64Buffer {
151    fn from(value: Vec<f64>) -> Self {
152        Self::owned(Arc::<[f64]>::from(value))
153    }
154}
155
156impl PartialEq for F64Buffer {
157    fn eq(&self, other: &Self) -> bool {
158        self.as_slice() == other.as_slice()
159    }
160}