mod error;
mod normalize;
mod parts;
mod semantics;
#[cfg(test)]
use crate::db::numeric::compare_numeric_or_strict_order;
use crate::{
db::{data::ScalarValueRef, index::key::ordered::semantics::OrderedEncode},
types::{Account, Principal, Subaccount, Timestamp, Ulid},
value::{StorageKey, Value},
};
#[cfg(test)]
use std::cmp::Ordering;
pub(crate) use error::OrderedValueEncodeError;
const NEGATIVE_MARKER: u8 = 0x00;
const ZERO_MARKER: u8 = 0x01;
const POSITIVE_MARKER: u8 = 0x02;
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct EncodedValue {
raw: Value,
encoded: Vec<u8>,
}
impl EncodedValue {
pub(crate) fn try_new(raw: Value) -> Result<Self, OrderedValueEncodeError> {
let encoded = encode_canonical_index_component(&raw)?;
Ok(Self { raw, encoded })
}
pub(crate) fn try_from_ref(raw: &Value) -> Result<Self, OrderedValueEncodeError> {
Self::try_new(raw.clone())
}
pub(crate) fn try_encode_all(values: &[Value]) -> Result<Vec<Self>, OrderedValueEncodeError> {
values.iter().map(Self::try_from_ref).collect()
}
#[must_use]
pub(crate) const fn encoded(&self) -> &[u8] {
self.encoded.as_slice()
}
}
impl AsRef<[u8]> for EncodedValue {
fn as_ref(&self) -> &[u8] {
self.encoded()
}
}
#[must_use]
#[cfg(test)]
pub(crate) fn compare_index_component_values(left: &Value, right: &Value) -> Ordering {
if std::mem::discriminant(left) == std::mem::discriminant(right)
&& let Some(ordering) = compare_numeric_or_strict_order(left, right)
{
return ordering;
}
Value::canonical_cmp_key(left, right)
}
pub(crate) fn encode_canonical_index_component(
value: &Value,
) -> Result<Vec<u8>, OrderedValueEncodeError> {
let mut out = Vec::new();
out.push(value.canonical_tag().to_u8());
encode_component_payload(&mut out, value)?;
Ok(out)
}
pub(crate) fn encode_canonical_index_component_from_scalar(
value: ScalarValueRef<'_>,
) -> Result<Vec<u8>, OrderedValueEncodeError> {
let mut out = Vec::new();
out.push(match value {
ScalarValueRef::Blob(_) => Value::Blob(Vec::new()).canonical_tag().to_u8(),
ScalarValueRef::Bool(_) => Value::Bool(false).canonical_tag().to_u8(),
ScalarValueRef::Date(_) => Value::Date(crate::types::Date::EPOCH)
.canonical_tag()
.to_u8(),
ScalarValueRef::Duration(_) => Value::Duration(crate::types::Duration::ZERO)
.canonical_tag()
.to_u8(),
ScalarValueRef::Float32(_) => Value::Float32(crate::types::Float32::default())
.canonical_tag()
.to_u8(),
ScalarValueRef::Float64(_) => Value::Float64(crate::types::Float64::default())
.canonical_tag()
.to_u8(),
ScalarValueRef::Int(_) => Value::Int(0).canonical_tag().to_u8(),
ScalarValueRef::Principal(_) => Value::Principal(crate::types::Principal::default())
.canonical_tag()
.to_u8(),
ScalarValueRef::Subaccount(_) => Value::Subaccount(crate::types::Subaccount::default())
.canonical_tag()
.to_u8(),
ScalarValueRef::Text(_) => Value::Text(String::new()).canonical_tag().to_u8(),
ScalarValueRef::Timestamp(_) => Value::Timestamp(crate::types::Timestamp::EPOCH)
.canonical_tag()
.to_u8(),
ScalarValueRef::Uint(_) => Value::Uint(0).canonical_tag().to_u8(),
ScalarValueRef::Ulid(_) => Value::Ulid(crate::types::Ulid::nil())
.canonical_tag()
.to_u8(),
ScalarValueRef::Unit => Value::Unit.canonical_tag().to_u8(),
});
match value {
ScalarValueRef::Blob(_) => {
Err(OrderedValueEncodeError::UnsupportedValueKind { kind: "Blob" })
}
ScalarValueRef::Bool(value) => {
out.push(u8::from(value));
Ok(out)
}
ScalarValueRef::Date(value) => {
value.encode_ordered(&mut out)?;
Ok(out)
}
ScalarValueRef::Duration(value) => {
value.encode_ordered(&mut out)?;
Ok(out)
}
ScalarValueRef::Float32(value) => {
out.extend_from_slice(&semantics::ordered_f32_bytes(value.get()));
Ok(out)
}
ScalarValueRef::Float64(value) => {
out.extend_from_slice(&semantics::ordered_f64_bytes(value.get()));
Ok(out)
}
ScalarValueRef::Int(value) => {
out.extend_from_slice(&semantics::ordered_i64_bytes(value));
Ok(out)
}
ScalarValueRef::Principal(value) => {
parts::push_terminated_bytes(&mut out, value.as_slice());
Ok(out)
}
ScalarValueRef::Subaccount(value) => {
out.extend_from_slice(&value.to_bytes());
Ok(out)
}
ScalarValueRef::Text(value) => {
parts::push_terminated_bytes(&mut out, value.as_bytes());
Ok(out)
}
ScalarValueRef::Timestamp(value) => {
value.encode_ordered(&mut out)?;
Ok(out)
}
ScalarValueRef::Uint(value) => {
out.extend_from_slice(&value.to_be_bytes());
Ok(out)
}
ScalarValueRef::Ulid(value) => {
out.extend_from_slice(&value.to_bytes());
Ok(out)
}
ScalarValueRef::Unit => Ok(out),
}
}
pub(crate) fn encode_canonical_index_component_from_storage_key(
value: StorageKey,
) -> Result<Vec<u8>, OrderedValueEncodeError> {
let mut out = Vec::new();
out.push(match value {
StorageKey::Account(_) => {
Value::Account(Account::from_parts(Principal::from_slice(&[]), None))
.canonical_tag()
.to_u8()
}
StorageKey::Int(_) => Value::Int(0).canonical_tag().to_u8(),
StorageKey::Principal(_) => Value::Principal(Principal::default())
.canonical_tag()
.to_u8(),
StorageKey::Subaccount(_) => Value::Subaccount(Subaccount::default())
.canonical_tag()
.to_u8(),
StorageKey::Timestamp(_) => Value::Timestamp(Timestamp::EPOCH).canonical_tag().to_u8(),
StorageKey::Uint(_) => Value::Uint(0).canonical_tag().to_u8(),
StorageKey::Ulid(_) => Value::Ulid(Ulid::nil()).canonical_tag().to_u8(),
StorageKey::Unit => Value::Unit.canonical_tag().to_u8(),
});
match value {
StorageKey::Account(value) => {
parts::push_account_payload(&mut out, &value)?;
Ok(out)
}
StorageKey::Int(value) => {
out.extend_from_slice(&semantics::ordered_i64_bytes(value));
Ok(out)
}
StorageKey::Principal(value) => {
parts::push_terminated_bytes(&mut out, value.as_slice());
Ok(out)
}
StorageKey::Subaccount(value) => {
out.extend_from_slice(&value.to_bytes());
Ok(out)
}
StorageKey::Timestamp(value) => {
value.encode_ordered(&mut out)?;
Ok(out)
}
StorageKey::Uint(value) => {
out.extend_from_slice(&value.to_be_bytes());
Ok(out)
}
StorageKey::Ulid(value) => {
out.extend_from_slice(&value.to_bytes());
Ok(out)
}
StorageKey::Unit => Ok(out),
}
}
fn encode_component_payload(
out: &mut Vec<u8>,
value: &Value,
) -> Result<(), OrderedValueEncodeError> {
match value {
Value::Account(v) => parts::push_account_payload(out, v),
Value::Blob(_) | Value::List(_) | Value::Map(_) => {
Err(OrderedValueEncodeError::UnsupportedValueKind {
kind: value.canonical_tag().label(),
})
}
Value::Bool(v) => {
out.push(u8::from(*v));
Ok(())
}
Value::Date(v) => v.encode_ordered(out),
Value::Decimal(v) => normalize::push_decimal_payload(out, *v),
Value::Duration(v) => v.encode_ordered(out),
Value::Enum(v) => parts::push_enum_payload(out, v),
Value::Float32(v) => {
out.extend_from_slice(&semantics::ordered_f32_bytes(v.get()));
Ok(())
}
Value::Float64(v) => {
out.extend_from_slice(&semantics::ordered_f64_bytes(v.get()));
Ok(())
}
Value::Int(v) => {
out.extend_from_slice(&semantics::ordered_i64_bytes(*v));
Ok(())
}
Value::Int128(v) => v.encode_ordered(out),
Value::IntBig(v) => normalize::push_signed_bigint_payload(out, v),
Value::Null => Err(OrderedValueEncodeError::NullNotIndexable),
Value::Principal(v) => {
parts::push_terminated_bytes(out, v.as_slice());
Ok(())
}
Value::Subaccount(v) => {
out.extend_from_slice(&v.to_bytes());
Ok(())
}
Value::Text(v) => {
parts::push_terminated_bytes(out, v.as_bytes());
Ok(())
}
Value::Timestamp(v) => v.encode_ordered(out),
Value::Uint(v) => {
out.extend_from_slice(&v.to_be_bytes());
Ok(())
}
Value::Uint128(v) => v.encode_ordered(out),
Value::UintBig(v) => normalize::push_unsigned_bigint_payload(out, v),
Value::Ulid(v) => {
out.extend_from_slice(&v.to_bytes());
Ok(())
}
Value::Unit => Ok(()),
}
}