From 838d01b0bcf46e29765726986658ffd369a5ef0a Mon Sep 17 00:00:00 2001 From: Oliver Anderson Date: Mon, 8 Jun 2026 09:24:42 +0200 Subject: [PATCH] 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 On-behalf-of: SAP oliver.anderson@sap.com --- arch/src/x86_64/helpers.rs | 97 ++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 25 deletions(-) diff --git a/arch/src/x86_64/helpers.rs b/arch/src/x86_64/helpers.rs index b1f04a09f..981732d28 100644 --- a/arch/src/x86_64/helpers.rs +++ b/arch/src/x86_64/helpers.rs @@ -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( input: &u32, serializer: S, -) -> result::Result { +) -> Result { 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 { +) -> Result { let hex: &str = <&str>::deserialize(deserializer)?; u32::from_str_radix(hex.strip_prefix("0x").unwrap_or(""), 16).map_err(|_| { ::custom(format!("{hex} is not a hex encoded 32 bit integer")) }) } +/// 64-bit version of `serialize_u32_hex` +pub(crate) fn serialize_u64_hex( + input: &u64, + serializer: S, +) -> Result { + 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 { + let hex: &str = <&str>::deserialize(deserializer)?; + u64::from_str_radix(hex.strip_prefix("0x").unwrap_or(""), 16).map_err(|_| { + ::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) -> 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::(), bar in any::()) { + fn hex_serialization_works_32(foo in any::(), bar in any::()) { 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::(), bar in any::()) { + let t = TestStruct64 { foo , bar }; + test_hex_serialization(t)?; } } }