Skip to main content

jay/
array.rs

1//! Dense multidimensional array: a shape and a flat row-major buffer.
2//!
3//! Buffers are either owned or borrowed from foreign memory (Arrow, the
4//! Python buffer protocol) through [`Buf`], which is what makes the data
5//! boundary zero-copy.
6
7use std::any::Any;
8use std::ops::Deref;
9use std::sync::Arc;
10
11use crate::complex::Cx;
12use crate::dtype::DType;
13use crate::exact::{Ext, Rat};
14
15/// Anything that keeps a foreign buffer's memory alive: the importing side
16/// stores its release guards here and the buffer outlives nothing else.
17pub type Owner = Arc<dyn Any + Send + Sync>;
18
19/// A flat element buffer, owned or borrowed.
20///
21/// A borrowed (foreign) buffer points into memory owned by someone else — an
22/// Arrow C data interface import, a Python buffer — and holds an `Owner`
23/// handle that keeps that memory alive for at least as long as the buffer.
24/// An owned buffer is refcounted, and [`Buf::slice`] of one is a window over
25/// the same allocation rather than a copy. However the buffer was made,
26/// cloning is a refcount bump and mutation copies first if the memory is
27/// shared, foreign or a window ([`Buf::to_mut`]), so a `Buf` behaves as a
28/// private value however cheaply it was cloned.
29pub struct Buf<T> {
30    repr: Repr<T>,
31}
32
33enum Repr<T> {
34    /// The whole of a refcounted `Vec`.
35    Owned(Arc<Vec<T>>),
36    /// The window `[off, off + len)` of a refcounted `Vec`, which is what
37    /// taking a cell or a section out of an owned array gives: a view over
38    /// the same allocation, never a copy. Writing to one copies first, as
39    /// writing to a shared whole does.
40    Slice { buf: Arc<Vec<T>>, off: usize, len: usize },
41    Foreign { ptr: *const T, len: usize, owner: Owner },
42}
43
44// SAFETY: no variant hands out aliased mutable access. A foreign buffer
45// is read-only for its whole life and its `owner` keeps the memory alive; an
46// owned buffer shares its `Vec` through an `Arc` and only ever mutates it
47// through `Arc::make_mut`, which copies unless this buffer is the sole
48// holder; a window over part of one becomes a `Vec` of its own before any
49// write, so it never mutates the allocation it shares. So `Buf` is exactly
50// as shareable as the `&[T]` it derefs to —
51// which, because an owned buffer is an `Arc<Vec<T>>` that may be dropped or
52// read from any thread holding a clone, needs `T: Send + Sync` on both.
53unsafe impl<T: Send + Sync> Send for Buf<T> {}
54// SAFETY: as above; `&Buf<T>` only ever hands out `&[T]`.
55unsafe impl<T: Send + Sync> Sync for Buf<T> {}
56
57impl<T> Buf<T> {
58    pub fn new() -> Buf<T> {
59        Buf { repr: Repr::Owned(Arc::new(Vec::new())) }
60    }
61
62    pub fn from_vec(v: Vec<T>) -> Buf<T> {
63        Buf { repr: Repr::Owned(Arc::new(v)) }
64    }
65
66    /// Borrow `len` elements at `ptr`, keeping `owner` alive alongside them.
67    ///
68    /// # Safety
69    ///
70    /// `ptr` must be aligned for `T` and point to `len` initialised elements
71    /// that stay valid, and are not mutated by anyone, for as long as `owner`
72    /// is alive. `len == 0` accepts a dangling `ptr`.
73    pub unsafe fn foreign(ptr: *const T, len: usize, owner: Owner) -> Buf<T> {
74        Buf { repr: Repr::Foreign { ptr, len, owner } }
75    }
76
77    /// True while the buffer still borrows foreign memory.
78    pub fn is_foreign(&self) -> bool {
79        matches!(self.repr, Repr::Foreign { .. })
80    }
81
82    /// The handle keeping this buffer's memory alive, for a borrowed
83    /// buffer.
84    ///
85    /// What the handle holds is the importing side's business, and a reader
86    /// that recognises one of its own can act on it: a device upload leaves
87    /// the device allocation in here, which is how an array carries its
88    /// location without becoming a different kind of array.
89    pub fn owner(&self) -> Option<&Owner> {
90        match &self.repr {
91            Repr::Owned(_) | Repr::Slice { .. } => None,
92            Repr::Foreign { owner, .. } => Some(owner),
93        }
94    }
95
96    pub fn as_slice(&self) -> &[T] {
97        match &self.repr {
98            Repr::Owned(v) => v,
99            Repr::Slice { buf, off, len } => &buf[*off..*off + *len],
100            Repr::Foreign { ptr, len, .. } => {
101                if *len == 0 {
102                    &[]
103                } else {
104                    // SAFETY: the `foreign` contract guarantees `len`
105                    // initialised, aligned, immutable elements at `ptr`, kept
106                    // alive by the owner this buffer holds.
107                    unsafe { std::slice::from_raw_parts(*ptr, *len) }
108                }
109            }
110        }
111    }
112}
113
114impl<T: Clone> Buf<T> {
115    /// The buffer as a uniquely owned `Vec`, copying once if it is foreign or
116    /// shared with another holder. Subsequent calls on the same buffer are
117    /// free until it is cloned again.
118    pub fn to_mut(&mut self) -> &mut Vec<T> {
119        // A window over part of a `Vec` becomes a `Vec` of its own first:
120        // what the caller writes — a change of length included — must not
121        // reach the other windows over the same allocation.
122        if !matches!(self.repr, Repr::Owned(_)) {
123            self.repr = Repr::Owned(Arc::new(self.as_slice().to_vec()));
124        }
125        match &mut self.repr {
126            Repr::Owned(v) => Arc::make_mut(v),
127            _ => unreachable!("just converted to a whole owned buffer"),
128        }
129    }
130
131    /// The contents as a `Vec`, moving it out when this buffer is the sole
132    /// holder of a whole one and copying otherwise.
133    pub fn into_vec(self) -> Vec<T> {
134        match self.repr {
135            Repr::Owned(v) => Arc::try_unwrap(v).unwrap_or_else(|v| v.as_slice().to_vec()),
136            Repr::Slice { ref buf, off, len } => buf[off..off + len].to_vec(),
137            Repr::Foreign { .. } => self.as_slice().to_vec(),
138        }
139    }
140
141    pub fn push(&mut self, value: T) {
142        self.to_mut().push(value);
143    }
144
145    pub fn extend_from_slice(&mut self, other: &[T]) {
146        self.to_mut().extend_from_slice(other);
147    }
148
149    /// Elements `[start, end)`, as a view: no element is copied, whatever
150    /// the buffer is. A foreign slice keeps borrowing and shares the same
151    /// owner; an owned one is a window over the same refcounted `Vec`, so
152    /// it holds that whole allocation alive for as long as it lives.
153    pub fn slice(&self, start: usize, end: usize) -> Buf<T> {
154        match &self.repr {
155            Repr::Owned(v) => {
156                assert!(start <= end && end <= v.len(), "slice out of range");
157                if start == 0 && end == v.len() {
158                    return Buf { repr: Repr::Owned(Arc::clone(v)) };
159                }
160                Buf { repr: Repr::Slice { buf: Arc::clone(v), off: start, len: end - start } }
161            }
162            Repr::Slice { buf, off, len } => {
163                assert!(start <= end && end <= *len, "slice out of range");
164                let repr =
165                    Repr::Slice { buf: Arc::clone(buf), off: off + start, len: end - start };
166                Buf { repr }
167            }
168            Repr::Foreign { ptr, len, owner } => {
169                assert!(start <= end && end <= *len, "slice out of range");
170                // SAFETY: `start <= len` keeps the offset inside the same
171                // allocation; the new buffer holds a clone of the owner.
172                unsafe { Buf::foreign(ptr.add(start), end - start, owner.clone()) }
173            }
174        }
175    }
176}
177
178impl<T> Deref for Buf<T> {
179    type Target = [T];
180
181    fn deref(&self) -> &[T] {
182        self.as_slice()
183    }
184}
185
186/// Cloning never copies elements: every shape of buffer is a refcount bump,
187/// and the copy happens later, in [`Buf::to_mut`], only if someone writes
188/// while the memory is still shared.
189impl<T: Clone> Clone for Buf<T> {
190    fn clone(&self) -> Buf<T> {
191        match &self.repr {
192            Repr::Owned(v) => Buf { repr: Repr::Owned(Arc::clone(v)) },
193            Repr::Slice { buf, off, len } => {
194                Buf { repr: Repr::Slice { buf: Arc::clone(buf), off: *off, len: *len } }
195            }
196            Repr::Foreign { ptr, len, owner } => {
197                // SAFETY: same pointer, same owner, same guarantees.
198                unsafe { Buf::foreign(*ptr, *len, owner.clone()) }
199            }
200        }
201    }
202}
203
204impl<T> Default for Buf<T> {
205    fn default() -> Buf<T> {
206        Buf::new()
207    }
208}
209
210impl<T: std::fmt::Debug> std::fmt::Debug for Buf<T> {
211    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
212        std::fmt::Debug::fmt(self.as_slice(), f)
213    }
214}
215
216impl<T: PartialEq> PartialEq for Buf<T> {
217    fn eq(&self, other: &Buf<T>) -> bool {
218        self.as_slice() == other.as_slice()
219    }
220}
221
222impl<T> From<Vec<T>> for Buf<T> {
223    fn from(v: Vec<T>) -> Buf<T> {
224        Buf::from_vec(v)
225    }
226}
227
228impl<'a, T> IntoIterator for &'a Buf<T> {
229    type Item = &'a T;
230    type IntoIter = std::slice::Iter<'a, T>;
231
232    fn into_iter(self) -> std::slice::Iter<'a, T> {
233        self.as_slice().iter()
234    }
235}
236
237impl<T> FromIterator<T> for Buf<T> {
238    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Buf<T> {
239        Buf::from_vec(Vec::from_iter(iter))
240    }
241}
242
243#[derive(Clone, Debug, PartialEq)]
244pub enum Data {
245    Bool(Buf<u8>),
246    I64(Buf<i64>),
247    /// Arbitrary-precision integers. Like boxes, these are heap-backed
248    /// pointers rather than machine words: never foreign, never fused,
249    /// never vectorised.
250    Ext(Buf<Ext>),
251    /// Exact ratios, each in lowest terms. Heap-backed, as `Ext` is.
252    Rat(Buf<Rat>),
253    F64(Buf<f64>),
254    /// Complex numbers, interleaved `[re, im]` — the layout numpy, C and a
255    /// pair of Arrow float columns all share.
256    Complex(Buf<Cx>),
257    Char(Buf<char>),
258    /// Boxes: every element is a whole array. Foreign memory never holds
259    /// these, so a boxed buffer is always owned and cloning it is a
260    /// refcount bump like any other.
261    Box(Buf<Array>),
262}
263
264impl Data {
265    pub fn dtype(&self) -> DType {
266        match self {
267            Data::Bool(_) => DType::Bool,
268            Data::I64(_) => DType::I64,
269            Data::Ext(_) => DType::Ext,
270            Data::Rat(_) => DType::Rat,
271            Data::F64(_) => DType::F64,
272            Data::Complex(_) => DType::Complex,
273            Data::Char(_) => DType::Char,
274            Data::Box(_) => DType::Box,
275        }
276    }
277
278    pub fn len(&self) -> usize {
279        match self {
280            Data::Bool(v) => v.len(),
281            Data::I64(v) => v.len(),
282            Data::Ext(v) => v.len(),
283            Data::Rat(v) => v.len(),
284            Data::F64(v) => v.len(),
285            Data::Complex(v) => v.len(),
286            Data::Char(v) => v.len(),
287            Data::Box(v) => v.len(),
288        }
289    }
290
291    pub fn is_empty(&self) -> bool {
292        self.len() == 0
293    }
294
295    /// True while the payload still borrows foreign memory.
296    pub fn is_foreign(&self) -> bool {
297        match self {
298            Data::Bool(v) => v.is_foreign(),
299            Data::I64(v) => v.is_foreign(),
300            Data::Ext(v) => v.is_foreign(),
301            Data::Rat(v) => v.is_foreign(),
302            Data::F64(v) => v.is_foreign(),
303            Data::Complex(v) => v.is_foreign(),
304            Data::Char(v) => v.is_foreign(),
305            Data::Box(v) => v.is_foreign(),
306        }
307    }
308
309    /// The handle keeping this payload's memory alive, for a borrowed one.
310    /// See [`Buf::owner`].
311    pub fn owner(&self) -> Option<&Owner> {
312        match self {
313            Data::Bool(v) => v.owner(),
314            Data::I64(v) => v.owner(),
315            Data::Ext(v) => v.owner(),
316            Data::Rat(v) => v.owner(),
317            Data::F64(v) => v.owner(),
318            Data::Complex(v) => v.owner(),
319            Data::Char(v) => v.owner(),
320            Data::Box(v) => v.owner(),
321        }
322    }
323
324    pub fn slice(&self, start: usize, end: usize) -> Data {
325        match self {
326            Data::Bool(v) => Data::Bool(v.slice(start, end)),
327            Data::I64(v) => Data::I64(v.slice(start, end)),
328            Data::Ext(v) => Data::Ext(v.slice(start, end)),
329            Data::Rat(v) => Data::Rat(v.slice(start, end)),
330            Data::F64(v) => Data::F64(v.slice(start, end)),
331            Data::Complex(v) => Data::Complex(v.slice(start, end)),
332            Data::Char(v) => Data::Char(v.slice(start, end)),
333            Data::Box(v) => Data::Box(v.slice(start, end)),
334        }
335    }
336
337    pub fn empty(dtype: DType) -> Data {
338        match dtype {
339            DType::Bool => Data::Bool(Buf::new()),
340            DType::I64 => Data::I64(Buf::new()),
341            DType::Ext => Data::Ext(Buf::new()),
342            DType::Rat => Data::Rat(Buf::new()),
343            DType::F64 => Data::F64(Buf::new()),
344            DType::Complex => Data::Complex(Buf::new()),
345            DType::Char => Data::Char(Buf::new()),
346            DType::Box => Data::Box(Buf::new()),
347        }
348    }
349
350    /// The fill element used by overtaking and framing. The boxed fill is
351    /// J's `a:`, a box holding an empty numeric list.
352    pub fn push_fill(&mut self) {
353        match self {
354            Data::Bool(v) => v.push(0),
355            Data::I64(v) => v.push(0),
356            Data::Ext(v) => v.push(Ext::default()),
357            Data::Rat(v) => v.push(Rat::zero()),
358            Data::F64(v) => v.push(0.0),
359            Data::Complex(v) => v.push(crate::complex::ZERO),
360            Data::Char(v) => v.push(' '),
361            Data::Box(v) => v.push(Array::box_fill()),
362        }
363    }
364
365    pub fn extend_from(&mut self, other: &Data) -> bool {
366        match (self, other) {
367            (Data::Bool(a), Data::Bool(b)) => a.extend_from_slice(b),
368            (Data::I64(a), Data::I64(b)) => a.extend_from_slice(b),
369            (Data::Ext(a), Data::Ext(b)) => a.extend_from_slice(b),
370            (Data::Rat(a), Data::Rat(b)) => a.extend_from_slice(b),
371            (Data::F64(a), Data::F64(b)) => a.extend_from_slice(b),
372            (Data::Complex(a), Data::Complex(b)) => a.extend_from_slice(b),
373            (Data::Char(a), Data::Char(b)) => a.extend_from_slice(b),
374            (Data::Box(a), Data::Box(b)) => a.extend_from_slice(b),
375            _ => return false,
376        }
377        true
378    }
379
380    /// Weave column-major buffers into one row-major block of shape
381    /// `[rows, columns.len()]`.
382    ///
383    /// This is the table boundary: a DataFrame arrives as one buffer per
384    /// column and libjay works rows-leading, so the elements have to be
385    /// woven once. The weave reads every column in order and writes its
386    /// result straight through, split across threads at the sizes that pay.
387    ///
388    /// None when the columns disagree on element type, when one is shorter
389    /// than `rows`, or when there are no columns at all — the importing
390    /// side has already reported that.
391    pub fn interleave(columns: &[Data], rows: usize) -> Option<Data> {
392        let cols = columns.len();
393        let first = columns.first()?;
394        if columns.iter().any(|c| c.dtype() != first.dtype() || c.len() < rows) {
395            return None;
396        }
397
398        /// One row of the output takes one element from each column, so a
399        /// chunk of the output is a run of whole rows plus, at either end,
400        /// the part of a row the neighbouring chunk does not hold.
401        fn weave<T: Copy + Default + Send + Sync>(columns: &[&[T]], rows: usize) -> Vec<T> {
402            let cols = columns.len();
403            let (out, _) = crate::par::fill(rows * cols, |start, part: &mut [T]| {
404                let mut rest = &mut part[..];
405                let mut at = start;
406                // The tail of a row that began in the chunk before this one.
407                let lead = ((cols - at % cols) % cols).min(rest.len());
408                if lead > 0 {
409                    let (head, tail) = rest.split_at_mut(lead);
410                    let r = at / cols;
411                    for (k, slot) in head.iter_mut().enumerate() {
412                        *slot = columns[at % cols + k][r];
413                    }
414                    at += lead;
415                    rest = tail;
416                }
417                let whole = rest.len() / cols;
418                let (body, tail) = rest.split_at_mut(whole * cols);
419                let r0 = at / cols;
420                for (k, row) in body.chunks_exact_mut(cols).enumerate() {
421                    for (slot, col) in row.iter_mut().zip(columns) {
422                        *slot = col[r0 + k];
423                    }
424                }
425                // The head of a row the next chunk finishes.
426                let r = r0 + whole;
427                for (c, slot) in tail.iter_mut().enumerate() {
428                    *slot = columns[c][r];
429                }
430                true
431            });
432            out
433        }
434
435        /// The same weave for the heap-backed types, which are neither
436        /// `Copy` nor worth a thread: Arrow carries none of them, so this
437        /// only ever runs on data libjay built itself.
438        fn weave_cloned<T: Clone>(columns: &[&[T]], rows: usize) -> Vec<T> {
439            let mut out = Vec::with_capacity(rows * columns.len());
440            for r in 0..rows {
441                for c in columns {
442                    out.push(c[r].clone());
443                }
444            }
445            out
446        }
447
448        macro_rules! by {
449            ($variant:ident, $weave:ident) => {{
450                let mut s = Vec::with_capacity(cols);
451                for c in columns {
452                    let Data::$variant(v) = c else { return None };
453                    s.push(v.as_slice());
454                }
455                Some(Data::$variant($weave(&s, rows).into()))
456            }};
457        }
458        match first.dtype() {
459            DType::Bool => by!(Bool, weave),
460            DType::I64 => by!(I64, weave),
461            DType::F64 => by!(F64, weave),
462            DType::Complex => by!(Complex, weave),
463            DType::Char => by!(Char, weave),
464            DType::Ext => by!(Ext, weave_cloned),
465            DType::Rat => by!(Rat, weave_cloned),
466            DType::Box => by!(Box, weave_cloned),
467        }
468    }
469
470    /// Widen to `to`. Returns None for unsupported conversions.
471    pub fn cast(&self, to: DType) -> Option<Data> {
472        if self.dtype() == to {
473            return Some(self.clone());
474        }
475        match (self, to) {
476            (Data::Bool(v), DType::I64) => Some(Data::I64(v.iter().map(|&x| x as i64).collect())),
477            (Data::Bool(v), DType::F64) => Some(Data::F64(v.iter().map(|&x| x as f64).collect())),
478            (Data::I64(v), DType::F64) => Some(Data::F64(v.iter().map(|&x| x as f64).collect())),
479            (Data::Bool(v), DType::Ext) => Some(Data::Ext(v.iter().map(|&x| Ext::from(x)).collect())),
480            (Data::I64(v), DType::Ext) => Some(Data::Ext(v.iter().map(|&x| Ext::from(x)).collect())),
481            (Data::Bool(v), DType::Rat) => {
482                Some(Data::Rat(v.iter().map(|&x| Rat::from_int(Ext::from(x))).collect()))
483            }
484            (Data::I64(v), DType::Rat) => {
485                Some(Data::Rat(v.iter().map(|&x| Rat::from_int(Ext::from(x))).collect()))
486            }
487            (Data::Ext(v), DType::Rat) => {
488                Some(Data::Rat(v.iter().map(|x| Rat::from_int(x.clone())).collect()))
489            }
490            (Data::Ext(v), DType::F64) => {
491                Some(Data::F64(v.iter().map(crate::exact::ext_to_f64).collect()))
492            }
493            (Data::Rat(v), DType::F64) => Some(Data::F64(v.iter().map(Rat::to_f64).collect())),
494            (Data::Bool(v), DType::Complex) => {
495                Some(Data::Complex(v.iter().map(|&x| [x as f64, 0.0]).collect()))
496            }
497            (Data::I64(v), DType::Complex) => {
498                Some(Data::Complex(v.iter().map(|&x| [x as f64, 0.0]).collect()))
499            }
500            (Data::Ext(v), DType::Complex) => {
501                Some(Data::Complex(v.iter().map(|x| [crate::exact::ext_to_f64(x), 0.0]).collect()))
502            }
503            (Data::Rat(v), DType::Complex) => {
504                Some(Data::Complex(v.iter().map(|x| [x.to_f64(), 0.0]).collect()))
505            }
506            (Data::F64(v), DType::Complex) => {
507                Some(Data::Complex(v.iter().map(|&x| [x, 0.0]).collect()))
508            }
509            _ => None,
510        }
511    }
512}
513
514#[derive(Clone, Debug, PartialEq)]
515pub struct Array {
516    pub shape: Vec<usize>,
517    pub data: Data,
518}
519
520impl Array {
521    pub fn new(shape: Vec<usize>, data: Data) -> Array {
522        debug_assert_eq!(shape.iter().product::<usize>(), data.len());
523        Array { shape, data }
524    }
525
526    pub fn scalar_i64(v: i64) -> Array {
527        Array { shape: vec![], data: Data::I64(vec![v].into()) }
528    }
529
530    pub fn scalar_f64(v: f64) -> Array {
531        Array { shape: vec![], data: Data::F64(vec![v].into()) }
532    }
533
534    pub fn scalar_bool(v: bool) -> Array {
535        Array { shape: vec![], data: Data::Bool(vec![v as u8].into()) }
536    }
537
538    pub fn from_i64(values: Vec<i64>) -> Array {
539        Array { shape: vec![values.len()], data: Data::I64(values.into()) }
540    }
541
542    pub fn from_f64(values: Vec<f64>) -> Array {
543        Array { shape: vec![values.len()], data: Data::F64(values.into()) }
544    }
545
546    pub fn from_chars(values: Vec<char>) -> Array {
547        Array { shape: vec![values.len()], data: Data::Char(values.into()) }
548    }
549
550    pub fn empty(dtype: DType) -> Array {
551        Array { shape: vec![0], data: Data::empty(dtype) }
552    }
553
554    /// `y` as a scalar box (J `<`).
555    pub fn boxed(value: Array) -> Array {
556        Array { shape: vec![], data: Data::Box(vec![value].into()) }
557    }
558
559    /// The element that fills a boxed array: J's `a:`, a box holding an
560    /// empty numeric list.
561    pub fn box_fill() -> Array {
562        Array::empty(DType::I64)
563    }
564
565    pub fn dtype(&self) -> DType {
566        self.data.dtype()
567    }
568
569    pub fn rank(&self) -> usize {
570        self.shape.len()
571    }
572
573    /// Total number of elements.
574    pub fn count(&self) -> usize {
575        self.shape.iter().product()
576    }
577
578    /// Number of items (major cells): leading axis length, 1 for a scalar.
579    pub fn items(&self) -> usize {
580        self.shape.first().copied().unwrap_or(1)
581    }
582
583    /// Elements per item.
584    pub fn item_size(&self) -> usize {
585        self.shape.iter().skip(1).product()
586    }
587
588    pub fn cast(&self, to: DType) -> Option<Array> {
589        Some(Array { shape: self.shape.clone(), data: self.data.cast(to)? })
590    }
591
592    /// Split into cells: the trailing `cell_rank` axes form the cell shape,
593    /// the leading axes form the frame.
594    pub fn cells(&self, frame_rank: usize) -> Vec<Array> {
595        debug_assert!(frame_rank <= self.rank());
596        let cell_shape: Vec<usize> = self.shape[frame_rank..].to_vec();
597        let cell_size: usize = cell_shape.iter().product();
598        let n: usize = self.shape[..frame_rank].iter().product();
599        (0..n)
600            .map(|i| Array {
601                shape: cell_shape.clone(),
602                data: self.data.slice(i * cell_size, (i + 1) * cell_size),
603            })
604            .collect()
605    }
606
607    /// One cell without materialising all of them.
608    pub fn cell_at(&self, frame_rank: usize, index: usize) -> Array {
609        let cell_shape: Vec<usize> = self.shape[frame_rank..].to_vec();
610        let cell_size: usize = cell_shape.iter().product();
611        Array {
612            shape: cell_shape,
613            data: self.data.slice(index * cell_size, (index + 1) * cell_size),
614        }
615    }
616
617    /// Item `i` (major cell along the leading axis).
618    pub fn item(&self, i: usize) -> Array {
619        debug_assert!(self.rank() >= 1);
620        self.cell_at(1, i)
621    }
622
623    pub fn as_i64_slice(&self) -> Option<&[i64]> {
624        match &self.data {
625            Data::I64(v) => Some(v),
626            _ => None,
627        }
628    }
629
630    /// The boxed elements, if the array holds boxes.
631    pub fn as_boxes(&self) -> Option<&[Array]> {
632        match &self.data {
633            Data::Box(v) => Some(v),
634            _ => None,
635        }
636    }
637
638    pub fn as_f64_slice(&self) -> Option<&[f64]> {
639        match &self.data {
640            Data::F64(v) => Some(v),
641            _ => None,
642        }
643    }
644
645    /// The extended integers, if the array holds them.
646    pub fn as_ext_slice(&self) -> Option<&[Ext]> {
647        match &self.data {
648            Data::Ext(v) => Some(v),
649            _ => None,
650        }
651    }
652
653    /// The rationals, if the array holds them.
654    pub fn as_rat_slice(&self) -> Option<&[Rat]> {
655        match &self.data {
656            Data::Rat(v) => Some(v),
657            _ => None,
658        }
659    }
660
661    pub fn as_complex_slice(&self) -> Option<&[Cx]> {
662        match &self.data {
663            Data::Complex(v) => Some(v),
664            _ => None,
665        }
666    }
667
668    /// Numeric contents widened to complex. None for character or boxed data.
669    pub fn to_complex_vec(&self) -> Option<Vec<Cx>> {
670        match &self.data {
671            Data::Bool(v) => Some(v.iter().map(|&x| [x as f64, 0.0]).collect()),
672            Data::I64(v) => Some(v.iter().map(|&x| [x as f64, 0.0]).collect()),
673            Data::Ext(v) => Some(v.iter().map(|x| [crate::exact::ext_to_f64(x), 0.0]).collect()),
674            Data::Rat(v) => Some(v.iter().map(|x| [x.to_f64(), 0.0]).collect()),
675            Data::F64(v) => Some(v.iter().map(|&x| [x, 0.0]).collect()),
676            Data::Complex(v) => Some(v.to_vec()),
677            Data::Char(_) | Data::Box(_) => None,
678        }
679    }
680
681    /// Numeric contents widened to f64. None for character data.
682    pub fn to_f64_vec(&self) -> Option<Vec<f64>> {
683        match &self.data {
684            Data::Bool(v) => Some(v.iter().map(|&x| x as f64).collect()),
685            Data::I64(v) => Some(v.iter().map(|&x| x as f64).collect()),
686            Data::Ext(v) => Some(v.iter().map(crate::exact::ext_to_f64).collect()),
687            Data::Rat(v) => Some(v.iter().map(Rat::to_f64).collect()),
688            Data::F64(v) => Some(v.to_vec()),
689            // A complex value is not a real one, even when its imaginary
690            // part is zero: the caller wants a real and must ask for it.
691            Data::Complex(_) | Data::Char(_) | Data::Box(_) => None,
692        }
693    }
694
695    /// Numeric contents as i64 if exactly representable.
696    pub fn to_i64_vec(&self) -> Option<Vec<i64>> {
697        match &self.data {
698            Data::Bool(v) => Some(v.iter().map(|&x| x as i64).collect()),
699            Data::I64(v) => Some(v.to_vec()),
700            // An exact value converts only when it really is a machine
701            // integer; anything else is a refusal, not a rounding.
702            Data::Ext(v) => v.iter().map(crate::exact::ext_to_i64).collect(),
703            Data::Rat(v) => {
704                v.iter().map(|x| x.to_int().as_ref().and_then(crate::exact::ext_to_i64)).collect()
705            }
706            Data::F64(v) => {
707                let mut out = Vec::with_capacity(v.len());
708                for &x in v.iter() {
709                    if x.fract() != 0.0 || x.abs() >= i64::MAX as f64 {
710                        return None;
711                    }
712                    out.push(x as i64);
713                }
714                Some(out)
715            }
716            Data::Complex(_) | Data::Char(_) | Data::Box(_) => None,
717        }
718    }
719}
720
721#[cfg(test)]
722mod tests {
723    use super::*;
724    use std::sync::atomic::{AtomicBool, Ordering};
725
726    /// Owns a vector and records its own drop, so a test can assert that a
727    /// foreign buffer kept it alive.
728    struct Guard {
729        values: Vec<i64>,
730        dropped: Arc<AtomicBool>,
731    }
732
733    impl Drop for Guard {
734        fn drop(&mut self) {
735            self.dropped.store(true, Ordering::SeqCst);
736        }
737    }
738
739    fn foreign_buf(values: Vec<i64>, dropped: Arc<AtomicBool>) -> Buf<i64> {
740        let guard = Arc::new(Guard { values, dropped });
741        let ptr = guard.values.as_ptr();
742        let len = guard.values.len();
743        // SAFETY: the guard owns the vector, is moved into the buffer's owner
744        // slot, and nothing mutates it afterwards.
745        unsafe { Buf::foreign(ptr, len, guard) }
746    }
747
748    #[test]
749    fn owned_buf_derefs_to_its_slice() {
750        let b: Buf<i64> = vec![1, 2, 3].into();
751        assert!(!b.is_foreign());
752        assert_eq!(&b[..], &[1, 2, 3]);
753        assert_eq!(b.len(), 3);
754        assert_eq!(b.iter().sum::<i64>(), 6);
755    }
756
757    #[test]
758    fn empty_buf_is_a_valid_empty_slice() {
759        let b: Buf<f64> = Buf::new();
760        assert_eq!(&b[..], &[] as &[f64]);
761        // SAFETY: zero length, so the dangling pointer is never dereferenced.
762        let f = unsafe { Buf::<f64>::foreign(std::ptr::null(), 0, Arc::new(())) };
763        assert_eq!(&f[..], &[] as &[f64]);
764    }
765
766    #[test]
767    fn cloning_an_owned_buf_shares_the_same_memory() {
768        let b: Buf<i64> = vec![1, 2, 3].into();
769        let c = b.clone();
770        assert_eq!(b.as_ptr(), c.as_ptr(), "owned clone copied the elements");
771        assert_eq!(&c[..], &[1, 2, 3]);
772    }
773
774    #[test]
775    fn writing_to_a_shared_owned_buf_copies_first() {
776        let b: Buf<i64> = vec![1, 2, 3].into();
777        let mut c = b.clone();
778        c.to_mut()[0] = 99;
779        assert_eq!(&b[..], &[1, 2, 3], "the other holder saw the write");
780        assert_eq!(&c[..], &[99, 2, 3]);
781        assert_ne!(b.as_ptr(), c.as_ptr());
782        // Sole holder again: further writes are in place.
783        let ptr = c.as_ptr();
784        c.to_mut()[1] = 98;
785        assert_eq!(c.as_ptr(), ptr, "unshared write copied");
786    }
787
788    #[test]
789    fn into_vec_moves_when_sole_holder_and_copies_when_shared() {
790        let b: Buf<i64> = vec![1, 2, 3].into();
791        let ptr = b.as_ptr();
792        let v = b.into_vec();
793        assert_eq!(v.as_ptr(), ptr, "sole holder copied instead of moving");
794
795        let b: Buf<i64> = vec![1, 2, 3].into();
796        let c = b.clone();
797        let v = b.into_vec();
798        assert_eq!(v, vec![1, 2, 3]);
799        assert_eq!(&c[..], &[1, 2, 3]);
800    }
801
802    #[test]
803    fn foreign_buf_reads_borrowed_memory_and_keeps_the_owner_alive() {
804        let dropped = Arc::new(AtomicBool::new(false));
805        let b = foreign_buf(vec![10, 20, 30], dropped.clone());
806        assert!(b.is_foreign());
807        assert_eq!(&b[..], &[10, 20, 30]);
808        assert!(!dropped.load(Ordering::SeqCst), "owner dropped while borrowed");
809        drop(b);
810        assert!(dropped.load(Ordering::SeqCst), "owner leaked after the buffer died");
811    }
812
813    #[test]
814    fn cloning_a_foreign_buf_shares_the_same_memory() {
815        let dropped = Arc::new(AtomicBool::new(false));
816        let b = foreign_buf(vec![1, 2, 3], dropped.clone());
817        let c = b.clone();
818        assert!(c.is_foreign());
819        assert_eq!(b.as_ptr(), c.as_ptr());
820        drop(b);
821        assert!(!dropped.load(Ordering::SeqCst), "owner dropped while a clone lives");
822        assert_eq!(&c[..], &[1, 2, 3]);
823    }
824
825    #[test]
826    fn slicing_a_foreign_buf_keeps_borrowing() {
827        let dropped = Arc::new(AtomicBool::new(false));
828        let b = foreign_buf(vec![1, 2, 3, 4], dropped.clone());
829        let s = b.slice(1, 3);
830        assert!(s.is_foreign());
831        assert_eq!(&s[..], &[2, 3]);
832        drop(b);
833        assert_eq!(&s[..], &[2, 3]);
834        assert!(!dropped.load(Ordering::SeqCst));
835    }
836
837    #[test]
838    fn mutating_a_foreign_buf_copies_first() {
839        let dropped = Arc::new(AtomicBool::new(false));
840        let mut b = foreign_buf(vec![1, 2, 3], dropped.clone());
841        b.push(4);
842        assert!(!b.is_foreign());
843        assert_eq!(&b[..], &[1, 2, 3, 4]);
844        // The original memory is untouched and released with the owner.
845        drop(b);
846        assert!(dropped.load(Ordering::SeqCst));
847    }
848
849    #[test]
850    fn copy_on_write_leaves_other_holders_alone() {
851        let dropped = Arc::new(AtomicBool::new(false));
852        let b = foreign_buf(vec![1, 2, 3], dropped.clone());
853        let mut c = b.clone();
854        c.to_mut()[0] = 99;
855        assert_eq!(&b[..], &[1, 2, 3]);
856        assert_eq!(&c[..], &[99, 2, 3]);
857    }
858
859    #[test]
860    fn foreign_data_slices_without_copying() {
861        let dropped = Arc::new(AtomicBool::new(false));
862        let a = Array::new(vec![2, 2], Data::I64(foreign_buf(vec![1, 2, 3, 4], dropped)));
863        assert!(a.data.is_foreign());
864        let row = a.item(1);
865        assert!(row.data.is_foreign());
866        assert_eq!(row.as_i64_slice(), Some(&[3, 4][..]));
867    }
868
869    #[test]
870    fn foreign_data_extends_by_copying() {
871        let dropped = Arc::new(AtomicBool::new(false));
872        let mut d = Data::I64(foreign_buf(vec![1, 2], dropped));
873        assert!(d.is_foreign());
874        assert!(d.extend_from(&Data::I64(vec![3].into())));
875        assert!(!d.is_foreign());
876        assert_eq!(d, Data::I64(vec![1, 2, 3].into()));
877    }
878}