arch: Helper functions for 64-bit hex (de-)serialization

We create 64-bit analogues of the already existing hex (de-)serializer
helper functions for the 32-bit case.

These are necessary because the CPU profile needs associated data
describing how to adjust feature MSRs whose values are 64-bits.

In this case we prefer some small amount of code duplication over
macros and/or traits since we do not expect the need for further
variants of these helpers.

Signed-off-by: Oliver Anderson <oliver.anderson@cyberus-technology.de>
On-behalf-of: SAP oliver.anderson@sap.com
This commit is contained in:
Oliver Anderson
2026-06-08 09:24:42 +02:00
committed by Rob Bradford
parent 2eb64a2b4f
commit 838d01b0bc

View File

@@ -3,8 +3,6 @@
// SPDX-License-Identifier: Apache-2.0
//
use std::result;
use serde::{Deserialize, Deserializer, Serializer, de};
/// Serializes the given `input` as a hex string (starting with "0x").
@@ -14,22 +12,42 @@ use serde::{Deserialize, Deserializer, Serializer, de};
pub(crate) fn serialize_u32_hex<S: Serializer>(
input: &u32,
serializer: S,
) -> result::Result<S::Ok, S::Error> {
) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&format!("{input:#x}"))
}
/// Deserializes a u32 from a hex string representation.
pub(crate) fn deserialize_u32_hex<'de, D: Deserializer<'de>>(
deserializer: D,
) -> result::Result<u32, D::Error> {
) -> Result<u32, D::Error> {
let hex: &str = <&str>::deserialize(deserializer)?;
u32::from_str_radix(hex.strip_prefix("0x").unwrap_or(""), 16).map_err(|_| {
<D::Error as de::Error>::custom(format!("{hex} is not a hex encoded 32 bit integer"))
})
}
/// 64-bit version of `serialize_u32_hex`
pub(crate) fn serialize_u64_hex<S: Serializer>(
input: &u64,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&format!("{input:#x}"))
}
/// 64-bit version of `deserialize_u32_hex`
pub(crate) fn deserialize_u64_hex<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<u64, D::Error> {
let hex: &str = <&str>::deserialize(deserializer)?;
u64::from_str_radix(hex.strip_prefix("0x").unwrap_or(""), 16).map_err(|_| {
<D::Error as de::Error>::custom(format!("{hex} is not a hex encoded 64 bit integer"))
})
}
#[cfg(test)]
mod unit_tests {
use std::fmt::Debug;
use proptest::prelude::*;
use serde::{Deserialize, Serialize};
@@ -49,34 +67,63 @@ mod unit_tests {
bar: u32,
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
struct TestStruct64 {
#[serde(
serialize_with = "serialize_u64_hex",
deserialize_with = "deserialize_u64_hex"
)]
foo: u64,
#[serde(
serialize_with = "serialize_u64_hex",
deserialize_with = "deserialize_u64_hex"
)]
bar: u64,
}
// Check that our hex serializers satisfy the two following invariants
// 1. Serialization followed by deserialization is the identity.
// 2. Values of type u32 are serialized to strings starting with "0x" and then
// 2. Values of type u32/u64 are serialized to strings starting with "0x" and then
// a sub-string where all characters are ascii hex digits (with the letters [a-f] always in lowercase).
fn test_hex_serialization<T>(t: T) -> Result<(), TestCaseError>
where
T: Serialize + Debug + Eq + Copy,
for<'de> T: Deserialize<'de>,
{
let t_string = serde_json::to_string(&t).unwrap();
let t_deserialized = serde_json::from_str(&t_string).unwrap();
prop_assert_eq!(t, t_deserialized);
let t_json = serde_json::to_value(t).unwrap();
let check_str_invariants = |value: &str| {
prop_assert!(value.starts_with("0x"));
prop_assert!(value.as_bytes()[2..].iter().all(u8::is_ascii_hexdigit));
prop_assert!(!value.as_bytes()[2..].iter().any(u8::is_ascii_uppercase));
Ok(())
};
let foo_str = t_json.get("foo").unwrap().as_str().unwrap();
let bar_str = t_json.get("bar").unwrap().as_str().unwrap();
check_str_invariants(foo_str)?;
check_str_invariants(bar_str)?;
Ok(())
}
proptest! {
#[test]
fn hex_serialization_works(foo in any::<u32>(), bar in any::<u32>()) {
fn hex_serialization_works_32(foo in any::<u32>(), bar in any::<u32>()) {
let t = TestStruct { foo , bar };
test_hex_serialization(t)?;
}
}
let t_string = serde_json::to_string(&t).unwrap();
let t_deserialized = serde_json::from_str(&t_string).unwrap();
prop_assert_eq!(t, t_deserialized);
let t_json = serde_json::to_value(t).unwrap();
let check_str_invariants = |value: &str| {
prop_assert!(value.starts_with("0x"));
prop_assert!(value.as_bytes()[2..].iter().all(u8::is_ascii_hexdigit));
prop_assert!(!value.as_bytes()[2..].iter().any(u8::is_ascii_uppercase));
Ok(())
};
let foo_str = t_json.get("foo").unwrap().as_str().unwrap();
let bar_str = t_json.get("bar").unwrap().as_str().unwrap();
check_str_invariants(foo_str)?;
check_str_invariants(bar_str)?;
proptest! {
#[test]
fn hex_serialization_works_64(foo in any::<u64>(), bar in any::<u64>()) {
let t = TestStruct64 { foo , bar };
test_hex_serialization(t)?;
}
}
}