public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
To: Christoph Heiss <c.heiss@proxmox.com>
Cc: pve-devel@lists.proxmox.com
Subject: Re: [PATCH proxmox v2 2/8] serde: add base64 module for byte arrays
Date: Tue, 24 Mar 2026 13:57:34 +0100	[thread overview]
Message-ID: <euwxondwcgtfvggbdg3aqdvojt6eyb2nprd5ncyjpsladmz63a@ayzaqft2zvaj> (raw)
In-Reply-To: <20260213143601.1424613-3-c.heiss@proxmox.com>

On Fri, Feb 13, 2026 at 03:35:55PM +0100, Christoph Heiss wrote:
> Allows to directly en-/decode [u8; N] to/from a base64 string, much like
> the already existing bytes_as_base64 allows for Vec<u8>.
> 
> Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
> ---
> Changes v1 -> v2:
>   * no changes
> 
>  proxmox-serde/src/lib.rs | 91 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 91 insertions(+)
> 
> diff --git a/proxmox-serde/src/lib.rs b/proxmox-serde/src/lib.rs
> index 28c3054d..c3ec4f41 100644
> --- a/proxmox-serde/src/lib.rs
> +++ b/proxmox-serde/src/lib.rs
> @@ -159,3 +159,94 @@ pub mod string_as_base64 {
>          <T as StrAsBase64>::de::<'de, D>(deserializer)
>      }
>  }
> +
> +/// Serialize `[u8; N]` or `Option<[u8; N]>` as base64 encoded.
> +///
> +/// If you do not need the convenience of handling both [u8; N] and Option transparently, you could
> +/// also use [`proxmox_base64`] directly.
> +///
> +/// Usage example:
> +/// ```
> +/// use serde::{Deserialize, Serialize};
> +///
> +/// #[derive(Debug, Deserialize, PartialEq, Serialize)]
> +/// struct Foo {
> +///     #[serde(with = "proxmox_serde::byte_array_as_base64")]
> +///     data: [u8; 4],
> +/// }
> +///
> +/// let obj = Foo { data: [1, 2, 3, 4] };
> +/// let json = serde_json::to_string(&obj).unwrap();
> +/// assert_eq!(json, r#"{"data":"AQIDBA=="}"#);
> +///
> +/// let deserialized: Foo = serde_json::from_str(&json).unwrap();
> +/// assert_eq!(obj, deserialized);
> +/// ```
> +pub mod byte_array_as_base64 {
> +    use serde::{Deserialize, Deserializer, Serializer};
> +
> +    /// Private trait to enable `byte_array_as_base64` for `Option<[u8; N]>` in addition to `[u8; N]`.
> +    #[doc(hidden)]
> +    pub trait ByteArrayAsBase64<const N: usize>: Sized {
> +        fn ser<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>;
> +        fn de<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>;
> +    }
> +
> +    fn finish_deserializing<'de, const N: usize, D: Deserializer<'de>>(
> +        string: String,
> +    ) -> Result<[u8; N], D::Error> {
> +        use serde::de::Error;
> +
> +        let vec = proxmox_base64::decode(string).map_err(|err| {

Something to think about (not necessarily act on just yet...):

The `proxmox-base64` crate contains commented-out `decode_slice`
functions, which I mainly left out because we had not needed them before
and I wasn't sure whether they need some bike shedding (eg.
`decode_*to*_slice?)` or, they could technically also accept a
`MaybeUninit<[u8]>` for convenience which could allow you to do
something like

    let mut data = MaybeUninit::<[u8; N]>::uninit();
    let size = proxmox_base64::decode_to_uninit(&mut data)?;
    check_that(size == N);
    Ok(data.assume_init())

Just not sure how much of an API surface we want the base64 crate to
have in the first place... (that mainly depends on how much of a
headache the future base64 crate APIs are going to be (probably very
painful...))

> +            let msg = format!("base64 decode: {}", err);
> +            Error::custom(msg)
> +        })?;
> +
> +        vec.as_slice().try_into().map_err(|_| {
> +            let msg = format!("expected {N} bytes, got {}", vec.len());
> +            Error::custom(msg)
> +        })
> +    }
> +
> +    impl<const N: usize> ByteArrayAsBase64<N> for [u8; N] {
> +        fn ser<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
> +            serializer.serialize_str(&proxmox_base64::encode(self))
> +        }
> +
> +        fn de<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
> +            finish_deserializing::<'de, N, D>(String::deserialize(deserializer)?)
> +        }
> +    }
> +
> +    impl<const N: usize> ByteArrayAsBase64<N> for Option<[u8; N]> {
> +        fn ser<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
> +            match self {
> +                Some(s) => Self::ser(&Some(*s), serializer),
> +                None => serializer.serialize_none(),
> +            }
> +        }
> +
> +        fn de<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
> +            match Option::<String>::deserialize(deserializer)? {
> +                Some(s) => Ok(Some(finish_deserializing::<'de, N, D>(s)?)),
> +                None => Ok(None),
> +            }
> +        }
> +    }
> +
> +    pub fn serialize<const N: usize, S, T>(data: &T, serializer: S) -> Result<S::Ok, S::Error>
> +    where
> +        S: Serializer,
> +        T: ByteArrayAsBase64<N>,
> +    {
> +        <T as ByteArrayAsBase64<N>>::ser(data, serializer)
> +    }
> +
> +    pub fn deserialize<'de, const N: usize, D, T>(deserializer: D) -> Result<T, D::Error>
> +    where
> +        D: Deserializer<'de>,
> +        T: ByteArrayAsBase64<N>,
> +    {
> +        <T as ByteArrayAsBase64<N>>::de::<'de, D>(deserializer)
> +    }
> +}
> -- 
> 2.52.0
> 
> 
> 
> 
> 




  reply	other threads:[~2026-03-24 12:57 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-13 14:35 [PATCH proxmox v2 0/8] sdn: add wireguard fabric configuration support Christoph Heiss
2026-02-13 14:35 ` [PATCH proxmox v2 1/8] serde: implement ini serializer Christoph Heiss
2026-03-24 11:13   ` Thomas Lamprecht
2026-03-24 13:08     ` Wolfgang Bumiller
2026-03-25 11:40     ` Christoph Heiss
2026-03-25 12:17       ` Thomas Lamprecht
2026-03-25 18:02         ` Christoph Heiss
2026-03-24 15:14   ` Wolfgang Bumiller
2026-02-13 14:35 ` [PATCH proxmox v2 2/8] serde: add base64 module for byte arrays Christoph Heiss
2026-03-24 12:57   ` Wolfgang Bumiller [this message]
2026-02-13 14:35 ` [PATCH proxmox v2 3/8] network-types: add ServiceEndpoint type as host/port tuple abstraction Christoph Heiss
2026-02-13 14:35 ` [PATCH proxmox v2 4/8] schema: provide integer schema for node ports Christoph Heiss
2026-02-13 14:35 ` [PATCH proxmox v2 5/8] schema: api-types: add ed25519 base64 encoded key schema Christoph Heiss
2026-02-13 14:35 ` [PATCH proxmox v2 6/8] wireguard: init configuration support crate Christoph Heiss
2026-02-13 14:36 ` [PATCH proxmox v2 7/8] wireguard: implement api for PublicKey Christoph Heiss
2026-02-13 14:36 ` [PATCH proxmox v2 8/8] wireguard: make per-peer preshared key optional Christoph Heiss

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=euwxondwcgtfvggbdg3aqdvojt6eyb2nprd5ncyjpsladmz63a@ayzaqft2zvaj \
    --to=w.bumiller@proxmox.com \
    --cc=c.heiss@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal