hex.rs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // Copyright (c) 2022-2023 Yuki Kishimoto
  2. // Distributed under the MIT software license
  3. //! Hex
  4. use core::fmt;
  5. /// Hex error
  6. #[derive(Debug, PartialEq, Eq)]
  7. pub enum Error {
  8. /// An invalid character was found
  9. InvalidHexCharacter {
  10. /// Char
  11. c: char,
  12. /// Char index
  13. index: usize,
  14. },
  15. /// A hex string's length needs to be even, as two digits correspond to
  16. /// one byte.
  17. OddLength,
  18. }
  19. impl std::error::Error for Error {}
  20. impl fmt::Display for Error {
  21. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  22. match self {
  23. Self::InvalidHexCharacter { c, index } => {
  24. write!(f, "Invalid character {} at position {}", c, index)
  25. }
  26. Self::OddLength => write!(f, "Odd number of digits"),
  27. }
  28. }
  29. }
  30. #[inline]
  31. fn from_digit(num: u8) -> char {
  32. if num < 10 {
  33. (b'0' + num) as char
  34. } else {
  35. (b'a' + num - 10) as char
  36. }
  37. }
  38. /// Hex encode
  39. pub fn encode<T>(data: T) -> String
  40. where
  41. T: AsRef<[u8]>,
  42. {
  43. let bytes: &[u8] = data.as_ref();
  44. let mut hex: String = String::with_capacity(2 * bytes.len());
  45. for byte in bytes.iter() {
  46. hex.push(from_digit(byte >> 4));
  47. hex.push(from_digit(byte & 0xF));
  48. }
  49. hex
  50. }
  51. const fn val(c: u8, idx: usize) -> Result<u8, Error> {
  52. match c {
  53. b'A'..=b'F' => Ok(c - b'A' + 10),
  54. b'a'..=b'f' => Ok(c - b'a' + 10),
  55. b'0'..=b'9' => Ok(c - b'0'),
  56. _ => Err(Error::InvalidHexCharacter {
  57. c: c as char,
  58. index: idx,
  59. }),
  60. }
  61. }
  62. /// Hex decode
  63. pub fn decode<T>(hex: T) -> Result<Vec<u8>, Error>
  64. where
  65. T: AsRef<[u8]>,
  66. {
  67. let hex = hex.as_ref();
  68. let len = hex.len();
  69. if len % 2 != 0 {
  70. return Err(Error::OddLength);
  71. }
  72. let mut bytes: Vec<u8> = Vec::with_capacity(len / 2);
  73. for i in (0..len).step_by(2) {
  74. let high = val(hex[i], i)?;
  75. let low = val(hex[i + 1], i + 1)?;
  76. bytes.push(high << 4 | low);
  77. }
  78. Ok(bytes)
  79. }
  80. #[cfg(test)]
  81. mod tests {
  82. use super::*;
  83. #[test]
  84. fn test_encode() {
  85. assert_eq!(encode("foobar"), "666f6f626172");
  86. }
  87. #[test]
  88. fn test_decode() {
  89. assert_eq!(
  90. decode("666f6f626172"),
  91. Ok(String::from("foobar").into_bytes())
  92. );
  93. }
  94. #[test]
  95. pub fn test_invalid_length() {
  96. assert_eq!(decode("1").unwrap_err(), Error::OddLength);
  97. assert_eq!(decode("666f6f6261721").unwrap_err(), Error::OddLength);
  98. }
  99. #[test]
  100. pub fn test_invalid_char() {
  101. assert_eq!(
  102. decode("66ag").unwrap_err(),
  103. Error::InvalidHexCharacter { c: 'g', index: 3 }
  104. );
  105. }
  106. }
  107. #[cfg(bench)]
  108. mod benches {
  109. use super::*;
  110. use crate::test::{black_box, Bencher};
  111. const EVENT_JSON: &str = r#"{"content":"uRuvYr585B80L6rSJiHocw==?iv=oh6LVqdsYYol3JfFnXTbPA==","created_at":1640839235,"id":"2be17aa3031bdcb006f0fce80c146dea9c1c0268b0af2398bb673365c6444d45","kind":4,"pubkey":"f86c44a2de95d9149b51c6a29afeabba264c18e2fa7c49de93424a0c56947785","sig":"a5d9290ef9659083c490b303eb7ee41356d8778ff19f2f91776c8dc4443388a64ffcf336e61af4c25c05ac3ae952d1ced889ed655b67790891222aaa15b99fdd","tags":[["p","13adc511de7e1cfcf1c6b7f6365fb5a03442d7bcacf565ea57fa7770912c023d"]]}"#;
  112. #[bench]
  113. pub fn hex_encode(bh: &mut Bencher) {
  114. bh.iter(|| {
  115. black_box(encode(EVENT_JSON));
  116. });
  117. }
  118. #[bench]
  119. pub fn hex_decode(bh: &mut Bencher) {
  120. let h = "7b22636f6e74656e74223a227552757659723538354238304c3672534a69486f63773d3d3f69763d6f68364c5671647359596f6c334a66466e58546250413d3d222c22637265617465645f6174223a313634303833393233352c226964223a2232626531376161333033316264636230303666306663653830633134366465613963316330323638623061663233393862623637333336356336343434643435222c226b696e64223a342c227075626b6579223a2266383663343461326465393564393134396235316336613239616665616262613236346331386532666137633439646539333432346130633536393437373835222c22736967223a226135643932393065663936353930383363343930623330336562376565343133353664383737386666313966326639313737366338646334343433333838613634666663663333366536316166346332356330356163336165393532643163656438383965643635356236373739303839313232326161613135623939666464222c2274616773223a5b5b2270222c2231336164633531316465376531636663663163366237663633363566623561303334343264376263616366353635656135376661373737303931326330323364225d5d7d";
  121. bh.iter(|| {
  122. black_box(decode(h)).unwrap();
  123. });
  124. }
  125. }