const_decoder/
wrappers.rs

1//! Decoder wrappers.
2
3use crate::decoder::Decoder;
4
5/// [`Decoder`] wrapper that skips whitespace during decoding instead of panicking.
6///
7/// # Examples
8///
9/// ```
10/// # use const_decoder::{Decoder, SkipWhitespace};
11/// const KEY: [u8; 64] = SkipWhitespace(Decoder::Hex).decode(b"
12///     9e55d1e1 aa1f455b 8baad9fd f9755036 55f8b359 d542fa7e
13///     4ce84106 d625b352 06fac1f2 2240cffd 637ead66 47188429
14///     fafda9c9 cb7eae43 386ac17f 61115075
15/// ");
16/// # assert_eq!(KEY[0], 0x9e);
17/// # assert_eq!(KEY[63], 0x75);
18/// ```
19#[derive(Debug, Clone, Copy)]
20pub struct SkipWhitespace(pub Decoder);
21
22impl SkipWhitespace {
23    /// Decodes `input` into a byte array.
24    ///
25    /// # Panics
26    ///
27    /// - Panics if the provided length is insufficient or too large for `input`.
28    /// - Panics if `input` contains invalid chars.
29    pub const fn decode<const N: usize>(self, input: &[u8]) -> [u8; N] {
30        self.0.do_decode(input, Some(Skipper::Whitespace))
31    }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq)]
35pub(crate) enum Skipper {
36    Whitespace,
37    Pem,
38}
39
40impl Skipper {
41    const fn detect_pem_header(input: &[u8], mut i: usize) -> Option<usize> {
42        if input.len() < i + 5 {
43            None
44        } else if input[i] == b'-'
45            && input[i + 1] == b'-'
46            && input[i + 2] == b'-'
47            && input[i + 3] == b'-'
48            && input[i + 4] == b'-'
49        {
50            i += 5;
51            while i < input.len() && input[i] != b'\n' {
52                i += 1;
53            }
54            Some(i)
55        } else {
56            None
57        }
58    }
59
60    pub const fn skip(self, input: &[u8], mut in_index: usize) -> usize {
61        if input[in_index].is_ascii_whitespace() {
62            in_index += 1;
63        } else if let Self::Pem = self {
64            if let Some(new_in_index) = Self::detect_pem_header(input, in_index) {
65                in_index = new_in_index;
66            }
67        }
68        in_index
69    }
70}
71
72/// Decoder for the PEM file format (Base64 with additional header / trailer lines).
73///
74/// # Examples
75///
76/// ```
77/// # use const_decoder::Pem;
78/// // X.25519 private key generated using OpenSSL:
79/// // `openssl genpkey -algorithm X25519`.
80/// const PRIVATE_KEY: [u8; 48] = Pem::decode(
81///     b"-----BEGIN PRIVATE KEY-----
82///       MC4CAQAwBQYDK2VuBCIEINAOV4yAyaoM2wmJPApQs3byDhw7oJRG47V0VHwGnctD
83///       -----END PRIVATE KEY-----",
84/// );
85/// ```
86#[derive(Debug, Clone, Copy)]
87pub struct Pem;
88
89impl Pem {
90    /// Decodes `input` into a byte array.
91    ///
92    /// # Panics
93    ///
94    /// - Panics if the provided length is insufficient or too large for `input`.
95    /// - Panics if `input` contains invalid chars.
96    pub const fn decode<const N: usize>(input: &[u8]) -> [u8; N] {
97        Decoder::Base64.do_decode(input, Some(Skipper::Pem))
98    }
99}