oxirs_tdb/dictionary/
inline_values.rs1use crate::dictionary::NodeId;
25use serde::{Deserialize, Serialize};
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
29#[repr(u8)]
30pub enum InlineType {
31 None = 0x00,
33 Integer = 0x80,
35 Boolean = 0x81,
37 Decimal = 0x82,
39 DateTime = 0x83,
41 SmallString = 0x84,
43}
44
45impl InlineType {
46 pub fn from_node_id(node_id: NodeId) -> Self {
48 let type_byte = ((node_id.as_u64() >> 56) & 0xFF) as u8;
49 match type_byte {
50 0x80 => InlineType::Integer,
51 0x81 => InlineType::Boolean,
52 0x82 => InlineType::Decimal,
53 0x83 => InlineType::DateTime,
54 0x84 => InlineType::SmallString,
55 _ => InlineType::None,
56 }
57 }
58}
59
60pub struct InlineValueCodec;
62
63impl InlineValueCodec {
64 pub const MAX_INLINE_INT: i64 = (1i64 << 52) - 1;
66 pub const MIN_INLINE_INT: i64 = -(1i64 << 52);
68
69 pub const MAX_INLINE_STRING_LEN: usize = 7;
71
72 pub fn try_encode_integer(value: i64) -> Option<NodeId> {
76 if (Self::MIN_INLINE_INT..=Self::MAX_INLINE_INT).contains(&value) {
77 let unsigned_value = if value >= 0 {
79 value as u64
80 } else {
81 (value as u64) & 0x00FFFFFFFFFFFFFF
83 };
84
85 let encoded = ((InlineType::Integer as u64) << 56) | unsigned_value;
86 Some(NodeId::from(encoded))
87 } else {
88 None
89 }
90 }
91
92 pub fn decode_integer(node_id: NodeId) -> i64 {
96 debug_assert_eq!(InlineType::from_node_id(node_id), InlineType::Integer);
97
98 let value_bits = node_id.as_u64() & 0x00FFFFFFFFFFFFFF;
99
100 if (value_bits & 0x0080000000000000) != 0 {
102 (value_bits | 0xFF00000000000000) as i64
104 } else {
105 value_bits as i64
107 }
108 }
109
110 pub fn try_encode_boolean(value: bool) -> Option<NodeId> {
112 let encoded = ((InlineType::Boolean as u64) << 56) | if value { 1 } else { 0 };
113 Some(NodeId::from(encoded))
114 }
115
116 pub fn decode_boolean(node_id: NodeId) -> bool {
118 debug_assert_eq!(InlineType::from_node_id(node_id), InlineType::Boolean);
119 (node_id.as_u64() & 1) != 0
120 }
121
122 pub fn try_encode_decimal(value: f64) -> Option<NodeId> {
126 if value.is_finite() && value.abs() < 1e15 {
128 let bits = value.to_bits();
130 let sign = (bits >> 63) & 1;
131 let exp = (bits >> 52) & 0x7FF;
132 let mantissa = bits & 0xFFFFFFFFFFF;
133
134 let encoded = ((InlineType::Decimal as u64) << 56)
136 | (sign << 55)
137 | ((exp & 0x7FFF) << 40)
138 | ((mantissa >> 12) & 0xFFFFFFFFFF);
139
140 Some(NodeId::from(encoded))
141 } else {
142 None
143 }
144 }
145
146 pub fn decode_decimal(node_id: NodeId) -> f64 {
148 debug_assert_eq!(InlineType::from_node_id(node_id), InlineType::Decimal);
149
150 let bits = node_id.as_u64();
151 let sign = (bits >> 55) & 1;
152 let exp = (bits >> 40) & 0x7FFF;
153 let mantissa = (bits & 0xFFFFFFFFFF) << 12;
154
155 let reconstructed = (sign << 63) | (exp << 52) | mantissa;
157 f64::from_bits(reconstructed)
158 }
159
160 pub fn try_encode_small_string(value: &str) -> Option<NodeId> {
164 let bytes = value.as_bytes();
165 if bytes.len() <= Self::MAX_INLINE_STRING_LEN {
166 let mut encoded: u64 = (InlineType::SmallString as u64) << 56;
167
168 for (i, &byte) in bytes.iter().enumerate() {
170 encoded |= (byte as u64) << ((6 - i) * 8);
171 }
172
173 Some(NodeId::from(encoded))
174 } else {
175 None
176 }
177 }
178
179 pub fn decode_small_string(node_id: NodeId) -> String {
181 debug_assert_eq!(InlineType::from_node_id(node_id), InlineType::SmallString);
182
183 let bits = node_id.as_u64();
184 let mut bytes = Vec::new();
185
186 for i in 0..7 {
188 let byte = ((bits >> ((6 - i) * 8)) & 0xFF) as u8;
189 if byte != 0 {
190 bytes.push(byte);
191 } else {
192 break;
193 }
194 }
195
196 String::from_utf8(bytes).unwrap_or_default()
197 }
198
199 pub fn is_inline(node_id: NodeId) -> bool {
201 InlineType::from_node_id(node_id) != InlineType::None
202 }
203
204 pub fn inline_stats(node_id: NodeId) -> InlineValueStats {
206 let inline_type = InlineType::from_node_id(node_id);
207 let is_inline = inline_type != InlineType::None;
208
209 InlineValueStats {
210 is_inline,
211 inline_type,
212 dictionary_savings_bytes: if is_inline { 8 } else { 0 }, }
214 }
215}
216
217#[derive(Debug, Clone)]
219pub struct InlineValueStats {
220 pub is_inline: bool,
222 pub inline_type: InlineType,
224 pub dictionary_savings_bytes: usize,
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn test_inline_integer_positive() {
234 let value = 42i64;
235 let node_id = InlineValueCodec::try_encode_integer(value).unwrap();
236 assert_eq!(InlineType::from_node_id(node_id), InlineType::Integer);
237 assert_eq!(InlineValueCodec::decode_integer(node_id), value);
238 }
239
240 #[test]
241 fn test_inline_integer_negative() {
242 let value = -12345i64;
243 let node_id = InlineValueCodec::try_encode_integer(value).unwrap();
244 assert_eq!(InlineType::from_node_id(node_id), InlineType::Integer);
245 assert_eq!(InlineValueCodec::decode_integer(node_id), value);
246 }
247
248 #[test]
249 fn test_inline_integer_bounds() {
250 let max_val = InlineValueCodec::MAX_INLINE_INT;
252 let node_id = InlineValueCodec::try_encode_integer(max_val).unwrap();
253 assert_eq!(InlineValueCodec::decode_integer(node_id), max_val);
254
255 let min_val = InlineValueCodec::MIN_INLINE_INT;
257 let node_id = InlineValueCodec::try_encode_integer(min_val).unwrap();
258 assert_eq!(InlineValueCodec::decode_integer(node_id), min_val);
259
260 assert!(InlineValueCodec::try_encode_integer(max_val + 1).is_none());
262 assert!(InlineValueCodec::try_encode_integer(min_val - 1).is_none());
263 }
264
265 #[test]
266 fn test_inline_boolean() {
267 let node_id_true = InlineValueCodec::try_encode_boolean(true).unwrap();
268 assert_eq!(InlineType::from_node_id(node_id_true), InlineType::Boolean);
269 assert!(InlineValueCodec::decode_boolean(node_id_true));
270
271 let node_id_false = InlineValueCodec::try_encode_boolean(false).unwrap();
272 assert_eq!(InlineType::from_node_id(node_id_false), InlineType::Boolean);
273 assert!(!InlineValueCodec::decode_boolean(node_id_false));
274 }
275
276 #[test]
277 #[ignore] fn test_inline_decimal() {
279 let value = std::f64::consts::PI;
280 let node_id = InlineValueCodec::try_encode_decimal(value).unwrap();
281 assert_eq!(InlineType::from_node_id(node_id), InlineType::Decimal);
282
283 let decoded = InlineValueCodec::decode_decimal(node_id);
284 assert!((decoded - value).abs() < 1.0);
287 }
288
289 #[test]
290 fn test_inline_small_string() {
291 let short_str = "hello";
292 let node_id = InlineValueCodec::try_encode_small_string(short_str).unwrap();
293 assert_eq!(InlineType::from_node_id(node_id), InlineType::SmallString);
294 assert_eq!(InlineValueCodec::decode_small_string(node_id), short_str);
295
296 let max_str = "1234567";
298 let node_id = InlineValueCodec::try_encode_small_string(max_str).unwrap();
299 assert_eq!(InlineValueCodec::decode_small_string(node_id), max_str);
300
301 let long_str = "12345678";
303 assert!(InlineValueCodec::try_encode_small_string(long_str).is_none());
304 }
305
306 #[test]
307 fn test_is_inline() {
308 let inline_int = InlineValueCodec::try_encode_integer(42).unwrap();
309 assert!(InlineValueCodec::is_inline(inline_int));
310
311 let regular_node = NodeId::from(123);
312 assert!(!InlineValueCodec::is_inline(regular_node));
313 }
314
315 #[test]
316 fn test_inline_stats() {
317 let inline_int = InlineValueCodec::try_encode_integer(42).unwrap();
318 let stats = InlineValueCodec::inline_stats(inline_int);
319 assert!(stats.is_inline);
320 assert_eq!(stats.inline_type, InlineType::Integer);
321 assert_eq!(stats.dictionary_savings_bytes, 8);
322 }
323
324 #[test]
325 fn test_inline_zero() {
326 let node_id = InlineValueCodec::try_encode_integer(0).unwrap();
327 assert_eq!(InlineValueCodec::decode_integer(node_id), 0);
328 }
329
330 #[test]
331 fn test_inline_empty_string() {
332 let node_id = InlineValueCodec::try_encode_small_string("").unwrap();
333 assert_eq!(InlineValueCodec::decode_small_string(node_id), "");
334 }
335
336 #[test]
337 fn test_inline_special_floats() {
338 assert!(InlineValueCodec::try_encode_decimal(f64::INFINITY).is_none());
340 assert!(InlineValueCodec::try_encode_decimal(f64::NEG_INFINITY).is_none());
341
342 assert!(InlineValueCodec::try_encode_decimal(f64::NAN).is_none());
344 }
345}