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