Skip to content

Commit d3bbc22

Browse files
authored
Base64 serialization
* constexpr enum flags * expose print_aperture_exception() utility * base64 serialization * add test
1 parent cc9494f commit d3bbc22

File tree

6 files changed

+183
-21
lines changed

6 files changed

+183
-21
lines changed

SConstruct

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ comm_test_env = Environment(
176176
comm_test_env.Append(RPATH = comm_test_env['LIBPATH'])
177177

178178

179-
##comm_test_env.ParseConfig('pkg-config --cflags --libs protobuf')
179+
comm_test_env.ParseConfig('pkg-config --cflags --libs openssl')
180180

181181
comm_test_source_files = [
182182
'test/AuthEnabledVDMSServer.cc',
@@ -186,6 +186,7 @@ comm_test_source_files = [
186186
'test/VDMSServer.cc',
187187
'test/VDMSServerTests.cc',
188188
'test/TimedQueueTests.cc',
189+
'test/Base64Tests.cc',
189190
]
190191

191192
comm_test = comm_test_env.Program('test/comm_test', comm_test_source_files)

include/comm/Enum.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66

77
#define ENUM_FLAGS(T1, T2) \
88
enum class T1 : T2; \
9-
inline T1 operator~ (T1 a ) { return static_cast<T1>(~static_cast<T2>(a)); } \
10-
inline T1 operator| (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) | static_cast<T2>(b))); } \
11-
inline T1 operator& (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) & static_cast<T2>(b))); } \
12-
inline T1 operator^ (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) ^ static_cast<T2>(b))); } \
13-
inline T1& operator|= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) |= static_cast<T2>(b))); } \
14-
inline T1& operator&= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) &= static_cast<T2>(b))); } \
15-
inline T1& operator^= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) ^= static_cast<T2>(b))); } \
9+
constexpr inline T1 operator~ (T1 a ) { return static_cast<T1>(~static_cast<T2>(a)); } \
10+
constexpr inline T1 operator| (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) | static_cast<T2>(b))); } \
11+
constexpr inline T1 operator& (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) & static_cast<T2>(b))); } \
12+
constexpr inline T1 operator^ (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) ^ static_cast<T2>(b))); } \
13+
constexpr inline T1& operator|= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) |= static_cast<T2>(b))); } \
14+
constexpr inline T1& operator&= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) &= static_cast<T2>(b))); } \
15+
constexpr inline T1& operator^= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) ^= static_cast<T2>(b))); } \
1616
enum class T1 : T2

include/util/Base64.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
*
3+
* @copyright Copyright (c) 2022 ApertureData Inc.
4+
*
5+
*/
6+
7+
#pragma once
8+
9+
#include <string>
10+
#include <vector>
11+
#include <cassert>
12+
#include <openssl/evp.h>
13+
14+
// Utilities to serialize binary data as base64 strings.
15+
// Base64 uses 4 ASCII characters to encode 3 bytes of binary data.
16+
// https://en.wikipedia.org/wiki/Base64
17+
class Base64 {
18+
public:
19+
// static methods only
20+
Base64() = delete;
21+
22+
// Returns the number of characters required to serialize data of size `bytes` as base64.
23+
// If the input size is not divisible by 3, up to 2 `=` chars are used as padding in the
24+
// final quadruplet so as to guarantee that the encoded size is always a multiple of 4.
25+
static constexpr std::size_t encoded_bytes( std::size_t bytes )
26+
{
27+
return ( ( ( bytes + 2 ) / 3 ) * 4 );
28+
}
29+
30+
// Returns the buffer size needed to decode the specified number of base64 characters.
31+
// Warning: Base64 decodes data in 3 byte chunks, so the required buffer size will exceed
32+
// the size of the encoded binary data if it is not divisible by 3 bytes.
33+
static constexpr std::size_t decoded_bytes( std::size_t chars )
34+
{
35+
return ( ( ( chars + 3 ) / 4 ) * 3 );
36+
}
37+
38+
template< typename IN >
39+
static std::string encode( const IN* in, std::size_t in_size ) {
40+
auto in_size_bytes = in_size * sizeof(IN);
41+
auto enc_bytes = encoded_bytes(in_size_bytes);
42+
std::string out(enc_bytes, '\0');
43+
auto written = EVP_EncodeBlock(reinterpret_cast< unsigned char* >(out.data()),
44+
reinterpret_cast< const unsigned char* >(in), in_size_bytes);
45+
assert(written == enc_bytes);
46+
return out;
47+
}
48+
49+
template< typename IN >
50+
static std::vector< unsigned char > decode( const IN* in, std::size_t in_size ) {
51+
auto in_size_bytes = in_size * sizeof(IN);
52+
assert(in_size_bytes % 4 == 0);
53+
auto dec_bytes = decoded_bytes(in_size_bytes);
54+
std::vector< unsigned char > out(dec_bytes);
55+
auto written = EVP_DecodeBlock( out.data(),
56+
reinterpret_cast< const unsigned char* >(in), in_size_bytes);
57+
assert(written == dec_bytes);
58+
if (in_size > 0) {
59+
for (auto* tail = in + (in_size - 1); *tail == IN('='); --tail) {
60+
--written;
61+
}
62+
out.resize(written);
63+
}
64+
return out;
65+
}
66+
67+
template< typename IN_STR >
68+
static std::string encode( const IN_STR& in ) {
69+
return encode(in.data(), in.size());
70+
}
71+
72+
template< typename IN_STR >
73+
static std::vector< unsigned char > decode( const IN_STR& in ) {
74+
return decode(in.data(), in.size());
75+
}
76+
};

include/util/ExceptionUtil.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
*
3+
* @copyright Copyright (c) 2022 ApertureData Inc.
4+
*
5+
*/
6+
7+
#pragma once
8+
9+
#include <util/TypeName.h>
10+
#include <sstream>
11+
#include <cstring>
12+
13+
template<typename EX>
14+
std::string print_aperture_exception(const EX& e) {
15+
std::ostringstream oss;
16+
oss << type_name(e) << " " << e.name << " (" << e.file << ":" << e.line << ")";
17+
if (e.errno_val != 0)
18+
oss << " [code=" << strerror(e.errno_val) << "]";
19+
if (!e.msg.empty())
20+
oss << " " << e.msg;
21+
return oss.str();
22+
}

test/Base64Tests.cc

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* @copyright Copyright (c) 2022 ApertureData Inc.
3+
*/
4+
5+
#include "gtest/gtest.h"
6+
7+
#include "util/Base64.h"
8+
9+
TEST(Base64Test, string)
10+
{
11+
std::string str("The quick brown fox jumps over the lazy dog.");
12+
auto b64 = Base64::encode(str);
13+
auto decoded_bytes = Base64::decode(b64);
14+
std::string round_trip(decoded_bytes.begin(), decoded_bytes.end());
15+
16+
EXPECT_EQ(str, round_trip);
17+
}
18+
19+
TEST(Base64Test, partial_string)
20+
{
21+
std::string str("The quick brown fox jumps over the lazy dog.");
22+
for (std::size_t i = 0; i < 20; ++i ) {
23+
auto b64 = Base64::encode(str.data(), i);
24+
auto decoded_bytes = Base64::decode(b64);
25+
std::string round_trip(decoded_bytes.begin(), decoded_bytes.end());
26+
27+
EXPECT_EQ(str.substr(0,i), round_trip);
28+
}
29+
}
30+
31+
TEST(Base64Test, serialize_pod)
32+
{
33+
struct my_pod {
34+
int x;
35+
float y;
36+
bool flag;
37+
std::array<char, 16> buf;
38+
};
39+
40+
std::vector< my_pod > my_pods = {
41+
my_pod{ 1, 2, true, "first thing" },
42+
my_pod{ 3, 4, false, "second thing" },
43+
my_pod{ 5, 6, true, "third thing" },
44+
my_pod{ 7, 8, false, "fourth thing" },
45+
};
46+
47+
auto b64 = Base64::encode(my_pods);
48+
auto decoded_bytes = Base64::decode(b64);
49+
ASSERT_EQ(decoded_bytes.size(), sizeof(my_pod) * my_pods.size());
50+
const auto* rt_pods = reinterpret_cast< const my_pod* >(decoded_bytes.data());
51+
for (std::size_t i = 0; i < my_pods.size(); ++i) {
52+
EXPECT_EQ(my_pods[i].x, rt_pods[i].x);
53+
EXPECT_EQ(my_pods[i].y, rt_pods[i].y);
54+
EXPECT_EQ(my_pods[i].flag, rt_pods[i].flag);
55+
EXPECT_EQ(my_pods[i].buf, rt_pods[i].buf);
56+
}
57+
}
58+
59+
TEST(Base64Test, corresponding_sizes) {
60+
for (int i = 0; i < 100; ++i) {
61+
auto rt_size = Base64::decoded_bytes( Base64::encoded_bytes(i));
62+
switch(i%3) {
63+
case 0: {
64+
EXPECT_EQ(rt_size, i);
65+
} break;
66+
case 1: {
67+
EXPECT_EQ(rt_size, i + 2);
68+
} break;
69+
case 2: {
70+
EXPECT_EQ(rt_size, i + 1);
71+
} break;
72+
default: break;
73+
}
74+
}
75+
}

tools/prometheus_ambassador/src/PrintCaughtException.cc

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,12 @@
66

77
#include "PrintCaughtException.h"
88
#include <util/TypeName.h>
9+
#include <util/ExceptionUtil.h>
910
#include <aperturedb/Exception.h>
1011
#include <comm/Exception.h>
1112
#include <sstream>
1213
#include <cstring>
1314

14-
namespace {
15-
template<typename EX>
16-
std::string print_aperture_exception(const EX& e) {
17-
std::ostringstream oss;
18-
oss << type_name(e) << " " << e.name << " (" << e.file << ":" << e.line << ")";
19-
if (e.errno_val != 0)
20-
oss << " [code=" << strerror(e.errno_val) << "]";
21-
if (!e.msg.empty())
22-
oss << " " << e.msg;
23-
return oss.str();
24-
}
25-
}
26-
2715
std::string print_caught_exception() {
2816
try {
2917
throw;

0 commit comments

Comments
 (0)