diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | external/cppcodec/base64_url.hpp | 75 | ||||
-rw-r--r-- | external/cppcodec/data/access.hpp | 328 | ||||
-rw-r--r-- | external/cppcodec/data/raw_result_buffer.hpp | 71 | ||||
-rw-r--r-- | external/cppcodec/detail/base64.hpp | 132 | ||||
-rw-r--r-- | external/cppcodec/detail/codec.hpp | 327 | ||||
-rw-r--r-- | external/cppcodec/detail/config.hpp | 40 | ||||
-rw-r--r-- | external/cppcodec/detail/stream_codec.hpp | 439 | ||||
-rw-r--r-- | external/cppcodec/parse_error.hpp | 109 | ||||
-rw-r--r-- | include/base64_url.hpp | 38 | ||||
-rw-r--r-- | project.conf | 1 | ||||
-rw-r--r-- | src/DownloadUtils.cpp | 4 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 6 | ||||
-rw-r--r-- | src/plugins/Matrix.cpp | 4 |
15 files changed, 1531 insertions, 48 deletions
@@ -77,7 +77,7 @@ Either set the `GDK_SCALE` environment variable or add `Xft.dpi` to `$HOME/.Xres For example a value of 96 for the `Xft.dpi` means 1.0 scaling and 144 (96*1.5) means 1.5 scaling.\ Note that at the moment, cached images will not be scaled with the dpi. Images do also not scale above their original size. ## Tabs -[tabbed](https://tools.suckless.org/tabbed/) can be used to put quickmedia windows into tabs. After installing `tabbed`, run `tabbed quickmedia launcher -e`. +[tabbed](https://tools.suckless.org/tabbed/) can be used to put quickmedia windows into tabs. After installing `tabbed`, run `tabbed -c -k quickmedia launcher -e`. # Dependencies ## Compile See project.conf \[dependencies]. @@ -158,4 +158,5 @@ Make video visible when reading comments (youtube). Convert nyaa.si/spotify/soundcloud date from ISO date string to local time. When ui is scaled then the predicated thumbnail size will be wrong since its scaled in Body but not in the plugins where they are requested. Check if get_page handlers in pages need to check if next batch is valid. If the server returns empty next batch we shouldn't fetch the first page... -Cloudflare kicks in when downloading manga on manganelo.. figure out a way to bypass it. This doesn't seem to happen when using python requests as is done in AutoMedia.
\ No newline at end of file +Cloudflare kicks in when downloading manga on manganelo.. figure out a way to bypass it. This doesn't seem to happen when using python requests as is done in AutoMedia. +Replace cppcodec with another library for base64 url encoding/decoding. Its way too large for what it does.
\ No newline at end of file diff --git a/external/cppcodec/base64_url.hpp b/external/cppcodec/base64_url.hpp new file mode 100644 index 0000000..04c2f8b --- /dev/null +++ b/external/cppcodec/base64_url.hpp @@ -0,0 +1,75 @@ +/** + * 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_BASE64_URL +#define CPPCODEC_BASE64_URL + +#include "detail/codec.hpp" +#include "detail/base64.hpp" + +namespace cppcodec { + +namespace detail { + +// The URL and filename safe alphabet is also specified by RFC4648, named "base64url". +// We keep the underscore ("base64_url") for consistency with the other codec variants. +static constexpr const char base64_url_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', + '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', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' +}; + +class base64_url +{ +public: + template <typename Codec> using codec_impl = stream_codec<Codec, base64_url>; + + static CPPCODEC_ALWAYS_INLINE constexpr size_t alphabet_size() { + static_assert(sizeof(base64_url_alphabet) == 64, "base64 alphabet must have 64 values"); + return sizeof(base64_url_alphabet); + } + static CPPCODEC_ALWAYS_INLINE constexpr char symbol(alphabet_index_t idx) + { + return base64_url_alphabet[idx]; + } + static CPPCODEC_ALWAYS_INLINE constexpr char normalized_symbol(char c) { return 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 base64 encodings. + static CPPCODEC_ALWAYS_INLINE constexpr bool should_ignore(char) { return false; } +}; + +} // namespace detail + +using base64_url = detail::codec<detail::base64<detail::base64_url>>; + +} // namespace cppcodec + +#endif // CPPCODEC_BASE64_URL diff --git a/external/cppcodec/data/access.hpp b/external/cppcodec/data/access.hpp new file mode 100644 index 0000000..432a3c7 --- /dev/null +++ b/external/cppcodec/data/access.hpp @@ -0,0 +1,328 @@ +/** + * 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 diff --git a/external/cppcodec/data/raw_result_buffer.hpp b/external/cppcodec/data/raw_result_buffer.hpp new file mode 100644 index 0000000..716c2e6 --- /dev/null +++ b/external/cppcodec/data/raw_result_buffer.hpp @@ -0,0 +1,71 @@ +/** + * 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 diff --git a/external/cppcodec/detail/base64.hpp b/external/cppcodec/detail/base64.hpp new file mode 100644 index 0000000..b6db6d7 --- /dev/null +++ b/external/cppcodec/detail/base64.hpp @@ -0,0 +1,132 @@ +/** + * Copyright (C) 2015 Topology LP + * Copyright (C) 2013 Adam Rudd (bit calculations) + * 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. + * + * Bit calculations adapted from https://github.com/adamvr/arduino-base64, + * commit 999595783185a0afcba156d7276dfeaa9cb5382f. + */ + +#ifndef CPPCODEC_DETAIL_BASE64 +#define CPPCODEC_DETAIL_BASE64 + +#include <stdexcept> +#include <stdint.h> + +#include "../data/access.hpp" +#include "../parse_error.hpp" +#include "config.hpp" +#include "stream_codec.hpp" + +namespace cppcodec { +namespace detail { + +template <typename CodecVariant> +class base64 : public CodecVariant::template codec_impl<base64<CodecVariant>> +{ +public: + static inline constexpr uint8_t binary_block_size() { return 3; } + static inline constexpr uint8_t encoded_block_size() { return 4; } + + static CPPCODEC_ALWAYS_INLINE constexpr uint8_t num_encoded_tail_symbols(uint8_t num_bytes) + { + return (num_bytes == 1) ? 2 // 2 symbols, 2 padding characters + : (num_bytes == 2) ? 3 // 3 symbols, 1 padding character + : 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] >> 2) // first 6 bits + : (I == 1) ? (((b[0] & 0x3) << 4) | (b[1] >> 4)) + : (I == 2) ? (((b[1] & 0xF) << 2) | (b[2] >> 6)) + : /*I == 3*/ (b[2] & 0x3F); // last 6 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 == 2> index_last( + const uint8_t* b /*binary block*/) noexcept + { + return (I == 1) ? ((b[0] & 0x3) << 4) // abbreviated 2nd symbol + : /*I == 2*/ ((b[1] & 0xF) << 2); // abbreviated 3rd symbol + } + + template <uint8_t I> + static CPPCODEC_ALWAYS_INLINE uint8_if<I != 1 && I != 2> 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); +}; + + +template <typename CodecVariant> +template <typename Result, typename ResultState> +CPPCODEC_ALWAYS_INLINE void base64<CodecVariant>::decode_block( + Result& decoded, ResultState& state, const alphabet_index_t* idx) +{ + uint_fast32_t dec = (idx[0] << 18) | (idx[1] << 12) | (idx[2] << 6) | idx[3]; + data::put(decoded, state, static_cast<uint8_t>(dec >> 16)); + data::put(decoded, state, static_cast<uint8_t>((dec >> 8) & 0xFF)); + data::put(decoded, state, static_cast<uint8_t>(dec & 0xFF)); +} + +template <typename CodecVariant> +template <typename Result, typename ResultState> +CPPCODEC_ALWAYS_INLINE void base64<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 base64 block: found 1, expected 2 or 3"); + } + + // idx_len == 2: decoded size 1 + data::put(decoded, state, static_cast<uint8_t>((idx[0] << 2) + ((idx[1] & 0x30) >> 4))); + if (idx_len == 2) { + return; + } + + // idx_len == 3: decoded size 2 + data::put(decoded, state, static_cast<uint8_t>(((idx[1] & 0xF) << 4) + ((idx[2] & 0x3C) >> 2))); +} + +} // namespace detail +} // namespace cppcodec + +#endif // CPPCODEC_DETAIL_BASE64 diff --git a/external/cppcodec/detail/codec.hpp b/external/cppcodec/detail/codec.hpp new file mode 100644 index 0000000..47d9802 --- /dev/null +++ b/external/cppcodec/detail/codec.hpp @@ -0,0 +1,327 @@ +/** + * 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 diff --git a/external/cppcodec/detail/config.hpp b/external/cppcodec/detail/config.hpp new file mode 100644 index 0000000..dc66f8f --- /dev/null +++ b/external/cppcodec/detail/config.hpp @@ -0,0 +1,40 @@ +/** + * 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 + diff --git a/external/cppcodec/detail/stream_codec.hpp b/external/cppcodec/detail/stream_codec.hpp new file mode 100644 index 0000000..d4204bc --- /dev/null +++ b/external/cppcodec/detail/stream_codec.hpp @@ -0,0 +1,439 @@ +/** + * 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 diff --git a/external/cppcodec/parse_error.hpp b/external/cppcodec/parse_error.hpp new file mode 100644 index 0000000..97438ad --- /dev/null +++ b/external/cppcodec/parse_error.hpp @@ -0,0 +1,109 @@ +/** + * 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 diff --git a/include/base64_url.hpp b/include/base64_url.hpp deleted file mode 100644 index 271940e..0000000 --- a/include/base64_url.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include <cppcodec/base64_rfc4648.hpp> - -static constexpr const char base64_url_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', - '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', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' -}; - -class base64_url_impl -{ -public: - template <typename Codec> using codec_impl = cppcodec::detail::stream_codec<Codec, base64_url_impl>; - - static CPPCODEC_ALWAYS_INLINE constexpr size_t alphabet_size() { - static_assert(sizeof(base64_url_alphabet) == 64, "base64 alphabet must have 64 values"); - return sizeof(base64_url_alphabet); - } - static CPPCODEC_ALWAYS_INLINE constexpr char symbol(cppcodec::detail::alphabet_index_t idx) - { - return base64_url_alphabet[idx]; - } - static CPPCODEC_ALWAYS_INLINE constexpr char normalized_symbol(char c) { return 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 base64 encodings. - static CPPCODEC_ALWAYS_INLINE constexpr bool should_ignore(char) { return false; } -}; - -using base64_url = cppcodec::detail::codec<cppcodec::detail::base64<base64_url_impl>>;
\ No newline at end of file diff --git a/project.conf b/project.conf index 71df929..0ef12d7 100644 --- a/project.conf +++ b/project.conf @@ -16,5 +16,4 @@ sfml-graphics = "2" x11 = "1" xrandr = "1" jsoncpp = "1" -cppcodec-1 = "0" gl = ">=0" diff --git a/src/DownloadUtils.cpp b/src/DownloadUtils.cpp index e1c628c..e83bced 100644 --- a/src/DownloadUtils.cpp +++ b/src/DownloadUtils.cpp @@ -1,7 +1,7 @@ #include "../include/DownloadUtils.hpp" #include "../include/Program.hpp" #include "../include/Storage.hpp" -#include "../include/base64_url.hpp" +#include "../external/cppcodec/base64_url.hpp" #include <SFML/System/Clock.hpp> #include <rapidjson/document.h> #include <rapidjson/filereadstream.h> @@ -55,7 +55,7 @@ namespace QuickMedia { Path media_file_path; if(cache_path.data.empty()) { media_dir = get_cache_dir().join("media"); - media_file_path = Path(media_dir).join(base64_url::encode(url)); + media_file_path = Path(media_dir).join(cppcodec::base64_url::encode(url)); } else { media_dir = cache_path.parent(); media_file_path = std::move(cache_path); diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index e49cb74..fad2b94 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -22,7 +22,7 @@ #include "../include/Notification.hpp" #include "../include/ImageViewer.hpp" #include "../include/ImageUtils.hpp" -#include "../include/base64_url.hpp" +#include "../external/cppcodec/base64_url.hpp" #include "../include/Entry.hpp" #include "../include/NetUtils.hpp" #include "../include/SfmlFixes.hpp" @@ -863,11 +863,11 @@ namespace QuickMedia { } static std::string base64_encode(const std::string &data) { - return base64_url::encode(data); + return cppcodec::base64_url::encode(data); } static std::string base64_decode(const std::string &data) { - return base64_url::decode<std::string>(data); + return cppcodec::base64_url::decode<std::string>(data); } enum class SearchSuggestionTab { diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 9568244..e1f9b58 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -4,7 +4,7 @@ #include "../../include/NetUtils.hpp" #include "../../include/Notification.hpp" #include "../../include/Program.hpp" -#include "../../include/base64_url.hpp" +#include "../../external/cppcodec/base64_url.hpp" #include "../../include/Json.hpp" #include "../../include/AsyncImageLoader.hpp" #include "../../include/Utils.hpp" @@ -3103,7 +3103,7 @@ namespace QuickMedia { } return true; - }, get_cache_dir().join("matrix").join("events").join(base64_url::encode(event_id))); + }, get_cache_dir().join("matrix").join("events").join(cppcodec::base64_url::encode(event_id))); if(download_result != DownloadResult::OK) { fprintf(stderr, "Failed to get message by id %s, error: %s\n", event_id.c_str(), response.c_str()); room->fetched_messages_by_event_id.insert(std::make_pair(event_id, nullptr)); |