Skip to main content

reifydb_core/key/
index_entry.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2026 ReifyDB
3
4use std::collections::Bound;
5
6use super::{EncodableKey, EncodableKeyRange, KeyKind};
7use crate::{
8	encoded::key::{EncodedKey, EncodedKeyRange},
9	interface::catalog::{id::IndexId, shape::ShapeId},
10	util::encoding::keycode::{deserializer::KeyDeserializer, serializer::KeySerializer},
11	value::index::{encoded::EncodedIndexKey, range::EncodedIndexKeyRange},
12};
13
14#[derive(Debug, Clone, PartialEq)]
15pub struct IndexEntryKey {
16	pub shape: ShapeId,
17	pub index: IndexId,
18	pub key: EncodedIndexKey,
19}
20
21impl IndexEntryKey {
22	pub fn new(shape: impl Into<ShapeId>, index: IndexId, key: EncodedIndexKey) -> Self {
23		Self {
24			shape: shape.into(),
25			index,
26			key,
27		}
28	}
29
30	pub fn encoded(shape: impl Into<ShapeId>, index: IndexId, key: EncodedIndexKey) -> EncodedKey {
31		Self::new(shape, index, key).encode()
32	}
33}
34
35#[derive(Debug, Clone, PartialEq)]
36pub struct IndexEntryKeyRange {
37	pub shape: ShapeId,
38	pub index: IndexId,
39}
40
41impl IndexEntryKeyRange {
42	fn decode_key(key: &EncodedKey) -> Option<Self> {
43		let mut de = KeyDeserializer::from_bytes(key.as_slice());
44
45		let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
46		if kind != Self::KIND {
47			return None;
48		}
49
50		let shape = de.read_shape_id().ok()?;
51		let index = de.read_index_id().ok()?;
52
53		Some(IndexEntryKeyRange {
54			shape,
55			index,
56		})
57	}
58}
59
60impl EncodableKeyRange for IndexEntryKeyRange {
61	const KIND: KeyKind = KeyKind::IndexEntry;
62
63	fn start(&self) -> Option<EncodedKey> {
64		let mut serializer = KeySerializer::with_capacity(19);
65		serializer.extend_u8(Self::KIND as u8).extend_shape_id(self.shape).extend_index_id(self.index);
66		Some(serializer.to_encoded_key())
67	}
68
69	fn end(&self) -> Option<EncodedKey> {
70		let mut serializer = KeySerializer::with_capacity(19);
71		serializer.extend_u8(Self::KIND as u8).extend_shape_id(self.shape).extend_index_id(self.index.prev());
72		Some(serializer.to_encoded_key())
73	}
74
75	fn decode(range: &EncodedKeyRange) -> (Option<Self>, Option<Self>)
76	where
77		Self: Sized,
78	{
79		let start_key = match &range.start {
80			Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
81			Bound::Unbounded => None,
82		};
83
84		let end_key = match &range.end {
85			Bound::Included(key) | Bound::Excluded(key) => Self::decode_key(key),
86			Bound::Unbounded => None,
87		};
88
89		(start_key, end_key)
90	}
91}
92
93impl EncodableKey for IndexEntryKey {
94	const KIND: KeyKind = KeyKind::IndexEntry;
95
96	fn encode(&self) -> EncodedKey {
97		let mut serializer = KeySerializer::with_capacity(20 + self.key.len());
98		serializer
99			.extend_u8(Self::KIND as u8)
100			.extend_shape_id(self.shape)
101			.extend_index_id(self.index)
102			.extend_raw(self.key.as_slice());
103		serializer.to_encoded_key()
104	}
105
106	fn decode(key: &EncodedKey) -> Option<Self> {
107		let mut de = KeyDeserializer::from_bytes(key.as_slice());
108
109		let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
110		if kind != Self::KIND {
111			return None;
112		}
113
114		let shape = de.read_shape_id().ok()?;
115		let index = de.read_index_id().ok()?;
116
117		let remaining = de.remaining();
118		if remaining > 0 {
119			let remaining_bytes = de.read_raw(remaining).ok()?;
120			let index_key = EncodedIndexKey::new(remaining_bytes.to_vec());
121			Some(Self {
122				shape,
123				index,
124				key: index_key,
125			})
126		} else {
127			None
128		}
129	}
130}
131
132impl IndexEntryKey {
133	pub fn index_range(shape: impl Into<ShapeId>, index: IndexId) -> EncodedKeyRange {
134		let range = IndexEntryKeyRange {
135			shape: shape.into(),
136			index,
137		};
138		EncodedKeyRange::new(Bound::Included(range.start().unwrap()), Bound::Excluded(range.end().unwrap()))
139	}
140
141	pub fn shape_range(shape: impl Into<ShapeId>) -> EncodedKeyRange {
142		let shape = shape.into();
143		let mut start_serializer = KeySerializer::with_capacity(10);
144		start_serializer.extend_u8(KeyKind::IndexEntry as u8).extend_shape_id(shape);
145
146		let next_primitive = shape.next();
147		let mut end_serializer = KeySerializer::with_capacity(10);
148		end_serializer.extend_u8(KeyKind::IndexEntry as u8).extend_shape_id(next_primitive);
149
150		EncodedKeyRange {
151			start: Bound::Included(start_serializer.to_encoded_key()),
152			end: Bound::Excluded(end_serializer.to_encoded_key()),
153		}
154	}
155
156	pub fn key_prefix_range(shape: impl Into<ShapeId>, index: IndexId, key_prefix: &[u8]) -> EncodedKeyRange {
157		let shape = shape.into();
158		let mut serializer = KeySerializer::with_capacity(20 + key_prefix.len());
159		serializer
160			.extend_u8(KeyKind::IndexEntry as u8)
161			.extend_shape_id(shape)
162			.extend_index_id(index)
163			.extend_raw(key_prefix);
164		let start = serializer.to_encoded_key();
165
166		let mut end = start.as_slice().to_vec();
167		end.push(0xFF);
168
169		EncodedKeyRange {
170			start: Bound::Included(start),
171			end: Bound::Excluded(EncodedKey::new(end)),
172		}
173	}
174
175	pub fn key_range(
176		shape: impl Into<ShapeId>,
177		index: IndexId,
178		index_range: EncodedIndexKeyRange,
179	) -> EncodedKeyRange {
180		let shape = shape.into();
181
182		let mut prefix_serializer = KeySerializer::with_capacity(19);
183		prefix_serializer.extend_u8(KeyKind::IndexEntry as u8).extend_shape_id(shape).extend_index_id(index);
184		let prefix = prefix_serializer.to_encoded_key().to_vec();
185
186		let start = match index_range.start {
187			Bound::Included(key) => {
188				let mut bytes = prefix.clone();
189				bytes.extend_from_slice(key.as_slice());
190				Bound::Included(EncodedKey::new(bytes))
191			}
192			Bound::Excluded(key) => {
193				let mut bytes = prefix.clone();
194				bytes.extend_from_slice(key.as_slice());
195				Bound::Excluded(EncodedKey::new(bytes))
196			}
197			Bound::Unbounded => Bound::Included(EncodedKey::new(prefix.clone())),
198		};
199
200		let end = match index_range.end {
201			Bound::Included(key) => {
202				let mut bytes = prefix.clone();
203				bytes.extend_from_slice(key.as_slice());
204				Bound::Included(EncodedKey::new(bytes))
205			}
206			Bound::Excluded(key) => {
207				let mut bytes = prefix.clone();
208				bytes.extend_from_slice(key.as_slice());
209				Bound::Excluded(EncodedKey::new(bytes))
210			}
211			Bound::Unbounded => {
212				let mut serializer = KeySerializer::with_capacity(19);
213				serializer
214					.extend_u8(KeyKind::IndexEntry as u8)
215					.extend_shape_id(shape)
216					.extend_index_id(index.prev());
217				Bound::Excluded(serializer.to_encoded_key())
218			}
219		};
220
221		EncodedKeyRange {
222			start,
223			end,
224		}
225	}
226}
227
228#[cfg(test)]
229pub mod tests {
230	use reifydb_value::value::value_type::ValueType;
231
232	use super::*;
233	use crate::{sort::SortDirection, value::index::shape::IndexShape};
234
235	#[test]
236	fn test_encode_decode() {
237		let layout = IndexShape::new(
238			&[ValueType::Uint8, ValueType::Uint8],
239			&[SortDirection::Asc, SortDirection::Asc],
240		)
241		.unwrap();
242
243		let mut index_key = layout.allocate_key();
244		layout.set_u64(&mut index_key, 0, 100u64);
245		layout.set_row_number(&mut index_key, 1, 1u64);
246
247		let entry = IndexEntryKey {
248			shape: ShapeId::table(42),
249			index: IndexId::primary(7),
250			key: index_key.clone(),
251		};
252
253		let encoded = entry.encode();
254		let decoded = IndexEntryKey::decode(&encoded).unwrap();
255
256		assert_eq!(decoded.shape, ShapeId::table(42));
257		assert_eq!(decoded.index, IndexId::primary(7));
258		assert_eq!(decoded.key.as_slice(), index_key.as_slice());
259	}
260
261	#[test]
262	fn test_ordering() {
263		let layout = IndexShape::new(&[ValueType::Uint8], &[SortDirection::Asc]).unwrap();
264
265		let mut key1 = layout.allocate_key();
266		layout.set_u64(&mut key1, 0, 100u64);
267
268		let mut key2 = layout.allocate_key();
269		layout.set_u64(&mut key2, 0, 200u64);
270
271		let entry1 = IndexEntryKey {
272			shape: ShapeId::table(1),
273			index: IndexId::primary(1),
274			key: key1,
275		};
276
277		let entry2 = IndexEntryKey {
278			shape: ShapeId::table(1),
279			index: IndexId::primary(1),
280			key: key2,
281		};
282
283		let encoded1 = entry1.encode();
284		let encoded2 = entry2.encode();
285
286		assert!(encoded1.as_slice() < encoded2.as_slice());
287	}
288
289	#[test]
290	fn test_index_range() {
291		let range = IndexEntryKey::index_range(ShapeId::table(10), IndexId::primary(5));
292
293		let layout = IndexShape::new(&[ValueType::Uint8], &[SortDirection::Asc]).unwrap();
294
295		let mut key = layout.allocate_key();
296		layout.set_u64(&mut key, 0, 50u64);
297
298		let entry = IndexEntryKey {
299			shape: ShapeId::table(10),
300			index: IndexId::primary(5),
301			key,
302		};
303
304		let encoded = entry.encode();
305
306		if let (Bound::Included(start), Bound::Excluded(end)) = (&range.start, &range.end) {
307			assert!(encoded.as_slice() >= start.as_slice());
308			assert!(encoded.as_slice() < end.as_slice());
309		} else {
310			panic!("Expected Included/Excluded bounds");
311		}
312
313		let entry2 = IndexEntryKey {
314			shape: ShapeId::table(10),
315			index: IndexId::primary(6),
316			key: layout.allocate_key(),
317		};
318
319		let encoded2 = entry2.encode();
320
321		if let (Bound::Included(start), Bound::Excluded(end)) = (&range.start, &range.end) {
322			assert!(encoded2.as_slice() < start.as_slice() || encoded2.as_slice() >= end.as_slice());
323		}
324	}
325
326	#[test]
327	fn test_key_prefix_range() {
328		let layout = IndexShape::new(
329			&[ValueType::Uint8, ValueType::Uint8],
330			&[SortDirection::Asc, SortDirection::Asc],
331		)
332		.unwrap();
333
334		let mut key = layout.allocate_key();
335		layout.set_u64(&mut key, 0, 100u64);
336		layout.set_row_number(&mut key, 1, 0u64);
337
338		let prefix = &key.as_slice()[..layout.fields[1].offset];
339		let range = IndexEntryKey::key_prefix_range(ShapeId::table(1), IndexId::primary(1), prefix);
340
341		layout.set_row_number(&mut key, 1, 999u64);
342		let entry = IndexEntryKey {
343			shape: ShapeId::table(1),
344			index: IndexId::primary(1),
345			key: key.clone(),
346		};
347
348		let encoded = entry.encode();
349
350		if let (Bound::Included(start), Bound::Excluded(end)) = (&range.start, &range.end) {
351			assert!(encoded.as_slice() >= start.as_slice());
352			assert!(encoded.as_slice() < end.as_slice());
353		}
354
355		let mut key2 = layout.allocate_key();
356		layout.set_u64(&mut key2, 0, 200u64);
357		layout.set_row_number(&mut key2, 1, 1u64);
358
359		let entry2 = IndexEntryKey {
360			shape: ShapeId::table(1),
361			index: IndexId::primary(1),
362			key: key2,
363		};
364
365		let encoded2 = entry2.encode();
366
367		if let Bound::Excluded(end) = &range.end {
368			assert!(encoded2.as_slice() >= end.as_slice());
369		}
370	}
371}