Skip to main content

faster_hex/
serde.rs

1#![warn(missing_docs)]
2
3use core::iter::FromIterator;
4
5mod internal {
6    use crate::{
7        decode::{hex_decode_array_with_case, hex_decode_with_case, CheckCase},
8        encode::encode,
9    };
10    use alloc::{borrow::Cow, string::String, vec, vec::Vec};
11    use core::{fmt, iter::FromIterator, mem::MaybeUninit};
12    use serde::{
13        de::{Error, Unexpected, Visitor},
14        Deserialize, Deserializer, Serialize, Serializer,
15    };
16
17    // Serde's Cow<str> deserializer always allocates. This adapter preserves
18    // String's deserialize_string hint and accepted inputs, but borrows text
19    // that the format can lend (for example, unescaped JSON from a slice).
20    struct Text<'a>(Cow<'a, str>);
21
22    impl<'de> Deserialize<'de> for Text<'de> {
23        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
24            struct TextVisitor;
25
26            impl<'de> Visitor<'de> for TextVisitor {
27                type Value = Text<'de>;
28
29                fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
30                    f.write_str("a string")
31                }
32
33                fn visit_str<E: Error>(self, text: &str) -> Result<Self::Value, E> {
34                    Ok(Text(Cow::Owned(text.into())))
35                }
36
37                fn visit_borrowed_str<E: Error>(self, text: &'de str) -> Result<Self::Value, E> {
38                    Ok(Text(Cow::Borrowed(text)))
39                }
40
41                fn visit_string<E: Error>(self, text: String) -> Result<Self::Value, E> {
42                    Ok(Text(Cow::Owned(text)))
43                }
44
45                fn visit_bytes<E: Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
46                    match core::str::from_utf8(bytes) {
47                        Ok(text) => self.visit_str(text),
48                        Err(_) => Err(E::invalid_value(Unexpected::Bytes(bytes), &self)),
49                    }
50                }
51
52                fn visit_borrowed_bytes<E: Error>(
53                    self,
54                    bytes: &'de [u8],
55                ) -> Result<Self::Value, E> {
56                    match core::str::from_utf8(bytes) {
57                        Ok(text) => self.visit_borrowed_str(text),
58                        Err(_) => Err(E::invalid_value(Unexpected::Bytes(bytes), &self)),
59                    }
60                }
61
62                fn visit_byte_buf<E: Error>(self, bytes: Vec<u8>) -> Result<Self::Value, E> {
63                    match String::from_utf8(bytes) {
64                        Ok(text) => self.visit_string(text),
65                        Err(error) => {
66                            Err(E::invalid_value(Unexpected::Bytes(error.as_bytes()), &self))
67                        }
68                    }
69                }
70            }
71            deserializer.deserialize_string(TextVisitor)
72        }
73    }
74
75    pub(crate) fn serialize<S, T>(
76        data: T,
77        serializer: S,
78        with_prefix: bool,
79        case: CheckCase,
80    ) -> Result<S::Ok, S::Error>
81    where
82        S: Serializer,
83        T: AsRef<[u8]>,
84    {
85        let src = data.as_ref();
86        let prefix: &[u8] = if with_prefix { b"0x" } else { b"" };
87        let len = src
88            .len()
89            .checked_mul(2)
90            .and_then(|len| len.checked_add(prefix.len()))
91            .ok_or_else(|| serde::ser::Error::custom(crate::Error::Overflow))?;
92        // Fixed values through 65 bytes (including H520) fit with the prefix.
93        // Initialize only the storage selected for this call.
94        let mut stack;
95        let mut heap;
96        let dst = if len <= 132 {
97            stack = [MaybeUninit::uninit(); 132];
98            &mut stack[..len]
99        } else {
100            heap = Vec::<u8>::with_capacity(len);
101            &mut heap.spare_capacity_mut()[..len]
102        };
103        for (slot, &byte) in dst.iter_mut().zip(prefix) {
104            slot.write(byte);
105        }
106        encode(src, &mut dst[prefix.len()..], case == CheckCase::Upper)
107            .map_err(serde::ser::Error::custom)?;
108        // SAFETY: The prefix and encoder initialized all len bytes as ASCII.
109        // MaybeUninit<u8> has u8's layout; unused capacity is excluded and the
110        // backing stack buffer or allocation stays live throughout serialization.
111        serializer.serialize_str(unsafe {
112            core::str::from_utf8_unchecked(core::slice::from_raw_parts(dst.as_ptr().cast(), len))
113        })
114    }
115
116    fn payload<E: Error>(text: &str, with_prefix: bool) -> Result<&str, E> {
117        let text = if with_prefix {
118            text.strip_prefix("0x")
119                .ok_or_else(|| E::custom("invalid prefix"))?
120        } else {
121            text
122        };
123        if !text.len().is_multiple_of(2) {
124            return Err(E::custom("invalid length"));
125        }
126        Ok(text)
127    }
128
129    fn decode<E: Error>(
130        text: &str,
131        with_prefix: bool,
132        case: CheckCase,
133        max_bytes: usize,
134    ) -> Result<Vec<u8>, E> {
135        let text = payload::<E>(text, with_prefix)?;
136        let len = text.len() / 2;
137        if len > max_bytes {
138            return Err(E::custom(format_args!(
139                "expected at most {max_bytes} decoded bytes, got {len}"
140            )));
141        }
142        let mut bytes = vec![0; len];
143        hex_decode_with_case(text.as_bytes(), &mut bytes, case).map_err(E::custom)?;
144        Ok(bytes)
145    }
146
147    fn decode_array<E: Error, const N: usize>(
148        text: &str,
149        with_prefix: bool,
150        case: CheckCase,
151    ) -> Result<[u8; N], E> {
152        let text = payload::<E>(text, with_prefix)?;
153        hex_decode_array_with_case(text.as_bytes(), case).map_err(E::custom)
154    }
155
156    pub(crate) fn deserialize_array<'de, D, const N: usize>(
157        deserializer: D,
158        with_prefix: bool,
159        case: CheckCase,
160    ) -> Result<[u8; N], D::Error>
161    where
162        D: Deserializer<'de>,
163    {
164        let text = Text::deserialize(deserializer)?;
165        decode_array(&text.0, with_prefix, case)
166    }
167
168    pub(crate) fn deserialize_option_array<'de, D, const N: usize>(
169        deserializer: D,
170        with_prefix: bool,
171        case: CheckCase,
172    ) -> Result<Option<[u8; N]>, D::Error>
173    where
174        D: Deserializer<'de>,
175    {
176        Option::<Text>::deserialize(deserializer)?
177            .map(|text| decode_array(&text.0, with_prefix, case))
178            .transpose()
179    }
180
181    pub(crate) fn deserialize<'de, D, T>(
182        deserializer: D,
183        with_prefix: bool,
184        check_case: CheckCase,
185        max_bytes: usize,
186    ) -> Result<T, D::Error>
187    where
188        D: Deserializer<'de>,
189        T: FromIterator<u8>,
190    {
191        let text = Text::deserialize(deserializer)?;
192        decode(&text.0, with_prefix, check_case, max_bytes).map(|bytes| bytes.into_iter().collect())
193    }
194
195    pub(crate) fn serialize_option<S, T>(
196        data: &Option<T>,
197        serializer: S,
198        with_prefix: bool,
199        case: CheckCase,
200    ) -> Result<S::Ok, S::Error>
201    where
202        S: Serializer,
203        T: AsRef<[u8]>,
204    {
205        // Some serializers attach an Option tag before serializing its value.
206        // Give them a hex representation that implements Serde's value protocol.
207        struct HexValue<'a> {
208            data: &'a [u8],
209            with_prefix: bool,
210            case: CheckCase,
211        }
212
213        impl Serialize for HexValue<'_> {
214            fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
215                serialize(self.data, serializer, self.with_prefix, self.case)
216            }
217        }
218
219        match data {
220            Some(data) => serializer.serialize_some(&HexValue {
221                data: data.as_ref(),
222                with_prefix,
223                case,
224            }),
225            None => serializer.serialize_none(),
226        }
227    }
228
229    pub(crate) fn deserialize_option<'de, D, T>(
230        deserializer: D,
231        with_prefix: bool,
232        check_case: CheckCase,
233        max_bytes: usize,
234    ) -> Result<Option<T>, D::Error>
235    where
236        D: Deserializer<'de>,
237        T: FromIterator<u8>,
238    {
239        // Let Serde retain its complete Option protocol, including untagged
240        // handling; the same decoder serves both optional and required text.
241        let bytes = Option::<Text>::deserialize(deserializer)?
242            .map(|text| decode(&text.0, with_prefix, check_case, max_bytes))
243            .transpose()?;
244        Ok(bytes.map(|bytes| bytes.into_iter().collect()))
245    }
246}
247
248/// Serializes a byte view as lowercase hex with a `0x` prefix.
249///
250/// Available with `serde`. This is the default serializer used by
251/// `#[serde(with = "faster_hex")]`. It accepts any [`AsRef<[u8]>`], reads that view
252/// once, and writes a Serde string in every format, including binary formats.
253/// Empty bytes serialize as `"0x"`. Use a named policy module to change the
254/// prefix or letter case. Temporary storage may allocate; allocation failure
255/// follows the allocator's error handling.
256///
257/// # Errors
258///
259/// Returns the serializer's error if writing the string fails, or if the encoded
260/// length including the prefix cannot be represented as a `usize`.
261///
262/// # Panics
263///
264/// Panics if temporary output storage would exceed [`isize::MAX`] bytes.
265///
266/// # Examples
267///
268/// ```
269/// #[derive(serde::Serialize)]
270/// struct Record {
271///     #[serde(with = "faster_hex")]
272///     bytes: Vec<u8>,
273/// }
274///
275/// let value = Record { bytes: vec![0xab, 1] };
276/// assert_eq!(serde_json::to_string(&value)?, r#"{"bytes":"0xab01"}"#);
277/// # Ok::<(), serde_json::Error>(())
278/// ```
279#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
280pub fn serialize<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
281where
282    S: serde::Serializer,
283    T: AsRef<[u8]>,
284{
285    withpfx_ignorecase::serialize(data, serializer)
286}
287
288/// Deserializes a `0x`-prefixed hex string into a byte collection.
289///
290/// Available with `serde`. This is the default deserializer for
291/// `#[serde(with = "faster_hex")]`. The prefix must be exactly `0x`; payload
292/// letters may use either case, including mixed case. Whitespace and separators
293/// are rejected. `"0x"` produces an empty collection.
294///
295/// Decoded bytes are collected into `T`, which implements [`FromIterator<u8>`],
296/// for example [`Vec<u8>`](alloc::vec::Vec) or
297/// [`VecDeque<u8>`](alloc::collections::VecDeque).
298/// Input text is borrowed when the format can lend it; transient,
299/// escaped or owned text may require storage. Use [`array`](mod@crate::array)
300/// for `[u8; N]` fields, or [`deserialize_bounded`] to limit decoded output.
301/// Allocation failure follows the allocator's error handling.
302///
303/// # Errors
304///
305/// Propagates deserializer errors, including invalid input types or UTF-8. Once
306/// text is available, checks the prefix, even payload length, then characters,
307/// in that order. Character diagnostics report the first invalid byte and its
308/// byte position within the payload, excluding `0x`. Collection starts only
309/// after successful decoding.
310///
311/// # Panics
312///
313/// A custom collector may panic, for example when its fixed capacity is exceeded.
314/// This adapter does not provide fallible collection.
315///
316/// # Examples
317///
318/// ```
319/// #[derive(serde::Deserialize)]
320/// struct Record {
321///     #[serde(with = "faster_hex")]
322///     bytes: Vec<u8>,
323/// }
324///
325/// let value: Record = serde_json::from_str(r#"{"bytes":"0x00aB"}"#)?;
326/// assert_eq!(value.bytes, [0, 0xab]);
327/// # Ok::<(), serde_json::Error>(())
328/// ```
329#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
330pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
331where
332    D: serde::Deserializer<'de>,
333    T: FromIterator<u8>,
334{
335    withpfx_ignorecase::deserialize(deserializer)
336}
337
338/// Deserializes at most `MAX` decoded bytes from a `0x`-prefixed hex string.
339///
340/// Available with `serde`. The prefix, case and collection behavior is the same
341/// as [`deserialize`]. `MAX` counts decoded bytes, so at most twice that many
342/// hex digits are accepted. A zero limit accepts `"0x"`.
343///
344/// The limit is checked before allocating decoded output or invoking the collector.
345/// It does not limit input text storage used by the format, error-message storage,
346/// or allocations performed by a custom collector. It also does not constrain
347/// serialization; pair this function with the ordinary [`serialize`] function.
348/// Allocation failure follows the allocator's error handling.
349///
350/// # Errors
351///
352/// Propagates deserializer errors. After obtaining text, checks the prefix, even
353/// payload length, the decoded-byte limit, then characters/case, in that order.
354/// An over-limit value is rejected even if it also contains invalid hex digits.
355///
356/// # Panics
357///
358/// A custom collector can still panic when full; the acceptance limit does not
359/// change its capacity or collection implementation.
360///
361/// # Examples
362///
363/// ```
364/// #[derive(serde::Serialize, serde::Deserialize)]
365/// struct Packet {
366///     #[serde(
367///         serialize_with = "faster_hex::serialize",
368///         deserialize_with = "faster_hex::deserialize_bounded::<2, _, _>"
369///     )]
370///     bytes: Vec<u8>,
371/// }
372///
373/// let packet: Packet = serde_json::from_str(r#"{"bytes":"0xab01"}"#)?;
374/// assert_eq!(packet.bytes, [0xab, 1]);
375/// assert!(serde_json::from_str::<Packet>(r#"{"bytes":"0x000102"}"#).is_err());
376/// # Ok::<(), serde_json::Error>(())
377/// ```
378#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
379pub fn deserialize_bounded<'de, const MAX: usize, D, T>(deserializer: D) -> Result<T, D::Error>
380where
381    D: serde::Deserializer<'de>,
382    T: FromIterator<u8>,
383{
384    withpfx_ignorecase::deserialize_bounded::<MAX, D, T>(deserializer)
385}
386
387// Required and optional adapters share one prefix/case policy declaration.
388macro_rules! serde_adapters {
389    ($mod_name:ident, $option_name:ident, $with_pfx:expr, $check_case:expr, $prefix:literal, $description:literal) => {
390        #[doc = $description]
391        #[doc = concat!(
392                                    r###"
393Use `#[serde(with = "...")]` for byte collections. Serialization reads one
394[`AsRef<[u8]>`] view; deserialization collects into [`FromIterator<u8>`] after
395validating the complete input. All formats use strings, including binary formats.
396
397A required prefix is exactly `0x`. Payloads contain only ASCII hex digits;
398empty payloads are accepted. For arrays use this module's [`array`] adapter;
399for a decoded-byte limit use [`deserialize_bounded`].
400
401# Examples
402
403```
404#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
405struct Record {
406    #[serde(with = "faster_hex::"###, stringify!($mod_name), r###"")]
407    bytes: Vec<u8>,
408}
409let record = Record { bytes: vec![0x12, 0x34] };
410let json = serde_json::to_string(&record)?;
411assert_eq!(json, r#"{"bytes":""###, $prefix, r###"1234"}"#);
412assert_eq!(serde_json::from_str::<Record>(&json)?, record);
413# Ok::<(), serde_json::Error>(())
414```
415"###
416                                )]
417        #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
418        pub mod $mod_name {
419            use crate::decode::CheckCase;
420            use crate::serde::internal;
421            use core::iter::FromIterator;
422
423            /// Serializes a byte view using this module's prefix and case policy.
424            ///
425            /// Reads [`AsRef::as_ref`] once and sends a string to every serializer.
426            /// Allocation behavior matches [`crate::serialize`].
427            ///
428            /// # Errors
429            ///
430            /// Returns an error if serialization fails or the encoded length overflows.
431            ///
432            /// # Panics
433            ///
434            /// Panics if temporary output storage would exceed [`isize::MAX`] bytes.
435            ///
436            /// # Examples
437            ///
438            /// See the [module example](self#examples).
439            pub fn serialize<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
440            where
441                S: serde::Serializer,
442                T: AsRef<[u8]>,
443            {
444                internal::serialize(data, serializer, $with_pfx, $check_case)
445            }
446
447            /// Deserializes a hex string using this module's prefix and case policy.
448            ///
449            /// Borrows input text when the format can lend it.
450            /// Allocation behavior matches [`crate::deserialize`].
451            ///
452            /// # Errors
453            ///
454            /// Propagates deserializer errors. Checks any required prefix, even payload
455            /// length, then characters/case before collection. Positions exclude the prefix.
456            ///
457            /// # Panics
458            ///
459            /// A custom collector may panic, for example when its fixed capacity is exceeded.
460            ///
461            /// # Examples
462            ///
463            /// See the [module example](self#examples).
464            pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
465            where
466                D: serde::Deserializer<'de>,
467                T: FromIterator<u8>,
468            {
469                internal::deserialize(deserializer, $with_pfx, $check_case, usize::MAX)
470            }
471
472            /// Deserializes at most `MAX` decoded bytes using this module's policy.
473            ///
474            /// The limit precedes decoded-output allocation and collection; it does not
475            /// bound input, error or collector storage. `MAX == 0` accepts an empty payload.
476            /// Allocation behavior matches [`crate::deserialize_bounded`].
477            ///
478            /// # Errors
479            ///
480            /// Propagates deserializer errors. Checks any required prefix, even length,
481            /// decoded-byte limit, then characters/case. Byte positions exclude the prefix.
482            ///
483            /// # Panics
484            ///
485            /// A custom collector may still panic; the limit does not change its capacity.
486            ///
487            /// # Examples
488            ///
489            /// See [`crate::deserialize_bounded`] for the Serde field attributes.
490            pub fn deserialize_bounded<'de, const MAX: usize, D, T>(
491                deserializer: D,
492            ) -> Result<T, D::Error>
493            where
494                D: serde::Deserializer<'de>,
495                T: FromIterator<u8>,
496            {
497                internal::deserialize(deserializer, $with_pfx, $check_case, MAX)
498            }
499
500            /// Exact-length arrays using the parent module's prefix and case policy.
501            ///
502            /// Deserialization writes into `[u8; N]` without an intermediate byte vector.
503            /// Input text is borrowed where possible; formats may need storage for
504            /// escaped or transient text. Serialization uses the parent adapter's
505            /// string representation, including any framing added by the format.
506            #[doc = concat!(
507                                        r###"
508# Examples
509
510```
511#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
512struct Record {
513    #[serde(with = "faster_hex::"###, stringify!($mod_name), r###"::array")]
514    id: [u8; 2],
515}
516let record = Record { id: [0x12, 0x34] };
517let json = serde_json::to_string(&record)?;
518assert_eq!(serde_json::from_str::<Record>(&json)?, record);
519# Ok::<(), serde_json::Error>(())
520```
521"###
522                                    )]
523            pub mod array {
524                use super::{internal, CheckCase};
525
526                pub use super::serialize;
527
528                /// Deserializes exactly `N` bytes into an owned array.
529                ///
530                /// Empty payloads succeed only for `N == 0`.
531                ///
532                /// # Errors
533                ///
534                /// Checks any required prefix, even payload length, exact decoded length,
535                /// then characters/case. Length errors count decoded bytes; invalid-byte
536                /// positions exclude the prefix. Format errors propagate.
537                ///
538                /// # Examples
539                ///
540                /// See the [module example](self#examples).
541                pub fn deserialize<'de, D, const N: usize>(
542                    deserializer: D,
543                ) -> Result<[u8; N], D::Error>
544                where
545                    D: serde::Deserializer<'de>,
546                {
547                    internal::deserialize_array(deserializer, $with_pfx, $check_case)
548                }
549            }
550        }
551
552        #[doc = concat!($description, " Optional values.")]
553        #[doc = concat!(
554                                    r###"
555Present values follow [`"###, stringify!($mod_name), r###"`](crate::"###,
556                                    stringify!($mod_name), r###"). Absent values use Serde's `None`
557representation (`null` in JSON). Binary formats retain their normal [`Option`]
558tags, so `Some(empty)` remains distinct from `None`.
559
560Use `#[serde(default)]` to accept a missing struct field. For fixed arrays use
561[`array`]; for a limit on present values use [`deserialize_bounded`].
562
563# Examples
564
565```
566#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
567struct Record {
568    #[serde(default, with = "faster_hex::"###, stringify!($option_name), r###"")]
569    bytes: Option<Vec<u8>>,
570}
571let record = Record { bytes: Some(vec![0x12, 0x34]) };
572let json = serde_json::to_string(&record)?;
573assert_eq!(serde_json::from_str::<Record>(&json)?, record);
574assert_eq!(serde_json::from_str::<Record>("{}")?.bytes, None);
575# Ok::<(), serde_json::Error>(())
576```
577"###
578                                )]
579        #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
580        pub mod $option_name {
581            use crate::decode::CheckCase;
582            use crate::serde::internal;
583            use core::iter::FromIterator;
584
585            /// Serializes an optional byte view, preserving Serde's [`Option`] tags.
586            ///
587            /// Present values use this module's hex string policy and read
588            /// [`AsRef::as_ref`] once. `None` uses the format's absent-value representation.
589            /// Allocation behavior matches [`crate::serialize`].
590            ///
591            /// # Errors
592            ///
593            /// Propagates serializer errors, including errors writing an Option tag.
594            /// Present values also fail if their encoded length overflows.
595            ///
596            /// # Panics
597            ///
598            /// Panics if temporary output storage would exceed [`isize::MAX`] bytes.
599            ///
600            /// # Examples
601            ///
602            /// See the [module example](self#examples).
603            pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
604            where
605                S: serde::Serializer,
606                T: AsRef<[u8]>,
607            {
608                internal::serialize_option(data, serializer, $with_pfx, $check_case)
609            }
610
611            /// Deserializes an optional hex string into a byte collection.
612            ///
613            /// Present strings use this module's policy; an empty payload is `Some(empty)`.
614            /// Allocation behavior matches [`crate::deserialize`].
615            ///
616            /// # Errors
617            ///
618            /// Propagates deserializer errors. Checks present text for any required prefix,
619            /// even payload length, then characters/case before collection. Invalid input
620            /// is an error, not `None`. Byte positions exclude the prefix.
621            ///
622            /// # Panics
623            ///
624            /// A custom collector may panic, for example when its fixed capacity is exceeded.
625            ///
626            /// # Examples
627            ///
628            /// See the [module example](self#examples).
629            pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
630            where
631                D: serde::Deserializer<'de>,
632                T: FromIterator<u8>,
633            {
634                internal::deserialize_option(deserializer, $with_pfx, $check_case, usize::MAX)
635            }
636
637            /// Deserializes an optional collection of at most `MAX` decoded bytes.
638            ///
639            /// Accepts `None` and empty payloads even when `MAX == 0`. Storage limits
640            /// and allocation behavior match [`crate::deserialize_bounded`].
641            ///
642            /// # Errors
643            ///
644            /// Propagates deserializer errors. Checks present text for any required prefix,
645            /// even payload length, decoded-byte limit, then characters/case. Invalid or
646            /// over-limit values are errors, not `None`. Byte positions exclude the prefix.
647            ///
648            /// # Panics
649            ///
650            /// A custom collector may still panic; the limit does not change its capacity.
651            ///
652            /// # Examples
653            ///
654            /// See [`crate::deserialize_bounded`] for the Serde field attributes.
655            pub fn deserialize_bounded<'de, const MAX: usize, D, T>(
656                deserializer: D,
657            ) -> Result<Option<T>, D::Error>
658            where
659                D: serde::Deserializer<'de>,
660                T: FromIterator<u8>,
661            {
662                internal::deserialize_option(deserializer, $with_pfx, $check_case, MAX)
663            }
664
665            /// Optional fixed-length arrays using this module's prefix and case policy.
666            ///
667            /// Present strings decode directly into `[u8; N]` without an intermediate
668            /// byte vector; input text may still need storage. `None` and `Some([])`
669            /// remain distinct in every format. Add `#[serde(default)]` for missing fields.
670            ///
671            /// # Examples
672            ///
673            /// See the [crate example](crate#serde-adapters) for optional array fields.
674            pub mod array {
675                use super::{internal, CheckCase};
676
677                pub use super::serialize;
678
679                /// Deserializes an optional array of exactly `N` bytes.
680                ///
681                /// An empty present payload requires `N == 0`.
682                ///
683                /// # Errors
684                ///
685                /// Checks present text for any required prefix, even payload length, exact
686                /// decoded length, then characters/case. Format errors propagate; invalid input
687                /// is an error, not `None`.
688                /// Lengths count decoded bytes; invalid-byte positions exclude the prefix.
689                ///
690                /// # Examples
691                ///
692                /// See the [crate example](crate#serde-adapters) for optional array fields.
693                pub fn deserialize<'de, D, const N: usize>(
694                    deserializer: D,
695                ) -> Result<Option<[u8; N]>, D::Error>
696                where
697                    D: serde::Deserializer<'de>,
698                {
699                    internal::deserialize_option_array(deserializer, $with_pfx, $check_case)
700                }
701            }
702        }
703    };
704}
705
706serde_adapters!(
707    withpfx_ignorecase,
708    option_withpfx_ignorecase,
709    true,
710    CheckCase::None,
711    "0x",
712    "Lowercase serialization with a 0x prefix; accepts either letter case."
713);
714serde_adapters!(
715    nopfx_ignorecase,
716    option_nopfx_ignorecase,
717    false,
718    CheckCase::None,
719    "",
720    "Lowercase serialization without a prefix; accepts either letter case."
721);
722serde_adapters!(
723    withpfx_lowercase,
724    option_withpfx_lowercase,
725    true,
726    CheckCase::Lower,
727    "0x",
728    "Lowercase hex with a required 0x prefix."
729);
730serde_adapters!(
731    nopfx_lowercase,
732    option_nopfx_lowercase,
733    false,
734    CheckCase::Lower,
735    "",
736    "Lowercase hex without a prefix."
737);
738serde_adapters!(
739    withpfx_uppercase,
740    option_withpfx_uppercase,
741    true,
742    CheckCase::Upper,
743    "0x",
744    "Uppercase hex with a required 0x prefix."
745);
746serde_adapters!(
747    nopfx_uppercase,
748    option_nopfx_uppercase,
749    false,
750    CheckCase::Upper,
751    "",
752    "Uppercase hex without a prefix."
753);