1use std::cmp::Ordering;
9use std::hash::{BuildHasher, Hasher};
10
11use nodedb_types::read_instant;
12
13use crate::msgpack_scan::reader::{read_integer, read_null, read_numeric, str_bounds};
14use crate::numeric_cmp::cmp_numeric;
15
16pub fn hash_field_bytes(buf: &[u8], range: (usize, usize)) -> u64 {
23 let slice = match buf.get(range.0..range.1) {
24 Some(s) => s,
25 None => return 0,
26 };
27 let hasher_builder = std::collections::hash_map::RandomState::new();
28 let mut hasher = hasher_builder.build_hasher();
29 hasher.write(slice);
30 hasher.finish()
31}
32
33pub fn hash_field_bytes_with(
36 buf: &[u8],
37 range: (usize, usize),
38 state: &std::collections::hash_map::RandomState,
39) -> u64 {
40 let slice = match buf.get(range.0..range.1) {
41 Some(s) => s,
42 None => return 0,
43 };
44 let mut hasher = state.build_hasher();
45 hasher.write(slice);
46 hasher.finish()
47}
48
49pub fn compare_field_bytes(
61 a_buf: &[u8],
62 a_range: (usize, usize),
63 b_buf: &[u8],
64 b_range: (usize, usize),
65) -> Ordering {
66 let a_off = a_range.0;
67 let b_off = b_range.0;
68
69 let a_tag = match a_buf.get(a_off) {
70 Some(&t) => t,
71 None => return Ordering::Less,
72 };
73 let b_tag = match b_buf.get(b_off) {
74 Some(&t) => t,
75 None => return Ordering::Greater,
76 };
77
78 let a_type = type_rank(a_buf, a_off, a_tag);
79 let b_type = type_rank(b_buf, b_off, b_tag);
80
81 if a_type != b_type {
82 return a_type.cmp(&b_type);
83 }
84
85 match a_type {
86 RANK_NULL => Ordering::Equal,
87 RANK_BOOL => {
88 let a_val = a_tag == 0xc3; let b_val = b_tag == 0xc3;
90 a_val.cmp(&b_val)
91 }
92 RANK_NUMBER => {
93 match (read_numeric(a_buf, a_off), read_numeric(b_buf, b_off)) {
96 (Some(a), Some(b)) => cmp_numeric(a, b),
97 (Some(_), None) => Ordering::Greater,
98 (None, Some(_)) => Ordering::Less,
99 (None, None) => Ordering::Equal,
100 }
101 }
102 RANK_INSTANT => {
103 match (read_instant(a_buf, a_off), read_instant(b_buf, b_off)) {
106 (Some((a_kind, a_us)), Some((b_kind, b_us))) => a_kind
107 .ext_type()
108 .cmp(&b_kind.ext_type())
109 .then(a_us.cmp(&b_us)),
110 (Some(_), None) => Ordering::Greater,
111 (None, Some(_)) => Ordering::Less,
112 (None, None) => Ordering::Equal,
113 }
114 }
115 RANK_STRING => {
116 match (str_bounds(a_buf, a_off), str_bounds(b_buf, b_off)) {
118 (Some((a_s, a_l)), Some((b_s, b_l))) => {
119 let a_bytes = &a_buf[a_s..a_s + a_l];
120 let b_bytes = &b_buf[b_s..b_s + b_l];
121 a_bytes.cmp(b_bytes)
122 }
123 _ => Ordering::Equal,
124 }
125 }
126 _ => {
127 let a_slice = &a_buf[a_range.0..a_range.1];
129 let b_slice = &b_buf[b_range.0..b_range.1];
130 a_slice.cmp(b_slice)
131 }
132 }
133}
134
135pub fn compare_field_i64(a_buf: &[u8], a_off: usize, b_buf: &[u8], b_off: usize) -> Ordering {
138 match (read_integer(a_buf, a_off), read_integer(b_buf, b_off)) {
139 (Some(a), Some(b)) => a.cmp(&b),
140 (Some(_), None) => Ordering::Greater,
141 (None, Some(_)) => Ordering::Less,
142 (None, None) => Ordering::Equal,
143 }
144}
145
146pub fn field_bytes_eq(
149 a_buf: &[u8],
150 a_range: (usize, usize),
151 b_buf: &[u8],
152 b_range: (usize, usize),
153) -> bool {
154 let a_slice = match a_buf.get(a_range.0..a_range.1) {
155 Some(s) => s,
156 None => return false,
157 };
158 let b_slice = match b_buf.get(b_range.0..b_range.1) {
159 Some(s) => s,
160 None => return false,
161 };
162 a_slice == b_slice
163}
164
165pub fn is_field_null(buf: &[u8], range: (usize, usize)) -> bool {
167 read_null(buf, range.0)
168}
169
170const RANK_NULL: u8 = 0;
171const RANK_BOOL: u8 = 1;
172const RANK_NUMBER: u8 = 2;
173const RANK_INSTANT: u8 = 3;
174const RANK_STRING: u8 = 4;
175const RANK_BINARY: u8 = 5;
176const RANK_ARRAY: u8 = 6;
177const RANK_MAP: u8 = 7;
178const RANK_EXT: u8 = 8;
179const RANK_UNKNOWN: u8 = 9;
180
181fn type_rank(buf: &[u8], off: usize, tag: u8) -> u8 {
187 match tag {
188 0xc0 => RANK_NULL,
189 0xc2 | 0xc3 => RANK_BOOL,
190 0x00..=0x7f | 0xe0..=0xff => RANK_NUMBER, 0xca..=0xd3 => RANK_NUMBER, 0xa0..=0xbf | 0xd9..=0xdb => RANK_STRING,
193 0xc4..=0xc6 => RANK_BINARY,
194 0x90..=0x9f | 0xdc | 0xdd => RANK_ARRAY,
195 0x80..=0x8f | 0xde | 0xdf => RANK_MAP,
196 0xd7 => {
197 if read_instant(buf, off).is_some() {
198 RANK_INSTANT
199 } else {
200 RANK_EXT
201 }
202 }
203 0xc7..=0xc9 | 0xd4..=0xd6 | 0xd8 => RANK_EXT,
204 _ => RANK_UNKNOWN,
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211 use serde_json::json;
212
213 fn encode(v: &serde_json::Value) -> Vec<u8> {
214 nodedb_types::json_msgpack::json_to_msgpack(v).expect("encode")
215 }
216
217 fn val_range(buf: &[u8]) -> (usize, usize) {
218 (0, buf.len())
219 }
220
221 #[test]
222 fn hash_same_bytes_same_hash() {
223 let buf = encode(&json!(42));
224 let state = std::collections::hash_map::RandomState::new();
225 let h1 = hash_field_bytes_with(&buf, val_range(&buf), &state);
226 let h2 = hash_field_bytes_with(&buf, val_range(&buf), &state);
227 assert_eq!(h1, h2);
228 }
229
230 #[test]
231 fn hash_different_values_likely_differ() {
232 let buf1 = encode(&json!(42));
233 let buf2 = encode(&json!(43));
234 let state = std::collections::hash_map::RandomState::new();
235 let h1 = hash_field_bytes_with(&buf1, val_range(&buf1), &state);
236 let h2 = hash_field_bytes_with(&buf2, val_range(&buf2), &state);
237 assert_ne!(h1, h2);
238 }
239
240 #[test]
241 fn compare_integers() {
242 let a = encode(&json!(10));
243 let b = encode(&json!(20));
244 assert_eq!(
245 compare_field_bytes(&a, val_range(&a), &b, val_range(&b)),
246 Ordering::Less
247 );
248 assert_eq!(
249 compare_field_bytes(&b, val_range(&b), &a, val_range(&a)),
250 Ordering::Greater
251 );
252 assert_eq!(
253 compare_field_bytes(&a, val_range(&a), &a, val_range(&a)),
254 Ordering::Equal
255 );
256 }
257
258 #[test]
259 fn compare_strings() {
260 let a = encode(&json!("apple"));
261 let b = encode(&json!("banana"));
262 assert_eq!(
263 compare_field_bytes(&a, val_range(&a), &b, val_range(&b)),
264 Ordering::Less
265 );
266 }
267
268 #[test]
269 fn compare_cross_type_null_vs_number() {
270 let a = encode(&json!(null));
271 let b = encode(&json!(42));
272 assert_eq!(
273 compare_field_bytes(&a, val_range(&a), &b, val_range(&b)),
274 Ordering::Less
275 );
276 }
277
278 #[test]
279 fn compare_cross_type_string_vs_number() {
280 let a = encode(&json!(42));
281 let b = encode(&json!("hello"));
282 assert_eq!(
283 compare_field_bytes(&a, val_range(&a), &b, val_range(&b)),
284 Ordering::Less
285 );
286 }
287
288 #[test]
289 fn compare_booleans() {
290 let a = encode(&json!(false));
291 let b = encode(&json!(true));
292 assert_eq!(
293 compare_field_bytes(&a, val_range(&a), &b, val_range(&b)),
294 Ordering::Less
295 );
296 }
297
298 #[test]
299 fn compare_negative_integers() {
300 let a = encode(&json!(-10));
301 let b = encode(&json!(-5));
302 assert_eq!(
303 compare_field_bytes(&a, val_range(&a), &b, val_range(&b)),
304 Ordering::Less
305 );
306 }
307
308 #[test]
309 fn field_bytes_eq_works() {
310 let a = encode(&json!("test"));
311 let b = encode(&json!("test"));
312 let c = encode(&json!("other"));
313 assert!(field_bytes_eq(&a, val_range(&a), &b, val_range(&b)));
314 assert!(!field_bytes_eq(&a, val_range(&a), &c, val_range(&c)));
315 }
316
317 #[test]
318 fn is_field_null_works() {
319 let null_buf = encode(&json!(null));
320 let int_buf = encode(&json!(42));
321 assert!(is_field_null(&null_buf, val_range(&null_buf)));
322 assert!(!is_field_null(&int_buf, val_range(&int_buf)));
323 }
324
325 #[test]
326 fn compare_floats() {
327 let a = encode(&json!(1.5));
328 let b = encode(&json!(2.5));
329 assert_eq!(
330 compare_field_bytes(&a, val_range(&a), &b, val_range(&b)),
331 Ordering::Less
332 );
333 }
334
335 fn cmp(a: &serde_json::Value, b: &serde_json::Value) -> Ordering {
336 let (a, b) = (encode(a), encode(b));
337 compare_field_bytes(&a, val_range(&a), &b, val_range(&b))
338 }
339
340 #[test]
341 fn integers_past_two_pow_53_compare_exactly() {
342 let above = json!(9_007_199_254_740_993_i64);
343 let at = json!(9_007_199_254_740_992_i64);
344 assert_eq!(cmp(&above, &at), Ordering::Greater);
345 assert_eq!(cmp(&at, &above), Ordering::Less);
346 assert_eq!(cmp(&above, &above), Ordering::Equal);
347 }
348
349 #[test]
350 fn nanosecond_timestamps_compare_exactly() {
351 let t1 = json!(1_700_000_000_000_000_001_i64);
352 let t2 = json!(1_700_000_000_000_000_002_i64);
353 assert_eq!(cmp(&t1, &t2), Ordering::Less);
354 assert_eq!(cmp(&t2, &t1), Ordering::Greater);
355 }
356
357 #[test]
358 fn uint64_above_i64_max_orders_above_every_i64() {
359 assert_eq!(cmp(&json!(u64::MAX), &json!(i64::MAX)), Ordering::Greater);
360 assert_eq!(cmp(&json!(u64::MAX - 1), &json!(u64::MAX)), Ordering::Less);
361 assert_eq!(cmp(&json!(i64::MIN), &json!(u64::MAX)), Ordering::Less);
362 let (a, b) = (encode(&json!(u64::MAX)), encode(&json!(-1)));
363 assert_eq!(compare_field_i64(&a, 0, &b, 0), Ordering::Greater);
364 }
365
366 #[test]
367 fn integer_against_float_compares_exactly() {
368 let above = json!(9_007_199_254_740_993_i64);
369 let float = json!(9_007_199_254_740_992.0_f64);
370 assert_eq!(cmp(&above, &float), Ordering::Greater);
371 assert_eq!(cmp(&float, &above), Ordering::Less);
372 assert_eq!(cmp(&json!(2), &json!(2.5)), Ordering::Less);
373 assert_eq!(cmp(&json!(3), &json!(3.0)), Ordering::Equal);
374 }
375
376 #[test]
379 fn nan_cells_sort_above_every_number() {
380 let float = |f: f64| {
381 nodedb_types::value_to_msgpack(&nodedb_types::Value::Float(f)).expect("encode float")
382 };
383 let nan = float(f64::NAN);
384 let inf = float(f64::INFINITY);
385 let max = encode(&json!(u64::MAX));
386 assert_eq!(
387 compare_field_bytes(&nan, val_range(&nan), &inf, val_range(&inf)),
388 Ordering::Greater
389 );
390 assert_eq!(
391 compare_field_bytes(&max, val_range(&max), &nan, val_range(&nan)),
392 Ordering::Less
393 );
394 assert_eq!(
395 compare_field_bytes(&nan, val_range(&nan), &nan, val_range(&nan)),
396 Ordering::Equal
397 );
398 let mut cells = [nan.clone(), encode(&json!(2)), float(-1.5), nan, max];
399 cells.sort_by(|a, b| compare_field_bytes(a, val_range(a), b, val_range(b)));
400 assert_eq!(cells[0], float(-1.5));
401 assert_eq!(cells[1], encode(&json!(2)));
402 assert_eq!(cells[2], encode(&json!(u64::MAX)));
403 }
404
405 #[test]
406 fn numbers_keep_cross_rank_order() {
407 assert_eq!(cmp(&json!(true), &json!(u64::MAX)), Ordering::Less);
408 assert_eq!(cmp(&json!(u64::MAX), &json!("")), Ordering::Less);
409 assert_eq!(cmp(&json!(null), &json!(-1.5)), Ordering::Less);
410 }
411
412 #[test]
413 fn compare_instants_negative_micros() {
414 use nodedb_types::{InstantKind, write_instant};
415 let mut a = Vec::new();
416 write_instant(&mut a, InstantKind::Utc, -10);
417 let mut b = Vec::new();
418 write_instant(&mut b, InstantKind::Utc, -5);
419 let mut c = Vec::new();
420 write_instant(&mut c, InstantKind::Utc, 1);
421 assert_eq!(
422 compare_field_bytes(&a, val_range(&a), &b, val_range(&b)),
423 Ordering::Less
424 );
425 assert_eq!(
426 compare_field_bytes(&a, val_range(&a), &c, val_range(&c)),
427 Ordering::Less
428 );
429 assert_eq!(
430 compare_field_bytes(&c, val_range(&c), &b, val_range(&b)),
431 Ordering::Greater
432 );
433 assert_eq!(
434 compare_field_bytes(&a, val_range(&a), &a, val_range(&a)),
435 Ordering::Equal
436 );
437 }
438
439 #[test]
440 fn compare_instant_kinds_order_utc_first() {
441 use nodedb_types::{InstantKind, write_instant};
442 let mut utc = Vec::new();
443 write_instant(&mut utc, InstantKind::Utc, 100);
444 let mut naive = Vec::new();
445 write_instant(&mut naive, InstantKind::Naive, 1);
446 assert_eq!(
447 compare_field_bytes(&utc, val_range(&utc), &naive, val_range(&naive)),
448 Ordering::Less
449 );
450 }
451
452 #[test]
453 fn compare_instant_vs_integer_uses_rank() {
454 use nodedb_types::{InstantKind, write_instant};
455 let mut inst = Vec::new();
458 write_instant(&mut inst, InstantKind::Naive, -1);
459 let int = encode(&json!(i64::MAX));
460 let s = encode(&json!(""));
461 assert_eq!(
462 compare_field_bytes(&int, val_range(&int), &inst, val_range(&inst)),
463 Ordering::Less
464 );
465 assert_eq!(
466 compare_field_bytes(&inst, val_range(&inst), &s, val_range(&s)),
467 Ordering::Less
468 );
469 }
470
471 #[test]
472 fn unknown_fixext8_ranks_as_ext() {
473 use nodedb_types::{InstantKind, write_instant};
474 let mut inst = Vec::new();
475 write_instant(&mut inst, InstantKind::Utc, 0);
476 let other = [0xd7, 0x09, 0, 0, 0, 0, 0, 0, 0, 0];
477 assert_eq!(
478 compare_field_bytes(&inst, val_range(&inst), &other, val_range(&other)),
479 Ordering::Less
480 );
481 }
482
483 #[test]
484 fn hash_from_extracted_field() {
485 let buf = encode(&json!({"id": 42}));
486 let range = crate::msgpack_scan::field::extract_field(&buf, 0, "id").unwrap();
487 let h = hash_field_bytes(&buf, range);
488 assert_ne!(h, 0);
489 }
490}