raphtory_api/core/input/
input_node.rs1use crate::core::utils::hashing;
8
9const MAX_U64_BYTES: [u8; 20] = [
10 49, 56, 52, 52, 54, 55, 52, 52, 48, 55, 51, 55, 48, 57, 53, 53, 49, 54, 49, 53,
11];
12
13pub fn parse_u64_strict(input: &str) -> Option<u64> {
14 if input.len() > 20 {
15 return None;
17 }
18 let byte_0 = b'0';
19 let byte_1 = b'1';
20 let byte_9 = b'9';
21 let mut input_iter = input.bytes();
22 let first = input_iter.next()?;
23 if first == byte_0 {
24 return input_iter.next().is_none().then_some(0);
25 }
26
27 let mut check_max = input.len() == 20;
28 if check_max {
29 if !(byte_1..=MAX_U64_BYTES[0]).contains(&first) {
30 return None;
31 }
32 } else if !(byte_1..=byte_9).contains(&first) {
33 return None;
34 }
35
36 let mut result = (first - byte_0) as u64;
37 for (next_byte, max_byte) in input_iter.zip(MAX_U64_BYTES[1..].iter().copied()) {
38 if check_max {
39 if !(byte_0..=max_byte).contains(&next_byte) {
40 return None;
41 }
42 check_max = next_byte == max_byte;
43 } else if !(byte_0..=byte_9).contains(&next_byte) {
44 return None;
45 }
46 result = result * 10 + (next_byte - byte_0) as u64;
47 }
48 Some(result)
49}
50
51pub trait InputNode: Clone {
52 fn id(&self) -> u64;
53 fn id_str(&self) -> Option<&str>;
54}
55
56impl InputNode for u64 {
57 fn id(&self) -> u64 {
58 *self
59 }
60
61 fn id_str(&self) -> Option<&str> {
62 None
63 }
64}
65
66impl InputNode for &str {
67 fn id(&self) -> u64 {
68 parse_u64_strict(self).unwrap_or_else(|| hashing::calculate_hash(self))
69 }
70
71 fn id_str(&self) -> Option<&str> {
72 Some(self)
73 }
74}
75
76impl InputNode for String {
77 fn id(&self) -> u64 {
78 let s: &str = self;
79 s.id()
80 }
81
82 fn id_str(&self) -> Option<&str> {
83 Some(self)
84 }
85}
86
87#[cfg(test)]
88mod test {
89 use crate::core::input::input_node::{parse_u64_strict, InputNode};
90 use proptest::prelude::*;
91
92 #[test]
93 fn test_weird_num_edge_cases() {
94 assert_ne!("+3".id(), "3".id());
95 assert_eq!(3.id(), "3".id());
96 assert_ne!("00".id(), "0".id());
97 assert_eq!("0".id(), 0.id());
98 }
99
100 #[test]
101 fn test_u64_string_works() {
102 proptest!(|(n in any::<u64>())| {
103 assert_eq!(n.to_string().id(), n);
104 });
105 }
106
107 #[test]
108 fn test_if_str_parses_it_is_a_u64() {
109 proptest!(|(s in any::<String>())| {
110 let res = parse_u64_strict(&s);
111 if let Some(n) = res {
112 assert_eq!(n.to_string(), s)
113 } else {
114 assert_ne!(s.id().to_string(), s)
115 }
116 });
117 }
118}