summaryrefslogtreecommitdiff
path: root/http
diff options
context:
space:
mode:
authorEd Tanous <ed@tanous.net>2023-12-13 21:49:58 +0300
committerEd Tanous <ed@tanous.net>2024-01-22 22:15:20 +0300
commitee192c065cc658b2489d04c791e2fb956a331699 (patch)
tree2c15d1c4b547fc3fcdfbb81ce056787817002e51 /http
parent18f8f608b966c802b3e2a389e3c1ec5a1fd9407b (diff)
downloadbmcweb-ee192c065cc658b2489d04c791e2fb956a331699.tar.xz
Make base64 encoder incremental
As part of https://gerrit.openbmc.org/c/openbmc/bmcweb/+/67667, it would be desirable if we could incrementally encode base64 in chunks. Given that base64 encoding requires encoding 3 characters to 4, there's a possibility that a chunk might not be mod 3 length. This commit moves the base64 encoder into a class that can run incrementally. Tested: Unit tests pass. More tests in next commit. Change-Id: Ic7da3fd4db865c99fcbd96ae06fdecb87628f94c Signed-off-by: Ed Tanous <ed@tanous.net>
Diffstat (limited to 'http')
-rw-r--r--http/utility.hpp138
1 files changed, 105 insertions, 33 deletions
diff --git a/http/utility.hpp b/http/utility.hpp
index dc7ea7f1b2..bf708915b6 100644
--- a/http/utility.hpp
+++ b/http/utility.hpp
@@ -114,62 +114,134 @@ constexpr size_t numArgsFromTag(int tag)
return ret;
};
-inline std::string base64encode(std::string_view data)
+class Base64Encoder
{
- const std::array<char, 64> key = {
+ char overflow1 = '\0';
+ char overflow2 = '\0';
+ uint8_t overflowCount = 0;
+
+ constexpr static std::array<char, 64> key = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
- size_t size = data.size();
- std::string ret;
- ret.resize((size + 2) / 3 * 4);
- auto it = ret.begin();
-
- size_t i = 0;
- while (i < size)
+ // Takes 3 ascii chars, and encodes them as 4 base64 chars
+ static void encodeTriple(char first, char second, char third,
+ std::string& output)
{
size_t keyIndex = 0;
- keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2;
- *it++ = key[keyIndex];
+ keyIndex = static_cast<size_t>(first & 0xFC) >> 2;
+ output += key[keyIndex];
- if (i + 1 < size)
- {
- keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
- keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4;
- *it++ = key[keyIndex];
+ keyIndex = static_cast<size_t>(first & 0x03) << 4;
+ keyIndex += static_cast<size_t>(second & 0xF0) >> 4;
+ output += key[keyIndex];
- if (i + 2 < size)
- {
- keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
- keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6;
- *it++ = key[keyIndex];
+ keyIndex = static_cast<size_t>(second & 0x0F) << 2;
+ keyIndex += static_cast<size_t>(third & 0xC0) >> 6;
+ output += key[keyIndex];
- keyIndex = static_cast<size_t>(data[i + 2] & 0x3F);
- *it++ = key[keyIndex];
+ keyIndex = static_cast<size_t>(third & 0x3F);
+ output += key[keyIndex];
+ }
+
+ public:
+ // Accepts a partial string to encode, and writes the encoded characters to
+ // the output stream. requires subsequently calling finalize to complete
+ // stream.
+ void encode(std::string_view data, std::string& output)
+ {
+ // Encode the last round of overflow chars first
+ if (overflowCount == 2)
+ {
+ if (!data.empty())
+ {
+ encodeTriple(overflow1, overflow2, data[0], output);
+ overflowCount = 0;
+ data.remove_prefix(1);
}
- else
+ }
+ else if (overflowCount == 1)
+ {
+ if (data.size() >= 2)
{
- keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
- *it++ = key[keyIndex];
- *it++ = '=';
+ encodeTriple(overflow1, data[0], data[1], output);
+ overflowCount = 0;
+ data.remove_prefix(2);
}
}
+
+ while (data.size() >= 3)
+ {
+ encodeTriple(data[0], data[1], data[2], output);
+ data.remove_prefix(3);
+ }
+
+ if (!data.empty() && overflowCount == 0)
+ {
+ overflow1 = data[0];
+ overflowCount++;
+ data.remove_prefix(1);
+ }
+
+ if (!data.empty() && overflowCount == 1)
+ {
+ overflow2 = data[0];
+ overflowCount++;
+ data.remove_prefix(1);
+ }
+ }
+
+ // Completes a base64 output, by writing any MOD(3) characters to the
+ // output, as well as any required trailing =
+ void finalize(std::string& output)
+ {
+ if (overflowCount == 0)
+ {
+ return;
+ }
+ size_t keyIndex = static_cast<size_t>(overflow1 & 0xFC) >> 2;
+ output += key[keyIndex];
+
+ keyIndex = static_cast<size_t>(overflow1 & 0x03) << 4;
+ if (overflowCount == 2)
+ {
+ keyIndex += static_cast<size_t>(overflow2 & 0xF0) >> 4;
+ output += key[keyIndex];
+ keyIndex = static_cast<size_t>(overflow2 & 0x0F) << 2;
+ output += key[keyIndex];
+ }
else
{
- keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
- *it++ = key[keyIndex];
- *it++ = '=';
- *it++ = '=';
+ output += key[keyIndex];
+ output += '=';
}
+ output += '=';
+ overflowCount = 0;
+ }
- i += 3;
+ // Returns the required output buffer in characters for an input of size
+ // inputSize
+ static size_t constexpr encodedSize(size_t inputSize)
+ {
+ // Base64 encodes 3 character blocks as 4 character blocks
+ // With a possibility of 2 trailing = characters
+ return (inputSize + 2) / 3 * 4;
}
+};
- return ret;
+inline std::string base64encode(std::string_view data)
+{
+ // Encodes a 3 character stream into a 4 character stream
+ std::string out;
+ Base64Encoder base64;
+ out.reserve(Base64Encoder::encodedSize(data.size()));
+ base64.encode(data, out);
+ base64.finalize(out);
+ return out;
}
// TODO this is temporary and should be deleted once base64 is refactored out of