1pub const DICT_VERSION: u8 = 3;
2
3pub const LIST_EMPTY: u8 = 0;
5pub const DICTIONARY_0: u8 = 236;
6pub const DICTIONARY_1: u8 = 237;
7pub const DICTIONARY_2: u8 = 238;
8pub const DICTIONARY_3: u8 = 239;
9
10pub const JID_PAIR: u8 = 250;
11pub const HEX_8: u8 = 251;
12pub const BINARY_8: u8 = 252;
13pub const BINARY_20: u8 = 253;
14pub const BINARY_32: u8 = 254;
15pub const NIBBLE_8: u8 = 255;
16pub const INTEROP_JID: u8 = 245;
17pub const FB_JID: u8 = 246;
18pub const AD_JID: u8 = 247;
19pub const LIST_8: u8 = 248;
20pub const LIST_16: u8 = 249;
21
22pub const PACKED_MAX: u8 = 127;
23pub const SINGLE_BYTE_MAX: u16 = 256;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum TokenKind {
27 Single(u8),
28 Double(u8, u8),
29}
30
31include!(concat!(env!("OUT_DIR"), "/token_maps.rs"));
32
33pub fn index_of_token(token: &str) -> Option<TokenKind> {
34 hashify_lookup(token.as_bytes()).copied()
35}
36
37pub fn get_single_token(index: u8) -> Option<&'static str> {
38 SINGLE_BYTE_TOKENS.get(index as usize).copied()
39}
40
41pub fn get_double_token(dict: u8, index: u8) -> Option<&'static str> {
42 DOUBLE_BYTE_TOKENS
43 .get(dict as usize)
44 .and_then(|d| d.get(index as usize))
45 .copied()
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn test_single_byte_token_roundtrip() {
54 for i in 1u8..=235 {
55 if let Some(token) = get_single_token(i) {
56 let result = index_of_token(token);
57 assert!(
58 matches!(result, Some(TokenKind::Single(idx)) if idx == i),
59 "Token '{}' at index {} doesn't round-trip",
60 token,
61 i,
62 );
63 }
64 }
65 }
66
67 #[test]
68 fn test_double_byte_token_roundtrip() {
69 for dict in 0..4u8 {
70 for idx in 0..255u8 {
71 if let Some(token) = get_double_token(dict, idx) {
72 let result = index_of_token(token);
73 assert!(
74 matches!(result, Some(TokenKind::Double(d, i)) if d == dict && i == idx),
75 "Token '{}' at dict {} index {} doesn't round-trip",
76 token,
77 dict,
78 idx,
79 );
80 }
81 }
82 }
83 }
84
85 #[test]
86 fn test_unknown_string_returns_none() {
87 assert!(index_of_token("xyzzy_not_a_token_12345").is_none());
88 }
89
90 #[test]
91 fn test_empty_string_returns_none() {
92 assert!(index_of_token("").is_none());
93 }
94
95 #[test]
96 fn test_token_boundary_indices() {
97 let token_0 = get_single_token(0);
98 assert_eq!(token_0, Some(""), "Index 0 should be empty string token");
99 assert!(get_single_token(LIST_8).is_none());
100 assert!(get_single_token(LIST_16).is_none());
101 assert!(get_single_token(JID_PAIR).is_none());
102 assert!(get_single_token(HEX_8).is_none());
103 assert!(get_single_token(BINARY_8).is_none());
104 assert!(get_single_token(BINARY_20).is_none());
105 assert!(get_single_token(BINARY_32).is_none());
106 assert!(get_single_token(NIBBLE_8).is_none());
107 }
108
109 #[test]
110 fn test_almost_matching_strings() {
111 let token = get_single_token(1).expect("single token at index 1 must exist");
112 assert!(index_of_token(&format!("{}_modified", token)).is_none());
113 assert!(index_of_token(&format!("prefix_{}", token)).is_none());
114 assert!(index_of_token(&format!("{}!", token)).is_none());
115 }
116
117 #[test]
118 fn test_out_of_bounds_dictionary() {
119 assert!(get_double_token(4, 0).is_none());
120 assert!(get_double_token(5, 100).is_none());
121 assert!(get_double_token(255, 0).is_none());
122 }
123}