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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2024-present, fjall-rs
// Copyright (c) 2026-present, Structured World Foundation
use Box;
use Arc;
/// Trait for custom user key comparison.
///
/// Comparators must be safe across unwind boundaries since they are stored
/// in tree structures that may be referenced inside `catch_unwind` blocks.
///
/// Implementations must define a **strict total order** suitable for use in
/// sorted data structures (memtable skip list, SST block index, merge heap).
/// Specifically:
///
/// - **Totality**: for all `a`, `b`, exactly one of `Less`, `Equal`, `Greater` holds
/// - **Transitivity**: `a < b` and `b < c` implies `a < c`
/// - **Antisymmetry**: `compare(a, b) == Less` iff `compare(b, a) == Greater`
/// - **Reflexivity**: `compare(a, a) == Equal`
///
/// - **Bytewise equality**: `compare(a, b) == Equal` **must** imply `a == b`
/// byte-for-byte. Bloom filters and hash indexes operate on raw bytes;
/// if two byte-different keys compare as equal, hash-based lookups will
/// produce false negatives.
///
/// Violating these invariants corrupts the sort order and produces incorrect
/// query results.
///
/// # Important
///
/// Once a tree is created with a comparator, it must always be opened with the
/// same comparator. The comparator's [`name`](UserComparator::name) is
/// persisted and checked on every subsequent open — a mismatch causes the open
/// to fail with [`Error::ComparatorMismatch`](crate::Error::ComparatorMismatch).
///
/// # Examples
///
/// ```
/// use lsm_tree::UserComparator;
/// use core::cmp::Ordering;
///
/// /// Comparator that orders u64 keys stored as big-endian bytes.
/// struct U64Comparator;
///
/// impl UserComparator for U64Comparator {
/// fn name(&self) -> &'static str {
/// "u64-big-endian"
/// }
///
/// fn compare(&self, a: &[u8], b: &[u8]) -> Ordering {
/// if a.len() == 8 && b.len() == 8 {
/// // Length checked, conversion cannot fail.
/// let a_u64 = u64::from_be_bytes(a.try_into().unwrap());
/// let b_u64 = u64::from_be_bytes(b.try_into().unwrap());
/// a_u64.cmp(&b_u64)
/// } else {
/// // Non-8-byte keys: fall back to lexicographic ordering
/// // to preserve the bytewise-equality invariant.
/// a.cmp(b)
/// }
/// }
/// }
/// ```
/// Whether two user keys are the SAME key — the single identity relation the
/// whole engine uses.
///
/// Identity is byte equality, and that is not a shortcut for ordering: the
/// [`UserComparator`] contract requires `compare(a, b) == Equal` to imply `a`
/// and `b` are byte-identical, so for any valid comparator the two relations are
/// the same one. Asking the comparator here would answer the same question at
/// the cost of a dynamic dispatch per entry on the read path.
///
/// The invariant is not a convention that could be relaxed. Filters and the
/// locator index are built over `hash64(user_key)` of the RAW bytes, so a
/// comparator that equated two different spellings would make a point lookup
/// for one of them hash to an entry the other never wrote — a false negative no
/// identity relation chosen here could repair. An engine cannot hash an
/// equivalence class it cannot enumerate.
///
/// Every place that decides "is this the same key as the previous one?" — MVCC
/// version collapsing on reads and in compaction, the writer's key count, the
/// row cache, the CDC event ordering, and salvage's suppression of a beheaded
/// version chain — goes through here, so the answer is stated once.
// `inline(always)`: the body is one slice compare, and this sits in the read
// path's per-entry loop — a call here would cost more than the comparison.
/// Default comparator using lexicographic byte ordering.
///
/// This is the comparator used when no custom comparator is configured,
/// preserving backward compatibility with existing trees.
;
/// Shared reference to a [`UserComparator`].
pub type SharedComparator = ;
/// Blanket impl so `Arc<T: UserComparator + ?Sized>` itself
/// satisfies the trait — used by callers that hold the
/// canonical [`SharedComparator`] (= `Arc<dyn UserComparator>`)
/// and want to pass it to generic APIs that take
/// `C: UserComparator + Clone`. The shared / dyn-dispatch cost
/// stays the same as before for those callers; the blanket only
/// removes a `.as_ref()` ceremony at every call site.
/// Maximum byte length for a comparator name.
///
/// Enforced on write (`persist_version`) and read (`Manifest::decode_from`).
pub const MAX_COMPARATOR_NAME_BYTES: usize = 256;
/// Returns the default comparator (lexicographic byte ordering).
///
/// Uses a shared static instance to avoid repeated allocations.