Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 66 additions & 12 deletions grovedb-element/src/element/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,13 +388,16 @@ impl Element {
/// tree's aggregate count when inserted. Sums (if any) still propagate.
///
/// Returns `InvalidInput` if `inner` is already wrapped in any wrapper
/// variant (`NonCounted` or `NotSummed`) — the wrappers are mutually
/// exclusive and may not nest in either direction. Use
/// `into_non_counted` to wrap idempotently when `inner` may already be
/// `NonCounted`; use that helper's `Result` return for the
/// variant (`NonCounted`, `NotSummed`, or `NotCountedOrSummed`) — the
/// wrappers are mutually exclusive and may not nest in either direction.
/// Use `into_non_counted` to wrap idempotently when `inner` may already
/// be `NonCounted`; use that helper's `Result` return for the
/// cross-wrapper case.
pub fn new_non_counted(inner: Element) -> Result<Self, ElementError> {
if matches!(inner, Element::NonCounted(_) | Element::NotSummed(_)) {
if matches!(
inner,
Element::NonCounted(_) | Element::NotSummed(_) | Element::NotCountedOrSummed(_)
) {
return Err(ElementError::InvalidInput(
"NonCounted cannot wrap another wrapper",
));
Expand All @@ -405,15 +408,19 @@ impl Element {
/// Wrap `self` in `NonCounted`. If `self` is already `NonCounted`,
/// returns it unchanged (idempotent on `NonCounted`).
///
/// Returns `InvalidInput` if `self` is `NotSummed` — the two wrappers
/// are mutually exclusive. Callers that need the unconditional wrapping
/// path should ensure the input is a non-wrapper variant before calling.
/// Returns `InvalidInput` if `self` is any other wrapper variant — the
/// wrappers are mutually exclusive. Callers that need the unconditional
/// wrapping path should ensure the input is a non-wrapper variant
/// before calling.
pub fn into_non_counted(self) -> Result<Self, ElementError> {
match self {
Element::NonCounted(_) => Ok(self),
Element::NotSummed(_) => Err(ElementError::InvalidInput(
"cannot wrap NotSummed in NonCounted; wrappers are mutually exclusive",
)),
Element::NotCountedOrSummed(_) => Err(ElementError::InvalidInput(
"cannot wrap NotCountedOrSummed in NonCounted; wrappers are mutually exclusive",
)),
other => Ok(Element::NonCounted(Box::new(other))),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
Expand All @@ -425,7 +432,8 @@ impl Element {
/// Only the four sum-tree variants are accepted: `SumTree`, `BigSumTree`,
/// `CountSumTree`, `ProvableCountSumTree`. Any other element — including
/// items, sum items, references, non-sum trees, and any wrapper
/// (`NonCounted`, `NotSummed`) — is rejected with `InvalidInput`.
/// (`NonCounted`, `NotSummed`, `NotCountedOrSummed`) — is rejected with
/// `InvalidInput`.
pub fn new_not_summed(inner: Element) -> Result<Self, ElementError> {
match inner {
Element::SumTree(..)
Expand All @@ -442,16 +450,62 @@ impl Element {
/// Wrap `self` in `NotSummed`. If `self` is already `NotSummed`, returns
/// it unchanged (idempotent on `NotSummed`).
///
/// Returns `InvalidInput` if `self` is `NonCounted` (the two wrappers
/// are mutually exclusive) or any non-sum-tree variant. Mirrors
/// [`Element::into_non_counted`].
/// Returns `InvalidInput` if `self` is any other wrapper (the three
/// wrappers are mutually exclusive) or any non-sum-tree variant.
/// Mirrors [`Element::into_non_counted`].
pub fn into_not_summed(self) -> Result<Self, ElementError> {
match self {
Element::NotSummed(_) => Ok(self),
Element::NonCounted(_) => Err(ElementError::InvalidInput(
"cannot wrap NonCounted in NotSummed; wrappers are mutually exclusive",
)),
Element::NotCountedOrSummed(_) => Err(ElementError::InvalidInput(
"cannot wrap NotCountedOrSummed in NotSummed; wrappers are mutually exclusive",
)),
other => Self::new_not_summed(other),
}
}

/// Wrap a sum-tree variant in `NotCountedOrSummed` so it contributes 0
/// to BOTH its parent's running sum AND its parent's count when
/// inserted.
///
/// Only the four sum-tree variants are accepted: `SumTree`, `BigSumTree`,
/// `CountSumTree`, `ProvableCountSumTree`. Any other element — including
/// items, sum items, references, non-sum trees, and any wrapper
/// (`NonCounted`, `NotSummed`, `NotCountedOrSummed`) — is rejected with
/// `InvalidInput`.
///
/// Note: at insert time the parent must be `CountSumTree` or
/// `ProvableCountSumTree`. The merk-layer insert guard enforces that.
pub fn new_not_counted_or_summed(inner: Element) -> Result<Self, ElementError> {
match inner {
Element::SumTree(..)
| Element::BigSumTree(..)
| Element::CountSumTree(..)
| Element::ProvableCountSumTree(..) => Ok(Element::NotCountedOrSummed(Box::new(inner))),
_ => Err(ElementError::InvalidInput(
"NotCountedOrSummed inner element must be a sum-tree variant (SumTree, \
BigSumTree, CountSumTree, or ProvableCountSumTree)",
)),
}
}

/// Wrap `self` in `NotCountedOrSummed`. If `self` is already
/// `NotCountedOrSummed`, returns it unchanged (idempotent).
///
/// Returns `InvalidInput` if `self` is any other wrapper (the three
/// wrappers are mutually exclusive) or any non-sum-tree variant.
pub fn into_not_counted_or_summed(self) -> Result<Self, ElementError> {
match self {
Element::NotCountedOrSummed(_) => Ok(self),
Element::NonCounted(_) => Err(ElementError::InvalidInput(
"cannot wrap NonCounted in NotCountedOrSummed; wrappers are mutually exclusive",
)),
Element::NotSummed(_) => Err(ElementError::InvalidInput(
"cannot wrap NotSummed in NotCountedOrSummed; wrappers are mutually exclusive",
)),
other => Self::new_not_counted_or_summed(other),
}
}
}
72 changes: 54 additions & 18 deletions grovedb-element/src/element/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,57 @@ impl Element {
matches!(self, Element::NotSummed(_))
}

/// Returns the wrapped element if `self` is a wrapper (`NonCounted` or
/// `NotSummed`), else `self`. Use this when you need to inspect the
/// actual element type and don't care whether it is wrapped.
/// Returns `true` if this element is wrapped in
/// `Element::NotCountedOrSummed`. The wrapper suppresses BOTH count
/// and sum propagation to the parent tree but leaves all other
/// behavior (storage, hashing, internal aggregation) unchanged.
pub fn is_not_counted_or_summed(&self) -> bool {
matches!(self, Element::NotCountedOrSummed(_))
}

/// Returns `true` if this element is wrapped in any of the wrapper
/// variants (`NonCounted`, `NotSummed`, `NotCountedOrSummed`). Useful
/// for paths that need to add the +1 wrapper-byte cost overhead
/// regardless of which wrapper is in use.
pub fn is_wrapped(&self) -> bool {
matches!(
self,
Element::NonCounted(_) | Element::NotSummed(_) | Element::NotCountedOrSummed(_)
)
}

/// Returns the wrapped element if `self` is any wrapper (`NonCounted`,
/// `NotSummed`, or `NotCountedOrSummed`), else `self`. Use this when
/// you need to inspect the actual element type and don't care whether
/// it is wrapped.
///
/// Only unwraps one level — the constructors and (de)serializers reject
/// any wrapper nesting, so a single unwrap is always sufficient.
pub fn underlying(&self) -> &Element {
match self {
Element::NonCounted(inner) | Element::NotSummed(inner) => inner,
Element::NonCounted(inner)
| Element::NotSummed(inner)
| Element::NotCountedOrSummed(inner) => inner,
other => other,
}
}

/// Mutable variant of [`underlying`].
pub fn underlying_mut(&mut self) -> &mut Element {
match self {
Element::NonCounted(inner) | Element::NotSummed(inner) => inner,
Element::NonCounted(inner)
| Element::NotSummed(inner)
| Element::NotCountedOrSummed(inner) => inner,
other => other,
}
}

/// Owned variant of [`underlying`].
pub fn into_underlying(self) -> Element {
match self {
Element::NonCounted(inner) | Element::NotSummed(inner) => *inner,
Element::NonCounted(inner)
| Element::NotSummed(inner)
| Element::NotCountedOrSummed(inner) => *inner,
other => other,
}
}
Expand All @@ -61,12 +87,12 @@ impl Element {
///
/// `NonCounted` delegates to its inner element — sums still propagate
/// when the wrapper is inserted into a sum-bearing parent.
/// `NotSummed` returns 0 — the wrapper's whole purpose is to contribute
/// nothing to the parent sum tree.
/// `NotSummed` and `NotCountedOrSummed` return 0 — the wrappers'
/// purpose is to contribute nothing to the parent sum tree.
pub fn sum_value_or_default(&self) -> i64 {
match self {
Element::NonCounted(inner) => inner.sum_value_or_default(),
Element::NotSummed(_) => 0,
Element::NotSummed(_) | Element::NotCountedOrSummed(_) => 0,
Element::SumItem(sum_value, _)
| Element::ItemWithSumItem(_, sum_value, _)
| Element::SumTree(_, sum_value, _)
Expand All @@ -79,12 +105,12 @@ impl Element {
/// Decoded the integer value in the CountTree element type, returns 1 for
/// everything else.
///
/// `NonCounted` returns 0 — the wrapper's whole purpose is to contribute
/// nothing to the parent count tree.
/// `NonCounted` and `NotCountedOrSummed` return 0 — both wrappers
/// suppress the parent count contribution.
/// `NotSummed` delegates to its inner — counts still propagate.
pub fn count_value_or_default(&self) -> u64 {
match self {
Element::NonCounted(_) => 0,
Element::NonCounted(_) | Element::NotCountedOrSummed(_) => 0,
Element::NotSummed(inner) => inner.count_value_or_default(),
Element::CountTree(_, count_value, _)
| Element::CountSumTree(_, count_value, ..)
Expand All @@ -101,10 +127,12 @@ impl Element {
/// propagates.
/// `NotSummed` returns `(inner_count, 0)` — sum is suppressed, count
/// still propagates.
/// `NotCountedOrSummed` returns `(0, 0)` — both are suppressed.
pub fn count_sum_value_or_default(&self) -> (u64, i64) {
match self {
Element::NonCounted(inner) => (0, inner.sum_value_or_default()),
Element::NotSummed(inner) => (inner.count_value_or_default(), 0),
Element::NotCountedOrSummed(_) => (0, 0),
Element::SumItem(sum_value, _)
| Element::ItemWithSumItem(_, sum_value, _)
| Element::SumTree(_, sum_value, _) => (1, *sum_value),
Expand All @@ -120,11 +148,11 @@ impl Element {

/// Decoded the integer value in the SumItem element type, returns 0 for
/// everything else. `NonCounted` delegates to its inner. `NotSummed`
/// returns 0.
/// and `NotCountedOrSummed` return 0.
pub fn big_sum_value_or_default(&self) -> i128 {
match self {
Element::NonCounted(inner) => inner.big_sum_value_or_default(),
Element::NotSummed(_) => 0,
Element::NotSummed(_) | Element::NotCountedOrSummed(_) => 0,
Element::SumItem(sum_value, _)
| Element::ItemWithSumItem(_, sum_value, _)
| Element::SumTree(_, sum_value, _)
Expand Down Expand Up @@ -394,7 +422,9 @@ impl Element {
| Element::MmrTree(.., flags)
| Element::BulkAppendTree(.., flags)
| Element::DenseAppendOnlyFixedSizeTree(.., flags) => flags,
Element::NonCounted(inner) | Element::NotSummed(inner) => inner.get_flags(),
Element::NonCounted(inner)
| Element::NotSummed(inner)
| Element::NotCountedOrSummed(inner) => inner.get_flags(),
}
}

Expand All @@ -417,7 +447,9 @@ impl Element {
| Element::MmrTree(.., flags)
| Element::BulkAppendTree(.., flags)
| Element::DenseAppendOnlyFixedSizeTree(.., flags) => flags,
Element::NonCounted(inner) | Element::NotSummed(inner) => inner.get_flags_owned(),
Element::NonCounted(inner)
| Element::NotSummed(inner)
| Element::NotCountedOrSummed(inner) => inner.get_flags_owned(),
}
}

Expand All @@ -440,7 +472,9 @@ impl Element {
| Element::MmrTree(.., flags)
| Element::BulkAppendTree(.., flags)
| Element::DenseAppendOnlyFixedSizeTree(.., flags) => flags,
Element::NonCounted(inner) | Element::NotSummed(inner) => inner.get_flags_mut(),
Element::NonCounted(inner)
| Element::NotSummed(inner)
| Element::NotCountedOrSummed(inner) => inner.get_flags_mut(),
}
}

Expand All @@ -463,7 +497,9 @@ impl Element {
| Element::MmrTree(.., flags)
| Element::BulkAppendTree(.., flags)
| Element::DenseAppendOnlyFixedSizeTree(.., flags) => *flags = new_flags,
Element::NonCounted(inner) | Element::NotSummed(inner) => inner.set_flags(new_flags),
Element::NonCounted(inner)
| Element::NotSummed(inner)
| Element::NotCountedOrSummed(inner) => inner.set_flags(new_flags),
}
}

Expand Down
Loading
Loading