Mercurial > malikania
diff tools/map/base64.hpp @ 86:cbdd3302998c
Tools: implement basic mlk-map, closes #620
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 31 Jan 2017 11:23:21 +0100 |
parents | |
children | 4b292c20124c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/map/base64.hpp Tue Jan 31 11:23:21 2017 +0100 @@ -0,0 +1,247 @@ +/* + * base64.hpp -- base64 encoding and decoding + * + * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_BASE64_HPP +#define MALIKANIA_BASE64_HPP + +/** + * \file base64.hpp + * \brief Base64 encoding and decoding. + * \author David Demelier <markand@malikania.fr> + * \version 2.0.0-dev + */ + +/** + * \page Base64 Base64 + * \brief Base64 encoding and decoding. + * + * The %base64 library let you encode and decode %base64 data from std::string + * and iterators. + * + * ## Encoding + * + * You can encode like this: + * + * ````cpp + * std::string b64 = base64::encode("Hello world!"); + * ```` + * + * ## Decoding + * + * And you can decode like this: + * + * ```` + * try { + * std::string text = base64::decode(msg); + * } catch (const std::exception &ex) { + * std::cerr << ex.what() << std::endl; + * } + * ```` + */ + +#include <cassert> +#include <cctype> +#include <stdexcept> +#include <string> + +namespace mlk { + +/** + * \brief main %base64 namespace. + */ +namespace base64 { + +/** + * Check if the character is a %base64 character, A-Za-z0-9 and +/. + * + * \param ch the character to test + * \return true if valid + */ +inline bool is_base64(char ch) noexcept +{ + return std::isalnum(ch) != 0 || ch == '+' || ch == '/'; +} + +/** + * Check if the given character is a valid character in %base64 string, + * including '='. + * + * \param ch the character + * \return true if the character is valid + */ +inline bool is_valid(char ch) noexcept +{ + return is_base64(ch) || ch == '='; +} + +/** + * Get the %base64 character from the 6-bits value. + * + * \pre value must be valid (< 64) + * \param value the value + * \return the %base64 character for value + */ +inline char lookup(unsigned char value) noexcept +{ + static const char *table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + assert(value < 64); + + return table[value]; +} + +/** + * Get the integer value from the %base64 character. + * + * \pre is_base64(ch) + * \param ch the %base64 character + * \return the integer value for the %base64 character ch + * ```` + * auto b64 = base64::rlookup('D') // 3 + * ```` + */ +inline unsigned char rlookup(char ch) noexcept +{ + assert(is_base64(ch)); + + if (ch >= '0' && ch <= '9') + return static_cast<unsigned char>(ch + 4); + if (ch >= 'A' && ch <= 'Z') + return static_cast<unsigned char>(ch - 65); + if (ch >= 'a' && ch <= 'z') + return static_cast<unsigned char>(ch - 71); + + return (ch == '+') ? 62U : 63U; +} + +/** + * Encode the input to the output. + * + * Requirements: + * - **InputIt** must be [InputIterator](http://en.cppreference.com/w/cpp/concept/InputIterator) + * - **OutputIt** must be [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator) + * + * \param input the beginning + * \param end the end of the data + * \param output the output destination + * \return output + */ +template <typename InputIt, typename OutputIt> +OutputIt encode(InputIt input, InputIt end, OutputIt output) +{ + while (input != end) { + char inputbuf[3] = { 0, 0, 0 }; + int count; + + for (count = 0; count < 3 && input != end; ++count) + inputbuf[count] = *input++; + + *output++ = lookup(inputbuf[0] >> 2 & 0x3f); + *output++ = lookup((inputbuf[0] << 4 & 0x3f) | (inputbuf[1] >> 4 & 0x0f)); + *output++ = (count < 2) ? '=' : lookup((inputbuf[1] << 2 & 0x3c) | (inputbuf[2] >> 6 & 0x03)); + *output++ = (count < 3) ? '=' : lookup(inputbuf[2] & 0x3f); + } + + return output; +} + +/** + * Decode the input to the output. + * + * Requirements: + * - **InputIt** must be [InputIterator](http://en.cppreference.com/w/cpp/concept/InputIterator) + * - **OutputIt** must be [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator) + * + * \param input the beginning + * \param end the end of the data + * \param output the output destination + * \return output + * \throw std::invalid_argument on bad %base64 string + */ +template <typename InputIt, typename OutputIt> +OutputIt decode(InputIt input, InputIt end, OutputIt output) +{ + while (input != end) { + char inputbuf[4] = { -1, -1, -1, -1 }; + int count; + + for (count = 0; count < 4 && input != end; ++count) { + // Check if the character is valid and get its value. + if ((*input == '=' && count <= 1) || !is_valid(*input)) + throw std::invalid_argument("invalid base64 string"); + if (is_base64(*input)) + inputbuf[count] = static_cast<char>(rlookup(*input)); + + input++; + } + + if (count != 4) + throw std::invalid_argument("truncated string"); + + *output++ = static_cast<char>(((inputbuf[0] << 2) & 0xfc) | ((inputbuf[1] >> 4) & 0x03)); + + if (inputbuf[2] != -1) + *output++ = static_cast<char>(((inputbuf[1] << 4) & 0xf0) | ((inputbuf[2] >> 2) & 0x0f)); + if (inputbuf[3] != -1) { + // "XY=Z" is not allowed. + if (inputbuf[2] == -1) + throw std::invalid_argument("invalid base64 string"); + + *output++ = static_cast<char>(((inputbuf[2] << 6) & 0xc0) | (inputbuf[3] & 0x3f)); + } + } + + return output; +} + +/** + * Encode a string. + * + * \param input the input string + * \return the %base64 formatted string + */ +inline std::string encode(const std::string &input) +{ + std::string result; + + encode(input.begin(), input.end(), std::back_inserter(result)); + + return result; +} + +/** + * Decode a string. + * + * \param input the %base64 formatted string + * \return the original string + * \throw std::invalid_argument on bad %base64 string + */ +inline std::string decode(const std::string &input) +{ + std::string result; + + decode(input.begin(), input.end(), std::back_inserter(result)); + + return result; +} + +} // !base64 + +} // !mlk + +#endif // !MALIKANIA_BASE64_HPP