change serde format for BigUint to use a hex string instead of an array of u32 chunks
authorJacob Lifshay <programmerjake@gmail.com>
Tue, 28 Feb 2023 06:35:56 +0000 (22:35 -0800)
committerJacob Lifshay <programmerjake@gmail.com>
Tue, 28 Feb 2023 06:35:56 +0000 (22:35 -0800)
register_allocator/src/loc_set.rs

index 7f987f677249b52469efecb9c28303815535bee8..71dd6207dd418bdb093c273fb5ad2d773c452f32 100644 (file)
@@ -5,11 +5,11 @@ use crate::{
 };
 use enum_map::{enum_map, EnumMap};
 use num_bigint::BigUint;
-use num_traits::Zero;
+use num_traits::{Num, Zero};
 use once_cell::race::OnceBox;
-use serde::{Deserialize, Serialize};
+use serde::{de, Deserialize, Serialize};
 use std::{
-    borrow::Borrow,
+    borrow::{Borrow, Cow},
     cell::Cell,
     collections::{
         btree_map::{self, Entry},
@@ -32,25 +32,88 @@ fn zero_biguint<'a>() -> &'a BigUint {
     )
 }
 
-#[derive(Deserialize)]
-struct LocSetSerialized {
-    starts_map: BTreeMap<NonZeroU32, EnumMap<LocKind, BigUint>>,
+#[derive(Debug, Clone)]
+struct BigUintAsHexString<'a>(Cow<'a, BigUint>);
+
+impl<'de, 'a> Deserialize<'de> for BigUintAsHexString<'a> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let text = String::deserialize(deserializer)?;
+        if let Some(text) = text.strip_prefix("0x") {
+            Ok(Self(Cow::Owned(
+                BigUint::from_str_radix(text, 0x10).map_err(<D::Error as de::Error>::custom)?,
+            )))
+        } else {
+            Err(<D::Error as de::Error>::custom(
+                "expected hex string to start with `0x`",
+            ))
+        }
+    }
+}
+
+impl Serialize for BigUintAsHexString<'_> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        format!("{:#x}", &*self.0).serialize(serializer)
+    }
+}
+
+#[derive(Deserialize, Serialize)]
+struct LocSetSerialized<'a> {
+    starts_map: BTreeMap<NonZeroU32, BTreeMap<LocKind, BigUintAsHexString<'a>>>,
 }
 
-impl TryFrom<LocSetSerialized> for LocSet {
+impl TryFrom<LocSetSerialized<'_>> for LocSet {
     type Error = Error;
 
     fn try_from(value: LocSetSerialized) -> Result<Self, Self::Error> {
-        Self::from_starts_map(value.starts_map)
+        Self::from_starts_map(
+            value
+                .starts_map
+                .into_iter()
+                .map(|(k, mut v)| {
+                    let v = enum_map! {
+                        k => v.remove(&k).map_or_else(BigUint::zero, |v| v.0.into_owned())
+                    };
+                    (k, v)
+                })
+                .collect(),
+        )
     }
 }
 
-#[derive(Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Clone, Default, PartialEq, Eq, Hash, Deserialize)]
 #[serde(try_from = "LocSetSerialized")]
 pub struct LocSet {
     starts_map: BTreeMap<NonZeroU32, EnumMap<LocKind, BigUint>>,
 }
 
+impl Serialize for LocSet {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        LocSetSerialized {
+            starts_map: self
+                .starts_map
+                .iter()
+                .map(|(&k, v)| {
+                    let v = v
+                        .iter()
+                        .map(|(k, v)| (k, BigUintAsHexString(Cow::Borrowed(v))))
+                        .collect::<BTreeMap<_, _>>();
+                    (k, v)
+                })
+                .collect(),
+        }
+        .serialize(serializer)
+    }
+}
+
 impl<'a> arbitrary::Arbitrary<'a> for LocSet {
     fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
         u.arbitrary_iter()?.collect()