CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
Base64.cpp
Go to the documentation of this file.
1/*
2 Base64.cpp and Base64.h
3
4 Copyright (C) 2004-2008 René Nyffenegger
5
6 This source code is provided 'as-is', without any express or implied
7 warranty. In no event will the author be held liable for any damages
8 arising from the use of this software.
9
10 Permission is granted to anyone to use this software for any purpose,
11 including commercial applications, and to alter it and redistribute it
12 freely, subject to the following restrictions:
13
14 1. The origin of this source code must not be misrepresented; you must not
15 claim that you wrote the original source code. If you use this source code
16 in a product, an acknowledgment in the product documentation would be
17 appreciated but is not required.
18
19 2. Altered source versions must be plainly marked as such, and must not be
20 misrepresented as being the original source code.
21
22 3. This notice may not be removed or altered from any source distribution.
23
24 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
25
26*/
27
28#include "Base64.h"
29#include "UnitTestFramework.h"
30#include "PsyTime.h"
31#include <iostream>
32
33static const std::string base64_chars =
34 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
35 "abcdefghijklmnopqrstuvwxyz"
36 "0123456789+/";
37
38
39static inline bool is_base64(unsigned char c) {
40 return (isalnum(c) || (c == '+') || (c == '/'));
41}
42
43/* Added for convenience by Thor List */
44std::string base64_encode(std::string string_to_encode) {
45 return base64_encode((const unsigned char*)string_to_encode.c_str(), (unsigned int)string_to_encode.length());
46}
47
48/* Added for convenience by Thor List */
49std::string base64_decode(const char* encoded_string) {
50 std::string str = encoded_string;
51 return base64_decode(str);
52}
53
54std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
55 std::string ret;
56 int i = 0;
57 int j = 0;
58 unsigned char char_array_3[3];
59 unsigned char char_array_4[4];
60
61 while (in_len--) {
62 char_array_3[i++] = *(bytes_to_encode++);
63 if (i == 3) {
64 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
65 char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
66 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
67 char_array_4[3] = char_array_3[2] & 0x3f;
68
69 for(i = 0; (i <4) ; i++)
70 ret += base64_chars[char_array_4[i]];
71 i = 0;
72 }
73 }
74
75 if (i)
76 {
77 for(j = i; j < 3; j++)
78 char_array_3[j] = '\0';
79
80 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
81 char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
82 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
83 char_array_4[3] = char_array_3[2] & 0x3f;
84
85 for (j = 0; (j < i + 1); j++)
86 ret += base64_chars[char_array_4[j]];
87
88 while((i++ < 3))
89 ret += '=';
90
91 }
92
93 return ret;
94
95}
96
97std::string base64_decode(std::string const& encoded_string) {
98 int in_len = (int)encoded_string.size();
99 int i = 0;
100 int j = 0;
101 int in_ = 0;
102 unsigned char char_array_4[4], char_array_3[3];
103 std::string ret;
104
105 while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
106 char_array_4[i++] = encoded_string[in_]; in_++;
107 if (i ==4) {
108 for (i = 0; i <4; i++)
109 char_array_4[i] = (unsigned char)base64_chars.find(char_array_4[i]);
110
111 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
112 char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
113 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
114
115 for (i = 0; (i < 3); i++)
116 ret += char_array_3[i];
117 i = 0;
118 }
119 }
120
121 if (i) {
122 for (j = i; j <4; j++)
123 char_array_4[j] = 0;
124
125 for (j = 0; j <4; j++)
126 char_array_4[j] = (unsigned char)base64_chars.find(char_array_4[j]);
127
128 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
129 char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
130 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
131
132 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
133 }
134
135 return ret;
136}
137
138
139// ----------------------------------------------------------------------------
140// Unit test
141// ----------------------------------------------------------------------------
142// The base64_* functions live at global scope (no namespace), but the unit
143// test framework, metric/progress helpers and GetTimeNow() all live in
144// namespace cmlabs. We put the test and its registration inside cmlabs so the
145// framework calls resolve, and reach the encode/decode functions via ::.
146namespace cmlabs {
147
149 unittest::progress(0, "RFC 4648 known vectors");
150
151 // 1. RFC 4648 section 10 test vectors: encode(input) == expected
152 {
153 struct Vec { const char* in; const char* out; };
154 static const Vec vectors[] = {
155 { "", "" },
156 { "f", "Zg==" },
157 { "fo", "Zm8=" },
158 { "foo", "Zm9v" },
159 { "foob", "Zm9vYg==" },
160 { "fooba", "Zm9vYmE=" },
161 { "foobar", "Zm9vYmFy" },
162 };
163 const int nvec = (int)(sizeof(vectors) / sizeof(vectors[0]));
164 for (int v = 0; v < nvec; v++) {
165 std::string in = vectors[v].in;
166 std::string enc = ::base64_encode(in);
167 if (enc != vectors[v].out) {
168 unittest::fail("Base64 test: encode(\"%s\") = \"%s\", expected \"%s\"",
169 vectors[v].in, enc.c_str(), vectors[v].out);
170 return false;
171 }
172 unittest::detail("encode(\"%s\") = \"%s\"", vectors[v].in, enc.c_str());
173
174 // And decode of the canonical encoding must reproduce the input.
175 std::string dec = ::base64_decode(std::string(vectors[v].out));
176 if (dec != in) {
177 unittest::fail("Base64 test: decode(\"%s\") = \"%s\", expected \"%s\"",
178 vectors[v].out, dec.c_str(), vectors[v].in);
179 return false;
180 }
181 }
182 }
183
184 // 2. Round-trip on random binary, including embedded NUL bytes.
185 // Use the buffer-based encode and the std::string-based decode so that
186 // embedded NULs are preserved (the const char* convenience overloads
187 // would truncate at the first NUL).
188 unittest::progress(35, "random binary round-trip");
189 {
190 // Deterministic PRNG so failures are reproducible.
191 uint32 seed = 0x1234abcdU;
192 for (int trial = 0; trial < 200; trial++) {
193 uint32 len = (seed % 256u); // 0..255 bytes
194 seed = seed * 1664525u + 1013904223u; // LCG step
195 std::string blob;
196 blob.reserve(len);
197 for (uint32 b = 0; b < len; b++) {
198 blob.push_back((char)(seed & 0xffu));
199 seed = seed * 1664525u + 1013904223u;
200 }
201 std::string enc = ::base64_encode((const unsigned char*)blob.data(), (unsigned int)blob.size());
202 std::string dec = ::base64_decode(enc);
203 if (dec != blob) {
204 unittest::fail("Base64 test: random round-trip mismatch at trial %d (len %u): got %u bytes back",
205 trial, (unsigned int)blob.size(), (unsigned int)dec.size());
206 return false;
207 }
208 }
209 }
210
211 // 3. Encoded output length / padding sanity for sizes 0..32.
212 unittest::progress(60, "padding & length invariants");
213 {
214 for (uint32 len = 0; len <= 32; len++) {
215 std::string blob(len, 'A');
216 std::string enc = ::base64_encode((const unsigned char*)blob.data(), (unsigned int)blob.size());
217 // Standard base64: 4 chars per 3 input bytes, padded to a multiple of 4.
218 size_t expectedLen = len == 0 ? 0 : (((size_t)len + 2) / 3) * 4;
219 if (enc.size() != expectedLen) {
220 unittest::fail("Base64 test: encode length for %u bytes = %u, expected %u",
221 len, (unsigned int)enc.size(), (unsigned int)expectedLen);
222 return false;
223 }
224 std::string dec = ::base64_decode(enc);
225 if (dec != blob) {
226 unittest::fail("Base64 test: length-vector round-trip mismatch at len %u", len);
227 return false;
228 }
229 }
230 }
231
232 // 4. Throughput metric: encode + decode a fixed payload many times.
233 unittest::progress(80, "throughput");
234 {
235 const uint32 payloadSize = 64u * 1024u; // 64 KB
236 std::string payload(payloadSize, '\0');
237 uint32 seed = 0xdeadbeefU;
238 for (uint32 b = 0; b < payloadSize; b++) {
239 payload[b] = (char)(seed & 0xffu);
240 seed = seed * 1664525u + 1013904223u;
241 }
242
243 const int iterations = 64; // 64 * 64KB = 4 MB through each path
244 uint64 t0 = GetTimeNow();
245 std::string enc, dec;
246 for (int it = 0; it < iterations; it++) {
247 enc = ::base64_encode((const unsigned char*)payload.data(), (unsigned int)payload.size());
248 dec = ::base64_decode(enc);
249 if (dec != payload) {
250 unittest::fail("Base64 test: throughput round-trip mismatch at iteration %d", it);
251 return false;
252 }
253 }
254 double us = (double)(GetTimeNow() - t0);
255 if (us < 1.0) us = 1.0;
256 double totalBytes = (double)payloadSize * iterations * 2.0; // encode + decode
257 double mbPerSec = (totalBytes / (1024.0 * 1024.0)) / (us / 1e6);
258 unittest::metric("encode_decode_throughput", mbPerSec, "MB/s", true);
259 unittest::detail("processed %.1f MB in %.0f us", totalBytes / (1024.0 * 1024.0), us);
260 }
261
262 unittest::progress(100, "done");
263 return true;
264}
265
268 "Base64 encode/decode RFC 4648 vectors, binary round-trip, throughput", "util");
269}
270
271} // namespace cmlabs
std::string base64_decode(const char *encoded_string)
Decode Base64 text.
Definition Base64.cpp:49
std::string base64_encode(std::string string_to_encode)
Base64-encode a string's bytes.
Definition Base64.cpp:44
static bool is_base64(unsigned char c)
Definition Base64.cpp:39
static const std::string base64_chars
Definition Base64.cpp:33
Base64 encoding/decoding of arbitrary binary data (standard alphabet, '=' padding).
std::string base64_decode(const char *encoded_string)
Decode Base64 text.
Definition Base64.cpp:49
std::string base64_encode(std::string string_to_encode)
Base64-encode a string's bytes.
Definition Base64.cpp:44
CMSDK time: µs-resolution 64-bit timestamps and the Time Mapping Constant (TMC).
Small, dependency-free unit test harness used by all CMSDK object tests.
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
Definition PsyTime.cpp:69
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
void Register_Base64_Tests()
Definition Base64.cpp:266
bool Base64_UnitTest()
Base64 round-trip unit test.
Definition Base64.cpp:148