use core::{slice, str};
use reifydb_codec::tag::ValueKind;
use reifydb_value::{
reifydb_assertions,
value::{date::Date, datetime::DateTime, diff_type::DiffType, duration::Duration, time::Time},
};
use crate::{
common::extern_c::wire::{
buffer::ExternCBuffer,
columns::{ExternCColumn, ExternCColumns},
},
flow::extern_c::wire::change::{ExternCChange, ExternCDiff, ExternCOrigin},
};
#[derive(Clone, Copy)]
pub struct BorrowedChange<'a> {
extern_c: &'a ExternCChange,
}
impl<'a> BorrowedChange<'a> {
pub unsafe fn from_raw(ptr: *const ExternCChange) -> Self {
reifydb_assertions! {
assert!(!ptr.is_null(), "BorrowedChange::from_raw: null pointer");
}
Self {
extern_c: unsafe { &*ptr },
}
}
pub fn origin(&self) -> ExternCOrigin {
self.extern_c.origin
}
pub fn version(&self) -> u64 {
self.extern_c.version
}
pub fn changed_at_nanos(&self) -> u64 {
self.extern_c.changed_at
}
pub fn diff_count(&self) -> usize {
self.extern_c.diff_count
}
pub fn diffs(&self) -> impl Iterator<Item = BorrowedDiff<'a>> + 'a {
let count = self.extern_c.diff_count;
let base = self.extern_c.diffs;
(0..count).map(move |i| {
let diff: &'a ExternCDiff = unsafe { &*base.add(i) };
BorrowedDiff {
extern_c: diff,
}
})
}
}
#[derive(Clone, Copy)]
pub struct BorrowedDiff<'a> {
extern_c: &'a ExternCDiff,
}
impl<'a> BorrowedDiff<'a> {
pub fn kind(&self) -> DiffType {
self.extern_c.diff_type
}
pub fn pre(&self) -> BorrowedColumns<'a> {
BorrowedColumns {
extern_c: &self.extern_c.pre,
}
}
pub fn post(&self) -> BorrowedColumns<'a> {
BorrowedColumns {
extern_c: &self.extern_c.post,
}
}
}
#[derive(Clone, Copy)]
pub struct BorrowedColumns<'a> {
extern_c: &'a ExternCColumns,
}
impl<'a> BorrowedColumns<'a> {
pub unsafe fn from_extern_c(ptr: *const ExternCColumns) -> Self {
reifydb_assertions! {
assert!(!ptr.is_null(), "BorrowedColumns::from_extern_c: null pointer");
}
Self {
extern_c: unsafe { &*ptr },
}
}
pub fn row_count(&self) -> usize {
self.extern_c.row_count
}
pub fn column_count(&self) -> usize {
self.extern_c.column_count
}
pub fn is_empty(&self) -> bool {
self.extern_c.row_count == 0 && self.extern_c.column_count == 0
}
pub fn row_numbers(&self) -> &'a [u64] {
if self.extern_c.row_numbers.is_null() || self.extern_c.row_count == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(self.extern_c.row_numbers, self.extern_c.row_count) }
}
}
pub fn time(&self) -> &'a [u64] {
if self.extern_c.time.is_null() || self.extern_c.row_count == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(self.extern_c.time, self.extern_c.row_count) }
}
}
pub fn columns(&self) -> impl Iterator<Item = BorrowedColumn<'a>> + 'a {
let count = self.extern_c.column_count;
let base = self.extern_c.columns;
(0..count).map(move |i| {
let col: &'a ExternCColumn = unsafe { &*base.add(i) };
BorrowedColumn {
extern_c: col,
}
})
}
pub fn column(&self, name: &str) -> Option<BorrowedColumn<'a>> {
self.columns().find(|c| c.name() == name)
}
pub fn column_at_index(&self, idx: usize) -> Option<BorrowedColumn<'a>> {
if idx >= self.extern_c.column_count {
return None;
}
let col: &'a ExternCColumn = unsafe { &*self.extern_c.columns.add(idx) };
Some(BorrowedColumn {
extern_c: col,
})
}
pub fn index_of(&self, name: &str) -> Option<usize> {
self.columns().position(|c| c.name() == name)
}
}
#[derive(Clone, Copy)]
pub struct BorrowedColumn<'a> {
extern_c: &'a ExternCColumn,
}
impl<'a> BorrowedColumn<'a> {
pub fn name(&self) -> &'a str {
unsafe { read_buffer_str(&self.extern_c.name) }
}
pub fn type_code(&self) -> ValueKind {
self.extern_c.data.type_code
}
pub fn row_count(&self) -> usize {
self.extern_c.data.row_count
}
pub fn data_bytes(&self) -> &'a [u8] {
unsafe { read_buffer(&self.extern_c.data.data) }
}
pub fn offsets(&self) -> &'a [u64] {
let buf = &self.extern_c.data.offsets;
if buf.ptr.is_null() || buf.len == 0 {
&[]
} else {
let count = buf.len / core::mem::size_of::<u64>();
unsafe { slice::from_raw_parts(buf.ptr as *const u64, count) }
}
}
pub fn defined_bitvec(&self) -> &'a [u8] {
unsafe { read_buffer(&self.extern_c.data.defined_bitvec) }
}
pub unsafe fn as_slice<T: Copy>(&self) -> Option<&'a [T]> {
let bytes = self.data_bytes();
let count = self.row_count();
let elem = core::mem::size_of::<T>();
if elem == 0 || count.checked_mul(elem)? != bytes.len() {
return None;
}
Some(unsafe { slice::from_raw_parts(bytes.as_ptr() as *const T, count) })
}
pub fn iter_str(&self) -> impl Iterator<Item = &'a str> + 'a {
let data = self.data_bytes();
let offsets = self.offsets();
let row_count = self.row_count();
let offsets_len = offsets.len();
(0..row_count).map(move |i| {
if i + 1 >= offsets_len {
return "";
}
let start = offsets[i] as usize;
let end = offsets[i + 1] as usize;
if end > data.len() {
return "";
}
str::from_utf8(&data[start..end]).unwrap_or("")
})
}
pub fn iter_bytes(&self) -> impl Iterator<Item = &'a [u8]> + 'a {
let data = self.data_bytes();
let offsets = self.offsets();
let row_count = self.row_count();
let offsets_len = offsets.len();
(0..row_count).map(move |i| {
if i + 1 >= offsets_len {
return &[][..];
}
let start = offsets[i] as usize;
let end = offsets[i + 1] as usize;
if end > data.len() {
return &[][..];
}
&data[start..end]
})
}
#[inline]
pub fn is_defined_at(&self, index: usize) -> bool {
let bv = self.defined_bitvec();
if bv.is_empty() {
return true;
}
match bv.get(index / 8) {
Some(b) => (b >> (index % 8)) & 1 == 1,
None => false,
}
}
#[inline]
pub fn utf8_at(&self, index: usize) -> Option<&'a str> {
if self.type_code() != ValueKind::Utf8 || !self.is_defined_at(index) {
return None;
}
let offsets = self.offsets();
if index + 1 >= offsets.len() {
return None;
}
let start = offsets[index] as usize;
let end = offsets[index + 1] as usize;
let data = self.data_bytes();
if end > data.len() || start > end {
return None;
}
str::from_utf8(&data[start..end]).ok()
}
#[inline]
pub fn blob_at(&self, index: usize) -> Option<&'a [u8]> {
if self.type_code() != ValueKind::Blob || !self.is_defined_at(index) {
return None;
}
let offsets = self.offsets();
if index + 1 >= offsets.len() {
return None;
}
let start = offsets[index] as usize;
let end = offsets[index + 1] as usize;
let data = self.data_bytes();
if end > data.len() || start > end {
return None;
}
Some(&data[start..end])
}
#[inline]
pub fn bool_at(&self, index: usize) -> Option<bool> {
if self.type_code() != ValueKind::Boolean || !self.is_defined_at(index) {
return None;
}
let bytes = self.data_bytes();
let byte = bytes.get(index / 8).copied()?;
Some((byte >> (index % 8)) & 1 == 1)
}
#[inline]
pub fn u64_at(&self, index: usize) -> Option<u64> {
if !self.is_defined_at(index) {
return None;
}
match self.type_code() {
ValueKind::Uint8 => unsafe { self.as_slice::<u64>()?.get(index).copied() },
ValueKind::Uint4 => unsafe { self.as_slice::<u32>()?.get(index).copied().map(u64::from) },
ValueKind::Uint2 => unsafe { self.as_slice::<u16>()?.get(index).copied().map(u64::from) },
ValueKind::Uint1 => unsafe { self.as_slice::<u8>()?.get(index).copied().map(u64::from) },
_ => None,
}
}
#[inline]
pub fn u32_at(&self, index: usize) -> Option<u32> {
if !self.is_defined_at(index) {
return None;
}
match self.type_code() {
ValueKind::Uint4 => unsafe { self.as_slice::<u32>()?.get(index).copied() },
ValueKind::Uint2 => unsafe { self.as_slice::<u16>()?.get(index).copied().map(u32::from) },
ValueKind::Uint1 => unsafe { self.as_slice::<u8>()?.get(index).copied().map(u32::from) },
_ => None,
}
}
#[inline]
pub fn u16_at(&self, index: usize) -> Option<u16> {
if !self.is_defined_at(index) {
return None;
}
match self.type_code() {
ValueKind::Uint2 => unsafe { self.as_slice::<u16>()?.get(index).copied() },
ValueKind::Uint1 => unsafe { self.as_slice::<u8>()?.get(index).copied().map(u16::from) },
_ => None,
}
}
#[inline]
pub fn u8_at(&self, index: usize) -> Option<u8> {
if self.type_code() != ValueKind::Uint1 || !self.is_defined_at(index) {
return None;
}
unsafe { self.as_slice::<u8>()?.get(index).copied() }
}
#[inline]
pub fn i64_at(&self, index: usize) -> Option<i64> {
if !self.is_defined_at(index) {
return None;
}
match self.type_code() {
ValueKind::Int8 => unsafe { self.as_slice::<i64>()?.get(index).copied() },
ValueKind::Int4 => unsafe { self.as_slice::<i32>()?.get(index).copied().map(i64::from) },
ValueKind::Int2 => unsafe { self.as_slice::<i16>()?.get(index).copied().map(i64::from) },
ValueKind::Int1 => unsafe { self.as_slice::<i8>()?.get(index).copied().map(i64::from) },
_ => None,
}
}
#[inline]
pub fn i32_at(&self, index: usize) -> Option<i32> {
if !self.is_defined_at(index) {
return None;
}
match self.type_code() {
ValueKind::Int4 => unsafe { self.as_slice::<i32>()?.get(index).copied() },
ValueKind::Int2 => unsafe { self.as_slice::<i16>()?.get(index).copied().map(i32::from) },
ValueKind::Int1 => unsafe { self.as_slice::<i8>()?.get(index).copied().map(i32::from) },
_ => None,
}
}
#[inline]
pub fn i16_at(&self, index: usize) -> Option<i16> {
if !self.is_defined_at(index) {
return None;
}
match self.type_code() {
ValueKind::Int2 => unsafe { self.as_slice::<i16>()?.get(index).copied() },
ValueKind::Int1 => unsafe { self.as_slice::<i8>()?.get(index).copied().map(i16::from) },
_ => None,
}
}
#[inline]
pub fn i8_at(&self, index: usize) -> Option<i8> {
if self.type_code() != ValueKind::Int1 || !self.is_defined_at(index) {
return None;
}
unsafe { self.as_slice::<i8>()?.get(index).copied() }
}
#[inline]
pub fn u128_at(&self, index: usize) -> Option<u128> {
if self.type_code() != ValueKind::Uint16 || !self.is_defined_at(index) {
return None;
}
unsafe { self.as_slice::<u128>()?.get(index).copied() }
}
#[inline]
pub fn i128_at(&self, index: usize) -> Option<i128> {
if self.type_code() != ValueKind::Int16 || !self.is_defined_at(index) {
return None;
}
unsafe { self.as_slice::<i128>()?.get(index).copied() }
}
#[inline]
pub fn f64_at(&self, index: usize) -> Option<f64> {
if !self.is_defined_at(index) {
return None;
}
match self.type_code() {
ValueKind::Float8 => unsafe { self.as_slice::<f64>()?.get(index).copied() },
ValueKind::Float4 => unsafe { self.as_slice::<f32>()?.get(index).copied().map(f64::from) },
_ => None,
}
}
#[inline]
pub fn f32_at(&self, index: usize) -> Option<f32> {
if self.type_code() != ValueKind::Float4 || !self.is_defined_at(index) {
return None;
}
unsafe { self.as_slice::<f32>()?.get(index).copied() }
}
#[inline]
pub fn date_at(&self, index: usize) -> Option<Date> {
if self.type_code() != ValueKind::Date || !self.is_defined_at(index) {
return None;
}
unsafe { self.as_slice::<Date>()?.get(index).copied() }
}
#[inline]
pub fn datetime_at(&self, index: usize) -> Option<DateTime> {
if self.type_code() != ValueKind::DateTime || !self.is_defined_at(index) {
return None;
}
unsafe { self.as_slice::<DateTime>()?.get(index).copied() }
}
#[inline]
pub fn time_at(&self, index: usize) -> Option<Time> {
if self.type_code() != ValueKind::Time || !self.is_defined_at(index) {
return None;
}
unsafe { self.as_slice::<Time>()?.get(index).copied() }
}
#[inline]
pub fn duration_at(&self, index: usize) -> Option<Duration> {
if self.type_code() != ValueKind::Duration || !self.is_defined_at(index) {
return None;
}
unsafe { self.as_slice::<Duration>()?.get(index).copied() }
}
}
unsafe fn read_buffer(buf: &ExternCBuffer) -> &[u8] {
if buf.ptr.is_null() || buf.len == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(buf.ptr, buf.len) }
}
}
unsafe fn read_buffer_str(buf: &ExternCBuffer) -> &str {
let bytes: &[u8] = unsafe { read_buffer(buf) };
str::from_utf8(bytes).unwrap_or("")
}
pub type DiffKind = DiffType;