// Column vectors and data batches: the engine's physical data units.
// A vector is a fixed array of values plus a validity bitmap-like array;
// batch size for scans is set by the CSV source (4096 rows).
///|
pub(all) enum ColumnVector {
I32(FixedArray[Int], FixedArray[Bool])
I64(FixedArray[Int64], FixedArray[Bool])
F64(FixedArray[Double], FixedArray[Bool])
BoolV(FixedArray[Bool], FixedArray[Bool])
StrV(FixedArray[String], FixedArray[Bool])
DateV(FixedArray[Int], FixedArray[Bool]) // epoch days
}
///|
pub fn ColumnVector::len(self : ColumnVector) -> Int {
match self {
I32(d, _) => d.length()
I64(d, _) => d.length()
F64(d, _) => d.length()
BoolV(d, _) => d.length()
StrV(d, _) => d.length()
DateV(d, _) => d.length()
}
}
///|
pub fn ColumnVector::dtype(self : ColumnVector) -> DataType {
match self {
I32(_, _) => Int32
I64(_, _) => Int64
F64(_, _) => Float64
BoolV(_, _) => Bool
StrV(_, _) => Str
DateV(_, _) => Date
}
}
///|
/// Element access with validity: invalid slots come back as Null.
pub fn ColumnVector::get(self : ColumnVector, i : Int) -> Scalar {
let valid = match self {
I32(_, v) => v[i]
I64(_, v) => v[i]
F64(_, v) => v[i]
BoolV(_, v) => v[i]
StrV(_, v) => v[i]
DateV(_, v) => v[i]
}
if !valid {
return Null
}
match self {
I32(d, _) => Int32(d[i])
I64(d, _) => Int64(d[i])
F64(d, _) => Float64(d[i])
BoolV(d, _) => Boolean(d[i])
StrV(d, _) => Str(d[i])
DateV(d, _) => Date(d[i])
}
}
///|
/// Allocate an all-valid vector of the given type and length.
pub fn ColumnVector::make(dtype : DataType, n : Int) -> ColumnVector {
let valid = FixedArray::make(n, true)
match dtype {
Int32 => I32(FixedArray::make(n, 0), valid)
Int64 => I64(FixedArray::make(n, 0L), valid)
Float64 => F64(FixedArray::make(n, 0.0), valid)
Bool => BoolV(FixedArray::make(n, false), valid)
Str => StrV(FixedArray::make(n, ""), valid)
Date => DateV(FixedArray::make(n, 0), valid)
}
}
///|
/// Write a scalar at position i. Writing Null marks the slot invalid.
/// Type must match the vector (caller responsibility, binder-checked).
pub fn ColumnVector::set(self : ColumnVector, i : Int, v : Scalar) -> Unit {
match (self, v) {
(I32(d, valid), Int32(x)) => {
d[i] = x
valid[i] = true
}
(I64(d, valid), Int64(x)) => {
d[i] = x
valid[i] = true
}
(F64(d, valid), Float64(x)) => {
d[i] = x
valid[i] = true
}
(BoolV(d, valid), Boolean(x)) => {
d[i] = x
valid[i] = true
}
(StrV(d, valid), Str(x)) => {
d[i] = x
valid[i] = true
}
(DateV(d, valid), Date(x)) => {
d[i] = x
valid[i] = true
}
(_, Null) =>
match self {
I32(_, valid) => valid[i] = false
I64(_, valid) => valid[i] = false
F64(_, valid) => valid[i] = false
BoolV(_, valid) => valid[i] = false
StrV(_, valid) => valid[i] = false
DateV(_, valid) => valid[i] = false
}
_ => () // type mismatch: unreachable from binder-checked paths
}
}
///|
/// A chunk of rows in column form.
pub struct DataBatch {
columns : Array[ColumnVector]
row_count : Int
}
///|
pub fn DataBatch::columns(self : DataBatch) -> Array[ColumnVector] {
self.columns
}
///|
pub fn DataBatch::row_count(self : DataBatch) -> Int {
self.row_count
}
///|
pub fn DataBatch::new(
columns : Array[ColumnVector],
row_count : Int,
) -> DataBatch {
{ columns, row_count, }
}