risingwave_common/types/sentinel.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
// Copyright 2024 RisingWave Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use enum_as_inner::EnumAsInner;
use risingwave_common_estimate_size::EstimateSize;
/// [`Sentinelled<T>`] wraps type `T` to provide smallest (smaller than any normal `T` value) and largest
/// (larger than ant normal `T` value) sentinel value for `T`.
///
/// Sentinel is a very common technique used to simplify tree/list/array algorithms. The main idea is to
/// insert sentinel node to the beginning or/and the end, so that algorithms don't need to handle complex
/// edge cases.
#[derive(Debug, Clone, PartialEq, Eq, EnumAsInner)]
pub enum Sentinelled<T> {
Smallest,
Normal(T),
Largest,
}
impl<T> Sentinelled<T> {
pub fn as_normal_expect(&self) -> &T {
self.as_normal().expect("expect normal key")
}
pub fn is_sentinel(&self) -> bool {
matches!(self, Self::Smallest | Self::Largest)
}
pub fn cmp_by(
&self,
other: &Self,
cmp_fn: impl FnOnce(&T, &T) -> std::cmp::Ordering,
) -> std::cmp::Ordering {
use Sentinelled::*;
match (self, other) {
(Smallest, Smallest) => std::cmp::Ordering::Equal,
(Smallest, _) => std::cmp::Ordering::Less,
(_, Smallest) => std::cmp::Ordering::Greater,
(Largest, Largest) => std::cmp::Ordering::Equal,
(Largest, _) => std::cmp::Ordering::Greater,
(_, Largest) => std::cmp::Ordering::Less,
(Normal(a), Normal(b)) => cmp_fn(a, b),
}
}
pub fn map<U>(self, map_fn: impl FnOnce(T) -> U) -> Sentinelled<U> {
use Sentinelled::*;
match self {
Smallest => Smallest,
Normal(inner) => Normal(map_fn(inner)),
Largest => Largest,
}
}
}
impl<T> From<T> for Sentinelled<T> {
fn from(inner: T) -> Self {
Self::Normal(inner)
}
}
impl<T> PartialOrd for Sentinelled<T>
where
T: Ord,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for Sentinelled<T>
where
T: Ord,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.cmp_by(other, T::cmp)
}
}
impl<T: EstimateSize> EstimateSize for Sentinelled<T> {
fn estimated_heap_size(&self) -> usize {
match self {
Self::Smallest => 0,
Self::Normal(inner) => inner.estimated_heap_size(),
Self::Largest => 0,
}
}
}