1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 // Functions for encoding and decoding data in Base64 as specified by RFC 3548
16 // and RFC 4648. See https://tools.ietf.org/html/rfc4648
17 #pragma once
18
19 #include <stdbool.h>
20 #include <stddef.h>
21
22 // C-compatible versions of a subset of the pw_base64 module.
23 #ifdef __cplusplus
24 extern "C" {
25 #endif // __cplusplus
26
27 // Returns the size of the given number of bytes when encoded as Base64. Base64
28 //
29 // Equivalent to pw::base64::EncodedSize().
30 #define PW_BASE64_ENCODED_SIZE(binary_size_bytes) \
31 (((size_t)binary_size_bytes + 2) / 3 * 4) // +2 to round up to a 3-byte group
32
33 // Encodes the provided data in Base64 and writes the result to the buffer.
34 // Exactly PW_BASE64_ENCODED_SIZE(binary_size_bytes) bytes will be written. The
35 // output buffer *MUST* be large enough for the encoded output!
36 //
37 // Equivalent to pw::base64::Encode().
38 void pw_Base64Encode(const void* binary_data,
39 const size_t binary_size_bytes,
40 char* output);
41
42 // Evaluates to the maximum size of decoded Base64 data in bytes.
43 //
44 // Equivalent to pw::base64::MaxDecodedSize().
45 #define PW_BASE64_MAX_DECODED_SIZE(base64_size_bytes) \
46 (((size_t)base64_size_bytes) / 4 * 3)
47
48 // Decodes the provided Base64 data into raw binary. The output buffer *MUST* be
49 // at least PW_BASE64_MAX_DECODED_SIZE bytes large.
50 //
51 // Equivalent to pw::base64::Decode().
52 size_t pw_Base64Decode(const char* base64,
53 size_t base64_size_bytes,
54 void* output);
55
56 // Returns true if provided char is a valid Base64 character.
57 bool pw_Base64IsValidChar(char base64_char);
58
59 // Returns true if the provided string is valid Base64 encoded data. Accepts
60 // either the standard (+/) or URL-safe (-_) alphabets.
61 //
62 // Equivalent to pw::base64::IsValid().
63 bool pw_Base64IsValid(const char* base64_data, size_t base64_size);
64
65 // C++ API, which uses the C functions internally.
66 #ifdef __cplusplus
67 } // extern "C"
68
69 #include <string_view>
70 #include <type_traits>
71
72 #include "pw_span/span.h"
73 #include "pw_string/string.h"
74
75 namespace pw::base64 {
76
77 /// @param[in] binary_size_bytes The size of the binary data in bytes, before
78 /// encoding.
79 ///
80 /// @returns The size of `binary_size_bytes` after Base64 encoding.
81 ///
82 /// @note Base64 encodes 3-byte groups into 4-character strings. The final group
83 /// is padded to be 3 bytes if it only has 1 or 2.
EncodedSize(size_t binary_size_bytes)84 constexpr size_t EncodedSize(size_t binary_size_bytes) {
85 return PW_BASE64_ENCODED_SIZE(binary_size_bytes);
86 }
87
88 /// Encodes the provided data in Base64 and writes the result to the buffer.
89 ///
90 /// @param[in] binary The binary data to encode.
91 ///
92 /// @param[out] The output buffer where the encoded data is placed. Exactly
93 /// `EncodedSize(binary_size_bytes)` bytes is written.
94 ///
95 /// @note Encodes to the standard alphabet with `+` and `/` for characters `62`
96 /// and `63`.
97 ///
98 /// @pre
99 /// * The output buffer **MUST** be large enough for the encoded output!
100 /// * The input and output buffers **MUST NOT** be the same; encoding cannot
101 /// occur in place.
102 ///
103 /// @warning The resulting string in the output is **NOT** null-terminated!
Encode(span<const std::byte> binary,char * output)104 inline void Encode(span<const std::byte> binary, char* output) {
105 pw_Base64Encode(binary.data(), binary.size_bytes(), output);
106 }
107
108 /// Encodes the provided data in Base64 if the result fits in the provided
109 /// buffer.
110 ///
111 /// @param[in] binary The binary data to encode.
112 ///
113 /// @param[out] output_buffer The output buffer where the encoded data is
114 /// placed.
115 ///
116 /// @warning The resulting string in the output is **NOT** null-terminated!
117 ///
118 /// @returns The number of bytes written. Returns `0` if the output buffer
119 /// is too small.
120 size_t Encode(span<const std::byte> binary, span<char> output_buffer);
121
122 /// Appends Base64 encoded binary data to the provided `pw::InlineString`.
123 ///
124 /// @param[in] binary The binary data that has already been Base64-encoded.
125 ///
126 /// @param[out] output The `pw::InlineString` that `binary` is appended to.
127 ///
128 /// If the data does not fit in the string, an assertion fails.
129 void Encode(span<const std::byte> binary, InlineString<>& output);
130
131 /// Creates a `pw::InlineString<>` large enough to hold
132 /// `kMaxBinaryDataSizeBytes` of binary data when encoded as Base64 and encodes
133 /// the provided span into it.
134 template <size_t kMaxBinaryDataSizeBytes>
Encode(span<const std::byte> binary)135 inline InlineString<EncodedSize(kMaxBinaryDataSizeBytes)> Encode(
136 span<const std::byte> binary) {
137 InlineString<EncodedSize(kMaxBinaryDataSizeBytes)> output;
138 Encode(binary, output);
139 return output;
140 }
141
142 /// Calculates the maximum size of Base64-encoded data after decoding.
143 ///
144 /// @param[in] base64_size_bytes The size of the Base64-encoded data.
145 ///
146 /// @pre `base64_size_bytes` must be a multiple of 4, since Base64 encodes
147 /// 3-byte groups into 4-character strings.
148 ///
149 /// @returns The maximum size of the Base64-encoded data represented by
150 /// `base64_bytes_size` after decoding. If the last 3-byte group has padding,
151 /// the actual decoded size will be 1 or 2 bytes less than the value returned
152 /// by `MaxDecodedSize()`.
MaxDecodedSize(size_t base64_size_bytes)153 constexpr size_t MaxDecodedSize(size_t base64_size_bytes) {
154 return PW_BASE64_MAX_DECODED_SIZE(base64_size_bytes);
155 }
156
157 /// Decodes the provided Base64 data into raw binary.
158 ///
159 /// @pre
160 /// * The output buffer **MUST** be at least `MaxDecodedSize()` bytes large.
161 /// * This function does NOT check that the input is valid! Use `IsValid()`
162 /// or the four-argument overload to check the input formatting.
163 ///
164 /// @param[in] base64 The Base64 data that should be decoded. Can be encoded
165 /// with either the standard (`+/`) or URL-safe (`-_`) alphabet. The data must
166 /// be padded to 4-character blocks with `=`.
167 ///
168 /// @param[out] output The output buffer where the raw binary will be placed.
169 /// The output buffer may be the same as the input buffer; decoding can occur
170 /// in place.
171 ///
172 /// @returns The number of bytes that were decoded.
Decode(std::string_view base64,void * output)173 inline size_t Decode(std::string_view base64, void* output) {
174 return pw_Base64Decode(base64.data(), base64.size(), output);
175 }
176
177 /// Decodes the provided Base64 data, if the data is valid and fits in the
178 /// output buffer.
179 ///
180 /// @returns The number of bytes written, which will be `0` if the data is
181 /// invalid or doesn't fit.
182 size_t Decode(std::string_view base64, span<std::byte> output_buffer);
183
184 /// Decodes a `pw::InlineString<>` in place.
185 template <typename T>
DecodeInPlace(InlineBasicString<T> & buffer)186 inline void DecodeInPlace(InlineBasicString<T>& buffer) {
187 static_assert(sizeof(T) == sizeof(char));
188 buffer.resize(Decode(buffer, buffer.data()));
189 }
190
191 /// @param[in] base64 The string to check. Can be encoded with either the
192 /// standard (`+/`) or URL-safe (`-_`) alphabet.
193 ///
194 /// @returns `true` if the provided string is valid Base64-encoded data.
IsValid(std::string_view base64)195 inline bool IsValid(std::string_view base64) {
196 return pw_Base64IsValid(base64.data(), base64.size());
197 }
198
199 /// @param[in] base64 The character to check. Can be encoded with either the
200 /// standard (`+/`) or URL-safe (`-_`) alphabet.
201 ///
202 /// @returns `true` if the provided character is a valid Base64 character.
IsValidChar(char base64)203 inline bool IsValidChar(char base64) { return pw_Base64IsValidChar(base64); }
204
205 } // namespace pw::base64
206
207 #endif // __cplusplus
208