code:
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to
 *  deal in the Software without restriction, including without limitation the
 *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 *  sell copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  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 OR COPYRIGHT HOLDERS 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.
 */

#ifndef CPPCODEC_BASE32_RFC4648
#define CPPCODEC_BASE32_RFC4648

#include "detail/codec.hpp"
#include "detail/base32.hpp"

namespace cppcodec {

namespace detail {

// RFC 4648 uses a simple alphabet: A-Z starting at index 0, then 2-7 starting at index 26.
static constexpr const char base32_rfc4648_alphabet[] = {
    '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', // at index 26
    '2', '3', '4', '5', '6', '7'
};

class base32_rfc4648
{
public:
    template <typename Codec> using codec_impl = stream_codec<Codec, base32_rfc4648>;

    static CPPCODEC_ALWAYS_INLINE constexpr size_t alphabet_size() {
        static_assert(sizeof(base32_rfc4648_alphabet) == 32, "base32 alphabet must have 32 values");
        return sizeof(base32_rfc4648_alphabet);
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char symbol(alphabet_index_t idx)
    {
        return base32_rfc4648_alphabet[idx];
    }
    static CPPCODEC_ALWAYS_INLINE constexpr char normalized_symbol(char c)
    {
        // Lower-case letters are accepted, though not generally expected.
        return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c;
    }

    static CPPCODEC_ALWAYS_INLINE constexpr bool generates_padding() { return true; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool requires_padding() { return true; }
    static CPPCODEC_ALWAYS_INLINE constexpr char padding_symbol() { return '='; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_padding_symbol(char c) { return c == '='; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_eof_symbol(char c) { return c == '\0'; }

    // RFC4648 does not specify any whitespace being allowed in base32 encodings.
    static CPPCODEC_ALWAYS_INLINE constexpr bool should_ignore(char) { return false; }
};

} // namespace detail

using base32_rfc4648 = detail::codec<detail::base32<detail::base32_rfc4648>>;

} // namespace cppcodec

#endif // CPPCODEC_BASE32_RFC4648


code:
/**
 *  Copyright (C) 2015 Topology LP
 *  Copyright (C) 2018 Jakob Petsovits
 *  All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to
 *  deal in the Software without restriction, including without limitation the
 *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 *  sell copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  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 OR COPYRIGHT HOLDERS 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.
 */

#ifndef CPPCODEC_DETAIL_DATA_ACCESS
#define CPPCODEC_DETAIL_DATA_ACCESS

#include <stdint.h> // for size_t
#include <string> // for static_assert() checking that string will be optimized
#include <type_traits> // for std::enable_if, std::remove_reference, and such
#include <utility> // for std::declval
#include <vector> // for static_assert() checking that vector will be optimized

#include "../detail/config.hpp" // for CPPCODEC_ALWAYS_INLINE

namespace cppcodec {
namespace data {

// This file contains a number of templated data accessors that can be
// implemented in the cppcodec::data namespace for types that don't fulfill
// the default type requirements:
// For result types: init(Result&, ResultState&, size_t capacity),
//     put(Result&, ResultState&, char), finish(Result&, State&)
// For const (read-only) types: char_data(const T&)
// For both const and result types: size(const T&)

template <typename T>
CPPCODEC_ALWAYS_INLINE size_t size(const T& t) { return t.size(); }

template <typename T, size_t N>
CPPCODEC_ALWAYS_INLINE constexpr size_t size(const T (&t)[N]) noexcept {
    return (void)t, N * sizeof(t[0]);
}

class general_t {};
class specific_t : public general_t {};

class empty_result_state {
    template <typename Result>
    CPPCODEC_ALWAYS_INLINE void size(const Result& result) { return size(result); }
};

// SFINAE: Generic fallback in case no specific state function applies.
template <typename Result>
CPPCODEC_ALWAYS_INLINE empty_result_state create_state(Result&, general_t)
{
    return empty_result_state();
}

//
// Generic templates for containers: Use these init()/put()/finish()
// implementations if no specialization was found.
//

template <typename Result>
CPPCODEC_ALWAYS_INLINE void init(Result& result, empty_result_state&, size_t capacity)
{
    result.resize(0);
    result.reserve(capacity);
}

template <typename Result>
CPPCODEC_ALWAYS_INLINE void finish(Result&, empty_result_state&)
{
    // Default is to push_back(), which already increases the size.
}

// For the put() default implementation, we try calling push_back() with either uint8_t or char,
// whichever compiles. Scary-fancy template magic from http://stackoverflow.com/a/1386390.
namespace fallback {
    struct flag { char c[2]; }; // sizeof > 1
    flag put_uint8(...);

    int operator,(flag, flag);
    template <typename T> void operator,(flag, T&); // map everything else to void
    char operator,(int, flag); // sizeof 1
}

template <typename Result> inline void put_uint8(Result& result, uint8_t c) { result.push_back(c); }

template <bool> struct put_impl;
template <> struct put_impl<true> { // put_uint8() available
    template<typename Result>
    static CPPCODEC_ALWAYS_INLINE void put(Result& result, uint8_t c)
    {
        put_uint8(result, c);
    }
};
template <> struct put_impl<false> { // put_uint8() not available
    template<typename Result>
    static CPPCODEC_ALWAYS_INLINE void put(Result& result, uint8_t c)
    {
        result.push_back(static_cast<char>(c));
    }
};

template <typename Result>
CPPCODEC_ALWAYS_INLINE void put(Result& result, empty_result_state&, uint8_t c)
{
    using namespace fallback;
    put_impl<sizeof(fallback::flag(), put_uint8(result, c), fallback::flag()) != 1>::put(result, c);
}

//
// Specialization for container types with direct mutable data access,
// e.g. std::vector<uint8_t>.
//
// The expected way to specialize is to draft a new xyz_result_state type and
// return an instance of it from a create_state() template specialization.
// You can then create overloads for init(), put() and finish()
// for the new result state type.
//
// If desired, a non-templated overload for both specific types
// (result & state) can be added to tailor it to that particular result type.
//

template <typename T>
constexpr auto data_is_mutable(T* t) -> decltype(t->data()[size_t(0)] = 'x', bool())
{
    return (void)t, true;
}
constexpr bool data_is_mutable(...) { return false; }

template <typename Result>
class direct_data_access_result_state
{
public:
    CPPCODEC_ALWAYS_INLINE void init(Result& result, size_t capacity)
    {
        // reserve() may not actually allocate the storage right away,
        // and it isn't guaranteed that it will be untouched upon the
        //.next resize(). In that light, resize from the start and
        // slightly reduce the size at the end if necessary.
        result.resize(capacity);

        // result.data() may perform a calculation to retrieve the address.
        // E.g. std::string (since C++11) will use small string optimization,
        // so it needs to check if it's using allocated data or (ab)using
        // its own member variables interpreted as char array.
        // (This result_state is used for std::string starting with C++17.)
        // Conditional code paths are slow so we only do it once, at the start.
        m_buffer = result.data();
    }
    CPPCODEC_ALWAYS_INLINE void put(Result&, char c)
    {
        m_buffer[m_offset++] = c;
    }
    CPPCODEC_ALWAYS_INLINE void finish(Result& result)
    {
        result.resize(m_offset);
    }
    CPPCODEC_ALWAYS_INLINE size_t size(const Result&)
    {
        return m_offset;
    }
private:
    // Make sure to get the mutable buffer decltype by using assignment.
    typename std::remove_reference<
            decltype(std::declval<Result>().data()[size_t(0)] = 'x')>::type* m_buffer;
    size_t m_offset = 0;
};

// SFINAE: Select a specific state based on the result type and possible result state type.
// Implement this if direct data access (`result.data()[0] = 'x') isn't already possible
// and you want to specialize it for your own result type.
// Note: The enable_if should ideally be part of the class declaration,
//       but Visual Studio C++ will not compile it that way.
//       Have it here in the factory function instead.
template <typename Result,
          typename = typename std::enable_if<
                  data_is_mutable(static_cast<Result*>(nullptr))>::type>
CPPCODEC_ALWAYS_INLINE direct_data_access_result_state<Result> create_state(Result&, specific_t)
{
    return direct_data_access_result_state<Result>();
}

static_assert(std::is_same<
        decltype(create_state(*static_cast<std::vector<uint8_t>*>(nullptr), specific_t())),
        direct_data_access_result_state<std::vector<uint8_t>>>::value,
        "std::vector<uint8_t> must be handled by direct_data_access_result_state");

// Specialized init(), put() and finish() functions for direct_data_access_result_state.
template <typename Result>
CPPCODEC_ALWAYS_INLINE void init(Result& result, direct_data_access_result_state<Result>& state, size_t capacity)
{
    state.init(result, capacity);
}

template <typename Result>
CPPCODEC_ALWAYS_INLINE void put(Result& result, direct_data_access_result_state<Result>& state, char c)
{
    state.put(result, c);
}

template <typename Result>
CPPCODEC_ALWAYS_INLINE void finish(Result& result, direct_data_access_result_state<Result>& state)
{
    state.finish(result);
}

//
// Specialization for container types with direct mutable array access,
// e.g. std::string. This is generally faster because bound checks are
// minimal and operator[] is more likely noexcept. In addition,
// std::string::push_back() needs to write a null character on every
// expansion, which should be more efficient when done in bulk by resize().
//
// Compared to the above, tracking an extra offset variable is cheap.
//

template <typename T>
constexpr auto array_access_is_mutable(T* t) -> decltype((*t)[size_t(0)] = 'x', bool())
{
    return (void)t, true;
}
constexpr bool array_access_is_mutable(...) { return false; }

template <typename Result>
class array_access_result_state
{
public:
    CPPCODEC_ALWAYS_INLINE void init(Result& result, size_t capacity)
    {
        // reserve() may not actually allocate the storage right away,
        // and it isn't guaranteed that it will be untouched upon the
        //.next resize(). In that light, resize from the start and
        // slightly reduce the size at the end if necessary.
        result.resize(capacity);
    }
    CPPCODEC_ALWAYS_INLINE void put(Result& result, char c)
    {
        result[m_offset++] = c;
    }
    CPPCODEC_ALWAYS_INLINE void finish(Result& result)
    {
        result.resize(m_offset);
    }
    CPPCODEC_ALWAYS_INLINE size_t size(const Result&)
    {
        return m_offset;
    }
private:
    size_t m_offset = 0;
};

// SFINAE: Select a specific state based on the result type and possible result state type.
// Note: The enable_if should ideally be part of the class declaration,
//       but Visual Studio C++ will not compile it that way.
//       Have it here in the factory function instead.
template <typename Result,
          typename = typename std::enable_if<
                  !data_is_mutable(static_cast<Result*>(nullptr)) // no more than one template option
                  && array_access_is_mutable(static_cast<Result*>(nullptr))>::type>
CPPCODEC_ALWAYS_INLINE array_access_result_state<Result> create_state(Result&, specific_t)
{
    return array_access_result_state<Result>();
}

#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L)
static_assert(std::is_same<
    decltype(create_state(*static_cast<std::string*>(nullptr), specific_t())),
    direct_data_access_result_state<std::string>>::value,
    "std::string (C++17 and later) must be handled by direct_data_access_result_state");
#elif __cplusplus < 201703 && !defined(_MSVC_LANG) // we can't trust MSVC to set this right
static_assert(std::is_same<
        decltype(create_state(*static_cast<std::string*>(nullptr), specific_t())),
        array_access_result_state<std::string>>::value,
        "std::string (pre-C++17) must be handled by array_access_result_state");
#endif

// Specialized init(), put() and finish() functions for array_access_result_state.
template <typename Result>
CPPCODEC_ALWAYS_INLINE void init(Result& result, array_access_result_state<Result>& state, size_t capacity)
{
    state.init(result, capacity);
}

template <typename Result>
CPPCODEC_ALWAYS_INLINE void put(Result& result, array_access_result_state<Result>& state, char c)
{
    state.put(result, c);
}

template <typename Result>
CPPCODEC_ALWAYS_INLINE void finish(Result& result, array_access_result_state<Result>& state)
{
    state.finish(result);
}

// char_data() is only used to read, not for result buffers.
template <typename T> inline const char* char_data(const T& t)
{
    return reinterpret_cast<const char*>(t.data());
}
template <typename T, size_t N> inline const char* char_data(const T (&t)[N]) noexcept
{
    return reinterpret_cast<const char*>(&(t[0]));
}

template <typename T> inline const uint8_t* uchar_data(const T& t)
{
    return reinterpret_cast<const uint8_t*>(char_data(t));
}

} // namespace data
} // namespace cppcodec

#endif

repo_name: acetone/mario

license: mit


code:
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to
 *  deal in the Software without restriction, including without limitation the
 *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 *  sell copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  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 OR COPYRIGHT HOLDERS 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.
 */

#ifndef CPPCODEC_DETAIL_RAW_RESULT_BUFFER
#define CPPCODEC_DETAIL_RAW_RESULT_BUFFER

#include <stdint.h> // for size_t
#include <stdlib.h> // for abort()

#include "access.hpp"

namespace cppcodec {
namespace data {

class raw_result_buffer
{
public:
    raw_result_buffer(char* data, size_t capacity)
        : m_ptr(data + capacity)
        , m_begin(data)
    {
    }

    char last() const { return *(m_ptr - 1); }
    void push_back(char c) { *m_ptr = c; ++m_ptr; }
    size_t size() const { return m_ptr - m_begin; }
    void resize(size_t size) { m_ptr = m_begin + size; }

private:
    char* m_ptr;
    char* m_begin;
};

template <> inline void init<raw_result_buffer>(
        raw_result_buffer& result, empty_result_state&, size_t capacity)
{
    // This version of init() doesn't do a reserve(), and instead checks whether the
    // initial size (capacity) is enough before resizing to 0.
    // The codec is expected not to exceed this capacity.
    if (capacity > result.size()) {
        abort();
    }
    result.resize(0);
}
template <> inline void finish<raw_result_buffer>(raw_result_buffer&, empty_result_state&) { }

} // namespace data
} // namespace cppcodec

#endif


code:
/**
 *  Copyright (C) 2015 Trustifier Inc.
 *  Copyright (C) 2015 Ahmed Masud
 *  Copyright (C) 2015 Topology LP
 *  Copyright (C) 2018 Jakob Petsovits
 *  All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to
 *  deal in the Software without restriction, including without limitation the
 *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 *  sell copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  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 OR COPYRIGHT HOLDERS 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.
 *
 *  Adapted from https://github.com/ahmed-masud/libbase32,
 *  commit 79761b2b79b0545697945efe0987a8d3004512f9.
 *  Quite different now.
 */

#ifndef CPPCODEC_DETAIL_BASE32
#define CPPCODEC_DETAIL_BASE32

#include <stdint.h>
#include <stdlib.h> // for abort()

#include "../data/access.hpp"
#include "../parse_error.hpp"
#include "config.hpp"
#include "stream_codec.hpp"

namespace cppcodec {
namespace detail {

template <typename CodecVariant>
class base32 : public CodecVariant::template codec_impl<base32<CodecVariant>>
{
public:
    static inline constexpr uint8_t binary_block_size() { return 5; }
    static inline constexpr uint8_t encoded_block_size() { return 8; }

    static CPPCODEC_ALWAYS_INLINE constexpr uint8_t num_encoded_tail_symbols(uint8_t num_bytes)
    {
        return (num_bytes == 1) ? 2    // 2 symbols, 6 padding characters
                : (num_bytes == 2) ? 4 // 4 symbols, 4 padding characters
                : (num_bytes == 3) ? 5 // 5 symbols, 3 padding characters
                : (num_bytes == 4) ? 7 // 7 symbols, 1 padding characters
                : throw std::domain_error("invalid number of bytes in a tail block");
    }

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE constexpr uint8_t index(
            const uint8_t* b /*binary block*/) noexcept
    {
        static_assert(I >= 0 && I < encoded_block_size(),
                "invalid encoding symbol index in a block");

        return (I == 0) ? ((b[0] >> 3) & 0x1F) // first 5 bits
                : (I == 1) ? (((b[0] << 2) & 0x1C) | ((b[1] >> 6) & 0x3))
                : (I == 2) ? ((b[1] >> 1) & 0x1F)
                : (I == 3) ? (((b[1] << 4) & 0x10) | ((b[2] >> 4) & 0xF))
                : (I == 4) ? (((b[2] << 1) & 0x1E) | ((b[3] >> 7) & 0x1))
                : (I == 5) ? ((b[3] >> 2) & 0x1F)
                : (I == 6) ? (((b[3] << 3) & 0x18) | ((b[4] >> 5) & 0x7))
                : /*I == 7*/ (b[4] & 0x1F); // last 5 bits;
    }

    template <bool B>
    using uint8_if = typename std::enable_if<B, uint8_t>::type;

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE constexpr
    uint8_if<I == 1 || I == 3 || I == 4 || I == 6> index_last(
            const uint8_t* b /*binary block*/) noexcept
    {
        return (I == 1) ? ((b[0] << 2) & 0x1C)     // abbreviated 2nd symbol
                : (I == 3) ? ((b[1] << 4) & 0x10)  // abbreviated 4th symbol
                : (I == 4) ? ((b[2] << 1) & 0x1E)  // abbreviated 5th symbol
                : /*I == 6*/ ((b[3] << 3) & 0x18); // abbreviated 7th symbol
    }

    template <uint8_t I>
    static CPPCODEC_ALWAYS_INLINE
    uint8_if<I != 1 && I != 3 && I != 4 && I != 6> index_last(
            const uint8_t* /*binary block*/)
    {
        throw std::domain_error("invalid last encoding symbol index in a tail");
    }

    template <typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void decode_block(
            Result& decoded, ResultState&, const alphabet_index_t* idx);

    template <typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void decode_tail(
            Result& decoded, ResultState&, const alphabet_index_t* idx, size_t idx_len);
};

//
//     11111111 10101010 10110011  10111100 10010100
// => 11111 11110 10101 01011 00111 01111 00100 10100
//

template <typename CodecVariant>
template <typename Result, typename ResultState>
CPPCODEC_ALWAYS_INLINE void base32<CodecVariant>::decode_block(
        Result& decoded, ResultState& state, const alphabet_index_t* idx)
{
    put(decoded, state, static_cast<uint8_t>(((idx[0] << 3) & 0xF8) | ((idx[1] >> 2) & 0x7)));
    put(decoded, state, static_cast<uint8_t>(((idx[1] << 6) & 0xC0) | ((idx[2] << 1) & 0x3E) | ((idx[3] >> 4) & 0x1)));
    put(decoded, state, static_cast<uint8_t>(((idx[3] << 4) & 0xF0) | ((idx[4] >> 1) & 0xF)));
    put(decoded, state, static_cast<uint8_t>(((idx[4] << 7) & 0x80) | ((idx[5] << 2) & 0x7C) | ((idx[6] >> 3) & 0x3)));
    put(decoded, state, static_cast<uint8_t>(((idx[6] << 5) & 0xE0) | (idx[7] & 0x1F)));
}

template <typename CodecVariant>
template <typename Result, typename ResultState>
CPPCODEC_ALWAYS_INLINE void base32<CodecVariant>::decode_tail(
        Result& decoded, ResultState& state, const alphabet_index_t* idx, size_t idx_len)
{
    if (idx_len == 1) {
        throw invalid_input_length(
                "invalid number of symbols in last base32 block: found 1, expected 2, 4, 5 or 7");
    }
    if (idx_len == 3) {
        throw invalid_input_length(
                "invalid number of symbols in last base32 block: found 3, expected 2, 4, 5 or 7");
    }
    if (idx_len == 6) {
        throw invalid_input_length(
                "invalid number of symbols in last base32 block: found 6, expected 2, 4, 5 or 7");
    }

    // idx_len == 2: decoded size 1
    put(decoded, state, static_cast<uint8_t>(((idx[0] << 3) & 0xF8) | ((idx[1] >> 2) & 0x7)));
    if (idx_len == 2) {
        return;
    }
    // idx_len == 4: decoded size 2
    put(decoded, state, static_cast<uint8_t>(((idx[1] << 6) & 0xC0) | ((idx[2] << 1) & 0x3E) | ((idx[3] >> 4) & 0x1)));
    if (idx_len == 4) {
        return;
    }
    // idx_len == 5: decoded size 3
    put(decoded, state, static_cast<uint8_t>(((idx[3] << 4) & 0xF0) | ((idx[4] >> 1) & 0xF)));
    if (idx_len == 5) {
        return;
    }
    // idx_len == 7: decoded size 4
    put(decoded, state, static_cast<uint8_t>(((idx[4] << 7) & 0x80) | ((idx[5] << 2) & 0x7C) | ((idx[6] >> 3) & 0x3)));
}

} // namespace detail
} // namespace cppcodec

#endif // CPPCODEC_DETAIL_BASE32


code:
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to
 *  deal in the Software without restriction, including without limitation the
 *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 *  sell copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  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 OR COPYRIGHT HOLDERS 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.
 */

#ifndef CPPCODEC_DETAIL_CODEC
#define CPPCODEC_DETAIL_CODEC

#include <assert.h>
#include <stdint.h>
#include <string>
#include <vector>

#include "../data/access.hpp"
#include "../data/raw_result_buffer.hpp"

namespace cppcodec {
namespace detail {

// SFINAE: Templates sometimes beat sensible overloads - make sure we don't call the wrong one.
template <typename T>
struct non_numeric : std::enable_if<!std::is_arithmetic<T>::value> { };

/**
 * Public interface for all the codecs. For API documentation, see README.md.
 */
template <typename CodecImpl>
class codec
{
public:
    //
    // Encoding

    // Convenient version, returns an std::string.
    static std::string encode(const uint8_t* binary, size_t binary_size);
    static std::string encode(const char* binary, size_t binary_size);
    // static std::string encode(const T& binary); -> provided by template below

    // Convenient version with templated result type.
    template <typename Result> static Result encode(const uint8_t* binary, size_t binary_size);
    template <typename Result> static Result encode(const char* binary, size_t binary_size);
    template <typename Result = std::string, typename T = std::vector<uint8_t>>
    static Result encode(const T& binary);

    // Reused result container version. Resizes encoded_result before writing to it.
    template <typename Result>
    static void encode(Result& encoded_result, const uint8_t* binary, size_t binary_size);
    template <typename Result>
    static void encode(Result& encoded_result, const char* binary, size_t binary_size);
    template <typename Result, typename T, typename non_numeric<T>::type* = nullptr>
    static void encode(Result& encoded_result, const T& binary);

    // Raw pointer output, assumes pre-allocated memory with size > encoded_size(binary_size).
    static size_t encode(
            char* encoded_result, size_t encoded_buffer_size,
            const uint8_t* binary, size_t binary_size) noexcept;
    static size_t encode(
            char* encoded_result, size_t encoded_buffer_size,
            const char* binary, size_t binary_size) noexcept;
    template<typename T>
    static size_t encode(
            char* encoded_result, size_t encoded_buffer_size,
            const T& binary) noexcept;

    // Calculate the exact length of the encoded string based on binary size.
    static constexpr size_t encoded_size(size_t binary_size) noexcept;

    //
    // Decoding

    // Convenient version, returns an std::vector<uint8_t>.
    static std::vector<uint8_t> decode(const char* encoded, size_t encoded_size);
    // static std::vector<uint8_t> decode(const T& encoded); -> provided by template below

    // Convenient version with templated result type.
    template <typename Result> static Result decode(const char* encoded, size_t encoded_size);
    template <typename Result = std::vector<uint8_t>, typename T = std::string>
    static Result decode(const T& encoded);

    // Reused result container version. Resizes binary_result before writing to it.
    template <typename Result>
    static void decode(Result& binary_result, const char* encoded, size_t encoded_size);
    template <typename Result, typename T, typename non_numeric<T>::type* = nullptr>
    static void decode(Result& binary_result, const T& encoded);

    // Raw pointer output, assumes pre-allocated memory with size > decoded_max_size(encoded_size).
    static size_t decode(
            uint8_t* binary_result, size_t binary_buffer_size,
            const char* encoded, size_t encoded_size);
    static size_t decode(
            char* binary_result, size_t binary_buffer_size,
            const char* encoded, size_t encoded_size);
    template<typename T> static size_t decode(
            uint8_t* binary_result, size_t binary_buffer_size, const T& encoded);
    template<typename T> static size_t decode(
            char* binary_result, size_t binary_buffer_size, const T& encoded);

    // Calculate the maximum size of the decoded binary buffer based on the encoded string length.
    static constexpr size_t decoded_max_size(size_t encoded_size) noexcept;
};

//
// Inline definitions of the above functions, using CRTP to call into CodecImpl
//

//
// Encoding

template <typename CodecImpl>
inline std::string codec<CodecImpl>::encode(const uint8_t* binary, size_t binary_size)
{
    return encode<std::string>(binary, binary_size);
}

template <typename CodecImpl>
inline std::string codec<CodecImpl>::encode(const char* binary, size_t binary_size)
{
    return encode<std::string>(reinterpret_cast<const uint8_t*>(binary), binary_size);
}

template <typename CodecImpl>
template <typename Result>
inline Result codec<CodecImpl>::encode(const uint8_t* binary, size_t binary_size)
{
    Result encoded_result;
    encode(encoded_result, binary, binary_size);
    return encoded_result;
}

template <typename CodecImpl>
template <typename Result>
inline Result codec<CodecImpl>::encode(const char* binary, size_t binary_size)
{
    return encode<Result>(reinterpret_cast<const uint8_t*>(binary), binary_size);
}

template <typename CodecImpl>
template <typename Result, typename T>
inline Result codec<CodecImpl>::encode(const T& binary)
{
    return encode<Result>(data::uchar_data(binary), data::size(binary));
}

template <typename CodecImpl>
template <typename Result>
inline void codec<CodecImpl>::encode(
    Result& encoded_result, const uint8_t* binary, size_t binary_size)
{
    // This overload is where we reserve buffer capacity and call into CodecImpl.
    size_t encoded_buffer_size = encoded_size(binary_size);
    auto state = data::create_state(encoded_result, data::specific_t());
    data::init(encoded_result, state, encoded_buffer_size);

    CodecImpl::encode(encoded_result, state, binary, binary_size);
    data::finish(encoded_result, state);
    assert(data::size(encoded_result) == encoded_buffer_size);
}

template <typename CodecImpl>
template <typename Result>
inline void codec<CodecImpl>::encode(
    Result& encoded_result, const char* binary, size_t binary_size)
{
    encode(encoded_result, reinterpret_cast<const uint8_t*>(binary), binary_size);
}

template <typename CodecImpl>
template <typename Result, typename T, typename non_numeric<T>::type*>
inline void codec<CodecImpl>::encode(Result& encoded_result, const T& binary)
{
    encode(encoded_result, data::uchar_data(binary), data::size(binary));
}

template <typename CodecImpl>
inline size_t codec<CodecImpl>::encode(
        char* encoded_result, size_t encoded_buffer_size,
        const uint8_t* binary, size_t binary_size) noexcept
{
    // This overload is where we wrap the result pointer & size.
    data::raw_result_buffer encoded(encoded_result, encoded_buffer_size);
    encode(encoded, binary, binary_size);

    size_t encoded_size = data::size(encoded);
    if (encoded_size < encoded_buffer_size) {
        encoded_result[encoded_size] = '\0';
    }
    return encoded_size;
}

template <typename CodecImpl>
inline size_t codec<CodecImpl>::encode(
        char* encoded_result, size_t encoded_buffer_size,
        const char* binary, size_t binary_size) noexcept
{
    // This overload is where we wrap the result pointer & size.
    return encode(encoded_result, encoded_buffer_size,
            reinterpret_cast<const uint8_t*>(binary), binary_size);
}

template <typename CodecImpl>
template <typename T>
inline size_t codec<CodecImpl>::encode(
        char* encoded_result, size_t encoded_buffer_size,
        const T& binary) noexcept
{
    return encode(encoded_result, encoded_buffer_size, data::uchar_data(binary), data::size(binary));
}

template <typename CodecImpl>
inline constexpr size_t codec<CodecImpl>::encoded_size(size_t binary_size) noexcept
{
    return CodecImpl::encoded_size(binary_size);
}

//
// Decoding

template <typename CodecImpl>
inline std::vector<uint8_t> codec<CodecImpl>::decode(const char* encoded, size_t encoded_size)
{
    return decode<std::vector<uint8_t>>(encoded, encoded_size);
}

template <typename CodecImpl>
template <typename Result>
inline Result codec<CodecImpl>::decode(const char* encoded, size_t encoded_size)
{
    Result result;
    decode(result, encoded, encoded_size);
    return result;
}

template <typename CodecImpl>
template <typename Result, typename T>
inline Result codec<CodecImpl>::decode(const T& encoded)
{
    return decode<Result>(data::char_data(encoded), data::size(encoded));
}

template <typename CodecImpl>
template <typename Result>
inline void codec<CodecImpl>::decode(Result& binary_result, const char* encoded, size_t encoded_size)
{
    // This overload is where we reserve buffer capacity and call into CodecImpl.
    size_t binary_buffer_size = decoded_max_size(encoded_size);
    auto state = data::create_state(binary_result, data::specific_t());
    data::init(binary_result, state, binary_buffer_size);

    CodecImpl::decode(binary_result, state, encoded, encoded_size);
    data::finish(binary_result, state);
    assert(data::size(binary_result) <= binary_buffer_size);
}

template <typename CodecImpl>
template <typename Result, typename T, typename non_numeric<T>::type*>
inline void codec<CodecImpl>::decode(Result& binary_result, const T& encoded)
{
    decode(binary_result, data::char_data(encoded), data::size(encoded));
}

template <typename CodecImpl>
inline size_t codec<CodecImpl>::decode(
        uint8_t* binary_result, size_t binary_buffer_size,
        const char* encoded, size_t encoded_size)
{
    return decode(reinterpret_cast<char*>(binary_result), binary_buffer_size, encoded, encoded_size);
}

template <typename CodecImpl>
inline size_t codec<CodecImpl>::decode(
        char* binary_result, size_t binary_buffer_size,
        const char* encoded, size_t encoded_size)
{
    // This overload is where we wrap the result pointer & size.
    data::raw_result_buffer binary(binary_result, binary_buffer_size);
    decode(binary, encoded, encoded_size);
    return data::size(binary);
}

template <typename CodecImpl>
template <typename T>
inline size_t codec<CodecImpl>::decode(
        uint8_t* binary_result, size_t binary_buffer_size, const T& encoded)
{
    return decode(reinterpret_cast<char*>(binary_result), binary_buffer_size, encoded);
}

template <typename CodecImpl>
template <typename T>
inline size_t codec<CodecImpl>::decode(char* binary_result, size_t binary_buffer_size, const T& encoded)
{
    return decode(binary_result, binary_buffer_size, data::char_data(encoded), data::size(encoded));
}

template <typename CodecImpl>
inline constexpr size_t codec<CodecImpl>::decoded_max_size(size_t encoded_size) noexcept
{
    return CodecImpl::decoded_max_size(encoded_size);
}

} // namespace detail
} // namespace cppcodec

#endif


code:
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to
 *  deal in the Software without restriction, including without limitation the
 *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 *  sell copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  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 OR COPYRIGHT HOLDERS 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.
 */

#ifndef CPPCODEC_DETAIL_CONFIG_HPP
#define CPPCODEC_DETAIL_CONFIG_HPP

#ifndef __has_attribute
  #define __has_attribute(x) 0
#endif

#if __GNUC__ || __has_attribute(always_inline)
#define CPPCODEC_ALWAYS_INLINE inline __attribute__((always_inline))
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER)
#define CPPCODEC_ALWAYS_INLINE inline __forceinline
#else
#define CPPCODEC_ALWAYS_INLINE inline
#endif

#endif // CPPCODEC_DETAIL_CONFIG_HPP


code:
/**
 *  Copyright (C) 2015 Topology LP
 *  Copyright (C) 2018 Jakob Petsovits
 *  All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to
 *  deal in the Software without restriction, including without limitation the
 *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 *  sell copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  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 OR COPYRIGHT HOLDERS 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.
 */

#ifndef CPPCODEC_DETAIL_STREAM_CODEC
#define CPPCODEC_DETAIL_STREAM_CODEC

#include <limits>
#include <stdlib.h> // for abort()
#include <stdint.h>

#include "../parse_error.hpp"
#include "config.hpp"

namespace cppcodec {
namespace detail {

using alphabet_index_t = uint_fast16_t;

template <typename Codec, typename CodecVariant>
class stream_codec
{
public:
    template <typename Result, typename ResultState> static void encode(
            Result& encoded_result, ResultState&, const uint8_t* binary, size_t binary_size);

    template <typename Result, typename ResultState> static void decode(
            Result& binary_result, ResultState&, const char* encoded, size_t encoded_size);

    static constexpr size_t encoded_size(size_t binary_size) noexcept;
    static constexpr size_t decoded_max_size(size_t encoded_size) noexcept;
};

template <bool GeneratesPadding> // default for CodecVariant::generates_padding() == false
struct padder {
    template <typename CodecVariant, typename Result, typename ResultState, typename EncodedBlockSizeT>
    CPPCODEC_ALWAYS_INLINE void operator()(Result&, ResultState&, EncodedBlockSizeT) { }
};

template<> // specialization for CodecVariant::generates_padding() == true
struct padder<true> {
    template <typename CodecVariant, typename Result, typename ResultState, typename EncodedBlockSizeT>
    CPPCODEC_ALWAYS_INLINE void operator()(
            Result& encoded, ResultState& state, EncodedBlockSizeT num_padding_characters)
    {
        for (EncodedBlockSizeT i = 0; i < num_padding_characters; ++i) {
            data::put(encoded, state, CodecVariant::padding_symbol());
        }
    }
};

template <size_t I>
struct enc {
    // Block encoding: Go from 0 to (block size - 1), append a symbol for each iteration unconditionally.
    template <typename Codec, typename CodecVariant, typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void block(Result& encoded, ResultState& state, const uint8_t* src)
    {
        using EncodedBlockSizeT = decltype(Codec::encoded_block_size());
        constexpr static const EncodedBlockSizeT SymbolIndex = static_cast<EncodedBlockSizeT>(I - 1);

        enc<I - 1>().template block<Codec, CodecVariant>(encoded, state, src);
        data::put(encoded, state, CodecVariant::symbol(Codec::template index<SymbolIndex>(src)));
    }

    // Tail encoding: Go from 0 until (runtime) num_symbols, append a symbol for each iteration.
    template <typename Codec, typename CodecVariant, typename Result, typename ResultState,
            typename EncodedBlockSizeT = decltype(Codec::encoded_block_size())>
    static CPPCODEC_ALWAYS_INLINE void tail(
            Result& encoded, ResultState& state, const uint8_t* src, EncodedBlockSizeT num_symbols)
    {
        constexpr static const EncodedBlockSizeT SymbolIndex = Codec::encoded_block_size() - I;
        constexpr static const EncodedBlockSizeT NumSymbols = SymbolIndex + static_cast<EncodedBlockSizeT>(1);

        if (num_symbols == NumSymbols) {
            data::put(encoded, state, CodecVariant::symbol(Codec::template index_last<SymbolIndex>(src)));
            padder<CodecVariant::generates_padding()> pad;
#ifdef _MSC_VER
            pad.operator()<CodecVariant>(encoded, state, Codec::encoded_block_size() - NumSymbols);
#else
            pad.template operator()<CodecVariant>(encoded, state, Codec::encoded_block_size() - NumSymbols);
#endif
            return;
        }
        data::put(encoded, state, CodecVariant::symbol(Codec::template index<SymbolIndex>(src)));
        enc<I - 1>().template tail<Codec, CodecVariant>(encoded, state, src, num_symbols);
    }
};

template<> // terminating specialization
struct enc<0> {
    template <typename Codec, typename CodecVariant, typename Result, typename ResultState>
    static CPPCODEC_ALWAYS_INLINE void block(Result&, ResultState&, const uint8_t*) { }

    template <typename Codec, typename CodecVariant, typename Result, typename ResultState,
            typename EncodedBlockSizeT = decltype(Codec::encoded_block_size())>
    static CPPCODEC_ALWAYS_INLINE void tail(Result&, ResultState&, const uint8_t*, EncodedBlockSizeT)
    {
        abort(); // Not reached: block() should be called if num_symbols == block size, not tail().
    }
};

template <typename Codec, typename CodecVariant>
template <typename Result, typename ResultState>
inline void stream_codec<Codec, CodecVariant>::encode(
        Result& encoded_result, ResultState& state,
        const uint8_t* src, size_t src_size)
{
    using encoder = enc<Codec::encoded_block_size()>;

    const uint8_t* src_end = src + src_size;

    if (src_size >= Codec::binary_block_size()) {
        src_end -= Codec::binary_block_size();

        for (; src <= src_end; src += Codec::binary_block_size()) {
            encoder::template block<Codec, CodecVariant>(encoded_result, state, src);
        }
        src_end += Codec::binary_block_size();
    }

    if (src_end > src) {
        auto remaining_src_len = src_end - src;
        if (!remaining_src_len || remaining_src_len >= Codec::binary_block_size()) {
            abort();
            return;
        }
        auto num_symbols = Codec::num_encoded_tail_symbols(static_cast<uint8_t>(remaining_src_len));
        encoder::template tail<Codec, CodecVariant>(encoded_result, state, src, num_symbols);
    }
}

// Range & lookup table generation, see
// http://stackoverflow.com/questions/13313980/populate-an-array-using-constexpr-at-compile-time
// and http://cplusadd.blogspot.ca/2013/02/c11-compile-time-lookup-tablearray-with.html

template<unsigned... Is> struct seq {};

template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N - 4, N - 4, N - 3, N - 2, N - 1, Is...> {
    // Clang up to 3.6 has a limit of 256 for template recursion,
    // so pass a few more symbols at once to make it work.
    static_assert(N % 4 == 0, "I must be divisible by 4 to eventually end at 0");
};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...> {};

template <size_t N>
struct lookup_table_t {
    alphabet_index_t lookup[N];
    static constexpr size_t size = N;
};

template<typename LambdaType, unsigned... Is>
constexpr lookup_table_t<sizeof...(Is)> make_lookup_table(seq<Is...>, LambdaType value_for_index) {
    return { { value_for_index(Is)... } };
}

template<unsigned N, typename LambdaType>
constexpr lookup_table_t<N> make_lookup_table(LambdaType evalFunc) {
    return make_lookup_table(gen_seq<N>(), evalFunc);
}

// CodecVariant::symbol() provides a symbol for an index.
// Use recursive templates to get the inverse lookup table for fast decoding.

template <typename T>
static CPPCODEC_ALWAYS_INLINE constexpr size_t num_possible_values()
{
    return static_cast<size_t>(
            static_cast<intmax_t>(std::numeric_limits<T>::max())
                    - static_cast<intmax_t>(std::numeric_limits<T>::min()) + 1);
}

template <typename CodecVariant, alphabet_index_t InvalidIdx, size_t I>
struct index_if_in_alphabet {
    static CPPCODEC_ALWAYS_INLINE constexpr alphabet_index_t for_symbol(char symbol)
    {
        return (CodecVariant::symbol(
                    static_cast<alphabet_index_t>(CodecVariant::alphabet_size() - I)) == symbol)
            ? static_cast<alphabet_index_t>(CodecVariant::alphabet_size() - I)
            : index_if_in_alphabet<CodecVariant, InvalidIdx, I - 1>::for_symbol(symbol);
    }
};
template <typename CodecVariant, alphabet_index_t InvalidIdx>
struct index_if_in_alphabet<CodecVariant, InvalidIdx, 0> { // terminating specialization
    static CPPCODEC_ALWAYS_INLINE constexpr alphabet_index_t for_symbol(char)
    {
        return InvalidIdx;
    }
};

template <typename CodecVariant, size_t I>
struct padding_searcher {
    static CPPCODEC_ALWAYS_INLINE constexpr bool exists_padding_symbol()
    {
        // Clang up to 3.6 has a limit of 256 for template recursion,
        // so pass a few more symbols at once to make it work.
        static_assert(I % 4 == 0, "I must be divisible by 4 to eventually end at 0");

        return CodecVariant::is_padding_symbol(
                        static_cast<char>(num_possible_values<char>() - I - 4))
                || CodecVariant::is_padding_symbol(
                        static_cast<char>(num_possible_values<char>() - I - 3))
                || CodecVariant::is_padding_symbol(
                        static_cast<char>(num_possible_values<char>() - I - 2))
                || CodecVariant::is_padding_symbol(
                        static_cast<char>(num_possible_values<char>() - I - 1))
                || padding_searcher<CodecVariant, I - 4>::exists_padding_symbol();
    }
};
template <typename CodecVariant>
struct padding_searcher<CodecVariant, 0> { // terminating specialization
    static CPPCODEC_ALWAYS_INLINE constexpr bool exists_padding_symbol() { return false; }
};

template <typename CodecVariant>
struct alphabet_index_info
{
    static constexpr const size_t num_possible_symbols = num_possible_values<char>();

    static constexpr const alphabet_index_t padding_idx = 1 << 8;
    static constexpr const alphabet_index_t invalid_idx = 1 << 9;
    static constexpr const alphabet_index_t eof_idx = 1 << 10;
    static constexpr const alphabet_index_t stop_character_mask = static_cast<alphabet_index_t>(~0xFFu);

    static constexpr const bool padding_allowed = padding_searcher<
            CodecVariant, num_possible_symbols>::exists_padding_symbol();

    static CPPCODEC_ALWAYS_INLINE constexpr bool allows_padding()
    {
        return padding_allowed;
    }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_padding(alphabet_index_t idx)
    {
        return allows_padding() ? (idx == padding_idx) : false;
    }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_invalid(alphabet_index_t idx) { return idx == invalid_idx; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_eof(alphabet_index_t idx) { return idx == eof_idx; }
    static CPPCODEC_ALWAYS_INLINE constexpr bool is_stop_character(alphabet_index_t idx)
    {
        return (idx & stop_character_mask) != 0;
    }

private:
    static CPPCODEC_ALWAYS_INLINE constexpr
    alphabet_index_t valid_index_or(alphabet_index_t a, alphabet_index_t b)
    {
        return a == invalid_idx ? b : a;
    }

    using idx_if_in_alphabet = index_if_in_alphabet<
            CodecVariant, invalid_idx, CodecVariant::alphabet_size()>;

    static CPPCODEC_ALWAYS_INLINE constexpr alphabet_index_t index_of(char symbol)
    {
        return valid_index_or(idx_if_in_alphabet::for_symbol(symbol),
            CodecVariant::is_eof_symbol(symbol) ? eof_idx
            : CodecVariant::is_padding_symbol(symbol) ? padding_idx
            : invalid_idx);
    }

    // GCC <= 4.9 has a bug with retaining constexpr when passing a function pointer.
    // To get around this, we'll create a callable with operator() and pass that one.
    // Unfortunately, MSVC prior to VS 2017 (for MinSizeRel or Release builds)
    // chokes on this by compiling the project in 20 minutes instead of seconds.
    // So let's define two separate variants and remove the old GCC one whenever we
    // decide not to support GCC < 5.0 anymore.
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
    struct index_at {
        CPPCODEC_ALWAYS_INLINE constexpr alphabet_index_t operator()(size_t symbol) const {
            return index_of(CodecVariant::normalized_symbol(static_cast<char>(symbol)));
        }
    };
#else
    static CPPCODEC_ALWAYS_INLINE constexpr alphabet_index_t index_at(size_t symbol)
    {
        return index_of(CodecVariant::normalized_symbol(static_cast<char>(symbol)));
    }
#endif

public:
    struct lookup {
        static CPPCODEC_ALWAYS_INLINE alphabet_index_t for_symbol(char symbol)
        {
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
            static constexpr const auto t = make_lookup_table<num_possible_symbols>(index_at());
#else
            static constexpr const auto t = make_lookup_table<num_possible_symbols>(&index_at);
#endif
            static_assert(t.size == num_possible_symbols,
                    "lookup table must cover each possible (character) symbol");
            return t.lookup[static_cast<uint8_t>(symbol)];
        }
    };
};

//
// At long last! The actual decode/encode functions.

template <typename Codec, typename CodecVariant>
template <typename Result, typename ResultState>
inline void stream_codec<Codec, CodecVariant>::decode(
        Result& binary_result, ResultState& state,
        const char* src_encoded, size_t src_size)
{
    using alphabet_index_lookup = typename alphabet_index_info<CodecVariant>::lookup;
    const char* src = src_encoded;
    const char* src_end = src + src_size;

    alphabet_index_t alphabet_indexes[Codec::encoded_block_size()] = {};
    alphabet_indexes[0] = alphabet_index_info<CodecVariant>::eof_idx;

    alphabet_index_t* const alphabet_index_start = &alphabet_indexes[0];
    alphabet_index_t* const alphabet_index_end = &alphabet_indexes[Codec::encoded_block_size()];
    alphabet_index_t* alphabet_index_ptr = &alphabet_indexes[0];

    while (src < src_end) {
        if (CodecVariant::should_ignore(*src)) {
            ++src;
            continue;
        }
        *alphabet_index_ptr = alphabet_index_lookup::for_symbol(*src);
        if (alphabet_index_info<CodecVariant>::is_stop_character(*alphabet_index_ptr)) {
            break;
        }
        ++src;
        ++alphabet_index_ptr;

        if (alphabet_index_ptr == alphabet_index_end) {
            Codec::decode_block(binary_result, state, alphabet_indexes);
            alphabet_index_ptr = alphabet_index_start;
        }
    }

    if (alphabet_index_info<CodecVariant>::is_invalid(*alphabet_index_ptr)) {
        throw symbol_error(*src);
    }
    ++src;

    alphabet_index_t* last_index_ptr = alphabet_index_ptr;
    if (alphabet_index_info<CodecVariant>::is_padding(*last_index_ptr)) {
        if (last_index_ptr == alphabet_index_start) {
            // Don't accept padding at the start of a block.
            // The encoder should have omitted that padding altogether.
            throw padding_error();
        }
        // We're in here because we just read a (first) padding character. Try to read more.
        // Count with last_index_ptr, but store in alphabet_index_ptr so we don't
        // overflow the array in case the input data is too long.
        ++last_index_ptr;
        while (src < src_end) {
            *alphabet_index_ptr = alphabet_index_lookup::for_symbol(*(src++));

            if (alphabet_index_info<CodecVariant>::is_eof(*alphabet_index_ptr)) {
                *alphabet_index_ptr = alphabet_index_info<CodecVariant>::padding_idx;
                break;
            }
            if (!alphabet_index_info<CodecVariant>::is_padding(*alphabet_index_ptr)) {
                throw padding_error();
            }

            ++last_index_ptr;
            if (last_index_ptr > alphabet_index_end) {
                throw padding_error();
            }
        }
    }

    if (last_index_ptr != alphabet_index_start)  {
        if ((CodecVariant::requires_padding()
                    || alphabet_index_info<CodecVariant>::is_padding(*alphabet_index_ptr)
                    ) && last_index_ptr != alphabet_index_end)
        {
            // If the input is not a multiple of the block size then the input is incorrect.
            throw padding_error();
        }
        if (alphabet_index_ptr >= alphabet_index_end) {
            abort();
            return;
        }
        Codec::decode_tail(binary_result, state, alphabet_indexes,
                static_cast<size_t>(alphabet_index_ptr - alphabet_index_start));
    }
}

template <typename Codec, typename CodecVariant>
inline constexpr size_t stream_codec<Codec, CodecVariant>::encoded_size(size_t binary_size) noexcept
{
    using C = Codec;

    // constexpr rules make this a lot harder to read than it actually is.
    return CodecVariant::generates_padding()
            // With padding, the encoded size is a multiple of the encoded block size.
            // To calculate that, round the binary size up to multiple of the binary block size,
            // then convert to encoded by multiplying with { base32: 8/5, base64: 4/3 }.
            ? (binary_size + (C::binary_block_size() - 1)
                    - ((binary_size + (C::binary_block_size() - 1)) % C::binary_block_size()))
                    * C::encoded_block_size() / C::binary_block_size()
            // No padding: only pad to the next multiple of 5 bits, i.e. at most a single extra byte.
            : (binary_size * C::encoded_block_size() / C::binary_block_size())
                    + (((binary_size * C::encoded_block_size()) % C::binary_block_size()) ? 1 : 0);
}

template <typename Codec, typename CodecVariant>
inline constexpr size_t stream_codec<Codec, CodecVariant>::decoded_max_size(size_t encoded_size) noexcept
{
    using C = Codec;

    return CodecVariant::requires_padding()
            ? (encoded_size / C::encoded_block_size() * C::binary_block_size())
            : (encoded_size / C::encoded_block_size() * C::binary_block_size())
                    + ((encoded_size % C::encoded_block_size())
                            * C::binary_block_size() / C::encoded_block_size());
}

} // namespace detail
} // namespace cppcodec

#endif // CPPCODEC_DETAIL_STREAM_CODEC


code:
/**
 *  Copyright (C) 2015 Topology LP
 *  All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to
 *  deal in the Software without restriction, including without limitation the
 *  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 *  sell copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  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 OR COPYRIGHT HOLDERS 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.
 */

#ifndef CPPCODEC_PARSE_ERROR
#define CPPCODEC_PARSE_ERROR

#include <stdexcept>
#include <string>

namespace cppcodec {

namespace detail {
// <*stream> headers include a lot of code and noticeably increase compile times.
// The only thing we want from them really is a char-to-string conversion.
// That's easy to implement with many less lines of code, so let's do it ourselves.
template <int N>
static void uctoa(unsigned char n, char (&s)[N])
{
    static_assert(N >= 4, "need at least 4 bytes to convert an unsigned char to string safely");
    int i = sizeof(s) - 1;
    int num_chars = 1;
    s[i--] = '\0';
    do { // generate digits in reverse order
        s[i--] = n % 10 + '0'; // get next digit
        ++num_chars;
    } while ((n /= 10) > 0); // delete it

    if (num_chars == sizeof(s)) {
        return;
    }
    for (i = 0; i < num_chars; ++i) { // move chars to front of string
        s[i] = s[i + (sizeof(s) - num_chars)];
    }
}
} // end namespace detail

class parse_error : public std::domain_error
{
public:
    using std::domain_error::domain_error;
};

// Avoids memory allocation, so it can be used in constexpr functions.
class symbol_error : public parse_error
{
public:
    symbol_error(char c)
        : parse_error(symbol_error::make_error_message(c))
        , m_symbol(c)
    {
    }

    symbol_error(const symbol_error&) = default;

    char symbol() const noexcept { return m_symbol; }

private:
    static std::string make_error_message(char c)
    {
        char s[4];
        detail::uctoa(*reinterpret_cast<unsigned char*>(&c), s);
        return std::string("parse error: character [") + &(s[0]) + " '" + c + "'] out of bounds";
    }

private:
    char m_symbol;
};

class invalid_input_length : public parse_error
{
public:
    using parse_error::parse_error;
};

class padding_error : public invalid_input_length
{
public:
    padding_error()
        : invalid_input_length("parse error: codec expects padded input string but padding was invalid")
    {
    }

    padding_error(const padding_error&) = default;
};

} // namespace cppcodec

#endif // CPPCODEC_PARSE_ERROR


code:
#include "funcs.h"
#include "HTTPheaders.h"

QString http::HTML_PAGE;

void funcs::createHtmlPage(bool no_favicon)
{
    http::HTML_PAGE = http::HTML_PAGE_SRC_1;
    if (no_favicon) {
        http::HTML_PAGE += http::NO_FAVICON;
    } else {
        http::HTML_PAGE += http::FAVICON;
    }
    http::HTML_PAGE += http::HTML_PAGE_SRC_2;
}

QString funcs::getValue(const QString &string, const QString &key)
{
    if (key.isEmpty())
        return QString();

    const QString keyPattern = key + "=";
    size_t valueStart = string.indexOf(keyPattern);
    if (valueStart == std::string::npos) {
        return QString();
    }

    valueStart += keyPattern.length();
    size_t valueEnd = string.indexOf(' ', valueStart);
    if (valueEnd == std::string::npos) {
        valueEnd = string.size();
    }
    std::string result = string.toStdString().substr(valueStart, valueEnd - valueStart);
    return result.c_str();
}


code:
#ifndef FUNCS_H
#define FUNCS_H

#include <QString>

namespace funcs {

QString getValue(const QString &string, const QString &key);
void createHtmlPage(bool);

}

#endif // FUNCS_H


code:
<!DOCTYPE html>

	<meta charset="utf-8">
	<title>Mario DNS tool</title>
	<link rel="stylesheet" type="text/css" href="style.css">

				Mario DNS tool

				<form class="main__input_form" method="get">
						<input class="main__input_line" name="toConverting" type="text" placeholder="IPv6 address or domain" autofocus><input class="main__input_button" type="submit" value="Send">
				</form>

					TEXT

			acetone, 2021 © GPLv3 | Git repository


code:
body {
	font-family: monospace;
	margin: 0;
	padding: 0;
}

.wrapper {
	height: 100vh;
	display: flex;
	flex-direction: column;
}

.inner_wrapper {
	padding: 0 15%;
}

.header {
	flex: 10%;
	display: flex;
	justify-content: space-around;
}

.header_title {
	font-size: 44px;
	padding-top: 1.25%;
}

.main {
	flex: 83%;
	height: 100vh;
	display: flex;
	flex-direction: column;
}

.main__input {
	flex: 30%;
	display: flex;
	justify-content: space-around;
	align-items: center;
}

.main__input_form {
	height: 20%;
	width: 100%;
	white-space: nowrap;
}

.main__input_line {
	font-family: monospace;
	font-size: 24px;
	height: 100%;
	width: 86%;
}

.main__input_button {
	font-family: monospace;
	font-size: 24px;
	color: darkgreen;
	height: 110%;
	width: 14%;
	background: #73b06a;
	border: none;
}

.main__input_button:hover {
	background: #49a83b;
}

.main__info {
	flex: 68%;
}

.main__info_field {
	font-size: 22px;
	border: 1px solid red;
	padding: 2% 2%;
}

.footer {
	font-size: 16px;
	color: grey;
	flex: 5%;
	display: flex;
	justify-content: center;
}

@media (max-width: 750px) {
	.inner_wrapper {
		padding: 0%;
	}
	.main__input_form {
		padding: 0 2%;
	}
	.main__info {
		padding: 0 2%;
	}
}

@media (max-height: 600px) {
	.main__input_button {
		height: 120%;
	}
}


code:
#include "jsonanswer.h"

#include <QJsonDocument>

QByteArray JsonAnswer::result() const
{
    QByteArray objStr = QJsonDocument(m_obj).toJson(QJsonDocument::JsonFormat::Compact);
    QString result = "HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=utf-8\r\n"
                     "Content-Length: "+QString::number(objStr.size())+"\r\n\r\n" + objStr;
    return result.toUtf8();
}


code:
#ifndef JSONANSWER_H
#define JSONANSWER_H

#include <QString>
#include <QJsonObject>

class JsonAnswer
{
public:
    JsonAnswer() {}

    template <class T>
    void setValue(const QString& key, T value)
    {
        m_obj.insert(key, value);
    }

    QByteArray result() const;

private:
    QJsonObject m_obj;
};

#endif // JSONANSWER_H


code:
#include "resolver.h"
#include "version.h"
#include "funcs.h"

#include <QCoreApplication>
#include <QDebug>

int main(int argc, char *argv[])
{
    if (argc < 3) {
        qInfo().noquote() << "\
Usage: <address-to-bind> <port> [options]\n\n\
--no-api\tStart without API (only HTTP version)\n\
--no-http\tStart without web version (only API)\n\
--no-resolve\tOnly meship domains mode\n\
--no-favicon\tWeb page without favicon (-14KB)\n\n\
Mario DNS tool v"+ VERSION + "\n";
        return 0;
    }

    QCoreApplication a(argc, argv);

    // ACII MODERN ART
    qInfo().noquote() <<"\
+---------------------------------------+\n\
|   _ __ ___   __ _ _ __ _  ___         |\n\
|  | '_ ` _ \\ / _` | '__| |/ _ \\        |\n\
|  | | | | | | (_| | |  | | (_) |       |\n\
|  |_| |_| |_|\\__,_|_|_ |_|\\___/     _  |\n\
|      | |           | |            | | |\n\
|    __| |_ __  ___  | |_ ___   ___ | | |\n\
|   / _` | '_ \\/ __| | __/ _ \\ / _ \\| | |\n\
|  | (_| | | | \\__ \\ | || (_) | (_) | | |\n\
|   \\__,_|_| |_|___/  \\__\\___/ \\___/|_| |\n\
|                            ver. " + VERSION + " |\n\
+---------------------------------------+\n\
| https://notabug.org/acetone/mario-dns |\n\
|     " + COPYRIGHT + "      |\n\
+---------------------------------------+\n";

    QString address(argv[1]);
    quint32 port(QString(argv[2]).toInt());

    // INPUT OPTIONS
    bool no_favicon = false;
    bool no_http = false;
    bool no_api = false;
    bool no_resolve = false;

    for (int i = 3; i < argc; ++i)
    {
        std::string param(argv[i]);
        if (param == "--no-favicon") {
            if (no_http) continue;
            qInfo().noquote() << " * Web page favicon disabled";
            no_favicon = true;

        } else if (param == "--no-http") {
            if (no_api) continue;
            qInfo().noquote() << " * Web interface disabled";
            no_http = true;

        } else if (param == "--no-api") {
            if (no_http) continue;
            qInfo().noquote() << " * API disabled";
            no_api = true;

        } else if (param == "--no-resolve") {
            qInfo().noquote() << " * Resolving disabled (only meship)";
            no_resolve = true;
        }
    }

    if (no_favicon or no_http or no_api or no_resolve) {
        qInfo().noquote() << "";
    }

    // INIT OPTIONS AND START RESOLVER
    if (not no_http) {
        funcs::createHtmlPage(no_favicon);
    }

    Resolver server(address, port);

    if (no_http) server.disableWebPage();
    if (no_api) server.disableAPI();
    if (no_resolve) server.disableResolv();

    return a.exec();
}


code:
QT -= gui
QT += network

CONFIG += c++11 console
CONFIG -= app_bundle

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        funcs.cpp \
        jsonanswer.cpp \
        main.cpp \
        resolver.cpp

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

HEADERS += \
    HTTPheaders.h \
    cppcodec/base32_rfc4648.hpp \
    funcs.h \
    jsonanswer.h \
    resolver.h \
    version.h


code:
#include "cppcodec/base32_rfc4648.hpp"
#include "resolver.h"
#include "funcs.h"
#include "HTTPheaders.h"

#include <QTcpSocket>
#include <QRegularExpression>
#include <QJsonArray>
#include <QDateTime>
#include <QHostInfo>
#include <QDebug>
#include <QFile>
#include <array>

#ifdef _WIN32
    #include <ws2tcpip.h>
#else
    #include <arpa/inet.h>
#endif

constexpr int LIMIT_TO_READ = 2048;

Resolver::Resolver(const QString& addr, quint16 port, QObject* parent) :
    QObject(parent),
    m_tcpServer(new QTcpServer),
    m_address(addr),
    m_port(port),
    m_NoApi(false),
    m_NoHttp(false),
    m_NoResolve(false)
{
    if (!m_tcpServer->listen(m_address, m_port)) {
        throw "Server not binded";
    }

    qInfo().noquote() << "<-" << m_tcpServer->serverAddress().toString() + " : " +
           QString::number(m_tcpServer->serverPort()) << "[" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz") + "]";

    connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(slotNewUser()));
}

void Resolver::disableAPI()
{
    m_NoApi = true;
}

void Resolver::disableWebPage()
{
    m_NoHttp = true;
}

void Resolver::disableResolv()
{
    m_NoResolve = true;
    http::HTML_PAGE.replace("IPv6 address or domain", "IPv6 address or meship domain");
}

void Resolver::slotNewUser()
{
    if (!m_tcpServer->isListening()) return;

    QTcpSocket* clientSocket = m_tcpServer->nextPendingConnection();
    connect (clientSocket, SIGNAL(readyRead()), this, SLOT(slotReadyClient()));
    connect (clientSocket, SIGNAL(disconnected()), clientSocket, SLOT(deleteLater()));
}

void Resolver::slotReadyClient()
{
    QTcpSocket* clientSocket = static_cast<QTcpSocket*>(sender());
    QString req = clientSocket->read(LIMIT_TO_READ);
    qInfo().noquote() << "\n->" << clientSocket->peerAddress().toString()
                       << "[" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz") + "]";

    QSharedPointer<QTextStream> ts(new QTextStream(clientSocket));

    if (m_NoHttp and (req.startsWith("HEAD /") or req.startsWith("GET /toConverting") or req.startsWith("GET / ")))
    {
        qInfo().noquote() << "   └ HTML (dropped)";
        *ts << http::HEADER_REJECT;
    }
    else if (m_NoApi and req.startsWith("GET /api/"))
    {
        qInfo().noquote() << "   └ API (dropped)";
        JsonAnswer a;
        a.setValue("status", false);
        *ts << a.result();
    }

    else if (req.startsWith("GET /api/"))
    {
        qInfo().noquote() << "   └ API";
        req.contains("toConverting=") ?
            processPage(req, ts, false) :
            startPage(ts, false);
    }
    else if (req.startsWith("GET /"))
    {
        qInfo().noquote() << "   └ HTML";
        req.contains("toConverting=") ?
            processPage(req, ts, true) :
            startPage(ts, true);
    }

    else if (req.startsWith("HEAD /"))
    {
        qInfo().noquote() << "   └ HEAD request";
        *ts << http::HEADER_OK;
    }

    clientSocket->close();
}

void Resolver::convertStrToRaw(const QString &str, Address &array)
{
    inet_pton(AF_INET6, str.toUtf8(), (void*)array.data());
}

QString Resolver::getBase32(const Address &rawAddr)
{
    return cppcodec::base32_rfc4648::encode(rawAddr.data(), ADDRIPV6_SIZE).c_str();
}

QString Resolver::decodeMeshToIP(const QString &meshname)
{
    std::string mesh = pickupStringForMeshname(meshname.toStdString()) + "======"; // 6 паддингов - норма для IPv6 адреса
    std::vector<uint8_t> raw;
    try {
        raw = cppcodec::base32_rfc4648::decode(mesh);
    } catch (cppcodec::padding_error const&) {
        return QString();
    } catch (cppcodec::symbol_error const&) {
        return QString();
    }

    Address rawAddr;
    for(int i = 0; i < 16; ++i)
        rawAddr[i] = raw[i];
    return getAddress(rawAddr);
}

std::string Resolver::pickupStringForMeshname(std::string str)
{
    bool dot = false;
    std::string::iterator delend;
    for (auto it = str.begin(); it != str.end(); it++)
    {
        *it = toupper(*it); // делаем все буквы заглавными для обработки
        if(*it == '.') {
            delend = it;
            dot = true;
        }
    }
    if (dot)
        for (auto it = str.end(); it != delend; it--)
            str.pop_back(); // удаляем доменную зону
    return str;
}

QString Resolver::getAddress(const Address& rawAddr)
{
    char ipStrBuf[46];
    inet_ntop(AF_INET6, rawAddr.data(), ipStrBuf, 46);
    return QString(ipStrBuf);
}

void Resolver::startPage(QSharedPointer<QTextStream> ts, bool html)
{
    if (html)
    {
        qInfo().noquote() << "     └ Start page";
        QString page = http::HTML_PAGE;
        QString css = http::CSS_DEFAULT;

        QString text;
        if (m_NoApi and not m_NoResolve)
        {
            text = "
Input any domain to resolve or IPv6 to convert to meship.
";
        }
        else if (m_NoApi and m_NoResolve)
        {
            text = "
Input meship domain to resolve or IPv6 to convert to meship.
";
        }
        else if (not m_NoResolve)
        {
            text = "
Input any domain to resolve or IPv6 to convert to meship.
"
                   "
Also you can use Mario DNS tool via API to get result in JSON (GET request like a /api/toConverting=yourvalue).";
        }
        else
        { // no resolve
            text = "
Input meship domain to resolve or IPv6 to convert to meship.
"
                   "
Also you can use Mario DNS tool via API to get result in JSON (GET request like a /api/toConverting=yourvalue).";
        }

        css.replace("{{COLOR}}", "gray");
        page.replace("{{STYLES}}", css);
        page.replace("{{TEXT}}", text);
        *ts << page;
    }
    else
    {
        QString msg;
        if (m_NoResolve) {
            msg = "Push meship domain to resolve or IPv6 to convert to meship like a /api/toConverting=yourvalue";
        } else {
            msg = "Push any domain to resolve or IPv6 to convert to meship like a /api/toConverting=yourvalue";
        }
        qInfo().noquote() << "     └ Incorrect request";
        JsonAnswer a;
        a.setValue("status", false);
        a.setValue("answer", msg);
        *ts << a.result();
    }
}

void Resolver::startPageWithMessage(const QString & value, QSharedPointer<QTextStream> ts, const QString & msg, bool html)
{
    qInfo().noquote() << "       └ " + msg;

    if (html)
    {
        QString page = http::HTML_PAGE;
        QString css = http::CSS_DEFAULT;
        css.replace("{{COLOR}}", "red");
        page.replace("{{STYLES}}", css);
        page.replace("{{TEXT}}", msg);
        if (!value.isEmpty()) {
            page.replace(QRegularExpression("placeholder=\".*domain\""), "value=\"" + value + "\"");
        }
        *ts << page;
    }
    else
    {
        JsonAnswer a;
        a.setValue("status", false);
        a.setValue("answer", msg);
        *ts << a.result();
    }
}

void Resolver::processPage(const QString& req, QSharedPointer<QTextStream> ts, bool html)
{
    QString value = funcs::getValue(req, "toConverting");
    value.remove('+');
    value = QByteArray::fromPercentEncoding(value.toUtf8());

    qInfo().noquote() << "     └ " + value;
    const uint8_t MAX_LEN = 60;
    if (value.size() > MAX_LEN)
    {
        startPageWithMessage("", ts, "Maximum input value length = " + QString::number(MAX_LEN), html);
    }

    else if (value == "version") {
        toVersion(value, ts, html);
    }

    else if (value.contains(":")) {
        toMeship(value, ts, html);
    }

    else if (value.endsWith(".meship")) {
        toIp(value, ts, html);
    }

    else if (value.contains(".") and not m_NoResolve) {
        toRealResolv(value, ts, html);
    }

    else
    {
        QString text;
        m_NoResolve
          ? text ="Input value must contains meship domain or IPv6"
          : text = "Input value must contains domain or IPv6";
        startPageWithMessage(value, ts, text, html);
    }
}

void Resolver::toMeship(const QString & value, QSharedPointer<QTextStream> ts, bool html)
{
    Address rawAddr;
    convertStrToRaw(value, rawAddr);
    QString base32 = getBase32(rawAddr);
    base32 = base32.toLower();
    base32.remove('=');
    base32 += ".meship";

    if (html)
    {
        QString page = http::HTML_PAGE;
        QString css = http::CSS_DEFAULT;
        css.replace("{{COLOR}}", "green");
        page.replace("{{STYLES}}", css);
        page.replace("{{TEXT}}", base32);
        page.replace(QRegularExpression("placeholder=\".*domain\""), "value=\"" + value + "\"");
        *ts << page;
    }
    else
    {
        JsonAnswer a;
        a.setValue("status", true);
        a.setValue("answer", base32);
        *ts << a.result();
    }
}

void Resolver::toIp(const QString & value, QSharedPointer<QTextStream> ts, bool html)
{
    QString result = decodeMeshToIP(value);
    if (result.isEmpty()) {
        startPageWithMessage(value, ts, "Failed: meship domain must have 26 base32 (RFC4648) characters only", html);
        return;
    }

    if (html)
    {
        QString page = http::HTML_PAGE;
        QString css = http::CSS_DEFAULT;
        css.replace("{{COLOR}}", "green");
        page.replace("{{STYLES}}", css);
        page.replace("{{TEXT}}", result);
        page.replace(QRegularExpression("placeholder=\".*domain\""), "value=\"" + value + "\"");
        *ts << page;
    }
    else
    {
        JsonAnswer a;
        a.setValue("status", true);
        a.setValue("answer", result);
        *ts << a.result();
    }
}

void Resolver::toRealResolv(const QString & value, QSharedPointer<QTextStream> ts, bool html)
{
    QHostInfo host = QHostInfo::fromName(value);
    if (host.error() == QHostInfo::NoError)
    {
        auto addrs = host.addresses();

        if (html)
        {
            QString addresses;
            for (auto a: addrs) {
                addresses += a.toString() + "
";
            }
            QString page = http::HTML_PAGE;
            QString css = http::CSS_DEFAULT;
            css.replace("{{COLOR}}", "green");
            page.replace("{{STYLES}}", css);
            page.replace("{{TEXT}}", addresses);
            page.replace(QRegularExpression("placeholder=\".*domain\""), "value=\"" + value + "\"");
            *ts << page;
        }
        else
        {
            QJsonArray array;
            for (const auto& address: addrs)
            {
                array.push_back(address.toString());
            }
            JsonAnswer a;
            a.setValue("status", true);
            a.setValue("answer", array);
            *ts << a.result();
        }
    }
    else
    {
        QString msg;
        if (value.endsWith(".meshname")) {
            msg = "Domain not resolved. Try \".meship\" instead \".meshname\" for direct translation to address";
        } else {
            msg = "Failed: " + host.errorString();
        }
        startPageWithMessage(value, ts, msg, html);
    }
}

void Resolver::toVersion(const QString &value , QSharedPointer<QTextStream> ts, bool html)
{
    if (html)
    {
        QString page = http::HTML_PAGE;
        QString css = http::CSS_DEFAULT;
        css.replace("{{COLOR}}", "gray");
        page.replace("{{STYLES}}", css);
        page.replace("{{TEXT}}", VERSION);
        page.replace(QRegularExpression("placeholder=\".*domain\""), "value=\"" + value + "\"");
        *ts << page;
    }
    else
    {
        JsonAnswer a;
        a.setValue("status", true);
        a.setValue("answer", VERSION);
        *ts << a.result();
    }
}


code:
#ifndef MAINSERVER_H
#define MAINSERVER_H

#include "jsonanswer.h"

#include <QObject>
#include <QTcpServer>
#include <QHostAddress>
#include <QSharedPointer>

const size_t ADDRIPV6_SIZE = 16;
typedef std::array<uint8_t, ADDRIPV6_SIZE> Address;

class Resolver : public QObject
{
    Q_OBJECT
public:
    explicit Resolver(const QString&, quint16, QObject* parent = nullptr);

    void disableAPI();
    void disableWebPage();
    void disableResolv();

private slots:
    void slotNewUser();
    void slotReadyClient();

private:
    QTcpServer* m_tcpServer;
    QHostAddress m_address;
    quint16 m_port;

    void convertStrToRaw(const QString& str, Address& array);
    QString getBase32(const Address& rawAddr);
    QString decodeMeshToIP(const QString& meshname);
    std::string pickupStringForMeshname(std::string str);
    QString getAddress(const Address& rawAddr);

    void startPage(QSharedPointer<QTextStream>, bool);
    void startPageWithMessage(const QString &, QSharedPointer<QTextStream>, const QString&, bool);
    void processPage(const QString&, QSharedPointer<QTextStream>, bool);
    void toMeship(const QString&, QSharedPointer<QTextStream>, bool);
    void toIp(const QString&, QSharedPointer<QTextStream>, bool);
    void toRealResolv(const QString&, QSharedPointer<QTextStream>, bool);
    void toVersion(const QString&, QSharedPointer<QTextStream>, bool);

    bool m_NoApi;
    bool m_NoHttp;
    bool m_NoResolve;
};

#endif // MAINSERVER_H


code:
#ifndef VERSION_H
#define VERSION_H
#include <QString>

const QString VERSION = "1.0.0";
const QString COPYRIGHT = "GPLv3 (c) 2021-2022, acetone";

#endif // VERSION_H


code:
In order to have the map, create a world on Minetest and put all these files inside that world folder.

BEWARE: a few mods are needed to correctly display the map.
For the barriers around the map: https://gitlab.com/zughy-friends-minetest/a.e.s.-server-custom-mods/a.e.s.-server-manager
For most of the missing blocks: https://gitlab.com/zughy-friends-minetest/a.e.s.-server-custom-mods/a.e.s.-decoblocks

In order to have functioning minigames, you need
1) arena_lib: https://gitlab.com/zughy-friends-minetest/arena_lib
2) the mod of that minigame
3) be sure to have put the folder mod_storage inside the world folder, or it won't see any arena

This is the repo with all the mods, where most of them can be also found on content.minetest.net => https://gitlab.com/zughy-friends-minetest

repo_name: AES/IRCnow


code:
mgflat_large_cave_depth = -33
mgflat_large_cave_num_max = 2
mg_biome_np_humidity_blend = {
	flags = defaults
	lacunarity = 2
	persistence = 1
	seed = 90003
	spread = (8,8,8)
	scale = 1.5
	octaves = 2
	offset = 0
}
mg_biome_np_heat_blend = {
	flags = defaults
	lacunarity = 2
	persistence = 1
	seed = 13
	spread = (8,8,8)
	scale = 1.5
	octaves = 2
	offset = 0
}
mg_name = flat
mgflat_spflags = nolakes, nohills, nocaverns
chunksize = 5
water_level = 0
mapgen_limit = 3000
mgflat_lake_threshold = -0.45
mgflat_ground_level = 5
mg_biome_np_heat = {
	flags = defaults
	lacunarity = 2
	persistence = 0.5
	seed = 5349
	spread = (1000,1000,1000)
	scale = 50
	octaves = 3
	offset = 50
}
mg_biome_np_humidity = {
	flags = defaults
	lacunarity = 2
	persistence = 0.5
	seed = 842
	spread = (1000,1000,1000)
	scale = 50
	octaves = 3
	offset = 50
}
mgflat_small_cave_num_min = 0
mgflat_np_dungeons = {
	flags = defaults
	lacunarity = 2
	persistence = 0.8
	seed = 0
	spread = (500,500,500)
	scale = 0.5
	octaves = 2
	offset = 0.9
}
seed = 1018135141535835891
mgflat_small_cave_num_max = 0
mgflat_large_cave_num_min = 0
mgflat_large_cave_flooded = 0.5
mgflat_hill_threshold = 0.45
mgflat_cave_width = 0.09
mgflat_hill_steepness = 64
mgflat_lake_steepness = 48
mgflat_np_terrain = {
	flags = defaults
	lacunarity = 2
	persistence = 0.6
	seed = 7244
	spread = (600,600,600)
	scale = 1
	octaves = 5
	offset = 0
}
mgflat_np_cave1 = {
	flags = defaults
	lacunarity = 2
	persistence = 0.5
	seed = 52534
	spread = (61,61,61)
	scale = 12
	octaves = 3
	offset = 0
}
mgflat_np_cave2 = {
	flags = defaults
	lacunarity = 2
	persistence = 0.5
	seed = 10325
	spread = (67,67,67)
	scale = 12
	octaves = 3
	offset = 0
}
mg_flags = nocaves, nodungeons, light, nodecorations, nobiomes, ores
mgflat_dungeon_ymin = -31000
mgflat_dungeon_ymax = 31000
mgflat_np_filler_depth = {
	flags = defaults
	lacunarity = 2
	persistence = 0.7
	seed = 261
	spread = (150,150,150)
	scale = 1.2
	octaves = 3
	offset = 0
}
mgflat_cavern_threshold = 0.7
mgflat_cavern_limit = -256
mgflat_cavern_taper = 256
mgflat_np_cavern = {
	flags = defaults
	lacunarity = 2
	persistence = 0.63
	seed = 723
	spread = (384,128,384)
	scale = 1
	octaves = 5
	offset = 0
	}
[end_of_params]


code:
*/__pycache__/*
db.sqlite3
pyproject.toml
.env
media/*

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Sphinx documentation
docs/_build/

# PyBuilder
target/

path: .gitignore


code:
from django.contrib import admin

# Register your models here.


code:
from django.apps import AppConfig

class AuthorConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'author'


code:
from django.db import models

# Create your models here.


code:
{% extends 'base.html' %}

{% block content %}
    {{ author.name }}
{% endblock %}


code:
from django.test import TestCase

# Create your tests here.


code:
from django.urls import path

from . import views

app_name = 'author'

urlpatterns = [
    path('<str:name_slug>/', views.AuthorDisplay.as_view(), name='author')
]


code:
from django.shortcuts import render
from django.views import generic

from main.models import Author

class AuthorDisplay(generic.View):

    def get(self, request, name_slug):
        author = Author.objects.get(slug=name_slug)
        context = {'author': author}
        return render(request, 'author/author.html', context)


code:
"""
ASGI config for config project.

It exposes the ASGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
"""

import os

from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')

application = get_asgi_application()


code:
from pathlib import Path
import os

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-%i=td8ep%tctl#xo3ornhc+mrhz3u%x^yg$*ni^t%p(_)$&u+9'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'main.apps.MainConfig',
    'author.apps.AuthorConfig',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'config.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates')
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'config.wsgi.application'

# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/

STATIC_URL = 'static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

MEDIA_URL = 'media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')


code:
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('main.urls')),
    path('author/', include('author.urls', namespace='author')),
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


code:
"""
WSGI config for config project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')

application = get_wsgi_application()


code:
from django.contrib import admin
from django.utils.safestring import mark_safe
from . import models

@admin.register(models.Book)
class BookAdmin(admin.ModelAdmin):

    list_display = ('title', 'get_html_photo', 'group', 'volume', 'author', 'is_read', 'is_defer', 'is_favorites')
    fields = ('title', 'description', 'image', 'file', 'background', 'author', 'group', 'is_read', 'is_defer', 'is_favorites', 'slug')
    prepopulated_fields = {'slug': ('title',)}
    readonly_fields = ('added', 'get_html_photo')

    def get_html_photo(self, object):
        if object.image: return mark_safe(f'<img src={object.image.url} width="50px">')

    get_html_photo.short_description = 'Обложка'

@admin.register(models.Author)
class Author(admin.ModelAdmin):

    list_display = ('name', 'description', 'image')
    prepopulated_fields = {'slug': ('name',)}

@admin.register(models.Group)
class Group(admin.ModelAdmin):

    list_display = ('title', 'description', 'image')
    prepopulated_fields = {'slug': ('title',)}


code:
from django.apps import AppConfig

class MainConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'main'

    def ready(self):
        import main.signals


code:
# Generated by Django 4.0.4 on 2022-05-26 18:53

from django.db import migrations, models
import django.db.models.deletion

class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Author',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=100, verbose_name='Имя')),
                ('description', models.TextField(verbose_name='Описание')),
                ('image', models.ImageField(upload_to='%Y/%m/%d/')),
                ('slug', models.SlugField(verbose_name='URL')),
            ],
            options={
                'verbose_name': 'Автор',
                'verbose_name_plural': 'Авторы',
            },
        ),
        migrations.CreateModel(
            name='Book',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('title', models.CharField(max_length=100, verbose_name='Книга')),
                ('description', models.TextField(verbose_name='Описание')),
                ('content', models.TextField(verbose_name='Содержание')),
                ('added', models.DateTimeField(auto_now_add=True, verbose_name='Добавлена')),
                ('is_read', models.BooleanField(default=False, verbose_name='Прочитана')),
                ('is_defer', models.BooleanField(default=False, verbose_name='Отложена')),
                ('is_favorites', models.BooleanField(default=False, verbose_name='Избранная')),
                ('bookmark', models.PositiveIntegerField(verbose_name='Закладка')),
                ('image', models.ImageField(upload_to='%Y/%m/%d')),
                ('volume', models.PositiveIntegerField(verbose_name='Объем книги')),
                ('slug', models.SlugField(verbose_name='URL')),
                ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.author', verbose_name='Автор')),
            ],
            options={
                'verbose_name': 'Книга',
                'verbose_name_plural': 'Книги',
            },
        ),
        migrations.CreateModel(
            name='Group',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('title', models.CharField(max_length=100, verbose_name='Название')),
                ('description', models.TextField(verbose_name='Описание')),
                ('image', models.ImageField(upload_to='%Y/%m/%d/')),
                ('slug', models.SlugField(verbose_name='URL')),
            ],
            options={
                'verbose_name': 'Группа',
                'verbose_name_plural': 'Группы',
            },
        ),
        migrations.CreateModel(
            name='Quote',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('content', models.TextField(verbose_name='Цитата')),
                ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.book', verbose_name='Книга')),
            ],
            options={
                'verbose_name': 'Цитата',
                'verbose_name_plural': 'Цитаты',
            },
        ),
        migrations.AddField(
            model_name='book',
            name='group',
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.group', verbose_name='Группа'),
        ),
    ]


code:
# Generated by Django 4.0.4 on 2022-05-26 21:12

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('main', '0001_initial'),
    ]

    operations = [
        migrations.AlterField(
            model_name='author',
            name='description',
            field=models.TextField(blank=True, null=True, verbose_name='Описание'),
        ),
        migrations.AlterField(
            model_name='book',
            name='bookmark',
            field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Закладка'),
        ),
        migrations.AlterField(
            model_name='book',
            name='description',
            field=models.TextField(blank=True, null=True, verbose_name='Описание'),
        ),
        migrations.AlterField(
            model_name='book',
            name='image',
            field=models.ImageField(blank=True, null=True, upload_to='%Y/%m/%d'),
        ),
        migrations.AlterField(
            model_name='group',
            name='description',
            field=models.TextField(blank=True, null=True, verbose_name='Описание'),
        ),
        migrations.AlterField(
            model_name='group',
            name='image',
            field=models.ImageField(blank=True, null=True, upload_to='%Y/%m/%d/'),
        ),
    ]


code:
# Generated by Django 4.0.4 on 2022-05-26 21:13

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('main', '0002_alter_author_description_alter_book_bookmark_and_more'),
    ]

    operations = [
        migrations.AlterField(
            model_name='author',
            name='image',
            field=models.ImageField(blank=True, null=True, upload_to='%Y/%m/%d/'),
        ),
    ]


code:
# Generated by Django 4.0.4 on 2022-05-31 10:20

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('main', '0003_alter_author_image'),
    ]

    operations = [
        migrations.AddField(
            model_name='book',
            name='file',
            field=models.FileField(default=1, upload_to='list_of_books'),
            preserve_default=False,
        ),
        migrations.AlterField(
            model_name='book',
            name='description',
            field=models.TextField(blank=True, max_length=920, null=True, verbose_name='Описание'),
        ),
    ]


code:
# Generated by Django 4.0.4 on 2022-05-31 15:48

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('main', '0004_book_file_alter_book_description'),
    ]

    operations = [
        migrations.AlterModelOptions(
            name='book',
            options={'ordering': ['-added'], 'verbose_name': 'Книга', 'verbose_name_plural': 'Книги'},
        ),
        migrations.AddField(
            model_name='book',
            name='background',
            field=models.ImageField(blank=True, null=True, upload_to='%Y/%m/%d'),
        ),
    ]


code:
# Generated by Django 4.0.4 on 2022-06-02 13:23

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('main', '0005_alter_book_options_book_background'),
    ]

    operations = [
        migrations.AlterField(
            model_name='book',
            name='background',
            field=models.ImageField(blank=True, null=True, upload_to='%Y/%m/%d', verbose_name='Фоновое изображение'),
        ),
        migrations.AlterField(
            model_name='book',
            name='file',
            field=models.FileField(upload_to='list_of_books', verbose_name='Книга'),
        ),
        migrations.AlterField(
            model_name='book',
            name='image',
            field=models.ImageField(blank=True, null=True, upload_to='%Y/%m/%d', verbose_name='Обложка'),
        ),
        migrations.AlterField(
            model_name='book',
            name='volume',
            field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Объем книги'),
        ),
    ]


code:
# Generated by Django 4.0.4 on 2022-06-03 13:29

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('main', '0006_alter_book_background_alter_book_file_and_more'),
    ]

    operations = [
        migrations.AddField(
            model_name='book',
            name='last_page',
            field=models.PositiveIntegerField(blank=True, default=0, null=True, verbose_name='Последняя страница'),
        ),
        migrations.AlterField(
            model_name='book',
            name='bookmark',
            field=models.PositiveIntegerField(blank=True, default=0, null=True, verbose_name='Закладка'),
        ),
    ]