use crate::{
db::executor::group::{StableHash, stable_hash_value, try_reserve_hash_set_entry},
error::InternalError,
value::Value,
};
use std::{
borrow::Borrow,
cmp::Ordering,
collections::HashSet,
hash::{Hash, Hasher},
};
#[derive(Clone, Debug, Eq, PartialEq)]
struct CanonicalValue(Value);
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db::executor) struct GroupKey {
raw: CanonicalValue,
hash: StableHash,
}
impl Hash for GroupKey {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.hash);
}
}
impl GroupKey {
pub(in crate::db::executor) fn from_value_with_hash(
value: &Value,
hash: StableHash,
) -> Result<Self, InternalError> {
Ok(Self::from_raw_with_hash(canonicalize_value(value)?, hash))
}
pub(in crate::db::executor) fn from_canonical_value(raw: Value) -> Result<Self, InternalError> {
let hash = stable_hash_value(&raw)?;
Ok(Self::from_raw_with_hash(raw, hash))
}
const fn from_raw_with_hash(raw: Value, hash: StableHash) -> Self {
Self {
raw: CanonicalValue(raw),
hash,
}
}
#[must_use]
pub(in crate::db::executor) const fn hash(&self) -> StableHash {
self.hash
}
#[must_use]
pub(in crate::db::executor) const fn canonical_value(&self) -> &Value {
&self.raw.0
}
pub(in crate::db::executor) fn into_canonical_value(self) -> Value {
self.raw.0
}
pub(in crate::db::executor) fn from_group_values(
group_values: Vec<Value>,
) -> Result<Self, InternalError> {
let canonical = canonicalize_owned_value(Value::List(group_values))?;
Self::from_canonical_value(canonical)
}
pub(in crate::db::executor) fn from_group_values_with_hash(
group_values: Vec<Value>,
hash: StableHash,
) -> Result<Self, InternalError> {
let canonical = canonicalize_owned_value(Value::List(group_values))?;
Ok(Self::from_raw_with_hash(canonical, hash))
}
pub(in crate::db::executor) fn from_single_group_value(
group_value: Value,
) -> Result<Self, InternalError> {
let canonical_group_value = canonicalize_owned_value(group_value)?;
Self::from_canonical_value(Value::List(vec![canonical_group_value]))
}
pub(in crate::db::executor) fn from_single_group_value_with_hash(
group_value: Value,
hash: StableHash,
) -> Result<Self, InternalError> {
let canonical_group_value = canonicalize_owned_value(group_value)?;
Ok(Self::from_raw_with_hash(
Value::List(vec![canonical_group_value]),
hash,
))
}
pub(in crate::db::executor) fn from_single_canonical_group_value(
group_value: Value,
) -> Result<Self, InternalError> {
Self::from_canonical_value(Value::List(vec![group_value]))
}
pub(in crate::db::executor) fn from_single_canonical_group_value_with_hash(
group_value: Value,
hash: StableHash,
) -> Self {
Self::from_raw_with_hash(Value::List(vec![group_value]), hash)
}
}
#[derive(Debug)]
pub(in crate::db::executor) struct GroupKeySet {
keys: HashSet<GroupKey>,
}
trait GroupKeyIdentity {
fn identity(&self) -> (StableHash, &Value);
}
impl GroupKeyIdentity for GroupKey {
fn identity(&self) -> (StableHash, &Value) {
(self.hash, self.canonical_value())
}
}
impl GroupKeyIdentity for (StableHash, &Value) {
fn identity(&self) -> (StableHash, &Value) {
*self
}
}
impl Eq for dyn GroupKeyIdentity + '_ {}
impl Hash for dyn GroupKeyIdentity + '_ {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.identity().0);
}
}
impl PartialEq for dyn GroupKeyIdentity + '_ {
fn eq(&self, other: &Self) -> bool {
let (left_hash, left) = self.identity();
let (right_hash, right) = other.identity();
left_hash == right_hash && compare_borrowed_group_values(left, right).is_eq()
}
}
impl<'a> Borrow<dyn GroupKeyIdentity + 'a> for GroupKey {
fn borrow(&self) -> &(dyn GroupKeyIdentity + 'a) {
self
}
}
impl GroupKeySet {
#[must_use]
pub(in crate::db::executor) fn new() -> Self {
Self {
keys: HashSet::new(),
}
}
pub(in crate::db::executor) fn contains_value(&self, value: &Value, hash: StableHash) -> bool {
self.keys.contains(&(hash, value) as &dyn GroupKeyIdentity)
}
#[must_use]
pub(in crate::db::executor) fn len(&self) -> usize {
self.keys.len()
}
pub(in crate::db::executor) fn insert_key(
&mut self,
key: GroupKey,
) -> Result<bool, InternalError> {
if self.keys.contains(&key) {
return Ok(false);
}
try_reserve_hash_set_entry(&mut self.keys)?;
Ok(self.keys.insert(key))
}
}
impl Default for GroupKeySet {
fn default() -> Self {
Self::new()
}
}
fn canonicalize_value(value: &Value) -> Result<Value, InternalError> {
match value {
Value::Decimal(decimal) => Ok(Value::Decimal(decimal.normalize())),
Value::List(items) => items
.iter()
.map(canonicalize_value)
.collect::<Result<Vec<_>, _>>()
.map(Value::List),
Value::Map(entries) => canonicalize_map_entries(entries),
_ => Ok(value.clone()),
}
}
fn canonicalize_map_entries(entries: &[(Value, Value)]) -> Result<Value, InternalError> {
normalize_canonical_map_entries(
entries
.iter()
.map(|(key, value)| Ok((canonicalize_value(key)?, canonicalize_value(value)?))),
)
}
pub(in crate::db::executor) fn compare_borrowed_group_values(
left: &Value,
right: &Value,
) -> Ordering {
match (left, right) {
(Value::List(left), Value::List(right)) => {
compare_borrowed_group_sequences(left.iter(), right.iter())
}
(Value::Map(left), Value::Map(right)) => compare_borrowed_group_sequences(
Value::ordered_map_entries(left).flat_map(|(key, value)| [key, value]),
Value::ordered_map_entries(right).flat_map(|(key, value)| [key, value]),
),
_ => Value::canonical_cmp(left, right),
}
}
fn compare_borrowed_group_sequences<'left, 'right>(
mut left: impl Iterator<Item = &'left Value>,
mut right: impl Iterator<Item = &'right Value>,
) -> Ordering {
loop {
match (left.next(), right.next()) {
(Some(left), Some(right)) => {
let ordering = compare_borrowed_group_values(left, right);
if !ordering.is_eq() {
return ordering;
}
}
(Some(_), None) => return Ordering::Greater,
(None, Some(_)) => return Ordering::Less,
(None, None) => return Ordering::Equal,
}
}
}
pub(in crate::db::executor) fn validate_borrowed_canonical_value(
value: &Value,
) -> Result<(), InternalError> {
match value {
Value::List(items) => {
for item in items {
validate_borrowed_canonical_value(item)?;
}
}
Value::Map(entries) => {
Value::validate_canonical_map_entries(entries)
.map_err(|_| InternalError::executor_invariant())?;
for (_, value) in entries {
validate_borrowed_canonical_value(value)?;
}
}
_ => {}
}
Ok(())
}
pub(in crate::db::executor) fn canonicalize_owned_value(
value: Value,
) -> Result<Value, InternalError> {
match value {
Value::Decimal(decimal) => Ok(Value::Decimal(decimal.normalize())),
Value::List(items) => items
.into_iter()
.map(canonicalize_owned_value)
.collect::<Result<Vec<_>, _>>()
.map(Value::List),
Value::Map(entries) => canonicalize_owned_map_entries(entries),
value => Ok(value),
}
}
fn canonicalize_owned_map_entries(entries: Vec<(Value, Value)>) -> Result<Value, InternalError> {
normalize_canonical_map_entries(entries.into_iter().map(|(key, value)| {
Ok((
canonicalize_owned_value(key)?,
canonicalize_owned_value(value)?,
))
}))
}
fn normalize_canonical_map_entries(
entries: impl IntoIterator<Item = Result<(Value, Value), InternalError>>,
) -> Result<Value, InternalError> {
let canonical_entries = entries.into_iter().collect::<Result<Vec<_>, _>>()?;
let normalized = Value::normalize_map_entries(canonical_entries)
.map_err(|_| InternalError::executor_invariant())?;
Ok(Value::Map(normalized))
}
#[cfg(test)]
mod tests;