123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- /*
- This is free and unencumbered software released into the public domain.
- Anyone is free to copy, modify, publish, use, compile, sell, or
- distribute this software, either in source code form or as a compiled
- binary, for any purpose, commercial or non-commercial, and by any
- means.
- In jurisdictions that recognize copyright laws, the author or authors
- of this software dedicate any and all copyright interest in the
- software to the public domain. We make this dedication for the benefit
- of the public at large and to the detriment of our heirs and
- successors. We intend this dedication to be an overt act of
- relinquishment in perpetuity of all present and future rights to this
- software under copyright law.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- For more information, please refer to <http://unlicense.org>
- */
- #ifndef PUBLIC_DOMAIN_BASE64_HPP_
- #define PUBLIC_DOMAIN_BASE64_HPP_
- #include <cstdint>
- #include <iterator>
- #include <stdexcept>
- #include <string>
- class base64_error : public std::runtime_error
- {
- public:
- using std::runtime_error::runtime_error;
- };
- class base64
- {
- public:
- enum class alphabet
- {
- /** the alphabet is detected automatically */
- auto_,
- /** the standard base64 alphabet is used */
- standard,
- /** like `standard` except that the characters `+` and `/` are replaced by `-` and `_` respectively*/
- url_filename_safe
- };
- enum class decoding_behavior
- {
- /** if the input is not padded, the remaining bits are ignored */
- moderate,
- /** if a padding character is encounter decoding is finished */
- loose
- };
- /**
- Encodes all the elements from `in_begin` to `in_end` to `out`.
- @warning The source and destination cannot overlap. The destination must be able to hold at least
- `required_encode_size(std::distance(in_begin, in_end))`, otherwise the behavior depends on the output iterator.
- @tparam Input_iterator the source; the returned elements are cast to `std::uint8_t` and should not be greater than
- 8 bits
- @tparam Output_iterator the destination; the elements written to it are from the type `char`
- @param in_begin the beginning of the source
- @param in_end the ending of the source
- @param out the destination iterator
- @param alphabet which alphabet should be used
- @returns the iterator to the next element past the last element copied
- @throws see `Input_iterator` and `Output_iterator`
- */
- template<typename Input_iterator, typename Output_iterator>
- static Output_iterator encode(Input_iterator in_begin, Input_iterator in_end, Output_iterator out,
- alphabet alphabet = alphabet::standard)
- {
- constexpr auto pad = '=';
- const char* alpha = alphabet == alphabet::url_filename_safe
- ? "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
- : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- while (in_begin != in_end) {
- std::uint8_t i0 = 0, i1 = 0, i2 = 0;
- // first character
- i0 = static_cast<std::uint8_t>(*in_begin);
- ++in_begin;
- *out = alpha[i0 >> 2 & 0x3f];
- ++out;
- // part of first character and second
- if (in_begin != in_end) {
- i1 = static_cast<std::uint8_t>(*in_begin);
- ++in_begin;
- *out = alpha[((i0 & 0x3) << 4) | (i1 >> 4 & 0x0f)];
- ++out;
- } else {
- *out = alpha[(i0 & 0x3) << 4];
- ++out;
- // last padding
- *out = pad;
- ++out;
- // last padding
- *out = pad;
- ++out;
- break;
- }
- // part of second character and third
- if (in_begin != in_end) {
- i2 = static_cast<std::uint8_t>(*in_begin);
- ++in_begin;
- *out = alpha[((i1 & 0xf) << 2) | (i2 >> 6 & 0x03)];
- ++out;
- } else {
- *out = alpha[(i1 & 0xf) << 2];
- ++out;
- // last padding
- *out = pad;
- ++out;
- break;
- }
- // rest of third
- *out = alpha[i2 & 0x3f];
- ++out;
- }
- return out;
- }
- /**
- Encodes a string.
- @param str the string that should be encoded
- @param alphabet which alphabet should be used
- @returns the encoded base64 string
- @throws see base64::encode()
- */
- static std::string encode(const std::string& str, alphabet alphabet = alphabet::standard)
- {
- std::string result;
- result.reserve(required_encode_size(str.length()) + 1);
- encode(str.begin(), str.end(), std::back_inserter(result), alphabet);
- return result;
- }
- /**
- Encodes a char array.
- @param buffer the char array
- @param size the size of the array
- @param alphabet which alphabet should be used
- @returns the encoded string
- */
- static std::string encode(const char* buffer, std::size_t size, alphabet alphabet = alphabet::standard)
- {
- std::string result;
- result.reserve(required_encode_size(size) + 1);
- encode(buffer, buffer + size, std::back_inserter(result), alphabet);
- return result;
- }
- /**
- Decodes all the elements from `in_begin` to `in_end` to `out`. `in_begin` may point to the same location as `out`,
- in other words: inplace decoding is possible.
- @warning The destination must be able to hold at least `required_decode_size(std::distance(in_begin, in_end))`,
- otherwise the behavior depends on the output iterator.
- @tparam Input_iterator the source; the returned elements are cast to `char`
- @tparam Output_iterator the destination; the elements written to it are from the type `std::uint8_t`
- @param in_begin the beginning of the source
- @param in_end the ending of the source
- @param out the destination iterator
- @param alphabet which alphabet should be used
- @param behavior the behavior when an error was detected
- @returns the iterator to the next element past the last element copied
- @throws base64_error depending on the set behavior
- @throws see `Input_iterator` and `Output_iterator`
- */
- template<typename Input_iterator, typename Output_iterator>
- static Output_iterator decode(Input_iterator in_begin, Input_iterator in_end, Output_iterator out,
- alphabet alphabet = alphabet::auto_,
- decoding_behavior behavior = decoding_behavior::moderate)
- {
- //constexpr auto pad = '=';
- std::uint8_t last = 0;
- auto bits = 0;
- while (in_begin != in_end) {
- auto c = *in_begin;
- ++in_begin;
- if (c == '=') {
- break;
- }
- auto part = _base64_value(alphabet, c);
- // enough bits for one byte
- if (bits + 6 >= 8) {
- *out = (last << (8 - bits)) | (part >> (bits - 2));
- ++out;
- bits -= 2;
- } else {
- bits += 6;
- }
- last = part;
- }
- // check padding
- if (behavior != decoding_behavior::loose) {
- while (in_begin != in_end) {
- auto c = *in_begin;
- ++in_begin;
- if (c != '=') {
- throw base64_error("invalid base64 character.");
- }
- }
- }
- return out;
- }
- /**
- Decodes a string.
- @param str the base64 encoded string
- @param alphabet which alphabet should be used
- @param behavior the behavior when an error was detected
- @returns the decoded string
- @throws see base64::decode()
- */
- static std::string decode(const std::string& str, alphabet alphabet = alphabet::auto_,
- decoding_behavior behavior = decoding_behavior::moderate)
- {
- std::string result;
- result.reserve(max_decode_size(str.length()));
- decode(str.begin(), str.end(), std::back_inserter(result), alphabet, behavior);
- return result;
- }
- /**
- Decodes a string.
- @param buffer the base64 encoded buffer
- @param size the size of the buffer
- @param alphabet which alphabet should be used
- @param behavior the behavior when an error was detected
- @returns the decoded string
- @throws see base64::decode()
- */
- static std::string decode(const char* buffer, std::size_t size, alphabet alphabet = alphabet::auto_,
- decoding_behavior behavior = decoding_behavior::moderate)
- {
- std::string result;
- result.reserve(max_decode_size(size));
- decode(buffer, buffer + size, std::back_inserter(result), alphabet, behavior);
- return result;
- }
- /**
- Decodes a string inplace.
- @param[in,out] str the base64 encoded string
- @param alphabet which alphabet should be used
- @param behavior the behavior when an error was detected
- @throws base64::decode_inplace()
- */
- static void decode_inplace(std::string& str, alphabet alphabet = alphabet::auto_,
- decoding_behavior behavior = decoding_behavior::moderate)
- {
- str.resize(decode(str.begin(), str.end(), str.begin(), alphabet, behavior) - str.begin());
- }
- /**
- Decodes a char array inplace.
- @param[in,out] str the string array
- @param size the length of the array
- @param alphabet which alphabet should be used
- @param behavior the behavior when an error was detected
- @returns the pointer to the next element past the last element decoded
- @throws base64::decode_inplace()
- */
- static char* decode_inplace(char* str, std::size_t size, alphabet alphabet = alphabet::auto_,
- decoding_behavior behavior = decoding_behavior::moderate)
- {
- return decode(str, str + size, str, alphabet, behavior);
- }
- /**
- Returns the required decoding size for a given size. The value is calculated with the following formula:
- $$
- \lceil \frac{size}{4} \rceil \cdot 3
- $$
- @param size the size of the encoded input
- @returns the size of the resulting decoded buffer; this the absolute maximum
- */
- static std::size_t max_decode_size(std::size_t size) noexcept
- {
- return (size / 4 + (size % 4 ? 1 : 0)) * 3;
- }
- /**
- Returns the required encoding size for a given size. The value is calculated with the following formula:
- $$
- \lceil \frac{size}{3} \rceil \cdot 4
- $$
- @param size the size of the decoded input
- @returns the size of the resulting encoded buffer
- */
- static std::size_t required_encode_size(std::size_t size) noexcept
- {
- return (size / 3 + (size % 3 ? 1 : 0)) * 4;
- }
- private:
- static std::uint8_t _base64_value(alphabet& alphabet, char c)
- {
- if (c >= 'A' && c <= 'Z') {
- return c - 'A';
- } else if (c >= 'a' && c <= 'z') {
- return c - 'a' + 26;
- } else if (c >= '0' && c <= '9') {
- return c - '0' + 52;
- }
- // comes down to alphabet
- if (alphabet == alphabet::standard) {
- if (c == '+') {
- return 62;
- } else if (c == '/') {
- return 63;
- }
- } else if (alphabet == alphabet::url_filename_safe) {
- if (c == '-') {
- return 62;
- } else if (c == '_') {
- return 63;
- }
- } // auto detect
- else {
- if (c == '+') {
- alphabet = alphabet::standard;
- return 62;
- } else if (c == '/') {
- alphabet = alphabet::standard;
- return 63;
- } else if (c == '-') {
- alphabet = alphabet::url_filename_safe;
- return 62;
- } else if (c == '_') {
- alphabet = alphabet::url_filename_safe;
- return 63;
- }
- }
- throw base64_error("invalid base64 character.");
- }
- };
- #endif // !PUBLIC_DOMAIN_BASE64_HPP_
|