1use std::{
5 collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque},
6 mem,
7 ops::Add,
8 sync::Arc,
9};
10
11use reifydb_codec::{
12 key::encoded::EncodedKey,
13 row::{bytes::EncodedBytes, shape::fingerprint::RowShapeFingerprint},
14};
15pub use reifydb_macro::HeapSize;
16use reifydb_value::{
17 byte_size::ByteSize,
18 count::Count,
19 util::hash::Hash128,
20 value::{
21 Value,
22 column_view::{ColumnView, ViewData},
23 container::{
24 bool_array, decimal_array::DecimalView, digest_array, fixed_array, primitive, varlen_array,
25 },
26 date::Date,
27 datetime::DateTime,
28 decimal::Decimal,
29 dictionary::DictionaryEntryId,
30 digest::Digest,
31 duration::Duration,
32 identity::IdentityId,
33 ordered_f32::OrderedF32,
34 ordered_f64::OrderedF64,
35 partition::Partition,
36 row_number::RowNumber,
37 time::Time,
38 uuid::{Uuid4, Uuid7},
39 },
40};
41
42use crate::{
43 key::any::TaggedKey,
44 state::{join::ContentVersion, timer::TimerKind},
45 value::index::encoded::EncodedIndexKey,
46};
47
48pub trait HeapSize {
49 fn heap_size(&self) -> usize;
50}
51
52#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
53pub struct StateMemory {
54 pub entries: Count,
55 pub bytes: ByteSize,
56}
57
58impl StateMemory {
59 pub const ZERO: Self = Self {
60 entries: Count::ZERO,
61 bytes: ByteSize::ZERO,
62 };
63
64 pub fn new(entries: Count, bytes: ByteSize) -> Self {
65 Self {
66 entries,
67 bytes,
68 }
69 }
70}
71
72impl Add for StateMemory {
73 type Output = Self;
74
75 fn add(self, rhs: Self) -> Self {
76 Self {
77 entries: self.entries + rhs.entries,
78 bytes: self.bytes + rhs.bytes,
79 }
80 }
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub struct StateCompleteness {
85 pub values_complete: bool,
86 pub membership_complete: bool,
87 pub absences_served: Count,
88 pub false_positives: Count,
89 pub revocations: Count,
90}
91
92impl StateCompleteness {
93 pub fn merge(self, rhs: Self) -> Self {
94 Self {
95 values_complete: self.values_complete && rhs.values_complete,
96 membership_complete: self.membership_complete && rhs.membership_complete,
97 absences_served: self.absences_served + rhs.absences_served,
98 false_positives: self.false_positives + rhs.false_positives,
99 revocations: self.revocations + rhs.revocations,
100 }
101 }
102}
103
104#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
105pub struct OperatorSample {
106 pub memory: Option<StateMemory>,
107 pub row_number_cache: Option<StateMemory>,
108}
109
110impl OperatorSample {
111 pub fn with_memory(memory: StateMemory) -> Self {
112 Self {
113 memory: Some(memory),
114 ..Self::default()
115 }
116 }
117
118 pub fn with_row_number_cache(mut self, memory: StateMemory) -> Self {
119 self.row_number_cache = Some(memory);
120 self
121 }
122}
123
124macro_rules! zero_heap {
125 ($($ty:ty),* $(,)?) => {
126 $(impl HeapSize for $ty {
127 fn heap_size(&self) -> usize {
128 0
129 }
130 })*
131 };
132}
133
134zero_heap!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, bool, char, ());
135zero_heap!(OrderedF32, OrderedF64, Date, DateTime, Time, Duration, IdentityId, Uuid4, Uuid7, RowNumber, Hash128);
136zero_heap!(Partition, RowShapeFingerprint, TimerKind, ContentVersion, DictionaryEntryId);
137
138const BIGNUM_APPROX_HEAP: usize = 32;
139
140impl HeapSize for Decimal {
141 fn heap_size(&self) -> usize {
142 BIGNUM_APPROX_HEAP
143 }
144}
145
146impl HeapSize for String {
147 fn heap_size(&self) -> usize {
148 self.capacity()
149 }
150}
151
152impl HeapSize for EncodedBytes {
153 fn heap_size(&self) -> usize {
154 self.as_slice().len()
155 }
156}
157
158impl HeapSize for EncodedKey {
159 fn heap_size(&self) -> usize {
160 match self {
161 EncodedKey::Inline {
162 ..
163 } => 0,
164 EncodedKey::Shared(bytes) => bytes.len() + 2 * mem::size_of::<usize>(),
165 }
166 }
167}
168
169impl HeapSize for TaggedKey {
170 fn heap_size(&self) -> usize {
171 match self {
172 TaggedKey::SortedViewRow(key) => key.run.len(),
173 TaggedKey::PartitionedSortedViewRow(key) => key.run.len(),
174 TaggedKey::RingBufferMetadata(key) => key.encoded_partition.capacity(),
175 TaggedKey::QueueDeduplication(key) => key.tail.heap_size(),
176 TaggedKey::IndexEntry(key) => key.key.heap_size(),
177 TaggedKey::OperatorState(key) => key.suffix.capacity(),
178 _ => 0,
179 }
180 }
181}
182
183impl HeapSize for EncodedIndexKey {
184 fn heap_size(&self) -> usize {
185 match self {
186 EncodedIndexKey::Inline {
187 ..
188 } => 0,
189 EncodedIndexKey::Heap(bytes) => bytes.capacity(),
190 }
191 }
192}
193
194impl<T: HeapSize> HeapSize for Option<T> {
195 fn heap_size(&self) -> usize {
196 self.as_ref().map_or(0, HeapSize::heap_size)
197 }
198}
199
200impl<T: HeapSize> HeapSize for Vec<T> {
201 fn heap_size(&self) -> usize {
202 self.capacity() * mem::size_of::<T>() + self.iter().map(HeapSize::heap_size).sum::<usize>()
203 }
204}
205
206impl<T: HeapSize, const N: usize> HeapSize for [T; N] {
207 fn heap_size(&self) -> usize {
208 self.iter().map(HeapSize::heap_size).sum::<usize>()
209 }
210}
211
212impl<T: HeapSize> HeapSize for VecDeque<T> {
213 fn heap_size(&self) -> usize {
214 self.capacity() * mem::size_of::<T>() + self.iter().map(HeapSize::heap_size).sum::<usize>()
215 }
216}
217
218impl HeapSize for Digest {
219 fn heap_size(&self) -> usize {
220 self.bucket_count() * (mem::size_of::<i32>() + mem::size_of::<u64>())
221 }
222}
223
224impl<T: HeapSize> HeapSize for Box<T> {
225 fn heap_size(&self) -> usize {
226 mem::size_of::<T>() + (**self).heap_size()
227 }
228}
229
230impl<T: HeapSize> HeapSize for Arc<T> {
231 fn heap_size(&self) -> usize {
232 mem::size_of::<usize>() * 2 + mem::size_of::<T>() + (**self).heap_size()
233 }
234}
235
236impl HeapSize for Arc<str> {
237 fn heap_size(&self) -> usize {
238 mem::size_of::<usize>() * 2 + self.len()
239 }
240}
241
242impl<T: HeapSize> HeapSize for Arc<[T]> {
243 fn heap_size(&self) -> usize {
244 mem::size_of::<usize>() * 2
245 + self.len() * mem::size_of::<T>()
246 + self.iter().map(HeapSize::heap_size).sum::<usize>()
247 }
248}
249
250impl<T: HeapSize> HeapSize for Box<[T]> {
251 fn heap_size(&self) -> usize {
252 self.len() * mem::size_of::<T>() + self.iter().map(HeapSize::heap_size).sum::<usize>()
253 }
254}
255
256impl HeapSize for Box<str> {
257 fn heap_size(&self) -> usize {
258 self.len()
259 }
260}
261
262impl<K: HeapSize, V: HeapSize> HeapSize for BTreeMap<K, V> {
263 fn heap_size(&self) -> usize {
264 self.len() * (mem::size_of::<K>() + mem::size_of::<V>())
265 + self.iter().map(|(k, v)| k.heap_size() + v.heap_size()).sum::<usize>()
266 }
267}
268
269impl<T: HeapSize> HeapSize for BTreeSet<T> {
270 fn heap_size(&self) -> usize {
271 self.len() * mem::size_of::<T>() + self.iter().map(HeapSize::heap_size).sum::<usize>()
272 }
273}
274
275impl<K: HeapSize, V: HeapSize, S> HeapSize for HashMap<K, V, S> {
276 fn heap_size(&self) -> usize {
277 self.capacity() * (mem::size_of::<K>() + mem::size_of::<V>() + 1)
278 + self.iter().map(|(k, v)| k.heap_size() + v.heap_size()).sum::<usize>()
279 }
280}
281
282impl<T: HeapSize, S> HeapSize for HashSet<T, S> {
283 fn heap_size(&self) -> usize {
284 self.capacity() * (mem::size_of::<T>() + 1) + self.iter().map(HeapSize::heap_size).sum::<usize>()
285 }
286}
287
288impl<A: HeapSize, B: HeapSize> HeapSize for (A, B) {
289 fn heap_size(&self) -> usize {
290 self.0.heap_size() + self.1.heap_size()
291 }
292}
293
294impl<A: HeapSize, B: HeapSize, C: HeapSize> HeapSize for (A, B, C) {
295 fn heap_size(&self) -> usize {
296 self.0.heap_size() + self.1.heap_size() + self.2.heap_size()
297 }
298}
299
300impl HeapSize for Value {
301 fn heap_size(&self) -> usize {
302 match self {
303 Value::Utf8(text) => text.capacity(),
304 Value::Blob(blob) => blob.as_bytes().len(),
305 Value::Decimal(_) => BIGNUM_APPROX_HEAP,
306 Value::Any(inner) => mem::size_of::<Value>() + inner.heap_size(),
307 Value::Digest(digest) => digest.heap_size(),
308 Value::List(items) | Value::Tuple(items) => items.heap_size(),
309 Value::Record(fields) => {
310 fields.capacity() * mem::size_of::<(String, Value)>()
311 + fields.iter()
312 .map(|(name, value)| name.capacity() + value.heap_size())
313 .sum::<usize>()
314 }
315 _ => 0,
316 }
317 }
318}
319
320impl HeapSize for ColumnView<'_> {
321 fn heap_size(&self) -> usize {
322 let nulls = self.logical_nulls().map_or(0, |nulls| nulls.len().div_ceil(8));
323 nulls + match &self.data {
324 ViewData::Digest {
325 container,
326 ..
327 } => {
328 varlen_array::heap_size(*container)
329 + digest_array::iter(container)
330 .flatten()
331 .map(|digest| mem::size_of::<Digest>() + digest.heap_size())
332 .sum::<usize>()
333 }
334 ViewData::Bool(a) => bool_array::heap_size(a),
335 ViewData::Decimal(DecimalView::Decimal128(d)) => primitive::heap_size(*d),
336 ViewData::Decimal(DecimalView::Decimal256(d)) => primitive::heap_size(*d),
337 ViewData::None {
338 ..
339 } => 0,
340 ViewData::Float4(a) => primitive::heap_size(*a),
341 ViewData::Float8(a) => primitive::heap_size(*a),
342 ViewData::Int1(a) => primitive::heap_size(*a),
343 ViewData::Int2(a) => primitive::heap_size(*a),
344 ViewData::Int4(a) => primitive::heap_size(*a),
345 ViewData::Int8(a) => primitive::heap_size(*a),
346 ViewData::Uint1(a) => primitive::heap_size(*a),
347 ViewData::Uint2(a) => primitive::heap_size(*a),
348 ViewData::Uint4(a) => primitive::heap_size(*a),
349 ViewData::Uint8(a) => primitive::heap_size(*a),
350 ViewData::Date(t) => primitive::heap_size(*t),
351 ViewData::DateTime(t) => primitive::heap_size(*t),
352 ViewData::Time(t) => primitive::heap_size(*t),
353 ViewData::Duration(t) => primitive::heap_size(*t),
354 ViewData::IdentityId(u)
355 | ViewData::Uuid4(u)
356 | ViewData::Uuid7(u)
357 | ViewData::Int16(u)
358 | ViewData::Uint16(u)
359 | ViewData::DictionaryId {
360 container: u,
361 ..
362 } => fixed_array::heap_size(u),
363 ViewData::Utf8 {
364 container: v,
365 ..
366 } => varlen_array::heap_size(*v),
367 ViewData::Blob {
368 container: v,
369 ..
370 }
371 | ViewData::Any {
372 container: v,
373 ..
374 } => varlen_array::heap_size(*v),
375 }
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::HeapSize;
382
383 #[derive(HeapSize)]
384 struct DerivedSample {
385 name: String,
386 values: Vec<u64>,
387 count: u64,
388 }
389
390 #[test]
391 fn derived_heap_size_sums_all_fields() {
392 let sample = DerivedSample {
395 name: String::with_capacity(32),
396 values: Vec::with_capacity(4),
397 count: 7,
398 };
399 assert_eq!(sample.count, 7);
400 assert_eq!(sample.heap_size(), 32 + 4 * 8);
401 }
402}