xmpp-parsers: Add a hexadecimal codec

Emmanuel Gil Peyrot created

That one accepts both uppercase and lowercase hexadecimal input, and
outputs in lowercase.

It requires no separator between bytes, unlike ColonSeparatedHex.

Change summary

parsers/src/util/helpers.rs | 48 +++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)

Detailed changes

parsers/src/util/helpers.rs 🔗

@@ -84,6 +84,27 @@ impl WhitespaceAwareBase64 {
     }
 }
 
+/// Codec for bytes of lowercase hexadecimal.
+pub struct Hex;
+
+impl Hex {
+    pub fn decode(s: &str) -> Result<Vec<u8>, Error> {
+        let mut bytes = Vec::with_capacity(s.len() / 2);
+        for i in 0..s.len() / 2 {
+            bytes.push(u8::from_str_radix(&s[2 * i..2 * i + 2], 16)?);
+        }
+        Ok(bytes)
+    }
+
+    pub fn encode(b: &[u8]) -> Option<String> {
+        let mut bytes = String::with_capacity(b.len() * 2);
+        for byte in b {
+            bytes.extend(format!("{:02x}", byte).chars());
+        }
+        Some(bytes)
+    }
+}
+
 /// Codec for colon-separated bytes of uppercase hexadecimal.
 pub struct ColonSeparatedHex;
 
@@ -121,3 +142,30 @@ impl JidCodec {
         Some(jid.to_string())
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn hex() {
+        let value = [0x01, 0xfe, 0xef];
+
+        // Test that we support both lowercase and uppercase as input.
+        let hex = Hex::decode("01feEF").unwrap();
+        assert_eq!(hex, &value);
+
+        // Test that we do output lowercase.
+        let hex = Hex::encode(&value).unwrap();
+        assert_eq!(hex, "01feef");
+    }
+
+    #[test]
+    fn bad_hex() {
+        // No colon supported.
+        Hex::decode("01:fe:EF").unwrap_err();
+
+        // No non-hex character allowed.
+        Hex::decode("01defg").unwrap_err();
+    }
+}