Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Base64.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4/*
5 * Note - used PUBLIC DOMAIN http://sourceforge.net/projects/libb64/files/libb64/libb64/libb64-1.2.src.zip/download
6 * code as a starting point.
7 */
8#include "Stroika/Foundation/StroikaPreComp.h"
9
10#include <algorithm>
11#include <cstdlib>
12
13#include "Stroika/Foundation/Containers/Common.h"
14#include "Stroika/Foundation/DataExchange/BadFormatException.h"
16#include "Stroika/Foundation/Memory/BLOB.h" // ONLY FOR QUICKHACK IMPL OF ENCODE...
18
19#include "Base64.h"
20
21using namespace Stroika::Foundation;
23using namespace Stroika::Foundation::Cryptography;
24using namespace Stroika::Foundation::Cryptography::Encoding;
25using namespace Stroika::Foundation::Cryptography::Encoding::Algorithm;
26using namespace Stroika::Foundation::Memory;
27
28using std::byte;
29
30/**
31 * IMPLEMENTATION NOTES:
32 *
33 * The public domain (private) code in this file - is designed to operate in a STREAM mode. I've preserved that
34 * internally, evne though my current API doesn't work that way, because we will soon support a STREAM based API here,
35 * and that will fit perfectly.
36 *
37 * @see http://libb64.sourceforge.net/ - which explains the design of the routines, and the queer nested loop / switch
38 * construct.
39 *
40 * -- LGP 2011-06-21
41 */
42
43/*
44 ********************************************************************************
45 *************************** Encoding::DecodeBase64 *****************************
46 ********************************************************************************
47 */
48namespace {
49 enum base64_decodestep_ {
50 step_a,
51 step_b,
52 step_c,
53 step_d
54 };
55 struct base64_decodestate_ {
56 base64_decodestep_ step{step_a};
57 byte plainchar{0};
58 };
59 int base64_decode_value_ (signed char value_in)
60 {
61 static constexpr signed char kDecoding[] = {62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1,
62 -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
63 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
64 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
65 value_in -= 43;
66 if (value_in < 0 || static_cast<unsigned char> (value_in) >= NEltsOf (kDecoding)) {
67 return -1;
68 }
69 Assert (0 <= value_in and static_cast<unsigned char> (value_in) < NEltsOf (kDecoding));
70 return kDecoding[(int)value_in];
71 }
72 size_t base64_decode_block_ (const signed char* code_in, size_t length_in, byte* plaintext_out, base64_decodestate_* state)
73 {
74 RequireNotNull (code_in);
75 RequireNotNull (plaintext_out);
76
77 const signed char* codechar = code_in;
78 byte* plainchar = plaintext_out;
79 signed char fragment = '\0';
80
81 *plainchar = state->plainchar;
82
83 switch (state->step) {
84 while (true) {
85 case step_a:
86 do {
87 if (codechar == code_in + length_in) {
88 state->step = step_a;
89 state->plainchar = *plainchar;
90 return plainchar - plaintext_out;
91 }
92 fragment = (signed char)base64_decode_value_ (*codechar++);
93 } while (fragment < 0);
94 *plainchar = byte ((fragment & 0x03f) << 2);
95 case step_b:
96 do {
97 if (codechar == code_in + length_in) {
98 state->step = step_b;
99 state->plainchar = *plainchar;
100 return plainchar - plaintext_out;
101 }
102 fragment = (signed char)base64_decode_value_ (*codechar++);
103 } while (fragment < 0);
104 *plainchar++ |= byte ((fragment & 0x030) >> 4);
105 *plainchar = byte ((fragment & 0x00f) << 4);
106 case step_c:
107 do {
108 if (codechar == code_in + length_in) {
109 state->step = step_c;
110 state->plainchar = *plainchar;
111 return plainchar - plaintext_out;
112 }
113 fragment = (signed char)base64_decode_value_ (*codechar++);
114 } while (fragment < 0);
115 *plainchar++ |= byte ((fragment & 0x03c) >> 2);
116 *plainchar = byte ((fragment & 0x003) << 6);
117 case step_d:
118 do {
119 if (codechar == code_in + length_in) {
120 state->step = step_d;
121 state->plainchar = *plainchar;
122 return plainchar - plaintext_out;
123 }
124 fragment = (signed char)base64_decode_value_ (*codechar++);
125 } while (fragment < 0);
126 *plainchar++ |= byte (fragment & 0x03f);
127 }
128 }
129 return plainchar - plaintext_out;
130 }
131}
132
133Memory::BLOB Algorithm::Base64::Decode (const Characters::String& s)
134{
135 //@todo - improve/fix this
136 return Decode (s.AsUTF8<string> ());
137}
138
139Memory::BLOB Algorithm::Base64::Decode (span<const char> s)
140{
141 if (s.empty ()) {
142 return Memory::BLOB{};
143 }
144 size_t dataSize1 = s.size ();
145 StackBuffer<byte> buf1{Memory::eUninitialized, dataSize1}; // MUCH more than big enuf
146 base64_decodestate_ state{};
147 size_t r = base64_decode_block_ (reinterpret_cast<const signed char*> (s.data ()), s.size (), buf1.begin (), &state);
148 Assert (r <= dataSize1);
149 // @todo - should validate this produced a good result? - maybe check resulting state?
150 return Memory::BLOB{buf1.begin (), buf1.begin () + r};
151}
152
153Memory::BLOB Algorithm::Base64::Decode (const string& s)
154{
155 if (s.empty ()) {
156 return Memory::BLOB{};
157 }
158 size_t dataSize1 = s.length ();
159 StackBuffer<byte> buf1{Memory::eUninitialized, dataSize1}; // MUCH more than big enuf
160 base64_decodestate_ state{};
161 size_t r = base64_decode_block_ (reinterpret_cast<const signed char*> (Containers::Start (s)), s.length (), buf1.begin (), &state);
162 Assert (r <= dataSize1);
163 // @todo - should validate this produced a good result? - maybe check resulting state?
164 return Memory::BLOB{buf1.begin (), buf1.begin () + r};
165}
166
167Memory::BLOB Algorithm::Base64::Decode (const u8string& s)
168{
169 if (s.empty ()) {
170 return Memory::BLOB{};
171 }
172 size_t dataSize1 = s.length ();
173 StackBuffer<byte> buf1{Memory::eUninitialized, dataSize1}; // MUCH more than big enuf
174 base64_decodestate_ state{};
175 size_t r = base64_decode_block_ (reinterpret_cast<const signed char*> (Containers::Start (s)), s.length (), buf1.begin (), &state);
176 Assert (r <= dataSize1);
177 // @todo - should validate this produced a good result? - maybe check resulting state?
178 return Memory::BLOB{buf1.begin (), buf1.begin () + r};
179}
180
181void Algorithm::Base64::Decode (const string& s, const Streams::OutputStream::Ptr<byte>& out)
182{
183 // QUICKIE implementation...
184 out.Write (Decode (s));
185}
186
187/*
188 ********************************************************************************
189 ************************ Algorithm::Base64::Encode *****************************
190 ********************************************************************************
191 */
192namespace {
193 enum base64_encodestep {
194 step_A,
195 step_B,
196 step_C
197 };
198 struct base64_encodestate {
199 base64_encodestep step{step_A};
200 signed char result{0};
201 int stepcount{0};
202 LineBreak fLineBreak;
203
204 base64_encodestate (LineBreak lb)
205 : fLineBreak{lb}
206 {
207 }
208 };
209
210 signed char base64_encode_value_ (signed char value_in)
211 {
212 const signed char BASE64_CHARS_[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
213 Assert (NEltsOf (BASE64_CHARS_) == (2 * 26 + 10 + 2 + 1));
214 if (value_in > 63) {
215 return '=';
216 }
217 return BASE64_CHARS_[(int)value_in];
218 }
219
220 size_t base64_encode_block_ (const byte* plaintext_in, size_t length_in, signed char* code_out, base64_encodestate* state)
221 {
222 const int CHARS_PER_LINE = 76;
223
224 const byte* plainchar = plaintext_in;
225 const byte* const plaintextend = plaintext_in + length_in;
226 signed char* codechar = code_out;
227 signed char result = state->result;
228
229 signed char fragment = '\0';
230 switch (state->step) {
231 while (1) {
232 case step_A:
233 if (plainchar == plaintextend) {
234 state->result = result;
235 state->step = step_A;
236 return codechar - code_out;
237 }
238 fragment = to_integer<signed char> (*plainchar++);
239 result = (fragment & 0x0fc) >> 2;
240 *codechar++ = base64_encode_value_ (result);
241 result = (fragment & 0x003) << 4;
242 case step_B:
243 if (plainchar == plaintextend) {
244 state->result = result;
245 state->step = step_B;
246 return codechar - code_out;
247 }
248 fragment = to_integer<signed char> (*plainchar++);
249 result |= (fragment & 0x0f0) >> 4;
250 *codechar++ = base64_encode_value_ (result);
251 result = (fragment & 0x00f) << 2;
252 case step_C:
253 if (plainchar == plaintextend) {
254 state->result = result;
255 state->step = step_C;
256 return codechar - code_out;
257 }
258 fragment = to_integer<signed char> (*plainchar++);
259 result |= (fragment & 0x0c0) >> 6;
260 *codechar++ = base64_encode_value_ (result);
261 result = (fragment & 0x03f) >> 0;
262 *codechar++ = base64_encode_value_ (result);
263
264 ++(state->stepcount);
265 if (state->stepcount == CHARS_PER_LINE / 4) {
266 switch (state->fLineBreak) {
267 case LineBreak::eLF_LB:
268 *codechar++ = '\n';
269 break;
270 case LineBreak::eCRLF_LB:
271 *codechar++ = '\r';
272 *codechar++ = '\n';
273 break;
274 }
275 state->stepcount = 0;
276 }
277 }
278 }
279 return codechar - code_out;
280 }
281 inline size_t base64_encode_blockend_ (signed char* code_out, base64_encodestate* state)
282 {
283 signed char* codechar = code_out;
284 switch (state->step) {
285 case step_B:
286 *codechar++ = base64_encode_value_ (state->result);
287 *codechar++ = '=';
288 *codechar++ = '=';
289 break;
290 case step_C:
291 *codechar++ = base64_encode_value_ (state->result);
292 *codechar++ = '=';
293 break;
294 case step_A:
295 break;
296 }
297 return static_cast<size_t> (codechar - code_out);
298 }
299}
300
301string Algorithm::Base64::Encode (const Streams::InputStream::Ptr<byte>& from, const Options& options)
302{
303#if 0
304 // Use look doing multiple base64_encode_block_() calls!
305#elif 1
306 // quick hack impl
308 const byte* start = bytes.begin ();
309 const byte* end = bytes.end ();
310 Require (start == end or start != nullptr);
311 Require (start == end or end != nullptr);
312 base64_encodestate state{options.fLineBreak};
313 size_t srcLen = end - start;
314 size_t bufSize = 4 * srcLen;
315 Assert (bufSize >= srcLen); // no overflow!
316 StackBuffer<signed char> data{Memory::eUninitialized, bufSize};
317 size_t mostBytesCopied = base64_encode_block_ (start, srcLen, data.begin (), &state);
318 size_t extraBytes = base64_encode_blockend_ (data.begin () + mostBytesCopied, &state);
319 size_t totalBytes = mostBytesCopied + extraBytes;
320 Assert (totalBytes <= bufSize);
321 return string{data.begin (), data.begin () + totalBytes};
322#else
323 Require (start == end or start != nullptr);
324 Require (start == end or end != nullptr);
325
326 base64_encodestate state{lb};
327 size_t srcLen = end - start;
328 size_t bufSize = 4 * srcLen;
329 Assert (bufSize >= srcLen); // no overflow!
330 StackBuffer<char> data{Memory::eUninitialized, bufSize};
331 size_t mostBytesCopied = base64_encode_block_ (start, srcLen, data.begin (), &state);
332 size_t extraBytes = base64_encode_blockend_ (data.begin () + mostBytesCopied, &state);
333 size_t totalBytes = mostBytesCopied + extraBytes;
334 Assert (totalBytes <= bufSize);
335 return string{data.begin (), data.begin () + totalBytes};
336#endif
337}
338
339string Algorithm::Base64::Encode (const Memory::BLOB& from, const Options& options)
340{
341 const byte* start = from.begin ();
342 const byte* end = from.end ();
343 Require (start == end or start != nullptr);
344 Require (start == end or end != nullptr);
345 base64_encodestate state{options.fLineBreak};
346 size_t srcLen = end - start;
347 size_t bufSize = 4 * srcLen;
348 Assert (bufSize >= srcLen); // no overflow!
349 StackBuffer<signed char> data{Memory::eUninitialized, bufSize};
350 size_t mostBytesCopied = base64_encode_block_ (start, srcLen, data.begin (), &state);
351 size_t extraBytes = base64_encode_blockend_ (data.begin () + mostBytesCopied, &state);
352 size_t totalBytes = mostBytesCopied + extraBytes;
353 Assert (totalBytes <= bufSize);
354 return string{data.begin (), data.begin () + totalBytes};
355}
#define RequireNotNull(p)
Definition Assertions.h:347
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual size_t length() const
Definition BLOB.inl:271
nonvirtual const byte * end() const
Definition BLOB.inl:258
nonvirtual const byte * begin() const
Definition BLOB.inl:253
nonvirtual size_t size() const
Definition BLOB.inl:281
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
nonvirtual String ReadAll(size_t upTo=numeric_limits< size_t >::max()) const
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
nonvirtual void Write(span< ELEMENT_TYPE2, EXTENT_2 > elts) const
CONTAINER::value_type * Start(CONTAINER &c)
For a contiguous container (such as a vector or basic_string) - find the pointer to the start of the ...