2 * Copyright (C) 2015 Topology LP
3 * Copyright (C) 2013 Adam Rudd (bit calculations)
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * Bit calculations adapted from https://github.com/adamvr/arduino-base64,
25 * commit 999595783185a0afcba156d7276dfeaa9cb5382f.
28 #ifndef CPPCODEC_DETAIL_BASE64
29 #define CPPCODEC_DETAIL_BASE64
32 #include <type_traits>
34 #include "../data/access.hpp"
35 #include "../parse_error.hpp"
36 #include "stream_codec.hpp"
41 template <typename CodecVariant>
42 class base64 : public CodecVariant::template codec_impl<base64<CodecVariant>>
45 static inline constexpr uint8_t binary_block_size() { return 3; }
46 static inline constexpr uint8_t encoded_block_size() { return 4; }
48 template <typename Result, typename ResultState>
49 static void encode_block(Result& encoded, ResultState&, const uint8_t* src);
51 template <typename Result, typename ResultState>
52 static void encode_tail(Result& encoded, ResultState&, const uint8_t* src, size_t src_len);
54 template <typename Result, typename ResultState>
55 static void pad(Result&, ResultState&, ...) { } // lower priority overload
57 template <typename Result, typename ResultState, typename V = CodecVariant,
58 typename std::enable_if<V::generates_padding()>::type* = nullptr>
59 static void pad(Result& encoded, ResultState&, size_t remaining_src_len);
61 template <typename Result, typename ResultState>
62 static void decode_block(Result& decoded, ResultState&, const uint8_t* idx);
64 template <typename Result, typename ResultState>
65 static void decode_tail(Result& decoded, ResultState&, const uint8_t* idx, size_t idx_len);
69 template <typename CodecVariant>
70 template <typename Result, typename ResultState>
71 inline void base64<CodecVariant>::encode_block(
72 Result& encoded, ResultState& state, const uint8_t* src)
74 using V = CodecVariant;
75 data::put(encoded, state, V::symbol(src[0] >> 2)); // first 6 bits
76 data::put(encoded, state, V::symbol(((src[0] & 0x3) << 4) + (src[1] >> 4))); // last 2 + next 4
77 data::put(encoded, state, V::symbol(((src[1] & 0xF) << 2) + (src[2] >> 6))); // last 4 + next 2
78 data::put(encoded, state, V::symbol(src[2] & 0x3F)); // last 6 bits
81 template <typename CodecVariant>
82 template <typename Result, typename ResultState>
83 inline void base64<CodecVariant>::encode_tail(
84 Result& encoded, ResultState& state, const uint8_t* src, size_t remaining_src_len)
86 using V = CodecVariant;
88 data::put(encoded, state, V::symbol(src[0] >> 2)); // encoded size 1
89 if (remaining_src_len == 1) {
90 data::put(encoded, state, V::symbol((src[0] & 0x03) << 4)); // size 2
93 data::put(encoded, state, V::symbol(((src[0] & 0x03) << 4) + (src[1] >> 4))); // size 2
94 if (remaining_src_len == 2) {
95 data::put(encoded, state, V::symbol((src[1] & 0x0f) << 2)); // size 3
98 abort(); // not reached: encode_block() should be called if remaining_src_len > 2, not this function
101 template <typename CodecVariant>
102 template <typename Result, typename ResultState, typename V,
103 typename std::enable_if<V::generates_padding()>::type*>
104 inline void base64<CodecVariant>::pad(
105 Result& encoded, ResultState& state, size_t remaining_src_len)
107 switch (remaining_src_len) {
108 case 1: // 2 symbols, 2 padding characters
109 data::put(encoded, state, CodecVariant::padding_symbol());
110 case 2: // 3 symbols, 1 padding character
111 data::put(encoded, state, CodecVariant::padding_symbol());
115 template <typename CodecVariant>
116 template <typename Result, typename ResultState>
117 inline void base64<CodecVariant>::decode_block(
118 Result& decoded, ResultState& state, const uint8_t* idx)
120 data::put(decoded, state, (uint8_t)((idx[0] << 2) + ((idx[1] & 0x30) >> 4)));
121 data::put(decoded, state, (uint8_t)(((idx[1] & 0xF) << 4) + ((idx[2] & 0x3C) >> 2)));
122 data::put(decoded, state, (uint8_t)(((idx[2] & 0x3) << 6) + idx[3]));
125 template <typename CodecVariant>
126 template <typename Result, typename ResultState>
127 inline void base64<CodecVariant>::decode_tail(
128 Result& decoded, ResultState& state, const uint8_t* idx, size_t idx_len)
131 throw invalid_input_length(
132 "invalid number of symbols in last base64 block: found 1, expected 2 or 3");
135 // idx_len == 2: decoded size 1
136 data::put(decoded, state, (uint8_t)((idx[0] << 2) + ((idx[1] & 0x30) >> 4)));
141 // idx_len == 3: decoded size 2
142 data::put(decoded, state, (uint8_t)(((idx[1] & 0xF) << 4) + ((idx[2] & 0x3C) >> 2)));
145 } // namespace detail
146 } // namespace cppcodec
148 #endif // CPPCODEC_DETAIL_BASE64