mod spatial;
mod table;
mod tensor;
mod text;
mod timestamp;
pub use spatial::{BoundingBox, BoundingBox3D, Geometry, Point, Point3D};
pub use table::{ColumnDef, ColumnType, IndexDef, IndexType, TTLDuration, TableSchema, TableType};
pub use tensor::Tensor;
pub use text::{Text, TextDoc};
pub use timestamp::Timestamp;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq)]
pub struct ArcVec(pub Arc<Vec<f32>>);
impl ArcVec {
pub fn new(vec: Vec<f32>) -> Self {
ArcVec(Arc::new(vec))
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, f32> {
self.0.iter()
}
pub fn to_vec(&self) -> Vec<f32> {
(*self.0).clone()
}
pub fn as_slice(&self) -> &[f32] {
self.0.as_ref()
}
}
impl Serialize for ArcVec {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.as_ref().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ArcVec {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let vec = Vec::<f32>::deserialize(deserializer)?;
Ok(ArcVec(Arc::new(vec)))
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct ArcString(pub Arc<str>);
impl ArcString {
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::ops::Deref for ArcString {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Serialize for ArcString {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ArcString {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
Ok(ArcString(Arc::from(s)))
}
}
impl std::fmt::Display for ArcString {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl PartialOrd for ArcString {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl From<String> for ArcString {
fn from(s: String) -> Self {
ArcString(Arc::from(s))
}
}
impl From<&str> for ArcString {
fn from(s: &str) -> Self {
ArcString(Arc::from(s))
}
}
impl PartialEq<String> for ArcString {
fn eq(&self, other: &String) -> bool {
&*self.0 == other.as_str()
}
}
impl PartialEq<&str> for ArcString {
fn eq(&self, other: &&str) -> bool {
&*self.0 == *other
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Value {
Integer(i64),
Float(f64),
Bool(bool),
Text(ArcString),
Vector(ArcVec),
Tensor(Box<Tensor>),
Spatial(Box<Geometry>),
TextDoc(Box<Text>),
Timestamp(Timestamp),
Null,
}
fn int_float_cmp(i: i64, f: f64) -> Option<std::cmp::Ordering> {
let f_trunc = f.trunc();
if f != f_trunc {
return (i as f64).partial_cmp(&f);
}
if f_trunc >= i64::MIN as f64 && f_trunc <= i64::MAX as f64 {
let f_as_i = f_trunc as i64;
return Some(i.cmp(&f_as_i));
}
(i as f64).partial_cmp(&f)
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self, other) {
(Value::Null, Value::Null) => Some(std::cmp::Ordering::Equal),
(Value::Null, _) => Some(std::cmp::Ordering::Less),
(_, Value::Null) => Some(std::cmp::Ordering::Greater),
(Value::Integer(a), Value::Integer(b)) => a.partial_cmp(b),
(Value::Float(a), Value::Float(b)) => a.partial_cmp(b),
(Value::Text(a), Value::Text(b)) => a.partial_cmp(b),
(Value::Bool(a), Value::Bool(b)) => a.partial_cmp(b),
(Value::Timestamp(a), Value::Timestamp(b)) => a.partial_cmp(b),
(Value::Integer(a), Value::Float(b)) => int_float_cmp(*a, *b),
(Value::Float(a), Value::Integer(b)) => int_float_cmp(*b, *a).map(|o| o.reverse()),
(Value::Timestamp(a), Value::Integer(b)) => a.as_micros().partial_cmp(b),
(Value::Integer(a), Value::Timestamp(b)) => a.partial_cmp(&b.as_micros()),
(Value::Timestamp(a), Value::Float(b)) => int_float_cmp(a.as_micros(), *b),
(Value::Float(a), Value::Timestamp(b)) => {
int_float_cmp(b.as_micros(), *a).map(|o| o.reverse())
}
(Value::Timestamp(a), Value::Text(s)) => crate::types::Timestamp::parse_iso(s.as_str())
.and_then(|t| a.as_micros().partial_cmp(&t.as_micros())),
(Value::Text(s), Value::Timestamp(b)) => crate::types::Timestamp::parse_iso(s.as_str())
.and_then(|t| t.as_micros().partial_cmp(&b.as_micros())),
_ => None,
}
}
}
fn float_eq(a: f64, b: f64) -> bool {
if a.is_nan() && b.is_nan() {
return true;
}
if a == 0.0 && b == 0.0 {
return true;
}
a.to_bits() == b.to_bits()
}
fn canonical_float_bits(f: f64) -> u64 {
if f.is_nan() {
return u64::MAX;
}
if f == 0.0 {
return 0.0f64.to_bits();
}
f.to_bits()
}
fn int_float_eq(i: i64, f: f64) -> bool {
let f_trunc = f.trunc();
if f != f_trunc {
return float_eq(i as f64, f);
}
if f_trunc >= i64::MIN as f64 && f_trunc <= i64::MAX as f64 {
return i == f_trunc as i64;
}
float_eq(i as f64, f)
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Integer(a), Value::Integer(b)) => a == b,
(Value::Float(a), Value::Float(b)) => float_eq(*a, *b),
(Value::Text(a), Value::Text(b)) => a == b,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Null, Value::Null) => true,
(Value::Timestamp(a), Value::Timestamp(b)) => a == b,
(Value::Integer(a), Value::Float(b)) => int_float_eq(*a, *b),
(Value::Float(a), Value::Integer(b)) => int_float_eq(*b, *a),
(Value::Timestamp(a), Value::Integer(b)) => a.as_micros() == *b,
(Value::Integer(a), Value::Timestamp(b)) => *a == b.as_micros(),
(Value::Timestamp(a), Value::Float(b)) => float_eq(a.as_micros() as f64, *b),
(Value::Float(a), Value::Timestamp(b)) => float_eq(*a, b.as_micros() as f64),
(Value::Timestamp(a), Value::Text(s)) => crate::types::Timestamp::parse_iso(s.as_str())
.is_some_and(|t| a.as_micros() == t.as_micros()),
(Value::Text(s), Value::Timestamp(b)) => crate::types::Timestamp::parse_iso(s.as_str())
.is_some_and(|t| t.as_micros() == b.as_micros()),
_ => false,
}
}
}
impl Eq for Value {}
impl std::hash::Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Value::Integer(i) => {
let f = *i as f64;
state.write_u8(0); canonical_float_bits(f).hash(state);
}
Value::Float(f) => {
state.write_u8(0); canonical_float_bits(*f).hash(state);
}
Value::Text(s) => {
state.write_u8(1);
s.hash(state);
}
Value::Bool(b) => {
state.write_u8(2);
b.hash(state);
}
Value::Null => {
state.write_u8(3);
}
Value::Timestamp(t) => {
let micros = t.as_micros();
let f = micros as f64;
state.write_u8(0); canonical_float_bits(f).hash(state);
}
other => {
state.write_u8(5);
format!("{:?}", other).hash(state);
}
}
}
}
impl Value {
pub fn text(s: String) -> Self {
Value::Text(ArcString(Arc::from(s)))
}
pub fn text_from(s: &str) -> Self {
Value::Text(ArcString(Arc::from(s)))
}
pub fn tensor(t: Tensor) -> Self {
Value::Tensor(Box::new(t))
}
pub fn spatial(g: Geometry) -> Self {
Value::Spatial(Box::new(g))
}
pub fn textdoc(t: Text) -> Self {
Value::TextDoc(Box::new(t))
}
pub fn to_hash_key(&self) -> String {
match self {
Value::Integer(i) => format!("i:{}", i),
Value::Float(f) => format!("f:{}", f.to_bits()),
Value::Text(s) => format!("t:{}", s),
Value::Bool(b) => format!("b:{}", b),
Value::Timestamp(t) => format!("ts:{}", t.as_micros()),
_ => format!("{:?}", self),
}
}
}
pub type Row = Vec<Value>;
pub type SqlRow = std::collections::HashMap<String, Value>;
pub type RowId = u64;
pub type PartitionId = u8;