use crate::{Read, Stack, JsonError};
#[must_use]
#[inline(always)]
pub(super) fn validate_hex(bytes: [u8; 4]) -> bool {
let bytes = u32::from_ne_bytes(bytes);
const HIGH_BIT: u32 = 1 << 7;
const HIGH_BITS: u32 = (HIGH_BIT << 24) | (HIGH_BIT << 16) | (HIGH_BIT << 8) | HIGH_BIT;
const ZERO_CHAR: u32 =
((b'0' as u32) << 24) | ((b'0' as u32) << 16) | ((b'0' as u32) << 8) | (b'0' as u32);
const DISTANCE_AFTER_NINE: u32 = HIGH_BIT - ((b'9' + 1) as u32);
const DISTANCES_AFTER_NINE: u32 = (DISTANCE_AFTER_NINE << 24) |
(DISTANCE_AFTER_NINE << 16) |
(DISTANCE_AFTER_NINE << 8) |
DISTANCE_AFTER_NINE;
const FIFTH_BIT: u32 = 1 << 5;
const FIFTH_BITS: u32 = (FIFTH_BIT << 24) | (FIFTH_BIT << 16) | (FIFTH_BIT << 8) | FIFTH_BIT;
const A_CHAR: u32 =
((b'a' as u32) << 24) | ((b'a' as u32) << 16) | ((b'a' as u32) << 8) | (b'a' as u32);
const DISTANCE_AFTER_F: u32 = HIGH_BIT - ((b'f' + 1) as u32);
const DISTANCES_AFTER_F: u32 = (DISTANCE_AFTER_F << 24) |
(DISTANCE_AFTER_F << 16) |
(DISTANCE_AFTER_F << 8) |
DISTANCE_AFTER_F;
let bytes_with_high_bits = bytes | HIGH_BITS;
let gte_zero = bytes_with_high_bits.wrapping_sub(ZERO_CHAR);
let gte_a = (bytes_with_high_bits | FIFTH_BITS).wrapping_sub(A_CHAR);
let lte_9 = bytes.wrapping_add(DISTANCES_AFTER_NINE);
let lte_f = (bytes | FIFTH_BITS).wrapping_add(DISTANCES_AFTER_F);
let number = gte_zero ^ lte_9;
let alpha = gte_a ^ lte_f;
let number_or_alpha = number | alpha;
let ascii = (!bytes) & HIGH_BITS;
(ascii & number_or_alpha) == HIGH_BITS
}
#[inline(always)]
pub(super) fn read_hex<'read, R: Read<'read>, S: Stack>(
hex: [u8; 4],
) -> Result<u32, JsonError<'read, R, S>> {
#[inline(always)]
fn hex_char<'read, R: Read<'read>, S: Stack>(char: u8) -> Result<u16, JsonError<'read, R, S>> {
Ok(match char {
b'0' => 0,
b'1' => 1,
b'2' => 2,
b'3' => 3,
b'4' => 4,
b'5' => 5,
b'6' => 6,
b'7' => 7,
b'8' => 8,
b'9' => 9,
b'a' | b'A' => 10,
b'b' | b'B' => 11,
b'c' | b'C' => 12,
b'd' | b'D' => 13,
b'e' | b'E' => 14,
b'f' | b'F' => 15,
_ => Err(JsonError::InternalError)?,
})
}
Ok(u32::from(
(hex_char(hex[0])? << 12) |
(hex_char(hex[1])? << 8) |
(hex_char(hex[2])? << 4) |
hex_char(hex[3])?,
))
}
#[test]
fn test_validate_hex() {
let mut hex = [0, b'f', b'f', b'f'];
for i in u8::MIN ..= u8::MAX {
hex[0] = i;
assert_eq!(validate_hex(hex), hex[0].is_ascii_hexdigit());
}
}
#[test]
fn bench_validate_hex() {
#[cfg(debug_assertions)]
const ITERATIONS: u64 = 1_000_000_000u64;
#[cfg(not(debug_assertions))]
const ITERATIONS: u64 = 20_000_000_000u64;
let hex = *b"ffff";
{
let start = std::time::Instant::now();
for _ in 0 .. ITERATIONS {
let _ = core::hint::black_box(validate_hex(core::hint::black_box(hex)));
}
println!("`validate_hex` took {}ms", start.elapsed().as_millis());
}
{
let start = std::time::Instant::now();
for _ in 0 .. ITERATIONS {
let hex = core::hint::black_box(hex);
core::hint::black_box(
hex[0].is_ascii_hexdigit() &&
hex[1].is_ascii_hexdigit() &&
hex[2].is_ascii_hexdigit() &&
hex[3].is_ascii_hexdigit(),
);
}
println!("4 * `is_ascii_hexdigit` took {}ms", start.elapsed().as_millis());
}
}