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
>
>
>
>
>
next prev parent 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