Base64Encoder() = default;
~Base64Encoder() = default;
In general, if you want to explicitly provide special member functions, then explicitly provide them all. Others will advise that if you can avoid defining the special member functions, then do so. Read more here.
class Base64Encoder {
public:
// ... no default operations declared ...
const std::string encode(const std::string s) const;
private:
// ...
const std::string encode(const std::string s) const;
std::string
requires <string>
be included.
s
is passed by value to const
, which incurs an unnecessary copy. Consider using std::string_view
if you have c++17. Read more here.
std::string encode(const std::string_view s) const;
Otherwise, pass by reference to const
.
std::string encode(const std::string& s) const;
constexpr static unsigned char get_base_64_char(ulong number_of_char) {
return base64_table.at(number_of_char);
}
ulong
is not a standard unsigned integer type. If you need a fixed-size integer, consider one of the types from <cstdint>
(e.g. std::uint8_t
). For this use-case, I'd just use std::size_t
.
Is number_of_char
a clear description of what the value represents? Would index
be clearer?
Do you need the bounds checking of base64_table.at()
?
const static unsigned char next_ascii(size_t current_index, const std::string s, size_t length_of_s);
The first const
is unnecessary.
size_t
is not guaranteed by the standard to exist in the global namespace. Use std::size_t
and include <cstddef>
. Read more here.
const static size_t MINIMAL_B64_STRING_LENGTH = 4;
Consider reserving upper case names for the preprocessor.
constexpr
?
std::stringstream encoding;
Do we need a std::stringstream
? We can actually calculate the destination buffer length. For base64 encoding, every 3 octets maps to 4 sextets. To find the encoded length \$m\$, find the total number of octets to be read (integral ceiling) and multiply it by the length of each sextet.
$$m = 4 \dot ((n + 2) / 3)$$
while (i < length_of_s) {
// if first sextet, ...
// else if second sextet, ...
// else if third sextet, ...
// else must be fourth sextet ...
}
Instead of cycling through each branch on every loop, consider a modulo approach. Loop through full sextets until you have a partial sextet left at the end (the remainder). Then you can branch based on what you have left.
for (auto remaining_sextets = s.size() / 3; remaining_sextets--;) {
encoded += /* first sextet masked and shifted */
encoded += /* second sextet masked and shifted */
encoded += /* third sextet masked and shifted */
encoded += /* fourth sextet masked and shifted */
}
switch (len % 3) {
case 2:
encoded += /* first sextet masked and shifted */
encoded += /* second sextet masked and shifted */
encoded += /* third sextet masked and shifted */
encoded += '=';
break;
case 1:
encoded += /* first sextet masked and shifted */
encoded += /* second sextet masked and shifted */
encoded += '=';
encoded += '=';
break;
case 0:
break;
}
return encoded;