|
1 | 1 | use std::borrow::Cow;
|
| 2 | +use std::cmp::Ordering; |
| 3 | +use std::hash::{Hash, Hasher}; |
2 | 4 | use std::sync::Arc;
|
3 | 5 | use std::{fmt, hash};
|
4 | 6 |
|
@@ -422,6 +424,55 @@ impl KeyValue {
|
422 | 424 | }
|
423 | 425 | }
|
424 | 426 |
|
| 427 | +#[derive(Debug, Clone, Copy)] |
| 428 | +struct F64Hasheable(f64); |
| 429 | + |
| 430 | +impl PartialEq for F64Hasheable { |
| 431 | + fn eq(&self, other: &Self) -> bool { |
| 432 | + self.0.to_bits() == other.0.to_bits() |
| 433 | + } |
| 434 | +} |
| 435 | + |
| 436 | +impl Eq for F64Hasheable {} |
| 437 | + |
| 438 | +impl Hash for F64Hasheable { |
| 439 | + fn hash<H: Hasher>(&self, state: &mut H) { |
| 440 | + self.0.to_bits().hash(state); |
| 441 | + } |
| 442 | +} |
| 443 | + |
| 444 | +impl Hash for KeyValue { |
| 445 | + fn hash<H: Hasher>(&self, state: &mut H) { |
| 446 | + self.key.hash(state); |
| 447 | + match &self.value { |
| 448 | + Value::F64(f) => F64Hasheable(*f).hash(state), |
| 449 | + Value::Array(a) => match a { |
| 450 | + Array::Bool(b) => b.hash(state), |
| 451 | + Array::I64(i) => i.hash(state), |
| 452 | + Array::F64(f) => f.iter().for_each(|f| F64Hasheable(*f).hash(state)), |
| 453 | + Array::String(s) => s.hash(state), |
| 454 | + }, |
| 455 | + Value::Bool(b) => b.hash(state), |
| 456 | + Value::I64(i) => i.hash(state), |
| 457 | + Value::String(s) => s.hash(state), |
| 458 | + }; |
| 459 | + } |
| 460 | +} |
| 461 | + |
| 462 | +impl PartialOrd for KeyValue { |
| 463 | + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
| 464 | + Some(self.cmp(other)) |
| 465 | + } |
| 466 | +} |
| 467 | + |
| 468 | +impl Ord for KeyValue { |
| 469 | + fn cmp(&self, other: &Self) -> Ordering { |
| 470 | + self.key.cmp(&other.key) |
| 471 | + } |
| 472 | +} |
| 473 | + |
| 474 | +impl Eq for KeyValue {} |
| 475 | + |
425 | 476 | /// Marker trait for errors returned by exporters
|
426 | 477 | pub trait ExportError: std::error::Error + Send + Sync + 'static {
|
427 | 478 | /// The name of exporter that returned this error
|
@@ -594,3 +645,74 @@ impl InstrumentationLibraryBuilder {
|
594 | 645 | }
|
595 | 646 | }
|
596 | 647 | }
|
| 648 | + |
| 649 | +#[cfg(test)] |
| 650 | +mod tests { |
| 651 | + use rand::Rng; |
| 652 | + |
| 653 | + use crate::KeyValue; |
| 654 | + use std::hash::DefaultHasher; |
| 655 | + use std::hash::{Hash, Hasher}; |
| 656 | + |
| 657 | + #[test] |
| 658 | + fn equality_kv_float() { |
| 659 | + let kv1 = KeyValue::new("key", 1.0); |
| 660 | + let kv2 = KeyValue::new("key", 1.0); |
| 661 | + assert_eq!(kv1, kv2); |
| 662 | + |
| 663 | + let kv1 = KeyValue::new("key", 1.0); |
| 664 | + let kv2 = KeyValue::new("key", 1.01); |
| 665 | + assert_ne!(kv1, kv2); |
| 666 | + |
| 667 | + let kv1 = KeyValue::new("key", std::f64::NAN); |
| 668 | + let kv2 = KeyValue::new("key", std::f64::NAN); |
| 669 | + assert_ne!(kv1, kv2, "NAN is not equal to itself"); |
| 670 | + |
| 671 | + let kv1 = KeyValue::new("key", std::f64::INFINITY); |
| 672 | + let kv2 = KeyValue::new("key", std::f64::INFINITY); |
| 673 | + assert_eq!(kv1, kv2); |
| 674 | + |
| 675 | + let mut rng = rand::thread_rng(); |
| 676 | + |
| 677 | + for _ in 0..100 { |
| 678 | + let random_value = rng.gen::<f64>(); |
| 679 | + let kv1 = KeyValue::new("key", random_value); |
| 680 | + let kv2 = KeyValue::new("key", random_value); |
| 681 | + assert_eq!(kv1, kv2); |
| 682 | + } |
| 683 | + } |
| 684 | + |
| 685 | + #[test] |
| 686 | + fn hash_kv_float() { |
| 687 | + let kv1 = KeyValue::new("key", 1.0); |
| 688 | + let kv2 = KeyValue::new("key", 1.0); |
| 689 | + assert_eq!(hash_helper(&kv1), hash_helper(&kv2)); |
| 690 | + |
| 691 | + let kv1 = KeyValue::new("key", 1.001); |
| 692 | + let kv2 = KeyValue::new("key", 1.001); |
| 693 | + assert_eq!(hash_helper(&kv1), hash_helper(&kv2)); |
| 694 | + |
| 695 | + let kv1 = KeyValue::new("key", std::f64::NAN); |
| 696 | + let kv2 = KeyValue::new("key", std::f64::NAN); |
| 697 | + assert_eq!(hash_helper(&kv1), hash_helper(&kv2)); |
| 698 | + |
| 699 | + let kv1 = KeyValue::new("key", std::f64::INFINITY); |
| 700 | + let kv2 = KeyValue::new("key", std::f64::INFINITY); |
| 701 | + assert_eq!(hash_helper(&kv1), hash_helper(&kv2)); |
| 702 | + |
| 703 | + let mut rng = rand::thread_rng(); |
| 704 | + |
| 705 | + for _ in 0..100 { |
| 706 | + let random_value = rng.gen::<f64>(); |
| 707 | + let kv1 = KeyValue::new("key", random_value); |
| 708 | + let kv2 = KeyValue::new("key", random_value); |
| 709 | + assert_eq!(hash_helper(&kv1), hash_helper(&kv2)); |
| 710 | + } |
| 711 | + } |
| 712 | + |
| 713 | + fn hash_helper<T: Hash>(item: &T) -> u64 { |
| 714 | + let mut hasher = DefaultHasher::new(); |
| 715 | + item.hash(&mut hasher); |
| 716 | + hasher.finish() |
| 717 | + } |
| 718 | +} |
0 commit comments