diff --git a/.gitignore b/.gitignore index 8448ec26..58f0e817 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ !config.json !*.py !debian/* +!.idea/* build .usages_clang diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 00000000..c2470e11 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +juci \ No newline at end of file diff --git a/.idea/jucipp.iml b/.idea/jucipp.iml new file mode 100644 index 00000000..f08604bb --- /dev/null +++ b/.idea/jucipp.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..79b3c948 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..b168f94e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..0388d1c5 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/3rd_party/json.h b/3rd_party/json.h new file mode 100644 index 00000000..00537155 --- /dev/null +++ b/3rd_party/json.h @@ -0,0 +1,6910 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.1.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2018 Niels Lohmann . + +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. +*/ + +#pragma once + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 1 +#define NLOHMANN_JSON_VERSION_PATCH 2 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NLOHMANN_JSON_FWD_HPP +#define NLOHMANN_JSON_FWD_HPP + +#include +#include +#include +#include + +namespace nlohmann { + template + struct adl_serializer; + + template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer> + class basic_json; + + template + class json_pointer; + + using json = basic_json<>; +} + +#endif + +#if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version" + #endif +#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000+__GNUC_MINOR__ * 100+__GNUC_PATCHLEVEL__) < 40900 + #error "unsupported GCC version" + #endif +#endif + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#if defined(__clang__) +#pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) +#define JSON_THROW(exception) throw exception +#define JSON_TRY try +#define JSON_CATCH(exception) catch(exception) +#else +#define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) +#endif + +#if defined(JSON_THROW_USER) + +#undef JSON_THROW +#define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + +#undef JSON_TRY +#define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + +#undef JSON_CATCH +#define JSON_CATCH JSON_CATCH_USER +#endif + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#define JSON_LIKELY(x) __builtin_expect(!!(x), 1) +#define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define JSON_LIKELY(x) x +#define JSON_UNLIKELY(x) x +#endif + +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) + +#define JSON_HAS_CPP_17 +#define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) +#define JSON_HAS_CPP_14 +#endif + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +#define NLOHMANN_JSON_HAS_HELPER(type) \ + template struct has_##type { \ + private: \ + template \ + static int detect(U &&); \ + static void detect(...); \ + public: \ + static constexpr bool value = \ + std::is_integral()))>::value; \ + } + +#include +#include + +namespace nlohmann { + namespace detail { + template + struct is_basic_json : std::false_type { + }; + + NLOHMANN_BASIC_JSON_TPL_DECLARATION + struct is_basic_json : std::true_type { + }; + + template + using enable_if_t = typename std::enable_if::type; + + template + using uncvref_t = typename std::remove_cv::type>::type; + + template + struct index_sequence { + using type = index_sequence; + using value_type = std::size_t; + + static constexpr std::size_t size() noexcept { + return sizeof...(Ints); + } + }; + + template + struct merge_and_renumber; + + template + struct merge_and_renumber, index_sequence> + : index_sequence { + }; + + template + struct make_index_sequence + : merge_and_renumber::type, + typename make_index_sequence::type> { + }; + + template<> + struct make_index_sequence<0> : index_sequence<> { + }; + template<> + struct make_index_sequence<1> : index_sequence<0> { + }; + + template + using index_sequence_for = make_index_sequence; + + template + struct conjunction : std::true_type { + }; + template + struct conjunction : B1 { + }; + template + struct conjunction : std::conditional, B1>::type { + }; + + template + struct negation : std::integral_constant { + }; + + template + struct priority_tag : priority_tag { + }; + template<> + struct priority_tag<0> { + }; + + template + struct is_complete_type : std::false_type { + }; + + template + struct is_complete_type : std::true_type { + }; + + NLOHMANN_JSON_HAS_HELPER(mapped_type); + + NLOHMANN_JSON_HAS_HELPER(key_type); + + NLOHMANN_JSON_HAS_HELPER(value_type); + + NLOHMANN_JSON_HAS_HELPER(iterator); + + template + struct is_compatible_object_type_impl : std::false_type { + }; + + template + struct is_compatible_object_type_impl { + static constexpr auto value = + std::is_constructible::value and + std::is_constructible::value; + }; + + template + struct is_compatible_object_type { + static auto constexpr value = is_compatible_object_type_impl< + conjunction>, + has_mapped_type, + has_key_type>::value, + typename BasicJsonType::object_t, CompatibleObjectType>::value; + }; + + template + struct is_basic_json_nested_type { + static auto constexpr value = std::is_same::value or + std::is_same::value or + std::is_same::value or + std::is_same::value; + }; + + template + struct is_compatible_array_type { + static auto constexpr value = + conjunction>, + negation>, + negation>, + negation>, + has_value_type, + has_iterator>::value; + }; + + template + struct is_compatible_integer_type_impl : std::false_type { + }; + + template + struct is_compatible_integer_type_impl { + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + std::is_constructible::value and + CompatibleLimits::is_integer and + RealLimits::is_signed == CompatibleLimits::is_signed; + }; + + template + struct is_compatible_integer_type { + static constexpr auto value = + is_compatible_integer_type_impl< + std::is_integral::value and + not std::is_same::value, + RealIntegerType, CompatibleNumberIntegerType>::value; + }; + + template + struct has_from_json { + private: + template::from_json( + std::declval(), std::declval()))>::value>> + static int detect(U &&); + + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; + }; + + template + struct has_non_default_from_json { + private: + template< + typename U, + typename = enable_if_t::from_json(std::declval()))>::value >> + static int detect(U &&); + + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; + }; + + template + struct has_to_json { + private: + template::to_json( + std::declval(), std::declval()))> + static int detect(U &&); + + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; + }; + + template + struct is_compatible_complete_type { + static constexpr bool value = + not std::is_base_of::value and + not is_basic_json::value and + not is_basic_json_nested_type::value and + has_to_json::value; + }; + + template + struct is_compatible_type + : conjunction, + is_compatible_complete_type> { + }; + + template + struct static_const { + static constexpr T value{}; + }; + + template + constexpr T static_const::value; + } +} + +#include +#include + +namespace nlohmann { + namespace detail { + class exception : public std::exception { + public: + const char *what() const noexcept override { + return m.what(); + } + + const int id; + + protected: + exception(int id_, const char *what_arg) : id(id_), m(what_arg) {} + + static std::string name(const std::string &ename, int id_) { + return "[json.exception."+ename+"."+std::to_string(id_)+"] "; + } + + private: + std::runtime_error m; + }; + + class parse_error : public exception { + public: + static parse_error create(int id_, std::size_t byte_, const std::string &what_arg) { + std::string w = exception::name("parse_error", id_)+"parse error"+ + (byte_ != 0 ? (" at "+std::to_string(byte_)) : "")+ + ": "+what_arg; + return parse_error(id_, byte_, w.c_str()); + } + + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char *what_arg) + : exception(id_, what_arg), byte(byte_) {} + }; + + class invalid_iterator : public exception { + public: + static invalid_iterator create(int id_, const std::string &what_arg) { + std::string w = exception::name("invalid_iterator", id_)+what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + invalid_iterator(int id_, const char *what_arg) + : exception(id_, what_arg) {} + }; + + class type_error : public exception { + public: + static type_error create(int id_, const std::string &what_arg) { + std::string w = exception::name("type_error", id_)+what_arg; + return type_error(id_, w.c_str()); + } + + private: + type_error(int id_, const char *what_arg) : exception(id_, what_arg) {} + }; + + class out_of_range : public exception { + public: + static out_of_range create(int id_, const std::string &what_arg) { + std::string w = exception::name("out_of_range", id_)+what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + out_of_range(int id_, const char *what_arg) : exception(id_, what_arg) {} + }; + + class other_error : public exception { + public: + static other_error create(int id_, const std::string &what_arg) { + std::string w = exception::name("other_error", id_)+what_arg; + return other_error(id_, w.c_str()); + } + + private: + other_error(int id_, const char *what_arg) : exception(id_, what_arg) {} + }; + } +} + +#include + +namespace nlohmann { + namespace detail { + enum class value_t : std::uint8_t { + null, + object, + array, + string, + boolean, + number_integer, + number_unsigned, + number_float, + discarded + }; + + inline bool operator<(const value_t lhs, const value_t rhs) noexcept { + static constexpr std::array order = {{ + 0, 3, 4, 5, + 1, 2, 2, 2 + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index]; + } + } +} + +#include +#include +#include + +namespace nlohmann { + namespace detail { + template::value and + not std::is_same::value, + int> = 0> + void get_arithmetic_value(const BasicJsonType &j, ArithmeticType &val) { + switch(static_cast(j)) { + case value_t::number_unsigned: { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is "+std::string(j.type_name()))); + } + } + + template + void from_json(const BasicJsonType &j, typename BasicJsonType::boolean_t &b) { + if(JSON_UNLIKELY(not j.is_boolean())) { + JSON_THROW(type_error::create(302, "type must be boolean, but is "+std::string(j.type_name()))); + } + b = *j.template get_ptr(); + } + + template + void from_json(const BasicJsonType &j, typename BasicJsonType::string_t &s) { + if(JSON_UNLIKELY(not j.is_string())) { + JSON_THROW(type_error::create(302, "type must be string, but is "+std::string(j.type_name()))); + } + s = *j.template get_ptr(); + } + + template + void from_json(const BasicJsonType &j, typename BasicJsonType::number_float_t &val) { + get_arithmetic_value(j, val); + } + + template + void from_json(const BasicJsonType &j, typename BasicJsonType::number_unsigned_t &val) { + get_arithmetic_value(j, val); + } + + template + void from_json(const BasicJsonType &j, typename BasicJsonType::number_integer_t &val) { + get_arithmetic_value(j, val); + } + + template::value, int> = 0> + void from_json(const BasicJsonType &j, EnumType &e) { + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); + } + + template + void from_json(const BasicJsonType &j, typename BasicJsonType::array_t &arr) { + if(JSON_UNLIKELY(not j.is_array())) { + JSON_THROW(type_error::create(302, "type must be array, but is "+std::string(j.type_name()))); + } + arr = *j.template get_ptr(); + } + + template::value, int> = 0> + void from_json(const BasicJsonType &j, std::forward_list &l) { + if(JSON_UNLIKELY(not j.is_array())) { + JSON_THROW(type_error::create(302, "type must be array, but is "+std::string(j.type_name()))); + } + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType &i) { + return i.template get(); + }); + } + + template::value, int> = 0> + void from_json(const BasicJsonType &j, std::valarray &l) { + if(JSON_UNLIKELY(not j.is_array())) { + JSON_THROW(type_error::create(302, "type must be array, but is "+std::string(j.type_name()))); + } + l.resize(j.size()); + std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l)); + } + + template + void from_json_array_impl(const BasicJsonType &j, CompatibleArrayType &arr, priority_tag<0>) { + using std::end; + + std::transform(j.begin(), j.end(), + std::inserter(arr, end(arr)), [](const BasicJsonType &i) { + return i.template get(); + }); + } + + template + auto from_json_array_impl(const BasicJsonType &j, CompatibleArrayType &arr, priority_tag<1>) + -> decltype( + arr.reserve(std::declval()), + void()) { + using std::end; + + arr.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(arr, end(arr)), [](const BasicJsonType &i) { + return i.template get(); + }); + } + + template + void from_json_array_impl(const BasicJsonType &j, std::array &arr, priority_tag<2>) { + for(std::size_t i = 0; i < N; ++i) { + arr[i] = j.at(i).template get(); + } + } + + template< + typename BasicJsonType, typename CompatibleArrayType, + enable_if_t< + is_compatible_array_type::value and + not std::is_same::value and + std::is_constructible< + BasicJsonType, typename CompatibleArrayType::value_type>::value, + int> = 0> + void from_json(const BasicJsonType &j, CompatibleArrayType &arr) { + if(JSON_UNLIKELY(not j.is_array())) { + JSON_THROW(type_error::create(302, "type must be array, but is "+ + std::string(j.type_name()))); + } + + from_json_array_impl(j, arr, priority_tag<2>{}); + } + + template::value, int> = 0> + void from_json(const BasicJsonType &j, CompatibleObjectType &obj) { + if(JSON_UNLIKELY(not j.is_object())) { + JSON_THROW(type_error::create(302, "type must be object, but is "+std::string(j.type_name()))); + } + + auto inner_object = j.template get_ptr(); + using value_type = typename CompatibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(obj, obj.begin()), + [](typename BasicJsonType::object_t::value_type const &p) { + return value_type(p.first, p.second.template get()); + }); + } + + template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value, + int> = 0> + void from_json(const BasicJsonType &j, ArithmeticType &val) { + switch(static_cast(j)) { + case value_t::number_unsigned: { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is "+std::string(j.type_name()))); + } + } + + template + void from_json(const BasicJsonType &j, std::pair &p) { + p = {j.at(0).template get(), j.at(1).template get()}; + } + + template + void from_json_tuple_impl(const BasicJsonType &j, Tuple &t, index_sequence) { + t = std::make_tuple(j.at(Idx).template get::type>()...); + } + + template + void from_json(const BasicJsonType &j, std::tuple &t) { + from_json_tuple_impl(j, t, index_sequence_for{}); + } + + struct from_json_fn { + private: + template + auto call(const BasicJsonType &j, T &val, priority_tag<1>) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) { + return from_json(j, val); + } + + template + void call(const BasicJsonType &, T &, priority_tag<0>) const noexcept { + static_assert(sizeof(BasicJsonType) == 0, + "could not find from_json() method in T's namespace"); +#ifdef _MSC_VER + + using decayed = uncvref_t; + static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0, + "forcing MSVC stacktrace to show which T we're talking about."); +#endif + } + + public: + template + void operator()(const BasicJsonType &j, T &val) const + noexcept(noexcept(std::declval().call(j, val, priority_tag<1>{}))) { + return call(j, val, priority_tag<1>{}); + } + }; + } + + namespace { + constexpr const auto &from_json = detail::static_const::value; + } +} + +namespace nlohmann { + namespace detail { + template + struct external_constructor; + + template<> + struct external_constructor { + template + static void construct(BasicJsonType &j, typename BasicJsonType::boolean_t b) noexcept { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } + }; + + template<> + struct external_constructor { + template + static void construct(BasicJsonType &j, const typename BasicJsonType::string_t &s) { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType &j, typename BasicJsonType::string_t &&s) { + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + }; + + template<> + struct external_constructor { + template + static void construct(BasicJsonType &j, typename BasicJsonType::number_float_t val) noexcept { + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } + }; + + template<> + struct external_constructor { + template + static void construct(BasicJsonType &j, typename BasicJsonType::number_unsigned_t val) noexcept { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } + }; + + template<> + struct external_constructor { + template + static void construct(BasicJsonType &j, typename BasicJsonType::number_integer_t val) noexcept { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } + }; + + template<> + struct external_constructor { + template + static void construct(BasicJsonType &j, const typename BasicJsonType::array_t &arr) { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType &j, typename BasicJsonType::array_t &&arr) { + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType &j, const CompatibleArrayType &arr) { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType &j, const std::vector &arr) { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for(const bool x : arr) { + j.m_value.array->push_back(x); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType &j, const std::valarray &arr) { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + j.assert_invariant(); + } + }; + + template<> + struct external_constructor { + template + static void construct(BasicJsonType &j, const typename BasicJsonType::object_t &obj) { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType &j, typename BasicJsonType::object_t &&obj) { + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType &j, const CompatibleObjectType &obj) { + using std::begin; + using std::end; + + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } + }; + + template::value, int> = 0> + void to_json(BasicJsonType &j, T b) noexcept { + external_constructor::construct(j, b); + } + + template::value, int> = 0> + void to_json(BasicJsonType &j, const CompatibleString &s) { + external_constructor::construct(j, s); + } + + template + void to_json(BasicJsonType &j, typename BasicJsonType::string_t &&s) { + external_constructor::construct(j, std::move(s)); + } + + template::value, int> = 0> + void to_json(BasicJsonType &j, FloatType val) noexcept { + external_constructor::construct(j, + static_cast(val)); + } + + template::value, int> = 0> + void to_json(BasicJsonType &j, CompatibleNumberUnsignedType val) noexcept { + external_constructor::construct(j, + static_cast(val)); + } + + template::value, int> = 0> + void to_json(BasicJsonType &j, CompatibleNumberIntegerType val) noexcept { + external_constructor::construct(j, + static_cast(val)); + } + + template::value, int> = 0> + void to_json(BasicJsonType &j, EnumType e) noexcept { + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); + } + + template + void to_json(BasicJsonType &j, const std::vector &e) { + external_constructor::construct(j, e); + } + + template::value or + std::is_same::value, + int> = 0> + void to_json(BasicJsonType &j, const CompatibleArrayType &arr) { + external_constructor::construct(j, arr); + } + + template::value, int> = 0> + void to_json(BasicJsonType &j, std::valarray arr) { + external_constructor::construct(j, std::move(arr)); + } + + template + void to_json(BasicJsonType &j, typename BasicJsonType::array_t &&arr) { + external_constructor::construct(j, std::move(arr)); + } + + template::value, int> = 0> + void to_json(BasicJsonType &j, const CompatibleObjectType &obj) { + external_constructor::construct(j, obj); + } + + template + void to_json(BasicJsonType &j, typename BasicJsonType::object_t &&obj) { + external_constructor::construct(j, std::move(obj)); + } + + template::value, int> = 0> + void to_json(BasicJsonType &j, T (&arr)[N]) { + external_constructor::construct(j, arr); + } + + template + void to_json(BasicJsonType &j, const std::pair &p) { + j = {p.first, p.second}; + } + + template + void to_json_tuple_impl(BasicJsonType &j, const Tuple &t, index_sequence) { + j = {std::get(t)...}; + } + + template + void to_json(BasicJsonType &j, const std::tuple &t) { + to_json_tuple_impl(j, t, index_sequence_for{}); + } + + struct to_json_fn { + private: + template + auto call(BasicJsonType &j, T &&val, priority_tag<1>) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) { + return to_json(j, std::forward(val)); + } + + template + void call(BasicJsonType &, T &&, priority_tag<0>) const noexcept { + static_assert(sizeof(BasicJsonType) == 0, + "could not find to_json() method in T's namespace"); + +#ifdef _MSC_VER + + using decayed = uncvref_t; + static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0, + "forcing MSVC stacktrace to show which T we're talking about."); +#endif + } + + public: + template + void operator()(BasicJsonType &j, T &&val) const + noexcept(noexcept(std::declval().call(j, std::forward(val), priority_tag<1>{}))) { + return call(j, std::forward(val), priority_tag<1>{}); + } + }; + } + + namespace { + constexpr const auto &to_json = detail::static_const::value; + } +} + +#include +#include +#include + +namespace nlohmann { + namespace detail { + struct input_adapter_protocol { + virtual std::char_traits::int_type get_character() = 0; + + virtual void unget_character() = 0; + + virtual ~input_adapter_protocol() = default; + }; + + using input_adapter_t = std::shared_ptr; + + class input_stream_adapter : public input_adapter_protocol { + public: + ~input_stream_adapter() override { + is.clear(); + } + + explicit input_stream_adapter(std::istream &i) + : is(i), sb(*i.rdbuf()) { + std::char_traits::int_type c; + if((c = get_character()) == 0xEF) { + if((c = get_character()) == 0xBB) { + if((c = get_character()) == 0xBF) { + return; + } else if(c != std::char_traits::eof()) { + is.unget(); + } + is.putback('\xBB'); + } else if(c != std::char_traits::eof()) { + is.unget(); + } + is.putback('\xEF'); + } else if(c != std::char_traits::eof()) { + is.unget(); + } + } + + input_stream_adapter(const input_stream_adapter &) = delete; + + input_stream_adapter &operator=(input_stream_adapter &) = delete; + + std::char_traits::int_type get_character() override { + return sb.sbumpc(); + } + + void unget_character() override { + sb.sungetc(); + } + + private: + std::istream &is; + std::streambuf &sb; + }; + + class input_buffer_adapter : public input_adapter_protocol { + public: + input_buffer_adapter(const char *b, const std::size_t l) + : cursor(b), limit(b+l), start(b) { + if(l >= 3 and b[0] == '\xEF' and b[1] == '\xBB' and b[2] == '\xBF') { + cursor += 3; + } + } + + input_buffer_adapter(const input_buffer_adapter &) = delete; + + input_buffer_adapter &operator=(input_buffer_adapter &) = delete; + + std::char_traits::int_type get_character() noexcept override { + if(JSON_LIKELY(cursor < limit)) { + return std::char_traits::to_int_type(*(cursor++)); + } + + return std::char_traits::eof(); + } + + void unget_character() noexcept override { + if(JSON_LIKELY(cursor > start)) { + --cursor; + } + } + + private: + const char *cursor; + + const char *limit; + + const char *start; + }; + + class input_adapter { + public: + input_adapter(std::istream &i) + : ia(std::make_shared(i)) {} + + input_adapter(std::istream &&i) + : ia(std::make_shared(i)) {} + + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b, std::size_t l) + : ia(std::make_shared(reinterpret_cast(b), l)) {} + + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b) + : input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))) {} + + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + input_adapter(IteratorType first, IteratorType last) { + assert(std::accumulate( + first, last, std::pair(true, 0), + [&first](std::pair res, decltype(*first) val) { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); + + static_assert( + sizeof(typename std::iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + const auto len = static_cast(std::distance(first, last)); + if(JSON_LIKELY(len > 0)) { + ia = std::make_shared(reinterpret_cast(&(*first)), len); + } else { + ia = std::make_shared(nullptr, len); + } + } + + template + input_adapter(T (&array)[N]) + : input_adapter(std::begin(array), std::end(array)) {} + + template::value and + std::is_base_of()))>::iterator_category>::value, + int>::type = 0> + input_adapter(const ContiguousContainer &c) + : input_adapter(std::begin(c), std::end(c)) {} + + operator input_adapter_t() { + return ia; + } + + private: + input_adapter_t ia = nullptr; + }; + } +} + +#include +#include +#include +#include + +namespace nlohmann { + namespace detail { + template + class lexer { + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + public: + enum class token_type { + uninitialized, + literal_true, + literal_false, + literal_null, + value_string, + value_unsigned, + value_integer, + value_float, + begin_array, + begin_object, + end_array, + end_object, + name_separator, + value_separator, + parse_error, + end_of_input, + literal_or_value + }; + + static const char *token_type_name(const token_type t) noexcept { + switch(t) { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + default: + return "unknown token"; + } + } + + explicit lexer(detail::input_adapter_t adapter) + : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} + + lexer(const lexer &) = delete; + + lexer &operator=(lexer &) = delete; + + private: + static char get_decimal_point() noexcept { + const auto loc = localeconv(); + assert(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + int get_codepoint() { + assert(current == 'u'); + int codepoint = 0; + + const auto factors = {12, 8, 4, 0}; + for(const auto factor : factors) { + get(); + + if(current >= '0' and current <= '9') { + codepoint += ((current-0x30) << factor); + } else if(current >= 'A' and current <= 'F') { + codepoint += ((current-0x37) << factor); + } else if(current >= 'a' and current <= 'f') { + codepoint += ((current-0x57) << factor); + } else { + return -1; + } + } + + assert(0x0000 <= codepoint and codepoint <= 0xFFFF); + return codepoint; + } + + bool next_byte_in_range(std::initializer_list ranges) { + assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); + add(current); + + for(auto range = ranges.begin(); range != ranges.end(); ++range) { + get(); + if(JSON_LIKELY(*range <= current and current <= *(++range))) { + add(current); + } else { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + token_type scan_string() { + reset(); + + assert(current == '\"'); + + while(true) { + switch(get()) { + case std::char_traits::eof(): { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + case '\"': { + return token_type::value_string; + } + + case '\\': { + switch(get()) { + case '\"': + add('\"'); + break; + + case '\\': + add('\\'); + break; + + case '/': + add('/'); + break; + + case 'b': + add('\b'); + break; + + case 'f': + add('\f'); + break; + + case 'n': + add('\n'); + break; + + case 'r': + add('\r'); + break; + + case 't': + add('\t'); + break; + + case 'u': { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; + + if(JSON_UNLIKELY(codepoint1 == -1)) { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + if(0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) { + if(JSON_LIKELY(get() == '\\' and get() == 'u')) { + const int codepoint2 = get_codepoint(); + + if(JSON_UNLIKELY(codepoint2 == -1)) { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + if(JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) { + codepoint = + + (codepoint1 << 10) + + +codepoint2 + + -0x35FDC00; + } else { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } else { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } else { + if(JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + + if(codepoint < 0x80) { + add(codepoint); + } else if(codepoint <= 0x7FF) { + add(0xC0 | (codepoint >> 6)); + add(0x80 | (codepoint & 0x3F)); + } else if(codepoint <= 0xFFFF) { + add(0xE0 | (codepoint >> 12)); + add(0x80 | ((codepoint >> 6) & 0x3F)); + add(0x80 | (codepoint & 0x3F)); + } else { + add(0xF0 | (codepoint >> 18)); + add(0x80 | ((codepoint >> 12) & 0x3F)); + add(0x80 | ((codepoint >> 6) & 0x3F)); + add(0x80 | (codepoint & 0x3F)); + } + + break; + } + + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: { + error_message = "invalid string: control character must be escaped"; + return token_type::parse_error; + } + + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: { + add(current); + break; + } + + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: { + if(JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) { + return token_type::parse_error; + } + break; + } + + case 0xE0: { + if(JSON_UNLIKELY(not(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) { + return token_type::parse_error; + } + break; + } + + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: { + if(JSON_UNLIKELY(not(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) { + return token_type::parse_error; + } + break; + } + + case 0xED: { + if(JSON_UNLIKELY(not(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) { + return token_type::parse_error; + } + break; + } + + case 0xF0: { + if(JSON_UNLIKELY(not(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { + return token_type::parse_error; + } + break; + } + + case 0xF1: + case 0xF2: + case 0xF3: { + if(JSON_UNLIKELY(not(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { + return token_type::parse_error; + } + break; + } + + case 0xF4: { + if(JSON_UNLIKELY(not(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) { + return token_type::parse_error; + } + break; + } + + default: { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + static void strtof(float &f, const char *str, char **endptr) noexcept { + f = std::strtof(str, endptr); + } + + static void strtof(double &f, const char *str, char **endptr) noexcept { + f = std::strtod(str, endptr); + } + + static void strtof(long double &f, const char *str, char **endptr) noexcept { + f = std::strtold(str, endptr); + } + + token_type scan_number() { + reset(); + + token_type number_type = token_type::value_unsigned; + + switch(current) { + case '-': { + add(current); + goto scan_number_minus; + } + + case '0': { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + add(current); + goto scan_number_any1; + } + + default: { + assert(false); + } + } + + scan_number_minus: + + number_type = token_type::value_integer; + switch(get()) { + case '0': { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + add(current); + goto scan_number_any1; + } + + default: { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + + scan_number_zero: + + switch(get()) { + case '.': { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + + scan_number_any1: + + switch(get()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + add(current); + goto scan_number_any1; + } + + case '.': { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + + scan_number_decimal1: + + number_type = token_type::value_float; + switch(get()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + add(current); + goto scan_number_decimal2; + } + + default: { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + + scan_number_decimal2: + + switch(get()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + + scan_number_exponent: + + number_type = token_type::value_float; + switch(get()) { + case '+': + case '-': { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + add(current); + goto scan_number_any2; + } + + default: { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + + scan_number_sign: + + switch(get()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + add(current); + goto scan_number_any2; + } + + default: { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + + scan_number_any2: + + switch(get()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + + scan_number_done: + + unget(); + + char *endptr = nullptr; + errno = 0; + + if(number_type == token_type::value_unsigned) { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + assert(endptr == token_buffer.data()+token_buffer.size()); + + if(errno == 0) { + value_unsigned = static_cast(x); + if(value_unsigned == x) { + return token_type::value_unsigned; + } + } + } else if(number_type == token_type::value_integer) { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + assert(endptr == token_buffer.data()+token_buffer.size()); + + if(errno == 0) { + value_integer = static_cast(x); + if(value_integer == x) { + return token_type::value_integer; + } + } + } + + strtof(value_float, token_buffer.data(), &endptr); + + assert(endptr == token_buffer.data()+token_buffer.size()); + + return token_type::value_float; + } + + token_type scan_literal(const char *literal_text, const std::size_t length, + token_type return_type) { + assert(current == literal_text[0]); + for(std::size_t i = 1; i < length; ++i) { + if(JSON_UNLIKELY(get() != literal_text[i])) { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + void reset() noexcept { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + std::char_traits::int_type get() { + ++chars_read; + current = ia->get_character(); + if(JSON_LIKELY(current != std::char_traits::eof())) { + token_string.push_back(std::char_traits::to_char_type(current)); + } + return current; + } + + void unget() { + --chars_read; + if(JSON_LIKELY(current != std::char_traits::eof())) { + ia->unget_character(); + assert(token_string.size() != 0); + token_string.pop_back(); + } + } + + void add(int c) { + token_buffer.push_back(std::char_traits::to_char_type(c)); + } + + public: + constexpr number_integer_t get_number_integer() const noexcept { + return value_integer; + } + + constexpr number_unsigned_t get_number_unsigned() const noexcept { + return value_unsigned; + } + + constexpr number_float_t get_number_float() const noexcept { + return value_float; + } + + string_t &&move_string() { + return std::move(token_buffer); + } + + constexpr std::size_t get_position() const noexcept { + return chars_read; + } + + std::string get_token_string() const { + std::string result; + for(const auto c : token_string) { + if('\x00' <= c and c <= '\x1F') { + std::stringstream ss; + ss << "(c) << ">"; + result += ss.str(); + } else { + result.push_back(c); + } + } + + return result; + } + + constexpr const char *get_error_message() const noexcept { + return error_message; + } + + token_type scan() { + do { + get(); + } while(current == ' ' or current == '\t' or current == '\n' or current == '\r'); + + switch(current) { + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + case 't': + return scan_literal("true", 4, token_type::literal_true); + case 'f': + return scan_literal("false", 5, token_type::literal_false); + case 'n': + return scan_literal("null", 4, token_type::literal_null); + + case '\"': + return scan_string(); + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + detail::input_adapter_t ia = nullptr; + + std::char_traits::int_type current = std::char_traits::eof(); + + std::size_t chars_read = 0; + + std::vector token_string{}; + + string_t token_buffer{}; + + const char *error_message = ""; + + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + const char decimal_point_char = '.'; + }; + } +} + +#include + +namespace nlohmann { + namespace detail { + template + class parser { + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + enum class parse_event_t : uint8_t { + object_start, + + object_end, + + array_start, + + array_end, + + key, + + value + }; + + using parser_callback_t = + std::function; + + explicit parser(detail::input_adapter_t adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true) + : callback(cb), m_lexer(adapter), allow_exceptions(allow_exceptions_) {} + + void parse(const bool strict, BasicJsonType &result) { + get_token(); + + parse_internal(true, result); + result.assert_invariant(); + + if(strict) { + get_token(); + expect(token_type::end_of_input); + } + + if(errored) { + result = value_t::discarded; + return; + } + + if(result.is_discarded()) { + result = nullptr; + } + } + + bool accept(const bool strict = true) { + get_token(); + + if(not accept_internal()) { + return false; + } + + return not strict or (get_token() == token_type::end_of_input); + } + + private: + void parse_internal(bool keep, BasicJsonType &result) { + assert(not errored); + + if(not result.is_discarded()) { + result.m_value.destroy(result.m_type); + result.m_type = value_t::discarded; + } + + switch(last_token) { + case token_type::begin_object: { + if(keep) { + if(callback) { + keep = callback(depth++, parse_event_t::object_start, result); + } + + if(not callback or keep) { + result.m_type = value_t::object; + result.m_value = value_t::object; + } + } + + get_token(); + + if(last_token == token_type::end_object) { + if(keep and callback and not callback(--depth, parse_event_t::object_end, result)) { + result.m_value.destroy(result.m_type); + result.m_type = value_t::discarded; + } + break; + } + + string_t key; + BasicJsonType value; + while(true) { + if(not expect(token_type::value_string)) { + return; + } + key = m_lexer.move_string(); + + bool keep_tag = false; + if(keep) { + if(callback) { + BasicJsonType k(key); + keep_tag = callback(depth, parse_event_t::key, k); + } else { + keep_tag = true; + } + } + + get_token(); + if(not expect(token_type::name_separator)) { + return; + } + + get_token(); + value.m_value.destroy(value.m_type); + value.m_type = value_t::discarded; + parse_internal(keep, value); + + if(JSON_UNLIKELY(errored)) { + return; + } + + if(keep and keep_tag and not value.is_discarded()) { + result.m_value.object->emplace(std::move(key), std::move(value)); + } + + get_token(); + if(last_token == token_type::value_separator) { + get_token(); + continue; + } + + if(not expect(token_type::end_object)) { + return; + } + break; + } + + if(keep and callback and not callback(--depth, parse_event_t::object_end, result)) { + result.m_value.destroy(result.m_type); + result.m_type = value_t::discarded; + } + break; + } + + case token_type::begin_array: { + if(keep) { + if(callback) { + keep = callback(depth++, parse_event_t::array_start, result); + } + + if(not callback or keep) { + result.m_type = value_t::array; + result.m_value = value_t::array; + } + } + + get_token(); + + if(last_token == token_type::end_array) { + if(callback and not callback(--depth, parse_event_t::array_end, result)) { + result.m_value.destroy(result.m_type); + result.m_type = value_t::discarded; + } + break; + } + + BasicJsonType value; + while(true) { + value.m_value.destroy(value.m_type); + value.m_type = value_t::discarded; + parse_internal(keep, value); + + if(JSON_UNLIKELY(errored)) { + return; + } + + if(keep and not value.is_discarded()) { + result.m_value.array->push_back(std::move(value)); + } + + get_token(); + if(last_token == token_type::value_separator) { + get_token(); + continue; + } + + if(not expect(token_type::end_array)) { + return; + } + break; + } + + if(keep and callback and not callback(--depth, parse_event_t::array_end, result)) { + result.m_value.destroy(result.m_type); + result.m_type = value_t::discarded; + } + break; + } + + case token_type::literal_null: { + result.m_type = value_t::null; + break; + } + + case token_type::value_string: { + result.m_type = value_t::string; + result.m_value = m_lexer.move_string(); + break; + } + + case token_type::literal_true: { + result.m_type = value_t::boolean; + result.m_value = true; + break; + } + + case token_type::literal_false: { + result.m_type = value_t::boolean; + result.m_value = false; + break; + } + + case token_type::value_unsigned: { + result.m_type = value_t::number_unsigned; + result.m_value = m_lexer.get_number_unsigned(); + break; + } + + case token_type::value_integer: { + result.m_type = value_t::number_integer; + result.m_value = m_lexer.get_number_integer(); + break; + } + + case token_type::value_float: { + result.m_type = value_t::number_float; + result.m_value = m_lexer.get_number_float(); + + if(JSON_UNLIKELY(not std::isfinite(result.m_value.number_float))) { + if(allow_exceptions) { + JSON_THROW(out_of_range::create(406, "number overflow parsing '"+ + m_lexer.get_token_string()+"'")); + } + expect(token_type::uninitialized); + } + break; + } + + case token_type::parse_error: { + if(not expect(token_type::uninitialized)) { + return; + } + break; + } + + default: { + if(not expect(token_type::literal_or_value)) { + return; + } + break; + } + } + + if(keep and callback and not callback(depth, parse_event_t::value, result)) { + result.m_value.destroy(result.m_type); + result.m_type = value_t::discarded; + } + } + + bool accept_internal() { + switch(last_token) { + case token_type::begin_object: { + get_token(); + + if(last_token == token_type::end_object) { + return true; + } + + while(true) { + if(last_token != token_type::value_string) { + return false; + } + + get_token(); + if(last_token != token_type::name_separator) { + return false; + } + + get_token(); + if(not accept_internal()) { + return false; + } + + get_token(); + if(last_token == token_type::value_separator) { + get_token(); + continue; + } + + return (last_token == token_type::end_object); + } + } + + case token_type::begin_array: { + get_token(); + + if(last_token == token_type::end_array) { + return true; + } + + while(true) { + if(not accept_internal()) { + return false; + } + + get_token(); + if(last_token == token_type::value_separator) { + get_token(); + continue; + } + + return (last_token == token_type::end_array); + } + } + + case token_type::value_float: { + return std::isfinite(m_lexer.get_number_float()); + } + + case token_type::literal_false: + case token_type::literal_null: + case token_type::literal_true: + case token_type::value_integer: + case token_type::value_string: + case token_type::value_unsigned: + return true; + + default: + return false; + } + } + + token_type get_token() { + return (last_token = m_lexer.scan()); + } + + bool expect(token_type t) { + if(JSON_UNLIKELY(t != last_token)) { + errored = true; + expected = t; + if(allow_exceptions) { + throw_exception(); + } else { + return false; + } + } + + return true; + } + + [[noreturn]] void throw_exception() const { + std::string error_msg = "syntax error - "; + if(last_token == token_type::parse_error) { + error_msg += std::string(m_lexer.get_error_message())+"; last read: '"+ + m_lexer.get_token_string()+"'"; + } else { + error_msg += "unexpected "+std::string(lexer_t::token_type_name(last_token)); + } + + if(expected != token_type::uninitialized) { + error_msg += "; expected "+std::string(lexer_t::token_type_name(expected)); + } + + JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg)); + } + + private: + int depth = 0; + + const parser_callback_t callback = nullptr; + + token_type last_token = token_type::uninitialized; + + lexer_t m_lexer; + + bool errored = false; + + token_type expected = token_type::uninitialized; + + const bool allow_exceptions = true; + }; + } +} + +namespace nlohmann { + namespace detail { + class primitive_iterator_t { + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value+1; + + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept { + return m_it; + } + + void set_begin() noexcept { + m_it = begin_value; + } + + void set_end() noexcept { + m_it = end_value; + } + + constexpr bool is_begin() const noexcept { + return m_it == begin_value; + } + + constexpr bool is_end() const noexcept { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { + return lhs.m_it-rhs.m_it; + } + + primitive_iterator_t &operator++() noexcept { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept { + auto result = *this; + m_it++; + return result; + } + + primitive_iterator_t &operator--() noexcept { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept { + auto result = *this; + m_it--; + return result; + } + + primitive_iterator_t &operator+=(difference_type n) noexcept { + m_it += n; + return *this; + } + + primitive_iterator_t &operator-=(difference_type n) noexcept { + m_it -= n; + return *this; + } + }; + } +} + +namespace nlohmann { + namespace detail { + template + struct internal_iterator { + typename BasicJsonType::object_t::iterator object_iterator{}; + + typename BasicJsonType::array_t::iterator array_iterator{}; + + primitive_iterator_t primitive_iterator{}; + }; + } +} + +namespace nlohmann { + namespace detail { + template + class iteration_proxy; + + template + class iter_impl { + friend iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + friend BasicJsonType; + friend iteration_proxy; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + using iterator_category = std::bidirectional_iterator_tag; + + using value_type = typename BasicJsonType::value_type; + + using difference_type = typename BasicJsonType::difference_type; + + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + iter_impl() = default; + + explicit iter_impl(pointer object) noexcept : m_object(object) { + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + default: { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + iter_impl(const iter_impl::type> &other) noexcept + : m_object(other.m_object), m_it(other.m_it) {} + + iter_impl &operator=(const iter_impl::type> &other) noexcept { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + private: + void set_begin() noexcept { + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: { + m_it.primitive_iterator.set_end(); + break; + } + + default: { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + void set_end() noexcept { + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + reference operator*() const { + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: { + if(JSON_LIKELY(m_it.primitive_iterator.is_begin())) { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + pointer operator->() const { + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: { + if(JSON_LIKELY(m_it.primitive_iterator.is_begin())) { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + iter_impl const operator++(int) { + auto result = *this; + ++(*this); + return result; + } + + iter_impl &operator++() { + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: { + std::advance(m_it.array_iterator, 1); + break; + } + + default: { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + iter_impl const operator--(int) { + auto result = *this; + --(*this); + return result; + } + + iter_impl &operator--() { + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: { + std::advance(m_it.array_iterator, -1); + break; + } + + default: { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + bool operator==(const iter_impl &other) const { + if(JSON_UNLIKELY(m_object != other.m_object)) { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + bool operator!=(const iter_impl &other) const { + return not operator==(other); + } + + bool operator<(const iter_impl &other) const { + if(JSON_UNLIKELY(m_object != other.m_object)) { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + bool operator<=(const iter_impl &other) const { + return not other.operator<(*this); + } + + bool operator>(const iter_impl &other) const { + return not operator<=(other); + } + + bool operator>=(const iter_impl &other) const { + return not operator<(other); + } + + iter_impl &operator+=(difference_type i) { + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: { + std::advance(m_it.array_iterator, i); + break; + } + + default: { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + iter_impl &operator-=(difference_type i) { + return operator+=(-i); + } + + iter_impl operator+(difference_type i) const { + auto result = *this; + result += i; + return result; + } + + friend iter_impl operator+(difference_type i, const iter_impl &it) { + auto result = it; + result += i; + return result; + } + + iter_impl operator-(difference_type i) const { + auto result = *this; + result -= i; + return result; + } + + difference_type operator-(const iter_impl &other) const { + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + return m_it.array_iterator-other.m_it.array_iterator; + + default: + return m_it.primitive_iterator-other.m_it.primitive_iterator; + } + } + + reference operator[](difference_type n) const { + assert(m_object != nullptr); + + switch(m_object->m_type) { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: { + if(JSON_LIKELY(m_it.primitive_iterator.get_value() == -n)) { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + typename object_t::key_type key() const { + assert(m_object != nullptr); + + if(JSON_LIKELY(m_object->is_object())) { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + } + + reference value() const { + return operator*(); + } + + private: + pointer m_object = nullptr; + + internal_iterator::type> m_it; + }; + } +} + +namespace nlohmann { + namespace detail { + template + class iteration_proxy { + private: + class iteration_proxy_internal { + private: + IteratorType anchor; + + std::size_t array_index = 0; + + public: + explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {} + + iteration_proxy_internal &operator*() { + return *this; + } + + iteration_proxy_internal &operator++() { + ++anchor; + ++array_index; + + return *this; + } + + bool operator!=(const iteration_proxy_internal &o) const noexcept { + return anchor != o.anchor; + } + + std::string key() const { + assert(anchor.m_object != nullptr); + + switch(anchor.m_object->type()) { + case value_t::array: + return std::to_string(array_index); + + case value_t::object: + return anchor.key(); + + default: + return ""; + } + } + + typename IteratorType::reference value() const { + return anchor.value(); + } + }; + + typename IteratorType::reference container; + + public: + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + iteration_proxy_internal begin() noexcept { + return iteration_proxy_internal(container.begin()); + } + + iteration_proxy_internal end() noexcept { + return iteration_proxy_internal(container.end()); + } + }; + } +} + +namespace nlohmann { + namespace detail { + template + class json_reverse_iterator : public std::reverse_iterator { + public: + using difference_type = std::ptrdiff_t; + + using base_iterator = std::reverse_iterator; + + using reference = typename Base::reference; + + json_reverse_iterator(const typename base_iterator::iterator_type &it) noexcept + : base_iterator(it) {} + + json_reverse_iterator(const base_iterator &it) noexcept : base_iterator(it) {} + + json_reverse_iterator const operator++(int) { + return static_cast(base_iterator::operator++(1)); + } + + json_reverse_iterator &operator++() { + return static_cast(base_iterator::operator++()); + } + + json_reverse_iterator const operator--(int) { + return static_cast(base_iterator::operator--(1)); + } + + json_reverse_iterator &operator--() { + return static_cast(base_iterator::operator--()); + } + + json_reverse_iterator &operator+=(difference_type i) { + return static_cast(base_iterator::operator+=(i)); + } + + json_reverse_iterator operator+(difference_type i) const { + return static_cast(base_iterator::operator+(i)); + } + + json_reverse_iterator operator-(difference_type i) const { + return static_cast(base_iterator::operator-(i)); + } + + difference_type operator-(const json_reverse_iterator &other) const { + return base_iterator(*this)-base_iterator(other); + } + + reference operator[](difference_type n) const { + return *(this->operator+(n)); + } + + auto key() const -> decltype(std::declval().key()) { + auto it = --this->base(); + return it.key(); + } + + reference value() const { + auto it = --this->base(); + return it.operator*(); + } + }; + } +} + +#include + +namespace nlohmann { + namespace detail { + template + struct output_adapter_protocol { + virtual void write_character(CharType c) = 0; + + virtual void write_characters(const CharType *s, std::size_t length) = 0; + + virtual ~output_adapter_protocol() = default; + }; + + template + using output_adapter_t = std::shared_ptr>; + + template + class output_vector_adapter : public output_adapter_protocol { + public: + explicit output_vector_adapter(std::vector &vec) : v(vec) {} + + void write_character(CharType c) override { + v.push_back(c); + } + + void write_characters(const CharType *s, std::size_t length) override { + std::copy(s, s+length, std::back_inserter(v)); + } + + private: + std::vector &v; + }; + + template + class output_stream_adapter : public output_adapter_protocol { + public: + explicit output_stream_adapter(std::basic_ostream &s) : stream(s) {} + + void write_character(CharType c) override { + stream.put(c); + } + + void write_characters(const CharType *s, std::size_t length) override { + stream.write(s, static_cast(length)); + } + + private: + std::basic_ostream &stream; + }; + + template> + class output_string_adapter : public output_adapter_protocol { + public: + explicit output_string_adapter(StringType &s) : str(s) {} + + void write_character(CharType c) override { + str.push_back(c); + } + + void write_characters(const CharType *s, std::size_t length) override { + str.append(s, length); + } + + private: + StringType &str; + }; + + template> + class output_adapter { + public: + output_adapter(std::vector &vec) + : oa(std::make_shared>(vec)) {} + + output_adapter(std::basic_ostream &s) + : oa(std::make_shared>(s)) {} + + output_adapter(StringType &s) + : oa(std::make_shared>(s)) {} + + operator output_adapter_t() { + return oa; + } + + private: + output_adapter_t oa = nullptr; + }; + } +} + +#include + +namespace nlohmann { + namespace detail { + namespace dtoa_impl { + template + Target reinterpret_bits(const Source source) { + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; + } + + struct diyfp { + static constexpr int kPrecision = 64; + + uint64_t f; + int e; + + constexpr diyfp() noexcept : f(0), e(0) {} + + constexpr diyfp(uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + static diyfp sub(const diyfp &x, const diyfp &y) noexcept { + assert(x.e == y.e); + assert(x.f >= y.f); + + return diyfp(x.f-y.f, x.e); + } + + static diyfp mul(const diyfp &x, const diyfp &y) noexcept { + static_assert(kPrecision == 64, "internal error"); + + const uint64_t u_lo = x.f & 0xFFFFFFFF; + const uint64_t u_hi = x.f >> 32; + const uint64_t v_lo = y.f & 0xFFFFFFFF; + const uint64_t v_hi = y.f >> 32; + + const uint64_t p0 = u_lo * v_lo; + const uint64_t p1 = u_lo * v_hi; + const uint64_t p2 = u_hi * v_lo; + const uint64_t p3 = u_hi * v_hi; + + const uint64_t p0_hi = p0 >> 32; + const uint64_t p1_lo = p1 & 0xFFFFFFFF; + const uint64_t p1_hi = p1 >> 32; + const uint64_t p2_lo = p2 & 0xFFFFFFFF; + const uint64_t p2_hi = p2 >> 32; + + uint64_t Q = p0_hi+p1_lo+p2_lo; + + Q += uint64_t{1} << (64-32-1); + + const uint64_t h = p3+p2_hi+p1_hi+(Q >> 32); + + return diyfp(h, x.e+y.e+64); + } + + static diyfp normalize(diyfp x) noexcept { + assert(x.f != 0); + + while((x.f >> 63) == 0) { + x.f <<= 1; + x.e--; + } + + return x; + } + + static diyfp normalize_to(const diyfp &x, const int target_exponent) noexcept { + const int delta = x.e-target_exponent; + + assert(delta >= 0); + assert(((x.f << delta) >> delta) == x.f); + + return diyfp(x.f << delta, target_exponent); + } + }; + + struct boundaries { + diyfp w; + diyfp minus; + diyfp plus; + }; + + template + boundaries compute_boundaries(FloatType value) { + assert(std::isfinite(value)); + assert(value > 0); + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; + constexpr int kBias = std::numeric_limits::max_exponent-1+(kPrecision-1); + constexpr int kMinExp = 1-kBias; + constexpr uint64_t kHiddenBit = uint64_t{1} << (kPrecision-1); + + using bits_type = typename std::conditional::type; + + const uint64_t bits = reinterpret_bits(value); + const uint64_t E = bits >> (kPrecision-1); + const uint64_t F = bits & (kHiddenBit-1); + + const bool is_denormal = (E == 0); + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F+kHiddenBit, static_cast(E)-kBias); + + const bool lower_boundary_is_closer = (F == 0 and E > 1); + const diyfp m_plus = diyfp(2 * v.f+1, v.e-1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f-1, v.e-2) + : diyfp(2 * v.f-1, v.e-1); + + const diyfp w_plus = diyfp::normalize(m_plus); + + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; + } + + constexpr int kAlpha = -60; + constexpr int kGamma = -32; + + struct cached_power { + uint64_t f; + int e; + int k; + }; + + inline cached_power get_cached_power_for_binary_exponent(int e) { + constexpr int kCachedPowersSize = 79; + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr cached_power kCachedPowers[] = + { + {0xAB70FE17C79AC6CA, -1060, -300}, + {0xFF77B1FCBEBCDC4F, -1034, -292}, + {0xBE5691EF416BD60C, -1007, -284}, + {0x8DD01FAD907FFC3C, -980, -276}, + {0xD3515C2831559A83, -954, -268}, + {0x9D71AC8FADA6C9B5, -927, -260}, + {0xEA9C227723EE8BCB, -901, -252}, + {0xAECC49914078536D, -874, -244}, + {0x823C12795DB6CE57, -847, -236}, + {0xC21094364DFB5637, -821, -228}, + {0x9096EA6F3848984F, -794, -220}, + {0xD77485CB25823AC7, -768, -212}, + {0xA086CFCD97BF97F4, -741, -204}, + {0xEF340A98172AACE5, -715, -196}, + {0xB23867FB2A35B28E, -688, -188}, + {0x84C8D4DFD2C63F3B, -661, -180}, + {0xC5DD44271AD3CDBA, -635, -172}, + {0x936B9FCEBB25C996, -608, -164}, + {0xDBAC6C247D62A584, -582, -156}, + {0xA3AB66580D5FDAF6, -555, -148}, + {0xF3E2F893DEC3F126, -529, -140}, + {0xB5B5ADA8AAFF80B8, -502, -132}, + {0x87625F056C7C4A8B, -475, -124}, + {0xC9BCFF6034C13053, -449, -116}, + {0x964E858C91BA2655, -422, -108}, + {0xDFF9772470297EBD, -396, -100}, + {0xA6DFBD9FB8E5B88F, -369, -92}, + {0xF8A95FCF88747D94, -343, -84}, + {0xB94470938FA89BCF, -316, -76}, + {0x8A08F0F8BF0F156B, -289, -68}, + {0xCDB02555653131B6, -263, -60}, + {0x993FE2C6D07B7FAC, -236, -52}, + {0xE45C10C42A2B3B06, -210, -44}, + {0xAA242499697392D3, -183, -36}, + {0xFD87B5F28300CA0E, -157, -28}, + {0xBCE5086492111AEB, -130, -20}, + {0x8CBCCC096F5088CC, -103, -12}, + {0xD1B71758E219652C, -77, -4}, + {0x9C40000000000000, -50, 4}, + {0xE8D4A51000000000, -24, 12}, + {0xAD78EBC5AC620000, 3, 20}, + {0x813F3978F8940984, 30, 28}, + {0xC097CE7BC90715B3, 56, 36}, + {0x8F7E32CE7BEA5C70, 83, 44}, + {0xD5D238A4ABE98068, 109, 52}, + {0x9F4F2726179A2245, 136, 60}, + {0xED63A231D4C4FB27, 162, 68}, + {0xB0DE65388CC8ADA8, 189, 76}, + {0x83C7088E1AAB65DB, 216, 84}, + {0xC45D1DF942711D9A, 242, 92}, + {0x924D692CA61BE758, 269, 100}, + {0xDA01EE641A708DEA, 295, 108}, + {0xA26DA3999AEF774A, 322, 116}, + {0xF209787BB47D6B85, 348, 124}, + {0xB454E4A179DD1877, 375, 132}, + {0x865B86925B9BC5C2, 402, 140}, + {0xC83553C5C8965D3D, 428, 148}, + {0x952AB45CFA97A0B3, 455, 156}, + {0xDE469FBD99A05FE3, 481, 164}, + {0xA59BC234DB398C25, 508, 172}, + {0xF6C69A72A3989F5C, 534, 180}, + {0xB7DCBF5354E9BECE, 561, 188}, + {0x88FCF317F22241E2, 588, 196}, + {0xCC20CE9BD35C78A5, 614, 204}, + {0x98165AF37B2153DF, 641, 212}, + {0xE2A0B5DC971F303A, 667, 220}, + {0xA8D9D1535CE3B396, 694, 228}, + {0xFB9B7CD9A4A7443C, 720, 236}, + {0xBB764C4CA7A44410, 747, 244}, + {0x8BAB8EEFB6409C1A, 774, 252}, + {0xD01FEF10A657842C, 800, 260}, + {0x9B10A4E5E9913129, 827, 268}, + {0xE7109BFBA19C0C9D, 853, 276}, + {0xAC2820D9623BF429, 880, 284}, + {0x80444B5E7AA7CF85, 907, 292}, + {0xBF21E44003ACDD2D, 933, 300}, + {0x8E679C2F5E44FF8F, 960, 308}, + {0xD433179D9C8CB841, 986, 316}, + {0x9E19DB92B4E31BA9, 1013, 324}, + }; + + assert(e >= -1500); + assert(e <= 1500); + const int f = kAlpha-e-1; + const int k = (f * 78913) / (1 << 18)+(f > 0); + + const int index = (-kCachedPowersMinDecExp+k+(kCachedPowersDecStep-1)) / kCachedPowersDecStep; + assert(index >= 0); + assert(index < kCachedPowersSize); + static_cast(kCachedPowersSize); + + const cached_power cached = kCachedPowers[index]; + assert(kAlpha <= cached.e+e+64); + assert(kGamma >= cached.e+e+64); + + return cached; + } + + inline int find_largest_pow10(const uint32_t n, uint32_t &pow10) { + if(n >= 1000000000) { + pow10 = 1000000000; + return 10; + } else if(n >= 100000000) { + pow10 = 100000000; + return 9; + } else if(n >= 10000000) { + pow10 = 10000000; + return 8; + } else if(n >= 1000000) { + pow10 = 1000000; + return 7; + } else if(n >= 100000) { + pow10 = 100000; + return 6; + } else if(n >= 10000) { + pow10 = 10000; + return 5; + } else if(n >= 1000) { + pow10 = 1000; + return 4; + } else if(n >= 100) { + pow10 = 100; + return 3; + } else if(n >= 10) { + pow10 = 10; + return 2; + } else { + pow10 = 1; + return 1; + } + } + + inline void grisu2_round(char *buf, int len, uint64_t dist, uint64_t delta, + uint64_t rest, uint64_t ten_k) { + assert(len >= 1); + assert(dist <= delta); + assert(rest <= delta); + assert(ten_k > 0); + + while(rest < dist + and delta-rest >= ten_k + and (rest+ten_k < dist or dist-rest > rest+ten_k-dist)) { + assert(buf[len-1] != '0'); + buf[len-1]--; + rest += ten_k; + } + } + + inline void grisu2_digit_gen(char *buffer, int &length, int &decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) { + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + assert(M_plus.e >= kAlpha); + assert(M_plus.e <= kGamma); + + uint64_t delta = diyfp::sub(M_plus, M_minus).f; + uint64_t dist = diyfp::sub(M_plus, w).f; + + const diyfp one(uint64_t{1} << -M_plus.e, M_plus.e); + + uint32_t p1 = static_cast(M_plus.f >> -one.e); + uint64_t p2 = M_plus.f & (one.f-1); + + assert(p1 > 0); + + uint32_t pow10; + const int k = find_largest_pow10(p1, pow10); + + int n = k; + while(n > 0) { + const uint32_t d = p1 / pow10; + const uint32_t r = p1 % pow10; + + assert(d <= 9); + buffer[length++] = static_cast('0'+d); + + p1 = r; + n--; + + const uint64_t rest = (uint64_t{p1} << -one.e)+p2; + if(rest <= delta) { + decimal_exponent += n; + + const uint64_t ten_n = uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + + } + + assert(p2 > delta); + + int m = 0; + for(;;) { + assert(p2 <= UINT64_MAX / 10); + p2 *= 10; + const uint64_t d = p2 >> -one.e; + const uint64_t r = p2 & (one.f-1); + + assert(d <= 9); + buffer[length++] = static_cast('0'+d); + + p2 = r; + m++; + + delta *= 10; + dist *= 10; + if(p2 <= delta) { + break; + } + } + + decimal_exponent -= m; + + const uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + } + + inline void grisu2(char *buf, int &len, int &decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) { + assert(m_plus.e == m_minus.e); + assert(m_plus.e == v.e); + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); + + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + const diyfp M_minus(w_minus.f+1, w_minus.e); + const diyfp M_plus(w_plus.f-1, w_plus.e); + + decimal_exponent = -cached.k; + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); + } + + template + void grisu2(char *buf, int &len, int &decimal_exponent, FloatType value) { + static_assert(diyfp::kPrecision >= std::numeric_limits::digits+3, + "internal error: not enough precision"); + + assert(std::isfinite(value)); + assert(value > 0); + +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); + } + + inline char *append_exponent(char *buf, int e) { + assert(e > -1000); + assert(e < 1000); + + if(e < 0) { + e = -e; + *buf++ = '-'; + } else { + *buf++ = '+'; + } + + uint32_t k = static_cast(e); + if(k < 10) { + *buf++ = '0'; + *buf++ = static_cast('0'+k); + } else if(k < 100) { + *buf++ = static_cast('0'+k / 10); + k %= 10; + *buf++ = static_cast('0'+k); + } else { + *buf++ = static_cast('0'+k / 100); + k %= 100; + *buf++ = static_cast('0'+k / 10); + k %= 10; + *buf++ = static_cast('0'+k); + } + + return buf; + } + + inline char *format_buffer(char *buf, int len, int decimal_exponent, + int min_exp, int max_exp) { + assert(min_exp < 0); + assert(max_exp > 0); + + const int k = len; + const int n = len+decimal_exponent; + + if(k <= n and n <= max_exp) { + std::memset(buf+k, '0', static_cast(n-k)); + + buf[n+0] = '.'; + buf[n+1] = '0'; + return buf+(n+2); + } + + if(0 < n and n <= max_exp) { + assert(k > n); + + std::memmove(buf+(n+1), buf+n, static_cast(k-n)); + buf[n] = '.'; + return buf+(k+1); + } + + if(min_exp < n and n <= 0) { + std::memmove(buf+(2+ -n), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf+2, '0', static_cast(-n)); + return buf+(2+(-n)+k); + } + + if(k == 1) { + buf += 1; + } else { + std::memmove(buf+2, buf+1, static_cast(k-1)); + buf[1] = '.'; + buf += 1+k; + } + + *buf++ = 'e'; + return append_exponent(buf, n-1); + } + + } + + template + char *to_chars(char *first, char *last, FloatType value) { + static_cast(last); + assert(std::isfinite(value)); + + if(std::signbit(value)) { + value = -value; + *first++ = '-'; + } + + if(value == 0) { + *first++ = '0'; + + *first++ = '.'; + *first++ = '0'; + return first; + } + + assert(last-first >= std::numeric_limits::max_digits10); + + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + assert(len <= std::numeric_limits::max_digits10); + + constexpr int kMinExp = -4; + + constexpr int kMaxExp = std::numeric_limits::digits10; + + assert(last-first >= kMaxExp+2); + assert(last-first >= 2+(-kMinExp-1)+std::numeric_limits::max_digits10); + assert(last-first >= std::numeric_limits::max_digits10+6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); + } + + } +} + +namespace nlohmann { + namespace detail { + template + class serializer { + using string_t = typename BasicJsonType::string_t; + using number_float_t = typename BasicJsonType::number_float_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + static constexpr uint8_t UTF8_ACCEPT = 0; + static constexpr uint8_t UTF8_REJECT = 1; + + public: + serializer(output_adapter_t s, const char ichar) + : o(std::move(s)), loc(std::localeconv()), + thousands_sep(loc->thousands_sep == nullptr ? '\0' : *(loc->thousands_sep)), + decimal_point(loc->decimal_point == nullptr ? '\0' : *(loc->decimal_point)), + indent_char(ichar), indent_string(512, indent_char) {} + + serializer(const serializer &) = delete; + + serializer &operator=(const serializer &) = delete; + + void dump(const BasicJsonType &val, const bool pretty_print, + const bool ensure_ascii, + const unsigned int indent_step, + const unsigned int current_indent = 0) { + switch(val.m_type) { + case value_t::object: { + if(val.m_value.object->empty()) { + o->write_characters("{}", 2); + return; + } + + if(pretty_print) { + o->write_characters("{\n", 2); + + const auto new_indent = current_indent+indent_step; + if(JSON_UNLIKELY(indent_string.size() < new_indent)) { + indent_string.resize(indent_string.size() * 2, ' '); + } + + auto i = val.m_value.object->cbegin(); + for(std::size_t cnt = 0; cnt < val.m_value.object->size()-1; ++cnt, ++i) { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + assert(i != val.m_value.object->cend()); + assert(std::next(i) == val.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } else { + o->write_character('{'); + + auto i = val.m_value.object->cbegin(); + for(std::size_t cnt = 0; cnt < val.m_value.object->size()-1; ++cnt, ++i) { + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + assert(i != val.m_value.object->cend()); + assert(std::next(i) == val.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + + o->write_character('}'); + } + + return; + } + + case value_t::array: { + if(val.m_value.array->empty()) { + o->write_characters("[]", 2); + return; + } + + if(pretty_print) { + o->write_characters("[\n", 2); + + const auto new_indent = current_indent+indent_step; + if(JSON_UNLIKELY(indent_string.size() < new_indent)) { + indent_string.resize(indent_string.size() * 2, ' '); + } + + for(auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend()-1; ++i) { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + assert(not val.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } else { + o->write_character('['); + + for(auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend()-1; ++i) { + dump(*i, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + assert(not val.m_value.array->empty()); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); + + o->write_character(']'); + } + + return; + } + + case value_t::string: { + o->write_character('\"'); + dump_escaped(*val.m_value.string, ensure_ascii); + o->write_character('\"'); + return; + } + + case value_t::boolean: { + if(val.m_value.boolean) { + o->write_characters("true", 4); + } else { + o->write_characters("false", 5); + } + return; + } + + case value_t::number_integer: { + dump_integer(val.m_value.number_integer); + return; + } + + case value_t::number_unsigned: { + dump_integer(val.m_value.number_unsigned); + return; + } + + case value_t::number_float: { + dump_float(val.m_value.number_float); + return; + } + + case value_t::discarded: { + o->write_characters("", 11); + return; + } + + case value_t::null: { + o->write_characters("null", 4); + return; + } + } + } + + private: + void dump_escaped(const string_t &s, const bool ensure_ascii) { + uint32_t codepoint; + uint8_t state = UTF8_ACCEPT; + std::size_t bytes = 0; + + for(std::size_t i = 0; i < s.size(); ++i) { + const auto byte = static_cast(s[i]); + + switch(decode(state, codepoint, byte)) { + case UTF8_ACCEPT: { + switch(codepoint) { + case 0x08: { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'b'; + break; + } + + case 0x09: { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 't'; + break; + } + + case 0x0A: { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'n'; + break; + } + + case 0x0C: { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'f'; + break; + } + + case 0x0D: { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'r'; + break; + } + + case 0x22: { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\"'; + break; + } + + case 0x5C: { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\\'; + break; + } + + default: { + if((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F))) { + if(codepoint <= 0xFFFF) { + std::snprintf(string_buffer.data()+bytes, 7, "\\u%04x", + static_cast(codepoint)); + bytes += 6; + } else { + std::snprintf(string_buffer.data()+bytes, 13, "\\u%04x\\u%04x", + static_cast(0xD7C0+(codepoint >> 10)), + static_cast(0xDC00+(codepoint & 0x3FF))); + bytes += 12; + } + } else { + string_buffer[bytes++] = s[i]; + } + break; + } + } + + if(string_buffer.size()-bytes < 13) { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + break; + } + + case UTF8_REJECT: { + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast(byte); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index "+std::to_string(i)+": 0x"+ss.str())); + } + + default: { + if(not ensure_ascii) { + string_buffer[bytes++] = s[i]; + } + break; + } + } + } + + if(JSON_LIKELY(state == UTF8_ACCEPT)) { + if(bytes > 0) { + o->write_characters(string_buffer.data(), bytes); + } + } else { + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex + << static_cast(static_cast(s.back())); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x"+ss.str())); + } + } + + template::value or + std::is_same::value, + int> = 0> + void dump_integer(NumberType x) { + if(x == 0) { + o->write_character('0'); + return; + } + + const bool is_negative = (x <= 0) and (x != 0); + std::size_t i = 0; + + while(x != 0) { + assert(i < number_buffer.size()-1); + + const auto digit = std::labs(static_cast(x % 10)); + number_buffer[i++] = static_cast('0'+digit); + x /= 10; + } + + if(is_negative) { + assert(i < number_buffer.size()-2); + number_buffer[i++] = '-'; + } + + std::reverse(number_buffer.begin(), number_buffer.begin()+i); + o->write_characters(number_buffer.data(), i); + } + + void dump_float(number_float_t x) { + if(not std::isfinite(x)) { + o->write_characters("null", 4); + return; + } + + static constexpr bool is_ieee_single_or_double + = (std::numeric_limits::is_iec559 and std::numeric_limits::digits == 24 and + std::numeric_limits::max_exponent == 128) or + (std::numeric_limits::is_iec559 and std::numeric_limits::digits == 53 and + std::numeric_limits::max_exponent == 1024); + + dump_float(x, std::integral_constant()); + } + + void dump_float(number_float_t x, std::true_type) { + char *begin = number_buffer.data(); + char *end = ::nlohmann::detail::to_chars(begin, begin+number_buffer.size(), x); + + o->write_characters(begin, static_cast(end-begin)); + } + + void dump_float(number_float_t x, std::false_type) { + static constexpr auto d = std::numeric_limits::max_digits10; + + std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), "%.*g", d, x); + + assert(len > 0); + + assert(static_cast(len) < number_buffer.size()); + + if(thousands_sep != '\0') { + const auto end = std::remove(number_buffer.begin(), + number_buffer.begin()+len, thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + assert((end-number_buffer.begin()) <= len); + len = (end-number_buffer.begin()); + } + + if(decimal_point != '\0' and decimal_point != '.') { + const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); + if(dec_pos != number_buffer.end()) { + *dec_pos = '.'; + } + } + + o->write_characters(number_buffer.data(), static_cast(len)); + + const bool value_is_int_like = + std::none_of(number_buffer.begin(), number_buffer.begin()+len+1, + [](char c) { + return (c == '.' or c == 'e'); + }); + + if(value_is_int_like) { + o->write_characters(".0", 2); + } + } + + static uint8_t decode(uint8_t &state, uint32_t &codep, const uint8_t byte) noexcept { + static const std::array utf8d = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, + 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + } + }; + + const uint8_t type = utf8d[byte]; + + codep = (state != UTF8_ACCEPT) + ? (byte & 0x3fu) | (codep << 6) + : static_cast(0xff >> type) & (byte); + + state = utf8d[256u+state * 16u+type]; + return state; + } + + private: + output_adapter_t o = nullptr; + + std::array number_buffer{{}}; + + const std::lconv *loc = nullptr; + + const char thousands_sep = '\0'; + + const char decimal_point = '\0'; + + std::array string_buffer{{}}; + + const char indent_char; + + string_t indent_string; + }; + } +} + +namespace nlohmann { + namespace detail { + template + class json_ref { + public: + using value_type = BasicJsonType; + + json_ref(value_type &&value) + : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) {} + + json_ref(const value_type &value) + : value_ref(const_cast(&value)), is_rvalue(false) {} + + json_ref(std::initializer_list init) + : owned_value(init), value_ref(&owned_value), is_rvalue(true) {} + + template + json_ref(Args &&... args) + : owned_value(std::forward(args)...), value_ref(&owned_value), is_rvalue(true) {} + + json_ref(json_ref &&) = default; + + json_ref(const json_ref &) = delete; + + json_ref &operator=(const json_ref &) = delete; + + value_type moved_or_copied() const { + if(is_rvalue) { + return std::move(*value_ref); + } + return *value_ref; + } + + value_type const &operator*() const { + return *static_cast(value_ref); + } + + value_type const *operator->() const { + return static_cast(value_ref); + } + + private: + mutable value_type owned_value = nullptr; + value_type *value_ref = nullptr; + const bool is_rvalue; + }; + } +} + +namespace nlohmann { + template + class json_pointer { + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend + class basic_json; + + public: + explicit json_pointer(const std::string &s = "") + : reference_tokens(split(s)) {} + + std::string to_string() const noexcept { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string &a, const std::string &b) { + return a+"/"+escape(b); + }); + } + + operator std::string() const { + return to_string(); + } + + static int array_index(const std::string &s) { + std::size_t processed_chars = 0; + const int res = std::stoi(s, &processed_chars); + + if(JSON_UNLIKELY(processed_chars != s.size())) { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '"+s+"'")); + } + + return res; + } + + private: + std::string pop_back() { + if(JSON_UNLIKELY(is_root())) { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; + } + + bool is_root() const { + return reference_tokens.empty(); + } + + json_pointer top() const { + if(JSON_UNLIKELY(is_root())) { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + BasicJsonType &get_and_create(BasicJsonType &j) const { + using size_type = typename BasicJsonType::size_type; + auto result = &j; + + for(const auto &reference_token : reference_tokens) { + switch(result->m_type) { + case detail::value_t::null: { + if(reference_token == "0") { + result = &result->operator[](0); + } else { + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: { + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: { + JSON_TRY { + result = &result->operator[](static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument &) { + JSON_THROW(detail::parse_error::create(109, 0, "array index '"+reference_token+"' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); + } + } + + return *result; + } + + BasicJsonType &get_unchecked(BasicJsonType *ptr) const { + using size_type = typename BasicJsonType::size_type; + for(const auto &reference_token : reference_tokens) { + if(ptr->m_type == detail::value_t::null) { + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const char x) { + return (x >= '0' and x <= '9'); + }); + + *ptr = (nums or reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch(ptr->m_type) { + case detail::value_t::object: { + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: { + if(JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '"+reference_token+ + "' must not begin with '0'")); + } + + if(reference_token == "-") { + ptr = &ptr->operator[](ptr->m_value.array->size()); + } else { + JSON_TRY { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument &) { + JSON_THROW(detail::parse_error::create(109, 0, "array index '"+reference_token+"' is not a number")); + } + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '"+reference_token+"'")); + } + } + + return *ptr; + } + + BasicJsonType &get_checked(BasicJsonType *ptr) const { + using size_type = typename BasicJsonType::size_type; + for(const auto &reference_token : reference_tokens) { + switch(ptr->m_type) { + case detail::value_t::object: { + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: { + if(JSON_UNLIKELY(reference_token == "-")) { + JSON_THROW(detail::out_of_range::create(402, + "array index '-' ("+std::to_string(ptr->m_value.array->size())+ + ") is out of range")); + } + + if(JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '"+reference_token+ + "' must not begin with '0'")); + } + + JSON_TRY { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument &) { + JSON_THROW(detail::parse_error::create(109, 0, "array index '"+reference_token+"' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '"+reference_token+"'")); + } + } + + return *ptr; + } + + const BasicJsonType &get_unchecked(const BasicJsonType *ptr) const { + using size_type = typename BasicJsonType::size_type; + for(const auto &reference_token : reference_tokens) { + switch(ptr->m_type) { + case detail::value_t::object: { + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: { + if(JSON_UNLIKELY(reference_token == "-")) { + JSON_THROW(detail::out_of_range::create(402, + "array index '-' ("+std::to_string(ptr->m_value.array->size())+ + ") is out of range")); + } + + if(JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '"+reference_token+ + "' must not begin with '0'")); + } + + JSON_TRY { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument &) { + JSON_THROW(detail::parse_error::create(109, 0, "array index '"+reference_token+"' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '"+reference_token+"'")); + } + } + + return *ptr; + } + + const BasicJsonType &get_checked(const BasicJsonType *ptr) const { + using size_type = typename BasicJsonType::size_type; + for(const auto &reference_token : reference_tokens) { + switch(ptr->m_type) { + case detail::value_t::object: { + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: { + if(JSON_UNLIKELY(reference_token == "-")) { + JSON_THROW(detail::out_of_range::create(402, + "array index '-' ("+std::to_string(ptr->m_value.array->size())+ + ") is out of range")); + } + + if(JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '"+reference_token+ + "' must not begin with '0'")); + } + + JSON_TRY { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument &) { + JSON_THROW(detail::parse_error::create(109, 0, "array index '"+reference_token+"' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '"+reference_token+"'")); + } + } + + return *ptr; + } + + static std::vector split(const std::string &reference_string) { + std::vector result; + + if(reference_string.empty()) { + return result; + } + + if(JSON_UNLIKELY(reference_string[0] != '/')) { + JSON_THROW(detail::parse_error::create(107, 1, + "JSON pointer must be empty or begin with '/' - was: '"+ + reference_string+"'")); + } + + for( + + std::size_t slash = reference_string.find_first_of('/', 1), + + start = 1; + + start != 0; + + start = slash+1, + + slash = reference_string.find_first_of('/', start)) { + auto reference_token = reference_string.substr(start, slash-start); + + for(std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos+1)) { + assert(reference_token[pos] == '~'); + + if(JSON_UNLIKELY(pos == reference_token.size()-1 or + (reference_token[pos+1] != '0' and + reference_token[pos+1] != '1'))) { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); + } + } + + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + static void replace_substring(std::string &s, const std::string &f, + const std::string &t) { + assert(not f.empty()); + for(auto pos = s.find(f); + pos != std::string::npos; + s.replace(pos, f.size(), t), + pos = s.find(f, pos+t.size())) {} + } + + static std::string escape(std::string s) { + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + static void unescape(std::string &s) { + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); + } + + static void flatten(const std::string &reference_string, + const BasicJsonType &value, + BasicJsonType &result) { + switch(value.m_type) { + case detail::value_t::array: { + if(value.m_value.array->empty()) { + result[reference_string] = nullptr; + } else { + for(std::size_t i = 0; i < value.m_value.array->size(); ++i) { + flatten(reference_string+"/"+std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: { + if(value.m_value.object->empty()) { + result[reference_string] = nullptr; + } else { + for(const auto &element : *value.m_value.object) { + flatten(reference_string+"/"+escape(element.first), element.second, result); + } + } + break; + } + + default: { + result[reference_string] = value; + break; + } + } + } + + static BasicJsonType + unflatten(const BasicJsonType &value) { + if(JSON_UNLIKELY(not value.is_object())) { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + } + + BasicJsonType result; + + for(const auto &element : *value.m_value.object) { + if(JSON_UNLIKELY(not element.second.is_primitive())) { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + } + + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + friend bool operator==(json_pointer const &lhs, + json_pointer const &rhs) noexcept { + return (lhs.reference_tokens == rhs.reference_tokens); + } + + friend bool operator!=(json_pointer const &lhs, + json_pointer const &rhs) noexcept { + return not(lhs == rhs); + } + + std::vector reference_tokens; + }; +} + +namespace nlohmann { + template + struct adl_serializer { + template + static void from_json(BasicJsonType &&j, ValueType &val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) { + ::nlohmann::from_json(std::forward(j), val); + } + + template + static void to_json(BasicJsonType &j, ValueType &&val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) { + ::nlohmann::to_json(j, std::forward(val)); + } + }; +} + +namespace nlohmann { + NLOHMANN_BASIC_JSON_TPL_DECLARATION + class basic_json { + private: + template friend + struct detail::external_constructor; + friend ::nlohmann::json_pointer; + friend ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; + + template + friend + class ::nlohmann::detail::iter_impl; + + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + + using lexer = ::nlohmann::detail::lexer; + using parser = ::nlohmann::detail::parser; + + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + using serializer = ::nlohmann::detail::serializer; + + public: + using value_t = detail::value_t; + + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; + + using initializer_list_t = std::initializer_list>; + + using exception = detail::exception; + + using parse_error = detail::parse_error; + + using invalid_iterator = detail::invalid_iterator; + + using type_error = detail::type_error; + + using out_of_range = detail::out_of_range; + + using other_error = detail::other_error; + + using value_type = basic_json; + + using reference = value_type &; + + using const_reference = const value_type &; + + using difference_type = std::ptrdiff_t; + + using size_type = std::size_t; + + using allocator_type = AllocatorType; + + using pointer = typename std::allocator_traits::pointer; + + using const_pointer = typename std::allocator_traits::const_pointer; + + using iterator = iter_impl; + + using const_iterator = iter_impl; + + using reverse_iterator = json_reverse_iterator; + + using const_reverse_iterator = json_reverse_iterator; + + static allocator_type get_allocator() { + return allocator_type(); + } + + static basic_json meta() { + basic_json result; + + result["copyright"] = "(C) 2013-2017 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["version"]["string"] = + std::to_string(NLOHMANN_JSON_VERSION_MAJOR)+"."+ + std::to_string(NLOHMANN_JSON_VERSION_MINOR)+"."+ + std::to_string(NLOHMANN_JSON_VERSION_PATCH); + result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, + {"version", std::to_string(__GNUC__)+"."+std::to_string(__GNUC_MINOR__)+"."+ + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + +#if defined(JSON_HAS_CPP_14) + + using object_comparator_t = std::less<>; +#else + using object_comparator_t = std::less; +#endif + + using object_t = ObjectType>>; + + using array_t = ArrayType>; + + using string_t = StringType; + + using boolean_t = BooleanType; + + using number_integer_t = NumberIntegerType; + + using number_unsigned_t = NumberUnsignedType; + + using number_float_t = NumberFloatType; + + private: + template + static T *create(Args &&... args) { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T *object) { + AllocatorTraits::deallocate(alloc, object, 1); + }; + std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, object.get(), std::forward(args)...); + assert(object != nullptr); + return object.release(); + } + + union json_value { + object_t *object; + + array_t *array; + + string_t *string; + + boolean_t boolean; + + number_integer_t number_integer; + + number_unsigned_t number_unsigned; + + number_float_t number_float; + + json_value() = default; + + json_value(boolean_t v) noexcept : boolean(v) {} + + json_value(number_integer_t v) noexcept : number_integer(v) {} + + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + + json_value(number_float_t v) noexcept : number_float(v) {} + + json_value(value_t t) { + switch(t) { + case value_t::object: { + object = create(); + break; + } + + case value_t::array: { + array = create(); + break; + } + + case value_t::string: { + string = create(""); + break; + } + + case value_t::boolean: { + boolean = boolean_t(false); + break; + } + + case value_t::number_integer: { + number_integer = number_integer_t(0); + break; + } + + case value_t::number_unsigned: { + number_unsigned = number_unsigned_t(0); + break; + } + + case value_t::number_float: { + number_float = number_float_t(0.0); + break; + } + + case value_t::null: { + object = nullptr; + break; + } + + default: { + object = nullptr; + if(JSON_UNLIKELY(t == value_t::null)) { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.1.2")); + } + break; + } + } + } + + json_value(const string_t &value) { + string = create(value); + } + + json_value(string_t &&value) { + string = create(std::move(value)); + } + + json_value(const object_t &value) { + object = create(value); + } + + json_value(object_t &&value) { + object = create(std::move(value)); + } + + json_value(const array_t &value) { + array = create(value); + } + + json_value(array_t &&value) { + array = create(std::move(value)); + } + + void destroy(value_t t) noexcept { + switch(t) { + case value_t::object: { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + default: { + break; + } + } + } + }; + + void assert_invariant() const noexcept { + assert(m_type != value_t::object or m_value.object != nullptr); + assert(m_type != value_t::array or m_value.array != nullptr); + assert(m_type != value_t::string or m_value.string != nullptr); + } + + public: + using parse_event_t = typename parser::parse_event_t; + + using parser_callback_t = typename parser::parser_callback_t; + + basic_json(const value_t v) + : m_type(v), m_value(v) { + assert_invariant(); + } + + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) { + assert_invariant(); + } + + template, + detail::enable_if_t< + detail::is_compatible_type::value, int> = 0> + basic_json(CompatibleType &&val) noexcept(noexcept( + JSONSerializer::to_json(std::declval(), + std::forward(val)))) { + JSONSerializer::to_json(*this, std::forward(val)); + assert_invariant(); + } + + template::value and + not std::is_same::value, int> = 0> + basic_json(const BasicJsonType &val) { + using other_boolean_t = typename BasicJsonType::boolean_t; + using other_number_float_t = typename BasicJsonType::number_float_t; + using other_number_integer_t = typename BasicJsonType::number_integer_t; + using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using other_string_t = typename BasicJsonType::string_t; + using other_object_t = typename BasicJsonType::object_t; + using other_array_t = typename BasicJsonType::array_t; + + switch(val.type()) { + case value_t::boolean: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::null: + *this = nullptr; + break; + case value_t::discarded: + m_type = value_t::discarded; + break; + } + assert_invariant(); + } + + basic_json(initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) { + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const detail::json_ref &element_ref) { + return (element_ref->is_array() and element_ref->size() == 2 and + (*element_ref)[0].is_string()); + }); + + if(not type_deduction) { + if(manual_type == value_t::array) { + is_an_object = false; + } + + if(JSON_UNLIKELY(manual_type == value_t::object and not is_an_object)) { + JSON_THROW(type_error::create(301, "cannot create object from initializer list")); + } + } + + if(is_an_object) { + m_type = value_t::object; + m_value = value_t::object; + + std::for_each(init.begin(), init.end(), [this](const detail::json_ref &element_ref) { + auto element = element_ref.moved_or_copied(); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + }); + } else { + m_type = value_t::array; + m_value.array = create(init.begin(), init.end()); + } + + assert_invariant(); + } + + static basic_json array(initializer_list_t init = {}) { + return basic_json(init, false, value_t::array); + } + + static basic_json object(initializer_list_t init = {}) { + return basic_json(init, false, value_t::object); + } + + basic_json(size_type cnt, const basic_json &val) + : m_type(value_t::array) { + m_value.array = create(cnt, val); + assert_invariant(); + } + + template::value or + std::is_same::value, int>::type = 0> + basic_json(InputIT first, InputIT last) { + assert(first.m_object != nullptr); + assert(last.m_object != nullptr); + + if(JSON_UNLIKELY(first.m_object != last.m_object)) { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); + } + + m_type = first.m_object->m_type; + + switch(m_type) { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: { + if(JSON_UNLIKELY(not first.m_it.primitive_iterator.is_begin() + or not last.m_it.primitive_iterator.is_end())) { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } + break; + } + + default: + break; + } + + switch(m_type) { + case value_t::number_integer: { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_unsigned: { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + + case value_t::number_float: { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: { + m_value = *first.m_object->m_value.string; + break; + } + + case value_t::object: { + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: { + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + default: + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from "+ + std::string(first.m_object->type_name()))); + } + + assert_invariant(); + } + + basic_json(const detail::json_ref &ref) + : basic_json(ref.moved_or_copied()) {} + + basic_json(const basic_json &other) + : m_type(other.m_type) { + other.assert_invariant(); + + switch(m_type) { + case value_t::object: { + m_value = *other.m_value.object; + break; + } + + case value_t::array: { + m_value = *other.m_value.array; + break; + } + + case value_t::string: { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: { + m_value = other.m_value.number_float; + break; + } + + default: + break; + } + + assert_invariant(); + } + + basic_json(basic_json &&other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) { + other.assert_invariant(); + + other.m_type = value_t::null; + other.m_value = {}; + + assert_invariant(); + } + + reference &operator=(basic_json other) noexcept( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) { + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + assert_invariant(); + return *this; + } + + ~basic_json() noexcept { + assert_invariant(); + m_value.destroy(m_type); + } + + public: + string_t dump(const int indent = -1, const char indent_char = ' ', + const bool ensure_ascii = false) const { + string_t result; + serializer s(detail::output_adapter(result), indent_char); + + if(indent >= 0) { + s.dump(*this, true, ensure_ascii, static_cast(indent)); + } else { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + constexpr value_t type() const noexcept { + return m_type; + } + + constexpr bool is_primitive() const noexcept { + return is_null() or is_string() or is_boolean() or is_number(); + } + + constexpr bool is_structured() const noexcept { + return is_array() or is_object(); + } + + constexpr bool is_null() const noexcept { + return (m_type == value_t::null); + } + + constexpr bool is_boolean() const noexcept { + return (m_type == value_t::boolean); + } + + constexpr bool is_number() const noexcept { + return is_number_integer() or is_number_float(); + } + + constexpr bool is_number_integer() const noexcept { + return (m_type == value_t::number_integer or m_type == value_t::number_unsigned); + } + + constexpr bool is_number_unsigned() const noexcept { + return (m_type == value_t::number_unsigned); + } + + constexpr bool is_number_float() const noexcept { + return (m_type == value_t::number_float); + } + + constexpr bool is_object() const noexcept { + return (m_type == value_t::object); + } + + constexpr bool is_array() const noexcept { + return (m_type == value_t::array); + } + + constexpr bool is_string() const noexcept { + return (m_type == value_t::string); + } + + constexpr bool is_discarded() const noexcept { + return (m_type == value_t::discarded); + } + + constexpr operator value_t() const noexcept { + return m_type; + } + + private: + boolean_t get_impl(boolean_t *) const { + if(JSON_LIKELY(is_boolean())) { + return m_value.boolean; + } + + JSON_THROW(type_error::create(302, "type must be boolean, but is "+std::string(type_name()))); + } + + object_t *get_impl_ptr(object_t *) noexcept { + return is_object() ? m_value.object : nullptr; + } + + constexpr const object_t *get_impl_ptr(const object_t *) const noexcept { + return is_object() ? m_value.object : nullptr; + } + + array_t *get_impl_ptr(array_t *) noexcept { + return is_array() ? m_value.array : nullptr; + } + + constexpr const array_t *get_impl_ptr(const array_t *) const noexcept { + return is_array() ? m_value.array : nullptr; + } + + string_t *get_impl_ptr(string_t *) noexcept { + return is_string() ? m_value.string : nullptr; + } + + constexpr const string_t *get_impl_ptr(const string_t *) const noexcept { + return is_string() ? m_value.string : nullptr; + } + + boolean_t *get_impl_ptr(boolean_t *) noexcept { + return is_boolean() ? &m_value.boolean : nullptr; + } + + constexpr const boolean_t *get_impl_ptr(const boolean_t *) const noexcept { + return is_boolean() ? &m_value.boolean : nullptr; + } + + number_integer_t *get_impl_ptr(number_integer_t *) noexcept { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + constexpr const number_integer_t *get_impl_ptr(const number_integer_t *) const noexcept { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + number_unsigned_t *get_impl_ptr(number_unsigned_t *) noexcept { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + constexpr const number_unsigned_t *get_impl_ptr(const number_unsigned_t *) const noexcept { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + number_float_t *get_impl_ptr(number_float_t *) noexcept { + return is_number_float() ? &m_value.number_float : nullptr; + } + + constexpr const number_float_t *get_impl_ptr(const number_float_t *) const noexcept { + return is_number_float() ? &m_value.number_float : nullptr; + } + + template + static ReferenceType get_ref_impl(ThisType &obj) { + auto ptr = obj.template get_ptr::type>(); + + if(JSON_LIKELY(ptr != nullptr)) { + return *ptr; + } + + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is "+ + std::string(obj.type_name()))); + } + + public: + template::type, basic_json_t>::value, + int> = 0> + basic_json get() const { + return *this; + } + + template::value and + detail::is_basic_json::value, int> = 0> + BasicJsonType get() const { + return *this; + } + + template, + detail::enable_if_t< + not detail::is_basic_json::value and + detail::has_from_json::value and + not detail::has_non_default_from_json::value, + int> = 0> + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) { + static_assert(not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + static_assert(std::is_default_constructible::value, + "types must be DefaultConstructible when used with get()"); + + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + } + + template, + detail::enable_if_t::value and + detail::has_non_default_from_json::value, + int> = 0> + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) { + static_assert(not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return JSONSerializer::from_json(*this); + } + + template::value, int>::type = 0> + PointerType get() noexcept { + return get_ptr(); + } + + template::value, int>::type = 0> + constexpr const PointerType get() const noexcept { + return get_ptr(); + } + + template::value, int>::type = 0> + PointerType get_ptr() noexcept { + using pointee_t = typename std::remove_const::type>::type>::type; + + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value, "incompatible pointer type"); + + return get_impl_ptr(static_cast(nullptr)); + } + + template::value and + std::is_const::type>::value, int>::type = 0> + constexpr const PointerType get_ptr() const noexcept { + using pointee_t = typename std::remove_const::type>::type>::type; + + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value, "incompatible pointer type"); + + return get_impl_ptr(static_cast(nullptr)); + } + + template::value, int>::type = 0> + ReferenceType get_ref() { + return get_ref_impl(*this); + } + + template::value and + std::is_const::type>::value, int>::type = 0> + ReferenceType get_ref() const { + return get_ref_impl(*this); + } + + template::value and + not std::is_same>::value and + not std::is_same::value and + not detail::is_basic_json::value + #ifndef _MSC_VER + and not std::is_same>::value +#endif +#if defined(JSON_HAS_CPP_17) + and not std::is_same::value +#endif + , int>::type = 0> + operator ValueType() const { + return get(); + } + + reference at(size_type idx) { + if(JSON_LIKELY(is_array())) { + JSON_TRY { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range &) { + JSON_THROW(out_of_range::create(401, "array index "+std::to_string(idx)+" is out of range")); + } + } else { + JSON_THROW(type_error::create(304, "cannot use at() with "+std::string(type_name()))); + } + } + + const_reference at(size_type idx) const { + if(JSON_LIKELY(is_array())) { + JSON_TRY { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range &) { + JSON_THROW(out_of_range::create(401, "array index "+std::to_string(idx)+" is out of range")); + } + } else { + JSON_THROW(type_error::create(304, "cannot use at() with "+std::string(type_name()))); + } + } + + reference at(const typename object_t::key_type &key) { + if(JSON_LIKELY(is_object())) { + JSON_TRY { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range &) { + JSON_THROW(out_of_range::create(403, "key '"+key+"' not found")); + } + } else { + JSON_THROW(type_error::create(304, "cannot use at() with "+std::string(type_name()))); + } + } + + const_reference at(const typename object_t::key_type &key) const { + if(JSON_LIKELY(is_object())) { + JSON_TRY { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range &) { + JSON_THROW(out_of_range::create(403, "key '"+key+"' not found")); + } + } else { + JSON_THROW(type_error::create(304, "cannot use at() with "+std::string(type_name()))); + } + } + + reference operator[](size_type idx) { + if(is_null()) { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + if(JSON_LIKELY(is_array())) { + if(idx >= m_value.array->size()) { + m_value.array->insert(m_value.array->end(), + idx-m_value.array->size()+1, + basic_json()); + } + + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with "+std::string(type_name()))); + } + + const_reference operator[](size_type idx) const { + if(JSON_LIKELY(is_array())) { + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with "+std::string(type_name()))); + } + + reference operator[](const typename object_t::key_type &key) { + if(is_null()) { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if(JSON_LIKELY(is_object())) { + return m_value.object->operator[](key); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with "+std::string(type_name()))); + } + + const_reference operator[](const typename object_t::key_type &key) const { + if(JSON_LIKELY(is_object())) { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with "+std::string(type_name()))); + } + + template + reference operator[](T *key) { + if(is_null()) { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + if(JSON_LIKELY(is_object())) { + return m_value.object->operator[](key); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with "+std::string(type_name()))); + } + + template + const_reference operator[](T *key) const { + if(JSON_LIKELY(is_object())) { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with "+std::string(type_name()))); + } + + template::value, int>::type = 0> + ValueType value(const typename object_t::key_type &key, const ValueType &default_value) const { + if(JSON_LIKELY(is_object())) { + const auto it = find(key); + if(it != end()) { + return *it; + } + + return default_value; + } + + JSON_THROW(type_error::create(306, "cannot use value() with "+std::string(type_name()))); + } + + string_t value(const typename object_t::key_type &key, const char *default_value) const { + return value(key, string_t(default_value)); + } + + template::value, int>::type = 0> + ValueType value(const json_pointer &ptr, const ValueType &default_value) const { + if(JSON_LIKELY(is_object())) { + JSON_TRY { + return ptr.get_checked(this); + } + JSON_CATCH (out_of_range &) { + return default_value; + } + } + + JSON_THROW(type_error::create(306, "cannot use value() with "+std::string(type_name()))); + } + + string_t value(const json_pointer &ptr, const char *default_value) const { + return value(ptr, string_t(default_value)); + } + + reference front() { + return *begin(); + } + + const_reference front() const { + return *cbegin(); + } + + reference back() { + auto tmp = end(); + --tmp; + return *tmp; + } + + const_reference back() const { + auto tmp = cend(); + --tmp; + return *tmp; + } + + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType pos) { + if(JSON_UNLIKELY(this != pos.m_object)) { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + IteratorType result = end(); + + switch(m_type) { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: { + if(JSON_UNLIKELY(not pos.m_it.primitive_iterator.is_begin())) { + JSON_THROW(invalid_iterator::create(205, "iterator out of range")); + } + + if(is_string()) { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + default: + JSON_THROW(type_error::create(307, "cannot use erase() with "+std::string(type_name()))); + } + + return result; + } + + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType first, IteratorType last) { + if(JSON_UNLIKELY(this != first.m_object or this != last.m_object)) { + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); + } + + IteratorType result = end(); + + switch(m_type) { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: { + if(JSON_LIKELY(not first.m_it.primitive_iterator.is_begin() + or not last.m_it.primitive_iterator.is_end())) { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } + + if(is_string()) { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + default: + JSON_THROW(type_error::create(307, "cannot use erase() with "+std::string(type_name()))); + } + + return result; + } + + size_type erase(const typename object_t::key_type &key) { + if(JSON_LIKELY(is_object())) { + return m_value.object->erase(key); + } + + JSON_THROW(type_error::create(307, "cannot use erase() with "+std::string(type_name()))); + } + + void erase(const size_type idx) { + if(JSON_LIKELY(is_array())) { + if(JSON_UNLIKELY(idx >= size())) { + JSON_THROW(out_of_range::create(401, "array index "+std::to_string(idx)+" is out of range")); + } + + m_value.array->erase(m_value.array->begin()+static_cast(idx)); + } else { + JSON_THROW(type_error::create(307, "cannot use erase() with "+std::string(type_name()))); + } + } + + template + iterator find(KeyT &&key) { + auto result = end(); + + if(is_object()) { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + template + const_iterator find(KeyT &&key) const { + auto result = cend(); + + if(is_object()) { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + template + size_type count(KeyT &&key) const { + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + iterator begin() noexcept { + iterator result(this); + result.set_begin(); + return result; + } + + const_iterator begin() const noexcept { + return cbegin(); + } + + const_iterator cbegin() const noexcept { + const_iterator result(this); + result.set_begin(); + return result; + } + + iterator end() noexcept { + iterator result(this); + result.set_end(); + return result; + } + + const_iterator end() const noexcept { + return cend(); + } + + const_iterator cend() const noexcept { + const_iterator result(this); + result.set_end(); + return result; + } + + reverse_iterator rbegin() noexcept { + return reverse_iterator(end()); + } + + const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + reverse_iterator rend() noexcept { + return reverse_iterator(begin()); + } + + const_reverse_iterator rend() const noexcept { + return crend(); + } + + const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(cend()); + } + + const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(cbegin()); + } + + public: + iteration_proxy items() noexcept { + return iteration_proxy(*this); + } + + iteration_proxy items() const noexcept { + return iteration_proxy(*this); + } + + bool empty() const noexcept { + switch(m_type) { + case value_t::null: { + return true; + } + + case value_t::array: { + return m_value.array->empty(); + } + + case value_t::object: { + return m_value.object->empty(); + } + + default: { + return false; + } + } + } + + size_type size() const noexcept { + switch(m_type) { + case value_t::null: { + return 0; + } + + case value_t::array: { + return m_value.array->size(); + } + + case value_t::object: { + return m_value.object->size(); + } + + default: { + return 1; + } + } + } + + size_type max_size() const noexcept { + switch(m_type) { + case value_t::array: { + return m_value.array->max_size(); + } + + case value_t::object: { + return m_value.object->max_size(); + } + + default: { + return size(); + } + } + } + + void clear() noexcept { + switch(m_type) { + case value_t::number_integer: { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: { + m_value.boolean = false; + break; + } + + case value_t::string: { + m_value.string->clear(); + break; + } + + case value_t::array: { + m_value.array->clear(); + break; + } + + case value_t::object: { + m_value.object->clear(); + break; + } + + default: + break; + } + } + + void push_back(basic_json &&val) { + if(JSON_UNLIKELY(not(is_null() or is_array()))) { + JSON_THROW(type_error::create(308, "cannot use push_back() with "+std::string(type_name()))); + } + + if(is_null()) { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + m_value.array->push_back(std::move(val)); + + val.m_type = value_t::null; + } + + reference operator+=(basic_json &&val) { + push_back(std::move(val)); + return *this; + } + + void push_back(const basic_json &val) { + if(JSON_UNLIKELY(not(is_null() or is_array()))) { + JSON_THROW(type_error::create(308, "cannot use push_back() with "+std::string(type_name()))); + } + + if(is_null()) { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + m_value.array->push_back(val); + } + + reference operator+=(const basic_json &val) { + push_back(val); + return *this; + } + + void push_back(const typename object_t::value_type &val) { + if(JSON_UNLIKELY(not(is_null() or is_object()))) { + JSON_THROW(type_error::create(308, "cannot use push_back() with "+std::string(type_name()))); + } + + if(is_null()) { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + m_value.object->insert(val); + } + + reference operator+=(const typename object_t::value_type &val) { + push_back(val); + return *this; + } + + void push_back(initializer_list_t init) { + if(is_object() and init.size() == 2 and (*init.begin())->is_string()) { + basic_json &&key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin()+1)->moved_or_copied())); + } else { + push_back(basic_json(init)); + } + } + + reference operator+=(initializer_list_t init) { + push_back(init); + return *this; + } + + template + void emplace_back(Args &&... args) { + if(JSON_UNLIKELY(not(is_null() or is_array()))) { + JSON_THROW(type_error::create(311, "cannot use emplace_back() with "+std::string(type_name()))); + } + + if(is_null()) { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + m_value.array->emplace_back(std::forward(args)...); + } + + template + std::pair emplace(Args &&... args) { + if(JSON_UNLIKELY(not(is_null() or is_object()))) { + JSON_THROW(type_error::create(311, "cannot use emplace() with "+std::string(type_name()))); + } + + if(is_null()) { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + auto res = m_value.object->emplace(std::forward(args)...); + + auto it = begin(); + it.m_it.object_iterator = res.first; + + return {it, res.second}; + } + + iterator insert(const_iterator pos, const basic_json &val) { + if(JSON_LIKELY(is_array())) { + if(JSON_UNLIKELY(pos.m_object != this)) { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); + return result; + } + + JSON_THROW(type_error::create(309, "cannot use insert() with "+std::string(type_name()))); + } + + iterator insert(const_iterator pos, basic_json &&val) { + return insert(pos, val); + } + + iterator insert(const_iterator pos, size_type cnt, const basic_json &val) { + if(JSON_LIKELY(is_array())) { + if(JSON_UNLIKELY(pos.m_object != this)) { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + return result; + } + + JSON_THROW(type_error::create(309, "cannot use insert() with "+std::string(type_name()))); + } + + iterator insert(const_iterator pos, const_iterator first, const_iterator last) { + if(JSON_UNLIKELY(not is_array())) { + JSON_THROW(type_error::create(309, "cannot use insert() with "+std::string(type_name()))); + } + + if(JSON_UNLIKELY(pos.m_object != this)) { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + if(JSON_UNLIKELY(first.m_object != last.m_object)) { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + if(JSON_UNLIKELY(first.m_object == this)) { + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); + } + + iterator result(this); + result.m_it.array_iterator = m_value.array->insert( + pos.m_it.array_iterator, + first.m_it.array_iterator, + last.m_it.array_iterator); + return result; + } + + iterator insert(const_iterator pos, initializer_list_t ilist) { + if(JSON_UNLIKELY(not is_array())) { + JSON_THROW(type_error::create(309, "cannot use insert() with "+std::string(type_name()))); + } + + if(JSON_UNLIKELY(pos.m_object != this)) { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist.begin(), ilist.end()); + return result; + } + + void insert(const_iterator first, const_iterator last) { + if(JSON_UNLIKELY(not is_object())) { + JSON_THROW(type_error::create(309, "cannot use insert() with "+std::string(type_name()))); + } + + if(JSON_UNLIKELY(first.m_object != last.m_object)) { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + if(JSON_UNLIKELY(not first.m_object->is_object())) { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } + + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } + + void update(const_reference j) { + if(is_null()) { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if(JSON_UNLIKELY(not is_object())) { + JSON_THROW(type_error::create(312, "cannot use update() with "+std::string(type_name()))); + } + if(JSON_UNLIKELY(not j.is_object())) { + JSON_THROW(type_error::create(312, "cannot use update() with "+std::string(j.type_name()))); + } + + for(auto it = j.cbegin(); it != j.cend(); ++it) { + m_value.object->operator[](it.key()) = it.value(); + } + } + + void update(const_iterator first, const_iterator last) { + if(is_null()) { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if(JSON_UNLIKELY(not is_object())) { + JSON_THROW(type_error::create(312, "cannot use update() with "+std::string(type_name()))); + } + + if(JSON_UNLIKELY(first.m_object != last.m_object)) { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + if(JSON_UNLIKELY(not first.m_object->is_object() + or not last.m_object->is_object())) { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } + + for(auto it = first; it != last; ++it) { + m_value.object->operator[](it.key()) = it.value(); + } + } + + void swap(reference other) noexcept( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + assert_invariant(); + } + + void swap(array_t &other) { + if(JSON_LIKELY(is_array())) { + std::swap(*(m_value.array), other); + } else { + JSON_THROW(type_error::create(310, "cannot use swap() with "+std::string(type_name()))); + } + } + + void swap(object_t &other) { + if(JSON_LIKELY(is_object())) { + std::swap(*(m_value.object), other); + } else { + JSON_THROW(type_error::create(310, "cannot use swap() with "+std::string(type_name()))); + } + } + + void swap(string_t &other) { + if(JSON_LIKELY(is_string())) { + std::swap(*(m_value.string), other); + } else { + JSON_THROW(type_error::create(310, "cannot use swap() with "+std::string(type_name()))); + } + } + + public: + friend bool operator==(const_reference lhs, const_reference rhs) noexcept { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if(lhs_type == rhs_type) { + switch(lhs_type) { + case value_t::array: + return (*lhs.m_value.array == *rhs.m_value.array); + + case value_t::object: + return (*lhs.m_value.object == *rhs.m_value.object); + + case value_t::null: + return true; + + case value_t::string: + return (*lhs.m_value.string == *rhs.m_value.string); + + case value_t::boolean: + return (lhs.m_value.boolean == rhs.m_value.boolean); + + case value_t::number_integer: + return (lhs.m_value.number_integer == rhs.m_value.number_integer); + + case value_t::number_unsigned: + return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned); + + case value_t::number_float: + return (lhs.m_value.number_float == rhs.m_value.number_float); + + default: + return false; + } + } else if(lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { + return (static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float); + } else if(lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { + return (lhs.m_value.number_float == static_cast(rhs.m_value.number_integer)); + } else if(lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) { + return (static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float); + } else if(lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) { + return (lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned)); + } else if(lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) { + return (static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer); + } else if(lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) { + return (lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned)); + } + + return false; + } + + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept { + return (lhs == basic_json(rhs)); + } + + template::value, int>::type = 0> + friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept { + return (basic_json(lhs) == rhs); + } + + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept { + return not(lhs == rhs); + } + + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept { + return (lhs != basic_json(rhs)); + } + + template::value, int>::type = 0> + friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept { + return (basic_json(lhs) != rhs); + } + + friend bool operator<(const_reference lhs, const_reference rhs) noexcept { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if(lhs_type == rhs_type) { + switch(lhs_type) { + case value_t::array: + return (*lhs.m_value.array) < (*rhs.m_value.array); + + case value_t::object: + return *lhs.m_value.object < *rhs.m_value.object; + + case value_t::null: + return false; + + case value_t::string: + return *lhs.m_value.string < *rhs.m_value.string; + + case value_t::boolean: + return lhs.m_value.boolean < rhs.m_value.boolean; + + case value_t::number_integer: + return lhs.m_value.number_integer < rhs.m_value.number_integer; + + case value_t::number_unsigned: + return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; + + case value_t::number_float: + return lhs.m_value.number_float < rhs.m_value.number_float; + + default: + return false; + } + } else if(lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } else if(lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } else if(lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } else if(lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } else if(lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } else if(lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } + + return operator<(lhs_type, rhs_type); + } + + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept { + return (lhs < basic_json(rhs)); + } + + template::value, int>::type = 0> + friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept { + return (basic_json(lhs) < rhs); + } + + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept { + return not(rhs < lhs); + } + + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept { + return (lhs <= basic_json(rhs)); + } + + template::value, int>::type = 0> + friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept { + return (basic_json(lhs) <= rhs); + } + + friend bool operator>(const_reference lhs, const_reference rhs) noexcept { + return not(lhs <= rhs); + } + + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept { + return (lhs > basic_json(rhs)); + } + + template::value, int>::type = 0> + friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept { + return (basic_json(lhs) > rhs); + } + + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept { + return not(lhs < rhs); + } + + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept { + return (lhs >= basic_json(rhs)); + } + + template::value, int>::type = 0> + friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept { + return (basic_json(lhs) >= rhs); + } + + friend std::ostream &operator<<(std::ostream &o, const basic_json &j) { + const bool pretty_print = (o.width() > 0); + const auto indentation = (pretty_print ? o.width() : 0); + + o.width(0); + + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } + + static basic_json parse(detail::input_adapter i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) { + basic_json result; + parser(i, cb, allow_exceptions).parse(true, result); + return result; + } + + static basic_json parse(detail::input_adapter &i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) { + basic_json result; + parser(i, cb, allow_exceptions).parse(true, result); + return result; + } + + static bool accept(detail::input_adapter i) { + return parser(i).accept(true); + } + + static bool accept(detail::input_adapter &i) { + return parser(i).accept(true); + } + + template::iterator_category>::value, int>::type = 0> + static basic_json parse(IteratorType first, IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) { + basic_json result; + parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result); + return result; + } + + template::iterator_category>::value, int>::type = 0> + static bool accept(IteratorType first, IteratorType last) { + return parser(detail::input_adapter(first, last)).accept(true); + } + + friend std::istream &operator>>(std::istream &i, basic_json &j) { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } + + const char *type_name() const noexcept { + { + switch(m_type) { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::discarded: + return "discarded"; + default: + return "number"; + } + } + } + + private: + value_t m_type = value_t::null; + + json_value m_value = {}; + + public: + reference operator[](const json_pointer &ptr) { + return ptr.get_unchecked(this); + } + + const_reference operator[](const json_pointer &ptr) const { + return ptr.get_unchecked(this); + } + + reference at(const json_pointer &ptr) { + return ptr.get_checked(this); + } + + const_reference at(const json_pointer &ptr) const { + return ptr.get_checked(this); + } + + basic_json flatten() const { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + basic_json unflatten() const { + return json_pointer::unflatten(*this); + } + + basic_json patch(const basic_json &json_patch) const { + basic_json result = *this; + + enum class patch_operations { + add, remove, replace, move, copy, test, invalid + }; + + const auto get_op = [](const std::string &op) { + if(op == "add") { + return patch_operations::add; + } + if(op == "remove") { + return patch_operations::remove; + } + if(op == "replace") { + return patch_operations::replace; + } + if(op == "move") { + return patch_operations::move; + } + if(op == "copy") { + return patch_operations::copy; + } + if(op == "test") { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + const auto operation_add = [&result](json_pointer &ptr, basic_json val) { + if(ptr.is_root()) { + result = val; + } else { + json_pointer top_pointer = ptr.top(); + if(top_pointer != ptr) { + result.at(top_pointer); + } + + const auto last_path = ptr.pop_back(); + basic_json &parent = result[ptr]; + + switch(parent.m_type) { + case value_t::null: + case value_t::object: { + parent[last_path] = val; + break; + } + + case value_t::array: { + if(last_path == "-") { + parent.push_back(val); + } else { + const auto idx = json_pointer::array_index(last_path); + if(JSON_UNLIKELY(static_cast(idx) > parent.size())) { + JSON_THROW(out_of_range::create(401, "array index "+std::to_string(idx)+" is out of range")); + } else { + parent.insert(parent.begin()+static_cast(idx), val); + } + } + break; + } + + default: { + assert(false); + } + } + } + }; + + const auto operation_remove = [&result](json_pointer &ptr) { + const auto last_path = ptr.pop_back(); + basic_json &parent = result.at(ptr); + + if(parent.is_object()) { + auto it = parent.find(last_path); + if(JSON_LIKELY(it != parent.end())) { + parent.erase(it); + } else { + JSON_THROW(out_of_range::create(403, "key '"+last_path+"' not found")); + } + } else if(parent.is_array()) { + parent.erase(static_cast(json_pointer::array_index(last_path))); + } + }; + + if(JSON_UNLIKELY(not json_patch.is_array())) { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + } + + for(const auto &val : json_patch) { + const auto get_value = [&val](const std::string &op, + const std::string &member, + bool string_type) -> basic_json & { + auto it = val.m_value.object->find(member); + + const auto error_msg = (op == "op") ? "operation" : "operation '"+op+"'"; + + if(JSON_UNLIKELY(it == val.m_value.object->end())) { + JSON_THROW(parse_error::create(105, 0, error_msg+" must have member '"+member+"'")); + } + + if(JSON_UNLIKELY(string_type and not it->second.is_string())) { + JSON_THROW(parse_error::create(105, 0, error_msg+" must have string member '"+member+"'")); + } + + return it->second; + }; + + if(JSON_UNLIKELY(not val.is_object())) { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + } + + const std::string op = get_value("op", "op", true); + const std::string path = get_value(op, "path", true); + json_pointer ptr(path); + + switch(get_op(op)) { + case patch_operations::add: { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: { + operation_remove(ptr); + break; + } + + case patch_operations::replace: { + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: { + const std::string from_path = get_value("move", "from", true); + json_pointer from_ptr(from_path); + + basic_json v = result.at(from_ptr); + + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: { + const std::string from_path = get_value("copy", "from", true); + const json_pointer from_ptr(from_path); + + basic_json v = result.at(from_ptr); + + operation_add(ptr, v); + break; + } + + case patch_operations::test: { + bool success = false; + JSON_TRY { + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_CATCH (out_of_range &) { + } + + if(JSON_UNLIKELY(not success)) { + JSON_THROW(other_error::create(501, "unsuccessful: "+val.dump())); + } + + break; + } + + case patch_operations::invalid: { + JSON_THROW(parse_error::create(105, 0, "operation value '"+op+"' is invalid")); + } + } + } + + return result; + } + + static basic_json diff(const basic_json &source, const basic_json &target, + const std::string &path = "") { + basic_json result(value_t::array); + + if(source == target) { + return result; + } + + if(source.type() != target.type()) { + result.push_back( + { + {"op", "replace"}, + {"path", path}, + {"value", target} + }); + } else { + switch(source.type()) { + case value_t::array: { + std::size_t i = 0; + while(i < source.size() and i < target.size()) { + auto temp_diff = diff(source[i], target[i], path+"/"+std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + const auto end_index = static_cast(result.size()); + while(i < source.size()) { + result.insert(result.begin()+end_index, object( + { + {"op", "remove"}, + {"path", path+"/"+std::to_string(i)} + })); + ++i; + } + + while(i < target.size()) { + result.push_back( + { + {"op", "add"}, + {"path", path+"/"+std::to_string(i)}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: { + for(auto it = source.cbegin(); it != source.cend(); ++it) { + const auto key = json_pointer::escape(it.key()); + + if(target.find(it.key()) != target.end()) { + auto temp_diff = diff(it.value(), target[it.key()], path+"/"+key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } else { + result.push_back(object( + { + {"op", "remove"}, + {"path", path+"/"+key} + })); + } + } + + for(auto it = target.cbegin(); it != target.cend(); ++it) { + if(source.find(it.key()) == source.end()) { + const auto key = json_pointer::escape(it.key()); + result.push_back( + { + {"op", "add"}, + {"path", path+"/"+key}, + {"value", it.value()} + }); + } + } + + break; + } + + default: { + result.push_back( + { + {"op", "replace"}, + {"path", path}, + {"value", target} + }); + break; + } + } + } + + return result; + } + + void merge_patch(const basic_json &patch) { + if(patch.is_object()) { + if(not is_object()) { + *this = object(); + } + for(auto it = patch.begin(); it != patch.end(); ++it) { + if(it.value().is_null()) { + erase(it.key()); + } else { + operator[](it.key()).merge_patch(it.value()); + } + } + } else { + *this = patch; + } + } + + }; +} + +namespace std { + template<> + inline void swap(nlohmann::json &j1, + nlohmann::json &j2) noexcept( + is_nothrow_move_constructible::value and + is_nothrow_move_assignable::value + ) { + j1.swap(j2); + } + + template<> + struct hash { + std::size_t operator()(const nlohmann::json &j) const { + const auto &h = hash(); + return h(j.dump()); + } + }; + + template<> + struct less<::nlohmann::detail::value_t> { + bool operator()(nlohmann::detail::value_t lhs, + nlohmann::detail::value_t rhs) const noexcept { + return nlohmann::detail::operator<(lhs, rhs); + } + }; + +} + +inline nlohmann::json operator "" _json(const char *s, std::size_t n) { + return nlohmann::json::parse(s, s+n); +} + +inline nlohmann::json::json_pointer operator "" _json_pointer(const char *s, std::size_t n) { + return nlohmann::json::json_pointer(std::string(s, n)); +} + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#endif +#if defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_LIKELY +#undef JSON_UNLIKELY +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL +#undef NLOHMANN_JSON_HAS_HELPER diff --git a/3rd_party/rapidxml.h b/3rd_party/rapidxml.h new file mode 100644 index 00000000..ee1b7d3b --- /dev/null +++ b/3rd_party/rapidxml.h @@ -0,0 +1,1639 @@ +#pragma once + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) +#endif + +#include + +#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) + +namespace rapidxml { + + class parse_error : public std::exception { + public: + + parse_error(const char *what, void *where) + : m_what(what), m_where(where) { + } + + virtual const char *what() const throw() { + return m_what; + } + + template + Ch *where() const { + return reinterpret_cast(m_where); + } + + private: + const char *m_what; + void *m_where; + }; +} + +#ifndef RAPIDXML_STATIC_POOL_SIZE + +#define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) +#endif +#ifndef RAPIDXML_DYNAMIC_POOL_SIZE + +#define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) +#endif +#ifndef RAPIDXML_ALIGNMENT + +#define RAPIDXML_ALIGNMENT sizeof(void *) +#endif +namespace rapidxml { + template + class xml_node; + + template + class xml_attribute; + + template + class xml_document; + + enum node_type { + node_document, + node_element, + node_data, + node_cdata, + node_comment, + node_declaration, + node_doctype, + node_pi + }; + + const int parse_no_data_nodes = 0x1; + + const int parse_no_element_values = 0x2; + + const int parse_no_string_terminators = 0x4; + + const int parse_no_entity_translation = 0x8; + + const int parse_no_utf8 = 0x10; + + const int parse_declaration_node = 0x20; + + const int parse_comment_nodes = 0x40; + + const int parse_doctype_node = 0x80; + + const int parse_pi_nodes = 0x100; + + const int parse_validate_closing_tags = 0x200; + + const int parse_trim_whitespace = 0x400; + + const int parse_normalize_whitespace = 0x800; + + const int parse_default = 0; + + const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; + + const int parse_fastest = parse_non_destructive | parse_no_data_nodes; + + const int parse_full = + parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; + + namespace internal { + + template + struct lookup_tables { + static const unsigned char lookup_whitespace[256]; + static const unsigned char lookup_node_name[256]; + static const unsigned char lookup_text[256]; + static const unsigned char lookup_text_pure_no_ws[256]; + static const unsigned char lookup_text_pure_with_ws[256]; + static const unsigned char lookup_attribute_name[256]; + static const unsigned char lookup_attribute_data_1[256]; + static const unsigned char lookup_attribute_data_1_pure[256]; + static const unsigned char lookup_attribute_data_2[256]; + static const unsigned char lookup_attribute_data_2_pure[256]; + static const unsigned char lookup_digits[256]; + static const unsigned char lookup_upcase[256]; + }; + + template + inline std::size_t measure(const Ch *p) { + const Ch *tmp = p; + while(*tmp) + ++tmp; + return tmp-p; + } + + template + inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) { + if(size1 != size2) + return false; + if(case_sensitive) { + for(const Ch *end = p1+size1; p1 < end; ++p1, ++p2) + if(*p1 != *p2) + return false; + } else { + for(const Ch *end = p1+size1; p1 < end; ++p1, ++p2) + if(lookup_tables<0>::lookup_upcase[static_cast(*p1)] != + lookup_tables<0>::lookup_upcase[static_cast(*p2)]) + return false; + } + return true; + } + } + + template + class memory_pool { + public: + + typedef void *(alloc_func)(std::size_t); + + typedef void (free_func)(void *); + + memory_pool() + : m_alloc_func(0), m_free_func(0) { + init(); + } + + ~memory_pool() { + clear(); + } + + xml_node *allocate_node(node_type type, + const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) { + void *memory = allocate_aligned(sizeof(xml_node)); + xml_node *node = new(memory) xml_node(type); + if(name) { + if(name_size > 0) + node->name(name, name_size); + else + node->name(name); + } + if(value) { + if(value_size > 0) + node->value(value, value_size); + else + node->value(value); + } + return node; + } + + xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) { + void *memory = allocate_aligned(sizeof(xml_attribute)); + xml_attribute *attribute = new(memory) xml_attribute; + if(name) { + if(name_size > 0) + attribute->name(name, name_size); + else + attribute->name(name); + } + if(value) { + if(value_size > 0) + attribute->value(value, value_size); + else + attribute->value(value); + } + return attribute; + } + + Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) { + assert(source || size); + if(size == 0) + size = internal::measure(source)+1; + Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); + if(source) + for(std::size_t i = 0; i < size; ++i) + result[i] = source[i]; + return result; + } + + xml_node *clone_node(const xml_node *source, xml_node *result = 0) { + if(result) { + result->remove_all_attributes(); + result->remove_all_nodes(); + result->type(source->type()); + } else + result = allocate_node(source->type()); + + result->name(source->name(), source->name_size()); + result->value(source->value(), source->value_size()); + + for(xml_node *child = source->first_node(); child; child = child->next_sibling()) + result->append_node(clone_node(child)); + for(xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) + result->append_attribute( + allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); + return result; + } + + void clear() { + while(m_begin != m_static_memory) { + char *previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; + if(m_free_func) + m_free_func(m_begin); + else + delete[] m_begin; + m_begin = previous_begin; + } + init(); + } + + void set_allocator(alloc_func *af, free_func *ff) { + assert(m_begin == m_static_memory && m_ptr == align(m_begin)); + m_alloc_func = af; + m_free_func = ff; + } + + private: + struct header { + char *previous_begin; + }; + + void init() { + m_begin = m_static_memory; + m_ptr = align(m_begin); + m_end = m_static_memory+sizeof(m_static_memory); + } + + char *align(char *ptr) { + std::size_t alignment = ((RAPIDXML_ALIGNMENT-(std::size_t(ptr) & (RAPIDXML_ALIGNMENT-1))) & + (RAPIDXML_ALIGNMENT-1)); + return ptr+alignment; + } + + char *allocate_raw(std::size_t size) { + void *memory; + if(m_alloc_func) { + memory = m_alloc_func(size); + assert( + memory); + } else { + memory = new char[size]; +#ifdef RAPIDXML_NO_EXCEPTIONS + if (!memory) + RAPIDXML_PARSE_ERROR("out of memory", 0); +#endif + } + return static_cast(memory); + } + + void *allocate_aligned(std::size_t size) { + char *result = align(m_ptr); + + if(result+size > m_end) { + std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; + if(pool_size < size) + pool_size = size; + + std::size_t alloc_size = sizeof(header)+(2 * RAPIDXML_ALIGNMENT-2)+ + pool_size; + char *raw_memory = allocate_raw(alloc_size); + + char *pool = align(raw_memory); + header *new_header = reinterpret_cast
(pool); + new_header->previous_begin = m_begin; + m_begin = raw_memory; + m_ptr = pool+sizeof(header); + m_end = raw_memory+alloc_size; + + result = align(m_ptr); + } + + m_ptr = result+size; + return result; + } + + char *m_begin; + char *m_ptr; + char *m_end; + char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; + alloc_func *m_alloc_func; + free_func *m_free_func; + }; + + template + class xml_base { + public: + + xml_base() + : m_name(0), m_value(0), m_parent(0) { + } + + Ch *name() const { + return m_name ? m_name : nullstr(); + } + + std::size_t name_size() const { + return m_name ? m_name_size : 0; + } + + Ch *value() const { + return m_value ? m_value : nullstr(); + } + + std::size_t value_size() const { + return m_value ? m_value_size : 0; + } + + void name(const Ch *name, std::size_t size) { + m_name = const_cast(name); + m_name_size = size; + } + + void name(const Ch *name) { + this->name(name, internal::measure(name)); + } + + void value(const Ch *value, std::size_t size) { + m_value = const_cast(value); + m_value_size = size; + } + + void value(const Ch *value) { + this->value(value, internal::measure(value)); + } + + xml_node *parent() const { + return m_parent; + } + + protected: + + static Ch *nullstr() { + static Ch zero = Ch('\0'); + return &zero; + } + + Ch *m_name; + Ch *m_value; + std::size_t m_name_size; + std::size_t m_value_size; + xml_node *m_parent; + }; + + template + class xml_attribute : public xml_base { + friend class xml_node; + + public: + + xml_attribute() { + } + + xml_document *document() const { + if(xml_node *node = this->parent()) { + while(node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } else + return 0; + } + + xml_attribute * + previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { + if(name) { + if(name_size == 0) + name_size = internal::measure(name); + for(xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) + if(internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } else + return this->m_parent ? m_prev_attribute : 0; + } + + xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { + if(name) { + if(name_size == 0) + name_size = internal::measure(name); + for(xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) + if(internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } else + return this->m_parent ? m_next_attribute : 0; + } + + private: + xml_attribute *m_prev_attribute; + xml_attribute *m_next_attribute; + }; + + template + class xml_node : public xml_base { + public: + + xml_node(node_type type) + : m_type(type), m_first_node(0), m_first_attribute(0) { + } + + node_type type() const { + return m_type; + } + + xml_document *document() const { + xml_node *node = const_cast *>(this); + while(node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + + xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { + if(name) { + if(name_size == 0) + name_size = internal::measure(name); + for(xml_node *child = m_first_node; child; child = child->next_sibling()) + if(internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } else + return m_first_node; + } + + xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { + assert(m_first_node); + if(name) { + if(name_size == 0) + name_size = internal::measure(name); + for(xml_node *child = m_last_node; child; child = child->previous_sibling()) + if(internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } else + return m_last_node; + } + + xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { + assert(this->m_parent); + if(name) { + if(name_size == 0) + name_size = internal::measure(name); + for(xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) + if(internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } else + return m_prev_sibling; + } + + xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { + assert(this->m_parent); + if(name) { + if(name_size == 0) + name_size = internal::measure(name); + for(xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) + if(internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } else + return m_next_sibling; + } + + xml_attribute * + first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { + if(name) { + if(name_size == 0) + name_size = internal::measure(name); + for(xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + if(internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } else + return m_first_attribute; + } + + xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const { + if(name) { + if(name_size == 0) + name_size = internal::measure(name); + for(xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) + if(internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } else + return m_first_attribute ? m_last_attribute : 0; + } + + void type(node_type type) { + m_type = type; + } + + void prepend_node(xml_node *child) { + assert(child && !child->parent() && child->type() != node_document); + if(first_node()) { + child->m_next_sibling = m_first_node; + m_first_node->m_prev_sibling = child; + } else { + child->m_next_sibling = 0; + m_last_node = child; + } + m_first_node = child; + child->m_parent = this; + child->m_prev_sibling = 0; + } + + void append_node(xml_node *child) { + assert(child && !child->parent() && child->type() != node_document); + if(first_node()) { + child->m_prev_sibling = m_last_node; + m_last_node->m_next_sibling = child; + } else { + child->m_prev_sibling = 0; + m_first_node = child; + } + m_last_node = child; + child->m_parent = this; + child->m_next_sibling = 0; + } + + void insert_node(xml_node *where, xml_node *child) { + assert(!where || where->parent() == this); + assert(child && !child->parent() && child->type() != node_document); + if(where == m_first_node) + prepend_node(child); + else if(where == 0) + append_node(child); + else { + child->m_prev_sibling = where->m_prev_sibling; + child->m_next_sibling = where; + where->m_prev_sibling->m_next_sibling = child; + where->m_prev_sibling = child; + child->m_parent = this; + } + } + + void remove_first_node() { + assert(first_node()); + xml_node *child = m_first_node; + m_first_node = child->m_next_sibling; + if(child->m_next_sibling) + child->m_next_sibling->m_prev_sibling = 0; + else + m_last_node = 0; + child->m_parent = 0; + } + + void remove_last_node() { + assert(first_node()); + xml_node *child = m_last_node; + if(child->m_prev_sibling) { + m_last_node = child->m_prev_sibling; + child->m_prev_sibling->m_next_sibling = 0; + } else + m_first_node = 0; + child->m_parent = 0; + } + + void remove_node(xml_node *where) { + assert(where && where->parent() == this); + assert(first_node()); + if(where == m_first_node) + remove_first_node(); + else if(where == m_last_node) + remove_last_node(); + else { + where->m_prev_sibling->m_next_sibling = where->m_next_sibling; + where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; + where->m_parent = 0; + } + } + + void remove_all_nodes() { + for(xml_node *node = first_node(); node; node = node->m_next_sibling) + node->m_parent = 0; + m_first_node = 0; + } + + void prepend_attribute(xml_attribute *attribute) { + assert(attribute && !attribute->parent()); + if(first_attribute()) { + attribute->m_next_attribute = m_first_attribute; + m_first_attribute->m_prev_attribute = attribute; + } else { + attribute->m_next_attribute = 0; + m_last_attribute = attribute; + } + m_first_attribute = attribute; + attribute->m_parent = this; + attribute->m_prev_attribute = 0; + } + + void append_attribute(xml_attribute *attribute) { + assert(attribute && !attribute->parent()); + if(first_attribute()) { + attribute->m_prev_attribute = m_last_attribute; + m_last_attribute->m_next_attribute = attribute; + } else { + attribute->m_prev_attribute = 0; + m_first_attribute = attribute; + } + m_last_attribute = attribute; + attribute->m_parent = this; + attribute->m_next_attribute = 0; + } + + void insert_attribute(xml_attribute *where, xml_attribute *attribute) { + assert(!where || where->parent() == this); + assert(attribute && !attribute->parent()); + if(where == m_first_attribute) + prepend_attribute(attribute); + else if(where == 0) + append_attribute(attribute); + else { + attribute->m_prev_attribute = where->m_prev_attribute; + attribute->m_next_attribute = where; + where->m_prev_attribute->m_next_attribute = attribute; + where->m_prev_attribute = attribute; + attribute->m_parent = this; + } + } + + void remove_first_attribute() { + assert(first_attribute()); + xml_attribute *attribute = m_first_attribute; + if(attribute->m_next_attribute) { + attribute->m_next_attribute->m_prev_attribute = 0; + } else + m_last_attribute = 0; + attribute->m_parent = 0; + m_first_attribute = attribute->m_next_attribute; + } + + void remove_last_attribute() { + assert(first_attribute()); + xml_attribute *attribute = m_last_attribute; + if(attribute->m_prev_attribute) { + attribute->m_prev_attribute->m_next_attribute = 0; + m_last_attribute = attribute->m_prev_attribute; + } else + m_first_attribute = 0; + attribute->m_parent = 0; + } + + void remove_attribute(xml_attribute *where) { + assert(first_attribute() && where->parent() == this); + if(where == m_first_attribute) + remove_first_attribute(); + else if(where == m_last_attribute) + remove_last_attribute(); + else { + where->m_prev_attribute->m_next_attribute = where->m_next_attribute; + where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; + where->m_parent = 0; + } + } + + void remove_all_attributes() { + for(xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) + attribute->m_parent = 0; + m_first_attribute = 0; + } + + private: + + xml_node(const xml_node &); + + void operator=(const xml_node &); + + + + // + + + node_type m_type; + xml_node *m_first_node; + xml_node *m_last_node; + xml_attribute *m_first_attribute; + xml_attribute *m_last_attribute; + xml_node *m_prev_sibling; + xml_node *m_next_sibling; + }; + + template + class xml_document : public xml_node, public memory_pool { + public: + + xml_document() + : xml_node(node_document) { + } + + template + void parse(Ch *text) { + assert(text); + + this->remove_all_nodes(); + this->remove_all_attributes(); + + parse_bom(text); + + while(1) { + skip(text); + if(*text == 0) + break; + + if(*text == Ch('<')) { + ++text; + if(xml_node *node = parse_node(text)) + this->append_node(node); + } else + RAPIDXML_PARSE_ERROR("expected <", text); + } + } + + void clear() { + this->remove_all_nodes(); + this->remove_all_attributes(); + memory_pool::clear(); + } + + private: + + struct whitespace_pred { + static unsigned char test(Ch ch) { + return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; + } + }; + + struct node_name_pred { + static unsigned char test(Ch ch) { + return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; + } + }; + + struct attribute_name_pred { + static unsigned char test(Ch ch) { + return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; + } + }; + + struct text_pred { + static unsigned char test(Ch ch) { + return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; + } + }; + + struct text_pure_no_ws_pred { + static unsigned char test(Ch ch) { + return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; + } + }; + + struct text_pure_with_ws_pred { + static unsigned char test(Ch ch) { + return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; + } + }; + + template + struct attribute_value_pred { + static unsigned char test(Ch ch) { + if(Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; + if(Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; + return 0; + } + }; + + template + struct attribute_value_pure_pred { + static unsigned char test(Ch ch) { + if(Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; + if(Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; + return 0; + } + }; + + template + static void insert_coded_character(Ch *&text, unsigned long code) { + if(Flags & parse_no_utf8) { + + text[0] = static_cast(code); + text += 1; + } else { + if(code < 0x80) { + text[0] = static_cast(code); + text += 1; + } else if(code < 0x800) { + text[1] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[0] = static_cast(code | 0xC0); + text += 2; + } else if(code < 0x10000) { + text[2] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[0] = static_cast(code | 0xE0); + text += 3; + } else if(code < 0x110000) { + text[3] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[2] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[0] = static_cast(code | 0xF0); + text += 4; + } else { + RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); + } + } + } + + template + static void skip(Ch *&text) { + Ch *tmp = text; + while(StopPred::test(*tmp)) + ++tmp; + text = tmp; + } + + template + static Ch *skip_and_expand_character_refs(Ch *&text) { + if(Flags & parse_no_entity_translation && + !(Flags & parse_normalize_whitespace) && + !(Flags & parse_trim_whitespace)) { + skip(text); + return text; + } + + skip(text); + + Ch *src = text; + Ch *dest = src; + while(StopPred::test(*src)) { + if(!(Flags & parse_no_entity_translation)) { + if(src[0] == Ch('&')) { + switch(src[1]) { + + case Ch('a'): + if(src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) { + *dest = Ch('&'); + ++dest; + src += 5; + continue; + } + if(src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) { + *dest = Ch('\''); + ++dest; + src += 6; + continue; + } + break; + + case Ch('q'): + if(src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) { + *dest = Ch('"'); + ++dest; + src += 6; + continue; + } + break; + + case Ch('g'): + if(src[2] == Ch('t') && src[3] == Ch(';')) { + *dest = Ch('>'); + ++dest; + src += 4; + continue; + } + break; + + case Ch('l'): + if(src[2] == Ch('t') && src[3] == Ch(';')) { + *dest = Ch('<'); + ++dest; + src += 4; + continue; + } + break; + + case Ch('#'): + if(src[2] == Ch('x')) { + unsigned long code = 0; + src += 3; + while(1) { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if(digit == 0xFF) + break; + code = code * 16+digit; + ++src; + } + insert_coded_character(dest, code); + } else { + unsigned long code = 0; + src += 2; + while(1) { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if(digit == 0xFF) + break; + code = code * 10+digit; + ++src; + } + insert_coded_character(dest, code); + } + if(*src == Ch(';')) + ++src; + else + RAPIDXML_PARSE_ERROR("expected ;", src); + continue; + + default: + break; + } + } + } + + if(Flags & parse_normalize_whitespace) { + if(whitespace_pred::test(*src)) { + *dest = Ch(' '); + ++dest; + ++src; + while(whitespace_pred::test(*src)) + ++src; + continue; + } + } + + *dest++ = *src++; + } + + text = src; + return dest; + } + + template + void parse_bom(Ch *&text) { + if(static_cast(text[0]) == 0xEF && + static_cast(text[1]) == 0xBB && + static_cast(text[2]) == 0xBF) { + text += 3; + } + } + + template + xml_node *parse_xml_declaration(Ch *&text) { + if(!(Flags & parse_declaration_node)) { + while(text[0] != Ch('?') || text[1] != Ch('>')) { + if(!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; + return 0; + } + + xml_node *declaration = this->allocate_node(node_declaration); + + skip(text); + + parse_node_attributes(text, declaration); + + if(text[0] != Ch('?') || text[1] != Ch('>')) + RAPIDXML_PARSE_ERROR("expected ?>", text); + text += 2; + return declaration; + } + + template + xml_node *parse_comment(Ch *&text) { + if(!(Flags & parse_comment_nodes)) { + while(text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) { + if(!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 3; + return 0; + } + + Ch *value = text; + + while(text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) { + if(!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + xml_node *comment = this->allocate_node(node_comment); + comment->value(value, text-value); + + if(!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + text += 3; + return comment; + } + + template + xml_node *parse_doctype(Ch *&text) { + Ch *value = text; + + while(*text != Ch('>')) { + switch(*text) { + + case Ch('['): { + ++text; + int depth = 1; + while(depth > 0) { + switch(*text) { + case Ch('['): + ++depth; + break; + case Ch(']'): + --depth; + break; + case 0: + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + ++text; + } + break; + } + + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + default: + ++text; + } + } + + if(Flags & parse_doctype_node) { + xml_node *doctype = this->allocate_node(node_doctype); + doctype->value(value, text-value); + + if(!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + text += 1; + return doctype; + } else { + text += 1; + return 0; + } + } + + template + xml_node *parse_pi(Ch *&text) { + if(Flags & parse_pi_nodes) { + xml_node *pi = this->allocate_node(node_pi); + + Ch *name = text; + skip(text); + if(text == name) + RAPIDXML_PARSE_ERROR("expected PI target", text); + pi->name(name, text-name); + + skip(text); + + Ch *value = text; + + while(text[0] != Ch('?') || text[1] != Ch('>')) { + if(*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + pi->value(value, text-value); + + if(!(Flags & parse_no_string_terminators)) { + pi->name()[pi->name_size()] = Ch('\0'); + pi->value()[pi->value_size()] = Ch('\0'); + } + text += 2; + return pi; + } else { + while(text[0] != Ch('?') || text[1] != Ch('>')) { + if(*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; + return 0; + } + } + + template + Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) { + if(!(Flags & parse_trim_whitespace)) + text = contents_start; + + Ch *value = text, *end; + if(Flags & parse_normalize_whitespace) + end = skip_and_expand_character_refs(text); + else + end = skip_and_expand_character_refs(text); + + if(Flags & parse_trim_whitespace) { + if(Flags & parse_normalize_whitespace) { + if(*(end-1) == Ch(' ')) + --end; + } else { + while(whitespace_pred::test(*(end-1))) + --end; + } + } + + if(!(Flags & parse_no_data_nodes)) { + xml_node *data = this->allocate_node(node_data); + data->value(value, end-value); + node->append_node(data); + } + + if(!(Flags & parse_no_element_values)) + if(*node->value() == Ch('\0')) + node->value(value, end-value); + + if(!(Flags & parse_no_string_terminators)) { + Ch ch = *text; + *end = Ch('\0'); + return ch; + } + + return *text; + } + + template + xml_node *parse_cdata(Ch *&text) { + if(Flags & parse_no_data_nodes) { + while(text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) { + if(!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 3; + return 0; + } + + Ch *value = text; + while(text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) { + if(!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + xml_node *cdata = this->allocate_node(node_cdata); + cdata->value(value, text-value); + + if(!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + text += 3; + return cdata; + } + + template + xml_node *parse_element(Ch *&text) { + xml_node *element = this->allocate_node(node_element); + + Ch *name = text; + skip(text); + if(text == name) + RAPIDXML_PARSE_ERROR("expected element name", text); + element->name(name, text-name); + + skip(text); + + parse_node_attributes(text, element); + + if(*text == Ch('>')) { + ++text; + parse_node_contents(text, element); + } else if(*text == Ch('/')) { + ++text; + if(*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; + } else + RAPIDXML_PARSE_ERROR("expected >", text); + + if(!(Flags & parse_no_string_terminators)) + element->name()[element->name_size()] = Ch('\0'); + + return element; + } + + template + xml_node *parse_node(Ch *&text) { + switch(text[0]) { + + default: + return parse_element(text); + + case Ch('?'): + ++text; + if((text[0] == Ch('x') || text[0] == Ch('X')) && + (text[1] == Ch('m') || text[1] == Ch('M')) && + (text[2] == Ch('l') || text[2] == Ch('L')) && + whitespace_pred::test(text[3])) { + text += 4; + return parse_xml_declaration(text); + } else { + return parse_pi(text); + } + + case Ch('!'): + + switch(text[1]) { + + case Ch('-'): + if(text[2] == Ch('-')) { + text += 3; + return parse_comment(text); + } + break; + + case Ch('['): + if(text[2] == Ch('C') && text[3] == Ch('D') && text[4] == Ch('A') && + text[5] == Ch('T') && text[6] == Ch('A') && text[7] == Ch('[')) { + text += 8; + return parse_cdata(text); + } + break; + + case Ch('D'): + if(text[2] == Ch('O') && text[3] == Ch('C') && text[4] == Ch('T') && + text[5] == Ch('Y') && text[6] == Ch('P') && text[7] == Ch('E') && + whitespace_pred::test(text[8])) { + text += 9; + return parse_doctype(text); + } + } + + ++text; + while(*text != Ch('>')) { + if(*text == 0) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + ++text; + return 0; + } + } + + template + void parse_node_contents(Ch *&text, xml_node *node) { + while(1) { + Ch *contents_start = text; + skip(text); + Ch next_char = *text; + + after_data_node: + + switch(next_char) { + + case Ch('<'): + if(text[1] == Ch('/')) { + text += 2; + if(Flags & parse_validate_closing_tags) { + Ch *closing_name = text; + skip(text); + if(!internal::compare(node->name(), node->name_size(), closing_name, text-closing_name, true)) + RAPIDXML_PARSE_ERROR("invalid closing tag name", text); + } else { + skip(text); + } + skip(text); + if(*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; + return; + } else { + ++text; + if(xml_node *child = parse_node(text)) + node->append_node(child); + } + break; + + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + default: + next_char = parse_and_append_data(node, text, contents_start); + goto after_data_node; + } + } + } + + template + void parse_node_attributes(Ch *&text, xml_node *node) { + while(attribute_name_pred::test(*text)) { + Ch *name = text; + ++text; + skip(text); + if(text == name) + RAPIDXML_PARSE_ERROR("expected attribute name", name); + + xml_attribute *attribute = this->allocate_attribute(); + attribute->name(name, text-name); + node->append_attribute(attribute); + + skip(text); + + if(*text != Ch('=')) + RAPIDXML_PARSE_ERROR("expected =", text); + ++text; + + if(!(Flags & parse_no_string_terminators)) + attribute->name()[attribute->name_size()] = 0; + + skip(text); + + Ch quote = *text; + if(quote != Ch('\'') && quote != Ch('"')) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; + + Ch *value = text, *end; + const int AttFlags = Flags & ~parse_normalize_whitespace; + if(quote == Ch('\'')) + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + else + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + + attribute->value(value, end-value); + + if(*text != quote) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; + + if(!(Flags & parse_no_string_terminators)) + attribute->value()[attribute->value_size()] = 0; + + skip(text); + } + } + }; + + namespace internal { + + template + const unsigned char lookup_tables::lookup_whitespace[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + template + const unsigned char lookup_tables::lookup_node_name[256] = + { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + + template + const unsigned char lookup_tables::lookup_text[256] = + { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + + template + const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = + { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + + template + const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = + { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + + template + const unsigned char lookup_tables::lookup_attribute_name[256] = + { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + + template + const unsigned char lookup_tables::lookup_attribute_data_1[256] = + { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + + template + const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = + { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + + template + const unsigned char lookup_tables::lookup_attribute_data_2[256] = + { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + + template + const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = + { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + + template + const unsigned char lookup_tables::lookup_digits[256] = + { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 + }; + + template + const unsigned char lookup_tables::lookup_upcase[256] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 + }; + } + +} + +#undef RAPIDXML_PARSE_ERROR + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/CMakeLists.txt b/CMakeLists.txt index 74cd0602..437b9b4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,7 @@ include_directories( ${LIBCLANG_INCLUDE_DIRS} ${ASPELL_INCLUDE_DIR} ${LIBGIT2_INCLUDE_DIRS} + 3rd_party ) add_subdirectory("src") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9c32003c..c2bd858c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,75 +1,77 @@ # Files used both in ../src and ../tests + +file(GLOB JUCI_SRC_BUILDSYSTEMS cmake.h cmake.cc meson.h meson.cc makefile_base.h makefile_base.cc) + set(JUCI_SHARED_FILES - autocomplete.cc - cmake.cc - compile_commands.cc - ctags.cc - dispatcher.cc - documentation_cppreference.cc - filesystem.cc - git.cc - menu.cc - meson.cc - project_build.cc - source.cc - source_base.cc - source_clang.cc - source_diff.cc - source_language_protocol.cc - source_spellcheck.cc - terminal.cc - usages_clang.cc -) -if(LIBLLDB_FOUND) + ${JUCI_SRC_BUILDSYSTEMS} + autocomplete.cc + compile_commands.cc + ctags.cc + dispatcher.cc + documentation_cppreference.cc + filesystem.cc + git.cc + menu.cc + project_build.cc + source.cc + source_base.cc + source_clang.cc + source_diff.cc + source_language_protocol.cc + source_spellcheck.cc + terminal.cc + usages_clang.cc + ) +if (LIBLLDB_FOUND) list(APPEND JUCI_SHARED_FILES debug_lldb.cc) -endif() +endif () add_library(juci_shared STATIC ${JUCI_SHARED_FILES}) target_link_libraries(juci_shared - ${GTKMM_LIBRARIES} - ${GTKSVMM_LIBRARIES} - ${Boost_LIBRARIES} - ${LIBLLDB_LIBRARIES} - ${ASPELL_LIBRARIES} - ${LIBGIT2_LIBRARIES} - clangmm - tiny-process-library -) + ${GTKMM_LIBRARIES} + ${GTKSVMM_LIBRARIES} + ${Boost_LIBRARIES} + ${LIBLLDB_LIBRARIES} + ${ASPELL_LIBRARIES} + ${LIBGIT2_LIBRARIES} + clangmm + tiny-process-library + ) add_executable(juci - config.cc - dialogs.cc - dialogs_unix.cc - directories.cc - entrybox.cc - info.cc - juci.cc - notebook.cc - project.cc - selection_dialog.cc - tooltips.cc - window.cc -) + config.cc + dialogs.cc + dialogs_unix.cc + directories.cc + entrybox.cc + info.cc + juci.cc + notebook.cc + project.cc + selection_dialog.cc + tooltips.cc + window.cc + ) target_link_libraries(juci juci_shared) install(TARGETS juci RUNTIME DESTINATION bin) -if(${CMAKE_SYSTEM_NAME} MATCHES Linux|.*BSD|DragonFly) +if (${CMAKE_SYSTEM_NAME} MATCHES Linux|.*BSD|DragonFly) install(FILES "${CMAKE_SOURCE_DIR}/share/juci.desktop" - DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") install(FILES "${CMAKE_SOURCE_DIR}/share/juci.svg" - DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps") -elseif(APPLE) + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps") +elseif (APPLE) install(CODE "execute_process(COMMAND /usr/bin/python ${CMAKE_SOURCE_DIR}/share/set_icon_macos.py ${CMAKE_SOURCE_DIR}/share/juci.png ${CMAKE_INSTALL_PREFIX}/bin/juci)") -endif() +endif () # add a target to generate API documentation with Doxygen set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules/") find_package(Plantuml) find_package(Doxygen) -if(DOXYGEN_FOUND) +if (DOXYGEN_FOUND) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) add_custom_target(doc - ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Generating API documentation with Doxygen to ${CMAKE_CURRENT_BINARY_DIR}" VERBATIM - ) -endif(DOXYGEN_FOUND) + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen to ${CMAKE_CURRENT_BINARY_DIR}" VERBATIM + ) +endif (DOXYGEN_FOUND) diff --git a/src/Doxyfile.in b/src/Doxyfile.in index 500232b6..056b1357 100644 --- a/src/Doxyfile.in +++ b/src/Doxyfile.in @@ -38,27 +38,27 @@ PROJECT_NAME = "juCi++" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = +PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = +PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = +OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -118,7 +118,7 @@ REPEAT_BRIEF = YES # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. -ABBREVIATE_BRIEF = +ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief @@ -152,7 +152,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -161,7 +161,7 @@ STRIP_FROM_PATH = # specify the list of include paths that are normally passed to the compiler # using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't @@ -228,13 +228,13 @@ TAB_SIZE = 4 # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. -ALIASES = +ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. -TCL_SUBST = +TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For @@ -281,7 +281,7 @@ OPTIMIZE_OUTPUT_VHDL = NO # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. -EXTENSION_MAPPING = +EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable @@ -629,7 +629,7 @@ GENERATE_DEPRECATEDLIST= YES # sections, marked by \if ... \endif and \cond # ... \endcond blocks. -ENABLED_SECTIONS = +ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the @@ -671,7 +671,7 @@ SHOW_NAMESPACES = YES # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. -FILE_VERSION_FILTER = +FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated @@ -684,7 +684,7 @@ FILE_VERSION_FILTER = # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. -LAYOUT_FILE = +LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib @@ -694,7 +694,7 @@ LAYOUT_FILE = # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. -CITE_BIB_FILES = +CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages @@ -753,7 +753,7 @@ WARN_FORMAT = "$file:$line: $text" # messages should be written. If left blank the output is written to standard # error (stderr). -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -790,7 +790,7 @@ INPUT_ENCODING = UTF-8 # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, # *.vhdl, *.ucf, *.qsf, *.as and *.js. -FILE_PATTERNS = +FILE_PATTERNS = # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -805,7 +805,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -821,7 +821,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -832,20 +832,20 @@ EXCLUDE_PATTERNS = # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = +EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. -EXAMPLE_PATTERNS = +EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands @@ -858,7 +858,7 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = +IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -875,7 +875,7 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. -INPUT_FILTER = +INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the @@ -884,7 +884,7 @@ INPUT_FILTER = # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. -FILTER_PATTERNS = +FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for @@ -899,14 +899,14 @@ FILTER_SOURCE_FILES = NO # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. -FILTER_SOURCE_PATTERNS = +FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = +USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -1018,7 +1018,7 @@ COLS_IN_ALPHA_INDEX = 5 # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output @@ -1062,7 +1062,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard @@ -1072,7 +1072,7 @@ HTML_HEADER = # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -1084,7 +1084,7 @@ HTML_FOOTER = # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = +HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets @@ -1097,7 +1097,7 @@ HTML_STYLESHEET = # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_STYLESHEET = +HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -1107,7 +1107,7 @@ HTML_EXTRA_STYLESHEET = # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_FILES = +HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to @@ -1236,7 +1236,7 @@ GENERATE_HTMLHELP = NO # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_FILE = +CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, @@ -1244,7 +1244,7 @@ CHM_FILE = # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -HHC_LOCATION = +HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). @@ -1257,7 +1257,7 @@ GENERATE_CHI = NO # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_INDEX_ENCODING = +CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it @@ -1288,7 +1288,7 @@ GENERATE_QHP = NO # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. -QCH_FILE = +QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace @@ -1313,7 +1313,7 @@ QHP_VIRTUAL_FOLDER = doc # filters). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom @@ -1321,21 +1321,21 @@ QHP_CUST_FILTER_NAME = # filters). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_ATTRS = +QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_SECT_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. -QHG_LOCATION = +QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To @@ -1468,7 +1468,7 @@ MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_EXTENSIONS = +MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site @@ -1476,7 +1476,7 @@ MATHJAX_EXTENSIONS = # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_CODEFILE = +MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and @@ -1536,7 +1536,7 @@ EXTERNAL_SEARCH = NO # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. -SEARCHENGINE_URL = +SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the @@ -1552,7 +1552,7 @@ SEARCHDATA_FILE = searchdata.xml # projects and redirect the results back to the right project. # This tag requires that the tag SEARCHENGINE is set to YES. -EXTERNAL_SEARCH_ID = +EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are @@ -1562,7 +1562,7 @@ EXTERNAL_SEARCH_ID = # EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... # This tag requires that the tag SEARCHENGINE is set to YES. -EXTRA_SEARCH_MAPPINGS = +EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output @@ -1626,7 +1626,7 @@ PAPER_TYPE = a4 # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. -EXTRA_PACKAGES = +EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for the # generated LaTeX document. The header should contain everything until the first @@ -1642,7 +1642,7 @@ EXTRA_PACKAGES = # to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_HEADER = +LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last @@ -1653,7 +1653,7 @@ LATEX_HEADER = # Note: Only use a user-defined footer if you know what you are doing! # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_FOOTER = +LATEX_FOOTER = # The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined # LaTeX style sheets that are included after the standard style sheets created @@ -1664,7 +1664,7 @@ LATEX_FOOTER = # list). # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_STYLESHEET = # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output @@ -1672,7 +1672,7 @@ LATEX_EXTRA_STYLESHEET = # markers available. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_EXTRA_FILES = +LATEX_EXTRA_FILES = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is # prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will @@ -1772,14 +1772,14 @@ RTF_HYPERLINKS = NO # default style sheet that doxygen normally uses. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_STYLESHEET_FILE = +RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is # similar to doxygen's config file. A template extensions file can be generated # using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_EXTENSIONS_FILE = +RTF_EXTENSIONS_FILE = # If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code # with syntax highlighting in the RTF output. @@ -1824,7 +1824,7 @@ MAN_EXTENSION = .3 # MAN_EXTENSION with the initial . removed. # This tag requires that the tag GENERATE_MAN is set to YES. -MAN_SUBDIR = +MAN_SUBDIR = # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real @@ -1937,7 +1937,7 @@ PERLMOD_PRETTY = YES # overwrite each other's variables. # This tag requires that the tag GENERATE_PERLMOD is set to YES. -PERLMOD_MAKEVAR_PREFIX = +PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor @@ -1978,7 +1978,7 @@ SEARCH_INCLUDES = YES # preprocessor. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = +INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the @@ -1986,7 +1986,7 @@ INCLUDE_PATH = # used. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -INCLUDE_FILE_PATTERNS = +INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. @@ -1996,7 +1996,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = +PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2005,7 +2005,7 @@ PREDEFINED = # definition found in the source code. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have @@ -2034,13 +2034,13 @@ SKIP_FUNCTION_MACROS = YES # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. -TAGFILES = +TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create a # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. -GENERATE_TAGFILE = +GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES, all external class will be listed in # the class index. If set to NO, only the inherited external classes will be @@ -2089,14 +2089,14 @@ CLASS_DIAGRAMS = YES # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. -MSCGEN_PATH = +MSCGEN_PATH = # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. # If left empty dia is assumed to be found in the default search path. -DIA_PATH = +DIA_PATH = # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. @@ -2145,7 +2145,7 @@ DOT_FONTSIZE = 10 # the path where dot can find it using this tag. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTPATH = +DOT_FONTPATH = # If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for # each documented class showing the direct and indirect inheritance relations. @@ -2289,26 +2289,26 @@ INTERACTIVE_SVG = NO # found. If left blank, it is assumed the dot tool can be found in the path. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_PATH = +DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the \dotfile # command). # This tag requires that the tag HAVE_DOT is set to YES. -DOTFILE_DIRS = +DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the \mscfile # command). -MSCFILE_DIRS = +MSCFILE_DIRS = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile # command). -DIAFILE_DIRS = +DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the # path where java can find the plantuml.jar file. If left blank, it is assumed @@ -2321,7 +2321,7 @@ PLANTUML_JAR_PATH = @PLANTUML_JARFILE@ # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. -PLANTUML_INCLUDE_PATH = +PLANTUML_INCLUDE_PATH = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes diff --git a/src/autocomplete.cc b/src/autocomplete.cc index 8d5b6848..03c0dcf9 100644 --- a/src/autocomplete.cc +++ b/src/autocomplete.cc @@ -4,30 +4,31 @@ Autocomplete::Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word) : view(view), interactive_completion(interactive_completion), strip_word(strip_word), state(State::IDLE) { view->get_buffer()->signal_changed().connect([this, &last_keyval] { - if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) { + if (CompletionDialog::get() && CompletionDialog::get()->is_visible()) { cancel_reparse(); return; } - if(!this->view->has_focus()) + if (!this->view->has_focus()) return; - if(is_continue_key(last_keyval) && (this->interactive_completion || state != State::IDLE)) + if (is_continue_key(last_keyval) && (this->interactive_completion || state != State::IDLE)) run(); else { stop(); - if(is_restart_key(last_keyval) && this->interactive_completion) + if (is_restart_key(last_keyval) && this->interactive_completion) run(); } }); - view->get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator &iterator, const Glib::RefPtr &mark) { - if(mark->get_name() == "insert") - stop(); - }); + view->get_buffer()->signal_mark_set().connect( + [this](const Gtk::TextBuffer::iterator &iterator, const Glib::RefPtr &mark) { + if (mark->get_name() == "insert") + stop(); + }); view->signal_key_release_event().connect([](GdkEventKey *key) { - if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) { - if(CompletionDialog::get()->on_key_release(key)) + if (CompletionDialog::get() && CompletionDialog::get()->is_visible()) { + if (CompletionDialog::get()->on_key_release(key)) return true; } return false; @@ -40,30 +41,31 @@ Autocomplete::Autocomplete(Gtk::TextView *view, bool &interactive_completion, gu } void Autocomplete::run() { - if(run_check()) { - if(!is_processing()) + if (run_check()) { + if (!is_processing()) return; - if(state == State::CANCELED) + if (state == State::CANCELED) state = State::RESTARTING; - if(state != State::IDLE) + if (state != State::IDLE) return; state = State::STARTING; before_add_rows(); - if(thread.joinable()) + if (thread.joinable()) thread.join(); auto buffer = view->get_buffer()->get_text(); auto iter = view->get_buffer()->get_insert()->get_iter(); auto line_nr = iter.get_line() + 1; auto column_nr = iter.get_line_index() + 1; - if(strip_word) { + if (strip_word) { auto pos = iter.get_offset() - 1; - while(pos >= 0 && ((buffer[pos] >= 'a' && buffer[pos] <= 'z') || (buffer[pos] >= 'A' && buffer[pos] <= 'Z') || - (buffer[pos] >= '0' && buffer[pos] <= '9') || buffer[pos] == '_')) { + while (pos >= 0 && + ((buffer[pos] >= 'a' && buffer[pos] <= 'z') || (buffer[pos] >= 'A' && buffer[pos] <= 'Z') || + (buffer[pos] >= '0' && buffer[pos] <= '9') || buffer[pos] == '_')) { buffer.replace(pos, 1, " "); column_nr--; pos--; @@ -71,7 +73,7 @@ void Autocomplete::run() { } thread = std::thread([this, line_nr, column_nr, buffer = std::move(buffer)] { auto lock = get_parse_lock(); - if(!is_processing()) + if (!is_processing()) return; stop_parse(); @@ -79,28 +81,26 @@ void Autocomplete::run() { rows.clear(); add_rows(buffer_raw, line_nr, column_nr); - if(is_processing()) { + if (is_processing()) { dispatcher.post([this]() { after_add_rows(); - if(state == State::RESTARTING) { + if (state == State::RESTARTING) { state = State::IDLE; reparse(); run(); - } - else if(state == State::CANCELED || rows.empty()) { + } else if (state == State::CANCELED || rows.empty()) { state = State::IDLE; reparse(); - } - else { + } else { auto start_iter = view->get_buffer()->get_insert()->get_iter(); - if(prefix.size() > 0 && !start_iter.backward_chars(prefix.size())) { + if (prefix.size() > 0 && !start_iter.backward_chars(prefix.size())) { state = State::IDLE; reparse(); return; } CompletionDialog::create(view, view->get_buffer()->create_mark(start_iter)); setup_dialog(); - for(auto &row : rows) { + for (auto &row : rows) { CompletionDialog::get()->add_row(row); row.clear(); } @@ -110,8 +110,7 @@ void Autocomplete::run() { CompletionDialog::get()->show(); } }); - } - else { + } else { dispatcher.post([this] { state = State::CANCELED; on_add_rows_error(); @@ -120,20 +119,20 @@ void Autocomplete::run() { }); } - if(state != State::IDLE) + if (state != State::IDLE) cancel_reparse(); } void Autocomplete::stop() { - if(state == State::STARTING || state == State::RESTARTING) + if (state == State::STARTING || state == State::RESTARTING) state = State::CANCELED; } void Autocomplete::setup_dialog() { - CompletionDialog::get()->on_show=[this] { + CompletionDialog::get()->on_show = [this] { on_show(); }; - + CompletionDialog::get()->on_hide = [this]() { view->get_buffer()->end_user_action(); tooltips.hide(); @@ -141,39 +140,40 @@ void Autocomplete::setup_dialog() { on_hide(); reparse(); }; - + CompletionDialog::get()->on_changed = [this](unsigned int index, const std::string &text) { - if(index >= rows.size()) { + if (index >= rows.size()) { tooltips.hide(); return; } - + on_changed(index, text); - + auto tooltip = get_tooltip(index); - if(tooltip.empty()) + if (tooltip.empty()) tooltips.hide(); else { tooltips.clear(); - auto create_tooltip_buffer = [ this, tooltip = std::move(tooltip) ]() { + auto create_tooltip_buffer = [this, tooltip = std::move(tooltip)]() { auto tooltip_buffer = Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()); - + tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), tooltip); - + return tooltip_buffer; }; - + auto iter = CompletionDialog::get()->start_mark->get_iter(); - tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter)); - + tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), + view->get_buffer()->create_mark(iter)); + tooltips.show(true); } }; - - CompletionDialog::get()->on_select=[this](unsigned int index, const std::string &text, bool hide_window) { - if(index>=rows.size()) + + CompletionDialog::get()->on_select = [this](unsigned int index, const std::string &text, bool hide_window) { + if (index >= rows.size()) return; - + on_select(index, text, hide_window); }; } diff --git a/src/autocomplete.h b/src/autocomplete.h index 1d5939bc..13a2500d 100644 --- a/src/autocomplete.h +++ b/src/autocomplete.h @@ -1,4 +1,5 @@ #pragma once + #include "dispatcher.h" #include "tooltips.h" #include @@ -13,7 +14,9 @@ class Autocomplete { Dispatcher dispatcher; public: - enum class State { IDLE, STARTING, RESTARTING, CANCELED }; + enum class State { + IDLE, STARTING, RESTARTING, CANCELED + }; std::string prefix; std::mutex prefix_mutex; @@ -40,19 +43,23 @@ class Autocomplete { /// The handler is not run in the main loop. std::function add_rows = [](std::string &, int, int) {}; - + std::function on_show = [] {}; std::function on_hide = [] {}; - std::function on_changed = [](unsigned int index, const std::string &text) {}; - std::function on_select = [](unsigned int index, const std::string &text, bool hide_window) {}; - - std::function get_tooltip = [](unsigned int index) {return std::string();}; + std::function on_changed = [](unsigned int index, + const std::string &text) {}; + std::function on_select = [](unsigned int index, + const std::string &text, + bool hide_window) {}; + + std::function get_tooltip = [](unsigned int index) { return std::string(); }; Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word); void run(); + void stop(); - + private: void setup_dialog(); }; diff --git a/src/cmake.cc b/src/cmake.cc index 1d23a314..734cf0a1 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -1,378 +1,124 @@ -#include "cmake.h" +#include +#include "project_build.h" +#include "makefile_base.h" #include "filesystem.h" -#include "dialogs.h" #include "config.h" -#include "terminal.h" -#include -#include "compile_commands.h" +#include "rapidxml.h" -CMake::CMake(const boost::filesystem::path &path) { - const auto find_cmake_project=[](const boost::filesystem::path &cmake_path) { - for(auto &line: filesystem::read_lines(cmake_path)) { - const static std::regex project_regex(R"(^ *project *\(.*\r?$)", std::regex::icase); - std::smatch sm; - if(std::regex_match(line, sm, project_regex)) - return true; - } - return false; - }; - - auto search_path=boost::filesystem::is_directory(path)?path:path.parent_path(); - while(true) { - auto search_cmake_path=search_path/"CMakeLists.txt"; - if(boost::filesystem::exists(search_cmake_path)) { - paths.emplace(paths.begin(), search_cmake_path); - if(find_cmake_project(search_cmake_path)) { - project_path=search_path; - break; - } - } - if(search_path==search_path.root_directory()) - break; - search_path=search_path.parent_path(); - } -} +namespace { + class CMakeProjectUpdater : public MakefileProjectUpdater { + public: + using MakefileProjectUpdater::MakefileProjectUpdater; -bool CMake::update_default_build(const boost::filesystem::path &default_build_path, bool force) { - if(project_path.empty() || !boost::filesystem::exists(project_path/"CMakeLists.txt") || default_build_path.empty()) - return false; - - if(!boost::filesystem::exists(default_build_path)) { - boost::system::error_code ec; - boost::filesystem::create_directories(default_build_path, ec); - if(ec) { - Terminal::get().print("Error: could not create "+default_build_path.string()+": "+ec.message()+"\n", true); - return false; + private: + std::string get_extra_arguments() const { + return m_configuration == Configuration::Debug ? " -DCMAKE_BUILD_TYPE=Debug" : " -DCMAKE_BUILD_TYPE=Release"; } - } - - if(!force && boost::filesystem::exists(default_build_path/"compile_commands.json")) - return true; - - auto compile_commands_path=default_build_path/"compile_commands.json"; - Dialog::Message message("Creating/updating default build"); - auto exit_status=Terminal::get().process(Config::get().project.cmake.command+' '+ - filesystem::escape_argument(project_path.string())+" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", default_build_path); - message.hide(); - if(exit_status==EXIT_SUCCESS) { -#ifdef _WIN32 //Temporary fix to MSYS2's libclang - auto compile_commands_file=filesystem::read(compile_commands_path); - auto replace_drive = [&compile_commands_file](const std::string& param) { - size_t pos=0; - auto param_size = param.length(); - while((pos=compile_commands_file.find(param+"/", pos))!=std::string::npos) { - if(pos+param_size+1 cmake_executables; - for(auto ¶meter: parameters) { - if(parameter.second.size()>1 && parameter.second[0].size()>0 && parameter.second[0].compare(0, 2, "${")!=0) { - auto executable=(parameter.first.parent_path()/parameter.second[0]).string(); - auto project_path_str=project_path.string(); - size_t pos=executable.find(project_path_str); - if(pos!=std::string::npos) - executable.replace(pos, project_path_str.size(), build_path.string()); - cmake_executables.emplace_back(executable); - } - } - - - CompileCommands compile_commands(build_path); - std::vector> command_files_and_maybe_executables; - for(auto &command: compile_commands.commands) { - auto command_file=filesystem::get_normal_path(command.file); - auto values=command.parameter_values("-o"); - if(!values.empty()) { - size_t pos; - values[0].erase(0, 11); - if((pos=values[0].find(".dir"))!=std::string::npos) { - auto executable=command.directory/values[0].substr(0, pos); - command_files_and_maybe_executables.emplace_back(command_file, executable); - } - } - } - - size_t best_match_size=-1; - boost::filesystem::path best_match_executable; - - for(auto &cmake_executable: cmake_executables) { - for(auto &command_file_and_maybe_executable: command_files_and_maybe_executables) { - auto &command_file=command_file_and_maybe_executable.first; - auto &maybe_executable=command_file_and_maybe_executable.second; - if(cmake_executable==maybe_executable) { - if(command_file==file_path) - return maybe_executable; - auto command_file_directory=command_file.parent_path(); - if(filesystem::file_in_path(file_path, command_file_directory)) { - auto size=static_cast(std::distance(command_file_directory.begin(), command_file_directory.end())); - if(best_match_size==static_cast(-1) || best_match_size drives = {"-I", "-isystem"}; + for(const auto &target: drives) { + size_t pos = 0; + auto param_size = target.length(); + while((pos = compile_commands_file.find(target+"/", pos)) != std::string::npos) { + if(pos+param_size+1 < compile_commands_file.size()) + compile_commands_file.replace(pos, param_size+2, target+compile_commands_file[pos+param_size+1]+":"); + else + break; } } + filesystem::write(compile_commands_path, compile_commands_file); +#endif } - } - if(!best_match_executable.empty()) - return best_match_executable; - - for(auto &command_file_and_maybe_executable: command_files_and_maybe_executables) { - auto &command_file=command_file_and_maybe_executable.first; - auto &maybe_executable=command_file_and_maybe_executable.second; - if(command_file==file_path) - return maybe_executable; - auto command_file_directory=command_file.parent_path(); - if(filesystem::file_in_path(file_path, command_file_directory)) { - auto size=static_cast(std::distance(command_file_directory.begin(), command_file_directory.end())); - if(best_match_size==static_cast(-1) || best_match_sizepath().extension().compare(std::string(".cbp")) == 0) + return boost::filesystem::absolute(it->path()); + } + return ""; } - } -} -void CMake::parse_variable_parameters(std::string &data) { - size_t pos=0; - bool inside_quote=false; - char last_char=0; - while(pos(const_cast(project_file.c_str())); } - - if(pos!=static_cast(-1)) - last_char=data[pos]; - pos++; - } - for(auto &var: variables) { - auto pos=data.find("${"+var.first+'}'); - while(pos!=std::string::npos) { - data.replace(pos, var.first.size()+3, var.second); - pos=data.find("${"+var.first+'}'); + + static bool is_fast_target(const std::string &target) { + if(target.size() > 5) + return target.substr(target.size()-5, 5) == "/fast"; + return false; } - } - - //Remove variables we do not know: - pos=data.find("${"); - auto pos_end=data.find('}', pos+2); - while(pos!=std::string::npos && pos_end!=std::string::npos) { - data.erase(pos, pos_end-pos+1); - pos=data.find("${"); - pos_end=data.find('}', pos+2); - } -} -void CMake::parse() { - read_files(); - remove_tabs(); - remove_comments(); - remove_newlines_inside_parentheses(); - parsed=true; -} + static bool is_executable(int type) noexcept { return type == 0 || type == 1 || type == 5; } -std::vector CMake::get_function_parameters(std::string &data) { - std::vector parameters; - size_t pos=0; - size_t parameter_pos=0; - bool inside_quote=false; - char last_char=0; - while(pos(-1)) - last_char=data[pos]; - pos++; - } - parameters.emplace_back(data.substr(parameter_pos)); - for(auto &var: variables) { - for(auto ¶meter: parameters) { - auto pos=parameter.find("${"+var.first+'}'); - while(pos!=std::string::npos) { - parameter.replace(pos, var.first.size()+3, var.second); - pos=parameter.find("${"+var.first+'}'); + static auto get_options(const rapidxml::xml_node<> *target) { + std::map options; + for(auto option = target->first_node("Option"); option != nullptr; option = option->next_sibling("Option")) { + auto attr = option->first_attribute(); + options[std::string(attr->name(), attr->name_size())] = std::string(attr->value(), attr->value_size()); } + return options; } - } - return parameters; -} -std::vector > > CMake::get_functions_parameters(const std::string &name) { - const std::regex function_regex("^ *"+name+R"( *\( *(.*)\) *\r?$)", std::regex::icase); - variables.clear(); - if(!parsed) - parse(); - std::vector > > functions; - for(size_t c=0;cstart_line) { - auto line=files[c].substr(start_line, end_line-start_line); - std::smatch sm; - const static std::regex set_regex(R"(^ *set *\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\) *\r?$)", std::regex::icase); - const static std::regex project_regex(R"(^ *project *\( *([^ ]+).*\) *\r?$)", std::regex::icase); - if(std::regex_match(line, sm, set_regex)) { - auto data=sm[2].str(); - while(data.size()>0 && data.back()==' ') - data.pop_back(); - parse_variable_parameters(data); - variables[sm[1].str()]=data; - } - else if(std::regex_match(line, sm, project_regex)) { - auto data=sm[1].str(); - parse_variable_parameters(data); - variables["CMAKE_PROJECT_NAME"]=data; //TODO: is this variable deprecated/non-standard? - variables["PROJECT_NAME"]=data; - } - if(std::regex_match(line, sm, function_regex)) { - auto data=sm[1].str(); - while(data.size()>0 && data.back()==' ') - data.pop_back(); - auto parameters=get_function_parameters(data); - functions.emplace_back(paths[c], parameters); - } + rapidxml::xml_document<> document; + std::vector executables; + }; + + GetExecutableHelper::GetExecutableHelper(const boost::filesystem::path &build_path) { + parse_project(build_path); + auto build = document.first_node("CodeBlocks_project_file")->first_node("Project")->first_node("Build"); + for(auto target = build->first_node("Target"); target != nullptr; target = target->next_sibling("Target")) { + auto title_attr = target->first_attribute("title"); + if(!is_fast_target(std::string(title_attr->value(), title_attr->value_size()))) { + auto options = get_options(target); + if(is_executable(options["type"][0]-'0')) + executables.emplace_back(options["output"]); } - pos=end_line+1; } } - return functions; + } + +boost::filesystem::path Project::CMakeBuild::get_executable(const boost::filesystem::path &) { + GetExecutableHelper helper(get_default_path()); + auto &exec = helper.get_executables(); + return exec.empty() ? "" : exec[0]; +} + +std::string Project::CMakeBuild::get_compile_command() { return Config::get().project.cmake.compile_command; } diff --git a/src/cmake.h b/src/cmake.h deleted file mode 100644 index f87010a3..00000000 --- a/src/cmake.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include -#include -#include -#include - -class CMake { -public: - CMake(const boost::filesystem::path &path); - boost::filesystem::path project_path; - - bool update_default_build(const boost::filesystem::path &default_build_path, bool force=false); - bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force=false); - - boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); - -private: - std::vector paths; - std::vector files; - std::unordered_map variables; - void read_files(); - void remove_tabs(); - void remove_comments(); - void remove_newlines_inside_parentheses(); - void parse_variable_parameters(std::string &data); - void parse(); - std::vector get_function_parameters(std::string &data); - std::vector > > get_functions_parameters(const std::string &name); - bool parsed=false; -}; diff --git a/src/compile_commands.cc b/src/compile_commands.cc index 27f946bd..412f1338 100644 --- a/src/compile_commands.cc +++ b/src/compile_commands.cc @@ -1,11 +1,12 @@ #include "compile_commands.h" #include "clangmm.h" -#include +#include #include +#include std::vector CompileCommands::Command::parameter_values(const std::string ¶meter_name) const { std::vector parameter_values; - + bool found_argument=false; for(auto ¶meter: parameters) { if(found_argument) { @@ -15,74 +16,77 @@ std::vector CompileCommands::Command::parameter_values(const std::s else if(parameter==parameter_name) found_argument=true; } - + return parameter_values; } CompileCommands::CompileCommands(const boost::filesystem::path &build_path) { try { - boost::property_tree::ptree root_pt; - boost::property_tree::json_parser::read_json((build_path/"compile_commands.json").string(), root_pt); - - auto commands_pt=root_pt.get_child(""); - for(auto &command: commands_pt) { - boost::filesystem::path directory=command.second.get("directory"); - auto parameters_str=command.second.get("command"); - boost::filesystem::path file=command.second.get("file"); - - std::vector parameters; - bool backslash=false; - bool single_quote=false; - bool double_quote=false; - size_t parameter_start_pos=std::string::npos; - size_t parameter_size=0; - auto add_parameter=[¶meters, ¶meters_str, ¶meter_start_pos, ¶meter_size] { - auto parameter=parameters_str.substr(parameter_start_pos, parameter_size); - // Remove escaping - for(size_t c=0;c> commands_pt; + + for(auto& command: commands_pt) { + boost::filesystem::path directory=command["directory"].get(); + boost::filesystem::path file=command["file"].get(); + + commands.emplace_back( + Command{directory, parse_command(command["command"]), boost::filesystem::absolute(file, build_path)}); } } catch(...) {} } +std::vector CompileCommands::parse_command(const std::string& parameters_str) const { + std::vector parameters; + bool backslash=false; + bool single_quote=false; + bool double_quote=false; + size_t parameter_start_pos= std::string::npos; + size_t parameter_size=0; + auto add_parameter=[¶meters, ¶meters_str, ¶meter_start_pos, ¶meter_size] { + auto parameter=parameters_str.substr(parameter_start_pos, parameter_size); + // Remove escaping + for(size_t c=0;c CompileCommands::get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { std::string default_std_argument="-std=c++1y"; - + std::vector arguments; if(!build_path.empty()) { clangmm::CompilationDatabase db(build_path.string()); @@ -110,7 +114,7 @@ std::vector CompileCommands::get_arguments(const boost::filesystem: } else arguments.emplace_back(default_std_argument); - + auto clang_version_string=clangmm::to_string(clang_getClangVersion()); const static std::regex clang_version_regex(R"(^[A-Za-z ]+([0-9.]+).*$)"); std::smatch sm; @@ -130,23 +134,23 @@ std::vector CompileCommands::get_arguments(const boost::filesystem: #endif } arguments.emplace_back("-fretain-comments-from-system-headers"); - + auto extension=file_path.extension().string(); if(extension==".h" || //TODO: temporary fix for .h-files (parse as c++) extension!=".c") arguments.emplace_back("-xc++"); - + if(extension.empty() || (1 CompileCommands::get_arguments(const boost::filesystem: arguments.emplace_back("-finclude-default-header"); arguments.emplace_back("-Wno-gcc-compat"); } - + if(!build_path.empty()) { arguments.emplace_back("-working-directory"); arguments.emplace_back(build_path.string()); diff --git a/src/compile_commands.h b/src/compile_commands.h index 642cd33f..b18a1cbc 100644 --- a/src/compile_commands.h +++ b/src/compile_commands.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -10,13 +11,17 @@ class CompileCommands { boost::filesystem::path directory; std::vector parameters; boost::filesystem::path file; - + std::vector parameter_values(const std::string ¶meter_name) const; }; - - CompileCommands(const boost::filesystem::path &build_path); + + explicit CompileCommands(const boost::filesystem::path &build_path); + std::vector commands; - + /// Return arguments for the given file using libclangmm - static std::vector get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); + static std::vector + get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); + + std::vector parse_command(const std::string& parameters_str) const; }; diff --git a/src/config.cc b/src/config.cc index 934b4388..83c35b3d 100644 --- a/src/config.cc +++ b/src/config.cc @@ -1,20 +1,177 @@ #include "config.h" -#include #include "files.h" #include #include "filesystem.h" #include "terminal.h" -#include +#include + +namespace { + /// Used to dispatch Terminal outputs after juCi++ GUI setup and configuration + Dispatcher dispatcher; + + void make_version_dependent_corrections(boost::property_tree::ptree &cfg, + const boost::property_tree::ptree &default_cfg, + const std::string &version) { + auto &keybindings_cfg = cfg.get_child("keybindings"); + try { + if(version <= "1.2.4") { + auto it_file_print = keybindings_cfg.find("print"); + if(it_file_print != keybindings_cfg.not_found() && it_file_print->second.data() == "p") { + dispatcher.post([] { + ::Terminal::get().print("Preference change: keybindings.print set to \"\"\n"); + }); + it_file_print->second.data() = ""; + } + } + } + catch(const std::exception &e) { + std::cerr << "Error correcting preferences: " << e.what() << std::endl; + } + } + + bool add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, + std::string parent_path = "") { + if(!parent_path.empty()) + parent_path += "."; + bool unchanged = true; + for(auto &node: default_cfg) { + auto path = parent_path+node.first; + try { + cfg.get(path); + } + catch(const std::exception &e) { + cfg.add(path, node.second.data()); + unchanged = false; + } + unchanged &= add_missing_nodes(cfg, node.second, path); + } + return unchanged; + } + + bool remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, + std::string parent_path = "") { + if(!parent_path.empty()) + parent_path += "."; + bool unchanged = true; + for(auto it = cfg.begin(); it != cfg.end();) { + auto path = parent_path+it->first; + try { + default_cfg.get(path); + unchanged &= remove_deprecated_nodes(it->second, default_cfg, path); + ++it; + } + catch(const std::exception &e) { + it = cfg.erase(it); + unchanged = false; + } + } + return unchanged; + } + + void update(boost::property_tree::ptree &cfg) { + auto &cfgs = Config::get(); + boost::property_tree::ptree default_cfg; + bool cfg_ok = true; + if(cfg.get("version") != JUCI_VERSION) { + std::stringstream ss; + ss << default_config_file; + boost::property_tree::read_json(ss, default_cfg); + cfg_ok = false; + auto it_version = cfg.find("version"); + if(it_version != cfg.not_found()) { + make_version_dependent_corrections(cfg, default_cfg, it_version->second.data()); + it_version->second.data() = JUCI_VERSION; + } + + auto style_path = cfgs.home_juci_path / "styles"; + filesystem::write(style_path / "juci-light.xml", juci_light_style); + filesystem::write(style_path / "juci-dark.xml", juci_dark_style); + filesystem::write(style_path / "juci-dark-blue.xml", juci_dark_blue_style); + } else + return; + cfg_ok &= add_missing_nodes(cfg, default_cfg); + cfg_ok &= remove_deprecated_nodes(cfg, default_cfg); + if(!cfg_ok) + boost::property_tree::write_json((cfgs.home_juci_path / "config" / "config.json").string(), cfg); + } + + void read(const boost::property_tree::ptree &cfg) { + auto &cfgs = Config::get(); + auto keybindings_pt = cfg.get_child("keybindings"); + for(auto &i : keybindings_pt) { + cfgs.menu.keys[i.first] = i.second.get_value(); + } + + auto source_json = cfg.get_child("source"); + cfgs.source.style = source_json.get("style"); + cfgs.source.font = source_json.get("font"); + cfgs.source.cleanup_whitespace_characters = source_json.get("cleanup_whitespace_characters"); + cfgs.source.show_whitespace_characters = source_json.get("show_whitespace_characters"); + cfgs.source.format_style_on_save = source_json.get("format_style_on_save"); + cfgs.source.format_style_on_save_if_style_file_found = source_json.get( + "format_style_on_save_if_style_file_found"); + cfgs.source.smart_brackets = source_json.get("smart_brackets"); + cfgs.source.smart_inserts = source_json.get("smart_inserts"); + if(cfgs.source.smart_inserts) + cfgs.source.smart_brackets = true; + cfgs.source.show_map = source_json.get("show_map"); + cfgs.source.map_font_size = source_json.get("map_font_size"); + cfgs.source.show_git_diff = source_json.get("show_git_diff"); + cfgs.source.show_background_pattern = source_json.get("show_background_pattern"); + cfgs.source.show_right_margin = source_json.get("show_right_margin"); + cfgs.source.right_margin_position = source_json.get("right_margin_position"); + cfgs.source.spellcheck_language = source_json.get("spellcheck_language"); + cfgs.source.default_tab_char = source_json.get("default_tab_char"); + cfgs.source.default_tab_size = source_json.get("default_tab_size"); + cfgs.source.auto_tab_char_and_size = source_json.get("auto_tab_char_and_size"); + cfgs.source.tab_indents_line = source_json.get("tab_indents_line"); + cfgs.source.wrap_lines = source_json.get("wrap_lines"); + cfgs.source.highlight_current_line = source_json.get("highlight_current_line"); + cfgs.source.show_line_numbers = source_json.get("show_line_numbers"); + cfgs.source.enable_multiple_cursors = source_json.get("enable_multiple_cursors"); + cfgs.source.auto_reload_changed_files = source_json.get("auto_reload_changed_files"); + cfgs.source.clang_format_style = source_json.get("clang_format_style"); + cfgs.source.clang_usages_threads = static_cast(source_json.get("clang_usages_threads")); + auto pt_doc_search = cfg.get_child("documentation_searches"); + for(auto &pt_doc_search_lang: pt_doc_search) { + cfgs.source.documentation_searches[pt_doc_search_lang.first].separator = pt_doc_search_lang.second.get( + "separator"); + auto &queries = cfgs.source.documentation_searches.find(pt_doc_search_lang.first)->second.queries; + for(auto &i: pt_doc_search_lang.second.get_child("queries")) { + queries[i.first] = i.second.get_value(); + } + } + + cfgs.window.theme_name = cfg.get("gtk_theme.name"); + cfgs.window.theme_variant = cfg.get("gtk_theme.variant"); + cfgs.window.version = cfg.get("version"); + + cfgs.project.default_build_path = cfg.get("project.default_build_path"); + cfgs.project.debug_build_path = cfg.get("project.debug_build_path"); + cfgs.project.cmake.command = cfg.get("project.cmake.command"); + cfgs.project.cmake.compile_command = cfg.get("project.cmake.compile_command"); + cfgs.project.meson.command = cfg.get("project.meson.command"); + cfgs.project.meson.compile_command = cfg.get("project.meson.compile_command"); + cfgs.project.save_on_compile_or_run = cfg.get("project.save_on_compile_or_run"); + cfgs.project.clear_terminal_on_compile = cfg.get("project.clear_terminal_on_compile"); + cfgs.project.ctags_command = cfg.get("project.ctags_command"); + cfgs.project.python_command = cfg.get("project.python_command"); + + cfgs.terminal.history_size = cfg.get("terminal.history_size"); + cfgs.terminal.font = cfg.get("terminal.font"); + } +} Config::Config() { - home_path=filesystem::get_home_path(); - if(home_path.empty()) + home_path = filesystem::get_home_path(); + if (home_path.empty()) throw std::runtime_error("Could not find home path"); - home_juci_path=home_path/".juci"; + home_juci_path = home_path / ".juci"; } void Config::load() { - auto config_json = (home_juci_path/"config"/"config.json").string(); // This causes some redundant copies, but assures windows support + auto config_json = (home_juci_path / "config" / + "config.json").string(); // This causes some redundant copies, but assures windows support boost::property_tree::ptree cfg; try { find_or_create_config_files(); @@ -22,9 +179,9 @@ void Config::load() { update(cfg); read(cfg); } - catch(const std::exception &e) { - dispatcher.post([config_json, e_what=std::string(e.what())] { - ::Terminal::get().print("Error: could not parse "+config_json+": "+e_what+"\n", true); + catch (const std::exception &e) { + dispatcher.post([config_json, e_what = std::string(e.what())] { + ::Terminal::get().print("Error: could not parse " + config_json + ": " + e_what + "\n", true); }); std::stringstream ss; ss << default_config_file; @@ -34,171 +191,26 @@ void Config::load() { } void Config::find_or_create_config_files() { - auto config_dir = home_juci_path/"config"; - auto config_json = config_dir/"config.json"; + auto config_dir = home_juci_path / "config"; + auto config_json = config_dir / "config.json"; boost::filesystem::create_directories(config_dir); // io exp captured by calling method if (!boost::filesystem::exists(config_json)) filesystem::write(config_json, default_config_file); - auto juci_style_path = home_juci_path/"styles"; + auto juci_style_path = home_juci_path / "styles"; boost::filesystem::create_directories(juci_style_path); // io exp captured by calling method - juci_style_path/="juci-light.xml"; - if(!boost::filesystem::exists(juci_style_path)) + juci_style_path /= "juci-light.xml"; + if (!boost::filesystem::exists(juci_style_path)) filesystem::write(juci_style_path, juci_light_style); - juci_style_path=juci_style_path.parent_path(); - juci_style_path/="juci-dark.xml"; - if(!boost::filesystem::exists(juci_style_path)) + juci_style_path = juci_style_path.parent_path(); + juci_style_path /= "juci-dark.xml"; + if (!boost::filesystem::exists(juci_style_path)) filesystem::write(juci_style_path, juci_dark_style); - juci_style_path=juci_style_path.parent_path(); - juci_style_path/="juci-dark-blue.xml"; - if(!boost::filesystem::exists(juci_style_path)) + juci_style_path = juci_style_path.parent_path(); + juci_style_path /= "juci-dark-blue.xml"; + if (!boost::filesystem::exists(juci_style_path)) filesystem::write(juci_style_path, juci_dark_blue_style); } - -void Config::update(boost::property_tree::ptree &cfg) { - boost::property_tree::ptree default_cfg; - bool cfg_ok=true; - if(cfg.get("version")!=JUCI_VERSION) { - std::stringstream ss; - ss << default_config_file; - boost::property_tree::read_json(ss, default_cfg); - cfg_ok=false; - auto it_version=cfg.find("version"); - if(it_version!=cfg.not_found()) { - make_version_dependent_corrections(cfg, default_cfg, it_version->second.data()); - it_version->second.data()=JUCI_VERSION; - } - - auto style_path=home_juci_path/"styles"; - filesystem::write(style_path/"juci-light.xml", juci_light_style); - filesystem::write(style_path/"juci-dark.xml", juci_dark_style); - filesystem::write(style_path/"juci-dark-blue.xml", juci_dark_blue_style); - } - else - return; - cfg_ok&=add_missing_nodes(cfg, default_cfg); - cfg_ok&=remove_deprecated_nodes(cfg, default_cfg); - if(!cfg_ok) - boost::property_tree::write_json((home_juci_path/"config"/"config.json").string(), cfg); -} - -void Config::make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, const std::string &version) { - auto &keybindings_cfg=cfg.get_child("keybindings"); - try { - if(version<="1.2.4") { - auto it_file_print=keybindings_cfg.find("print"); - if(it_file_print!=keybindings_cfg.not_found() && it_file_print->second.data()=="p") { - dispatcher.post([] { - ::Terminal::get().print("Preference change: keybindings.print set to \"\"\n"); - }); - it_file_print->second.data()=""; - } - } - } - catch(const std::exception &e) { - std::cerr << "Error correcting preferences: " << e.what() << std::endl; - } -} - -bool Config::add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) { - if(parent_path.size()>0) - parent_path+="."; - bool unchanged=true; - for(auto &node: default_cfg) { - auto path=parent_path+node.first; - try { - cfg.get(path); - } - catch(const std::exception &e) { - cfg.add(path, node.second.data()); - unchanged=false; - } - unchanged&=add_missing_nodes(cfg, node.second, path); - } - return unchanged; -} - -bool Config::remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) { - if(parent_path.size()>0) - parent_path+="."; - bool unchanged=true; - for(auto it=cfg.begin();it!=cfg.end();) { - auto path=parent_path+it->first; - try { - default_cfg.get(path); - unchanged&=remove_deprecated_nodes(it->second, default_cfg, path); - ++it; - } - catch(const std::exception &e) { - it=cfg.erase(it); - unchanged=false; - } - } - return unchanged; -} - -void Config::read(const boost::property_tree::ptree &cfg) { - auto keybindings_pt = cfg.get_child("keybindings"); - for (auto &i : keybindings_pt) { - menu.keys[i.first] = i.second.get_value(); - } - - auto source_json = cfg.get_child("source"); - source.style=source_json.get("style"); - source.font=source_json.get("font"); - source.cleanup_whitespace_characters=source_json.get("cleanup_whitespace_characters"); - source.show_whitespace_characters=source_json.get("show_whitespace_characters"); - source.format_style_on_save=source_json.get("format_style_on_save"); - source.format_style_on_save_if_style_file_found=source_json.get("format_style_on_save_if_style_file_found"); - source.smart_brackets=source_json.get("smart_brackets"); - source.smart_inserts=source_json.get("smart_inserts"); - if(source.smart_inserts) - source.smart_brackets=true; - source.show_map = source_json.get("show_map"); - source.map_font_size = source_json.get("map_font_size"); - source.show_git_diff = source_json.get("show_git_diff"); - source.show_background_pattern = source_json.get("show_background_pattern"); - source.show_right_margin = source_json.get("show_right_margin"); - source.right_margin_position = source_json.get("right_margin_position"); - source.spellcheck_language = source_json.get("spellcheck_language"); - source.default_tab_char = source_json.get("default_tab_char"); - source.default_tab_size = source_json.get("default_tab_size"); - source.auto_tab_char_and_size = source_json.get("auto_tab_char_and_size"); - source.tab_indents_line = source_json.get("tab_indents_line"); - source.wrap_lines = source_json.get("wrap_lines"); - source.highlight_current_line = source_json.get("highlight_current_line"); - source.show_line_numbers = source_json.get("show_line_numbers"); - source.enable_multiple_cursors = source_json.get("enable_multiple_cursors"); - source.auto_reload_changed_files = source_json.get("auto_reload_changed_files"); - source.clang_format_style = source_json.get("clang_format_style"); - source.clang_usages_threads = static_cast(source_json.get("clang_usages_threads")); - auto pt_doc_search=cfg.get_child("documentation_searches"); - for(auto &pt_doc_search_lang: pt_doc_search) { - source.documentation_searches[pt_doc_search_lang.first].separator=pt_doc_search_lang.second.get("separator"); - auto &queries=source.documentation_searches.find(pt_doc_search_lang.first)->second.queries; - for(auto &i: pt_doc_search_lang.second.get_child("queries")) { - queries[i.first]=i.second.get_value(); - } - } - - window.theme_name=cfg.get("gtk_theme.name"); - window.theme_variant=cfg.get("gtk_theme.variant"); - window.version = cfg.get("version"); - - project.default_build_path=cfg.get("project.default_build_path"); - project.debug_build_path=cfg.get("project.debug_build_path"); - project.cmake.command=cfg.get("project.cmake.command"); - project.cmake.compile_command=cfg.get("project.cmake.compile_command"); - project.meson.command=cfg.get("project.meson.command"); - project.meson.compile_command=cfg.get("project.meson.compile_command"); - project.save_on_compile_or_run=cfg.get("project.save_on_compile_or_run"); - project.clear_terminal_on_compile=cfg.get("project.clear_terminal_on_compile"); - project.ctags_command=cfg.get("project.ctags_command"); - project.python_command=cfg.get("project.python_command"); - - terminal.history_size=cfg.get("terminal.history_size"); - terminal.font=cfg.get("terminal.font"); -} diff --git a/src/config.h b/src/config.h index 1c1000be..ba56be69 100644 --- a/src/config.h +++ b/src/config.h @@ -1,11 +1,8 @@ #pragma once -#include + #include #include #include -#include -#include -#include "dispatcher.h" class Config { public: @@ -13,20 +10,20 @@ class Config { public: std::unordered_map keys; }; - + class Window { public: std::string theme_name; std::string theme_variant; std::string version; }; - + class Terminal { public: int history_size; std::string font; }; - + class Project { public: class CMake { @@ -34,12 +31,13 @@ class Config { std::string command; std::string compile_command; }; + class Meson { public: std::string command; std::string compile_command; }; - + std::string default_build_path; std::string debug_build_path; CMake cmake; @@ -49,7 +47,7 @@ class Config { std::string ctags_command; std::string python_command; }; - + class Source { public: class DocumentationSearch { @@ -57,27 +55,27 @@ class Config { std::string separator; std::unordered_map queries; }; - + std::string style; std::string font; std::string spellcheck_language; - + bool cleanup_whitespace_characters; std::string show_whitespace_characters; - + bool format_style_on_save; bool format_style_on_save_if_style_file_found; - + bool smart_brackets; bool smart_inserts; - + bool show_map; std::string map_font_size; bool show_git_diff; bool show_background_pattern; bool show_right_margin; unsigned right_margin_position; - + bool auto_tab_char_and_size; char default_tab_char; unsigned default_tab_size; @@ -87,39 +85,33 @@ class Config { bool show_line_numbers; bool enable_multiple_cursors; bool auto_reload_changed_files; - + std::string clang_format_style; unsigned clang_usages_threads; - + std::unordered_map documentation_searches; }; + private: Config(); + public: static Config &get() { static Config singleton; return singleton; } - + void load(); - + Menu menu; Window window; Terminal terminal; Project project; Source source; - + boost::filesystem::path home_path; boost::filesystem::path home_juci_path; private: - /// Used to dispatch Terminal outputs after juCi++ GUI setup and configuration - Dispatcher dispatcher; - void find_or_create_config_files(); - void update(boost::property_tree::ptree &cfg); - void make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, const std::string &version); - bool add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path=""); - bool remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path=""); - void read(const boost::property_tree::ptree &cfg); }; diff --git a/src/ctags.cc b/src/ctags.cc index 55b9fb43..72bcda59 100644 --- a/src/ctags.cc +++ b/src/ctags.cc @@ -10,13 +10,13 @@ std::pair > Ctags::get_result(const boost::filesystem::path &path) { auto build=Project::Build::create(path); - auto run_path=build->project_path; + auto run_path=build->get_project_path(); std::string exclude; if(!run_path.empty()) { auto relative_default_path=filesystem::get_relative_path(build->get_default_path(), run_path); if(!relative_default_path.empty()) exclude+=" --exclude="+relative_default_path.string(); - + auto relative_debug_path=filesystem::get_relative_path(build->get_debug_path(), run_path); if(!relative_debug_path.empty()) exclude+=" --exclude="+relative_debug_path.string(); @@ -28,7 +28,7 @@ std::pair > Ctags::g else run_path=path.parent_path(); } - + std::stringstream stdin_stream; //TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below auto stdout_stream=std::make_unique(); @@ -58,7 +58,7 @@ Ctags::Location Ctags::get_location(const std::string &line, bool markup) { if(!((chr>='a' && chr<='z') || (chr>='A' && chr<='Z') || (chr>='0' && chr<='9') || chr=='_')) location.symbol.erase(8, 1); } - + location.file_path=sm[2].str(); location.source=sm[4].str(); try { @@ -70,11 +70,11 @@ Ctags::Location Ctags::get_location(const std::string &line, bool markup) { location.scope=sm[7].str(); if(!sm[5].str().empty()) { location.index=sm[3].str().size(); - + size_t pos=location.source.find(location.symbol); if(pos!=std::string::npos) location.index+=pos; - + if(markup) { location.source=Glib::Markup::escape_text(location.source); auto symbol=Glib::Markup::escape_text(location.symbol); @@ -95,7 +95,7 @@ Ctags::Location Ctags::get_location(const std::string &line, bool markup) { } else std::cerr << "Warning (ctags): please report to the juCi++ project that the following line was not parsed:\n" << line << std::endl; - + return location; } @@ -127,7 +127,7 @@ std::vector Ctags::get_locations(const boost::filesystem::path if(result.second->tellg()==0) return std::vector(); result.second->seekg(0, std::ios::beg); - + //insert name into type size_t c=0; size_t bracket_count=0; @@ -141,9 +141,9 @@ std::vector Ctags::get_locations(const boost::filesystem::path } auto full_type=type; full_type.insert(c, name); - + auto parts=get_type_parts(full_type); - + std::string line; long best_score=LONG_MIN; std::vector best_locations; @@ -157,11 +157,11 @@ std::vector Ctags::get_locations(const boost::filesystem::path } else if(location.symbol!=name) continue; - + location.file_path=result.first/location.file_path; - + auto source_parts=get_type_parts(location.source); - + //Find match score long score=0; size_t source_index=0; @@ -192,7 +192,7 @@ std::vector Ctags::get_locations(const boost::filesystem::path if(!found) --score; } - + if(score>best_score) { best_score=score; best_locations.clear(); @@ -201,6 +201,6 @@ std::vector Ctags::get_locations(const boost::filesystem::path else if(score==best_score) best_locations.emplace_back(location); } - + return best_locations; } diff --git a/src/ctags.h b/src/ctags.h index ad56dca6..f8650b58 100644 --- a/src/ctags.h +++ b/src/ctags.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -14,14 +15,18 @@ class Ctags { std::string symbol; std::string scope; std::string source; + operator bool() const { return !file_path.empty(); } }; - - static std::pair > get_result(const boost::filesystem::path &path); - + + static std::pair > + get_result(const boost::filesystem::path &path); + static Location get_location(const std::string &line, bool markup); - - static std::vector get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type); + + static std::vector + get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type); + private: static std::vector get_type_parts(const std::string &type); }; diff --git a/src/debug_lldb.cc b/src/debug_lldb.cc index 71b1d3c3..1058aa5b 100644 --- a/src/debug_lldb.cc +++ b/src/debug_lldb.cc @@ -34,12 +34,12 @@ std::tuple, std::string, std::vector > Deb std::vector environment; std::string executable; std::vector arguments; - + size_t start_pos=std::string::npos; bool quote=false; bool double_quote=false; size_t backslash_count=0; - for(size_t c=0;c<=command.size();c++) { + for(size_t c=0;c<=command.size();c++) { if(c==command.size() || (!quote && !double_quote && backslash_count%2==0 && command[c]==' ')) { if(c>0 && start_pos!=std::string::npos) { auto argument=command.substr(start_pos, c-start_pos); @@ -58,7 +58,7 @@ std::tuple, std::string, std::vector > Deb else break; } - + if(!env_arg) { executable=filesystem::unescape_argument(argument); #ifdef _WIN32 @@ -83,7 +83,7 @@ std::tuple, std::string, std::vector > Deb if(c(lldb::SBDebugger::Create(true, log, nullptr)); listener=std::make_unique("juCi++ lldb listener"); } - + //Create executable string and argument array auto parsed_run_arguments=parse_run_arguments(command); auto &environment_from_arguments=std::get<0>(parsed_run_arguments); auto &executable=std::get<1>(parsed_run_arguments); auto &arguments=std::get<2>(parsed_run_arguments); - + std::vector argv; argv.reserve(arguments.size()); for(auto &argument : arguments) argv.emplace_back(argument.c_str()); argv.emplace_back(nullptr); - + auto target=debugger->CreateTarget(executable.c_str()); if(!target.IsValid()) { Terminal::get().async_print("Error (debug): Could not create debug target to: "+executable+'\n', true); @@ -115,7 +115,7 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat handler(-1); return; } - + //Set breakpoints for(auto &breakpoint: breakpoints) { if(!(target.BreakpointCreateByLocation(breakpoint.first.string().c_str(), breakpoint.second)).IsValid()) { @@ -125,7 +125,7 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat return; } } - + lldb::SBError error; if(!remote_host.empty()) { auto connect_string="connect://"+remote_host; @@ -147,14 +147,14 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat } } } - + // Create environment array std::vector environment; environment.reserve(environment_from_arguments.size()); for(auto &e: environment_from_arguments) environment.emplace_back(e.c_str()); environment.emplace_back(nullptr); - + process->RemoteLaunch(argv.data(), environment.data(), nullptr, nullptr, nullptr, nullptr, lldb::eLaunchFlagNone, false, error); if(!error.Fail()) process->Continue(); @@ -171,7 +171,7 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat for(size_t c=0;c(target.Launch(*listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error)); } if(error.Fail()) { @@ -184,12 +184,12 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat debug_thread.join(); for(auto &handler: on_start) handler(*process); - + for(auto &command: startup_commands) { lldb::SBCommandReturnObject command_return_object; debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, false); } - + debug_thread=std::thread([this]() { lldb::SBEvent event; while(true) { @@ -198,7 +198,7 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged)>0) { auto state=process->GetStateFromEvent(event); this->state=state; - + if(state==lldb::StateType::eStateStopped) { for(uint32_t c=0;cGetNumThreads();c++) { auto thread=process->GetThreadAtIndex(c); @@ -208,12 +208,12 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat } } } - + lock.unlock(); for(auto &handler: on_event) handler(event); lock.lock(); - + if(state==lldb::StateType::eStateExited || state==lldb::StateType::eStateCrashed) { auto exit_status=state==lldb::StateType::eStateCrashed?-1:process->GetExitStatus(); lock.unlock(); @@ -314,17 +314,17 @@ std::vector Debug::LLDB::get_backtrace() { for(uint32_t c_f=0;c_f Debug::LLDB::get_variables() { for(uint32_t value_index=0;value_index Debug::LLDB::get_variables() { variable.name=value.GetName(); value.GetDescription(stream); variable.value=stream.GetData(); - + auto declaration=value.GetDeclaration(); if(declaration.IsValid()) { variable.declaration_found=true; @@ -370,7 +370,7 @@ std::vector Debug::LLDB::get_variables() { variable.line_index=declaration.GetColumn(); if(variable.line_index==0) variable.line_index=1; - + auto file_spec=declaration.GetFileSpec(); variable.file_path=filesystem::get_normal_path(file_spec.GetDirectory()); variable.file_path/=file_spec.GetFilename(); @@ -383,7 +383,7 @@ std::vector Debug::LLDB::get_variables() { variable.line_index=line_entry.GetColumn(); if(variable.line_index==0) variable.line_index=1; - + auto file_spec=line_entry.GetFileSpec(); variable.file_path=filesystem::get_normal_path(file_spec.GetDirectory()); variable.file_path/=file_spec.GetFilename(); @@ -417,14 +417,14 @@ std::string Debug::LLDB::get_value(const std::string &variable, const boost::fil std::unique_lock lock(mutex); if(state==lldb::StateType::eStateStopped) { auto frame=process->GetSelectedThread().GetSelectedFrame(); - + auto values=frame.GetVariables(true, true, true, false); //First try to find variable based on name, file and line number if(!file_path.empty()) { for(uint32_t value_index=0;value_index #include #include @@ -18,6 +19,7 @@ namespace Debug { int line_nr; int line_index; }; + class Variable { public: uint32_t thread_index_id; @@ -29,60 +31,79 @@ namespace Debug { int line_nr; int line_index; }; + private: LLDB(); + public: static LLDB &get() { static LLDB singleton; return singleton; } - + std::list> on_start; /// The handlers are not run in the main loop. std::list> on_exit; /// The handlers are not run in the main loop. std::list> on_event; - + std::mutex mutex; - void start(const std::string &command, const boost::filesystem::path &path="", - const std::vector > &breakpoints={}, - const std::vector &startup_commands={}, const std::string &remote_host=""); + void start(const std::string &command, const boost::filesystem::path &path = "", + const std::vector > &breakpoints = {}, + const std::vector &startup_commands = {}, const std::string &remote_host = ""); + void continue_debug(); //can't use continue as function name void stop(); + void kill(); + void step_over(); + void step_into(); + void step_out(); + std::pair run_command(const std::string &command); + std::vector get_backtrace(); + std::vector get_variables(); - void select_frame(uint32_t frame_index, uint32_t thread_index_id=0); - + + void select_frame(uint32_t frame_index, uint32_t thread_index_id = 0); + void cancel(); - - std::string get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index); - std::string get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index); - + + std::string + get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, + unsigned int line_index); + + std::string + get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index); + bool is_invalid(); + bool is_stopped(); + bool is_running(); - + void add_breakpoint(const boost::filesystem::path &file_path, int line_nr); + void remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count); - + void write(const std::string &buffer); - + private: - std::tuple, std::string, std::vector> parse_run_arguments(const std::string &command); - + std::tuple, std::string, std::vector> + parse_run_arguments(const std::string &command); + std::unique_ptr debugger; std::unique_ptr listener; std::unique_ptr process; std::thread debug_thread; - + lldb::StateType state; - + size_t buffer_size; }; } diff --git a/src/dialogs.cc b/src/dialogs.cc index ab5bc550..efd95a9e 100644 --- a/src/dialogs.cc +++ b/src/dialogs.cc @@ -1,28 +1,28 @@ #include "dialogs.h" #include -Dialog::Message::Message(const std::string &text): Gtk::Window(Gtk::WindowType::WINDOW_POPUP) { - auto g_application=g_application_get_default(); - auto gio_application=Glib::wrap(g_application, true); - auto application=Glib::RefPtr::cast_static(gio_application); +Dialog::Message::Message(const std::string &text) : Gtk::Window(Gtk::WindowType::WINDOW_POPUP) { + auto g_application = g_application_get_default(); + auto gio_application = Glib::wrap(g_application, true); + auto application = Glib::RefPtr::cast_static(gio_application); set_transient_for(*application->get_active_window()); - + set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT); set_modal(true); set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_NOTIFICATION); - property_decorated()=false; + property_decorated() = false; set_skip_taskbar_hint(true); - - auto box=Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL)); - auto label=Gtk::manage(new Gtk::Label(text)); + + auto box = Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL)); + auto label = Gtk::manage(new Gtk::Label(text)); label->set_padding(10, 10); box->pack_start(*label); add(*box); - + show_all_children(); show_now(); - - while(Gtk::Main::events_pending()) + + while (Gtk::Main::events_pending()) Gtk::Main::iteration(false); } @@ -31,8 +31,8 @@ bool Dialog::Message::on_delete_event(GdkEventAny *event) { } std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::string &title, - const std::vector> &buttons, - Gtk::FileChooserAction action) { + const std::vector> &buttons, + Gtk::FileChooserAction action) { // Workaround for crash on MacOS when filtering files in file/folder dialogs. // See also https://github.com/cppit/jucipp/issues/259. // TODO 2018: check if this bug has been fixed @@ -55,25 +55,26 @@ std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::s #else Gtk::FileChooserDialog dialog(title, action); #endif - - auto g_application=g_application_get_default(); - auto gio_application=Glib::wrap(g_application, true); - auto application=Glib::RefPtr::cast_static(gio_application); + + auto g_application = g_application_get_default(); + auto gio_application = Glib::wrap(g_application, true); + auto application = Glib::RefPtr::cast_static(gio_application); dialog.set_transient_for(*application->get_active_window()); dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT); - - if(title=="Save File As") - gtk_file_chooser_set_filename(reinterpret_cast(dialog.gobj()), path.string().c_str()); - else if(!path.empty()) - gtk_file_chooser_set_current_folder(reinterpret_cast(dialog.gobj()), path.string().c_str()); + + if (title == "Save File As") + gtk_file_chooser_set_filename(reinterpret_cast(dialog.gobj()), path.string().c_str()); + else if (!path.empty()) + gtk_file_chooser_set_current_folder(reinterpret_cast(dialog.gobj()), path.string().c_str()); else { boost::system::error_code ec; - auto current_path=boost::filesystem::current_path(ec); - if(!ec) - gtk_file_chooser_set_current_folder(reinterpret_cast(dialog.gobj()), current_path.string().c_str()); + auto current_path = boost::filesystem::current_path(ec); + if (!ec) + gtk_file_chooser_set_current_folder(reinterpret_cast(dialog.gobj()), + current_path.string().c_str()); } - - for (auto &button : buttons) + + for (auto &button : buttons) dialog.add_button(button.first, button.second); return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : ""; } diff --git a/src/dialogs.h b/src/dialogs.h index e1deeb53..ad46d450 100644 --- a/src/dialogs.h +++ b/src/dialogs.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -7,20 +8,25 @@ class Dialog { public: static std::string open_folder(const boost::filesystem::path &path); + static std::string open_file(const boost::filesystem::path &path); + static std::string new_file(const boost::filesystem::path &path); + static std::string new_folder(const boost::filesystem::path &path); + static std::string save_file_as(const boost::filesystem::path &path); - + class Message : public Gtk::Window { public: Message(const std::string &text); + protected: bool on_delete_event(GdkEventAny *event) override; }; - + private: static std::string gtk_dialog(const boost::filesystem::path &path, const std::string &title, - const std::vector> &buttons, - Gtk::FileChooserAction gtk_options); + const std::vector> &buttons, + Gtk::FileChooserAction gtk_options); }; diff --git a/src/dialogs_unix.cc b/src/dialogs_unix.cc index 2512ce8a..cbd1f052 100644 --- a/src/dialogs_unix.cc +++ b/src/dialogs_unix.cc @@ -2,31 +2,31 @@ std::string Dialog::open_folder(const boost::filesystem::path &path) { return gtk_dialog(path, "Open Folder", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Open", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Open", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); } std::string Dialog::new_file(const boost::filesystem::path &path) { return gtk_dialog(path, "New File", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_SAVE); + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_SAVE); } std::string Dialog::new_folder(const boost::filesystem::path &path) { return gtk_dialog(path, "New Folder", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Create", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Create", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); } std::string Dialog::open_file(const boost::filesystem::path &path) { return gtk_dialog(path, "Open File", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Select", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_OPEN); + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Select", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_OPEN); } std::string Dialog::save_file_as(const boost::filesystem::path &path) { return gtk_dialog(path, "Save File As", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Save", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_SAVE); + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_SAVE); } diff --git a/src/dialogs_win.cc b/src/dialogs_win.cc index 72d18691..cb0f27f4 100644 --- a/src/dialogs_win.cc +++ b/src/dialogs_win.cc @@ -12,125 +12,127 @@ #include #include //TODO: remove + using namespace std; //TODO: remove class Win32Dialog { public: Win32Dialog() {}; - + ~Win32Dialog() { - if(dialog!=nullptr) + if (dialog != nullptr) dialog->Release(); } - + /** Returns the selected item's path as a string */ - std::string open(const std::wstring &title, unsigned option=0) { - if(!init(CLSID_FileOpenDialog)) + std::string open(const std::wstring &title, unsigned option = 0) { + if (!init(CLSID_FileOpenDialog)) return ""; - - if(!set_title(title) || !add_option(option)) + + if (!set_title(title) || !add_option(option)) return ""; - if(!set_folder()) + if (!set_folder()) return ""; - + return show(); } - - std::string save(const std::wstring &title, const boost::filesystem::path &file_path="", unsigned option=0) { - if(!init(CLSID_FileSaveDialog)) + + std::string save(const std::wstring &title, const boost::filesystem::path &file_path = "", unsigned option = 0) { + if (!init(CLSID_FileSaveDialog)) return ""; - - if(!set_title(title) || !add_option(option)) + + if (!set_title(title) || !add_option(option)) return ""; - if(!set_folder()) + if (!set_folder()) return ""; std::vector extensions; - if(!file_path.empty()) { - if(file_path.has_extension() && file_path.filename()!=file_path.extension()) { - auto extension=(L"*"+file_path.extension().native()).c_str(); + if (!file_path.empty()) { + if (file_path.has_extension() && file_path.filename() != file_path.extension()) { + auto extension = (L"*" + file_path.extension().native()).c_str(); extensions.emplace_back(COMDLG_FILTERSPEC{extension, extension}); - if(!set_default_file_extension(extension)) + if (!set_default_file_extension(extension)) return ""; } } extensions.emplace_back(COMDLG_FILTERSPEC{L"All files", L"*.*"}); - if(dialog->SetFileTypes(extensions.size(), extensions.data())!=S_OK) + if (dialog->SetFileTypes(extensions.size(), extensions.data()) != S_OK) return ""; - + return show(); } private: - IFileDialog *dialog=nullptr; + IFileDialog *dialog = nullptr; DWORD options; - + bool init(CLSID type) { - if(CoCreateInstance(type, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog))!=S_OK) + if (CoCreateInstance(type, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog)) != S_OK) return false; - if(dialog->GetOptions(&options)!=S_OK) + if (dialog->GetOptions(&options) != S_OK) return false; return true; } - + /** available options are listed at https://msdn.microsoft.com/en-gb/library/windows/desktop/dn457282(v=vs.85).aspx */ bool add_option(unsigned option) { - if(dialog->SetOptions(options | option)!=S_OK) + if (dialog->SetOptions(options | option) != S_OK) return false; return true; } - + bool set_title(const std::wstring &title) { - if(dialog->SetTitle(title.c_str())!=S_OK) + if (dialog->SetTitle(title.c_str()) != S_OK) return false; return true; } - + /** Sets the extensions the browser can find */ bool set_default_file_extension(const std::wstring &file_extension) { - if(dialog->SetDefaultExtension(file_extension.c_str())!=S_OK) + if (dialog->SetDefaultExtension(file_extension.c_str()) != S_OK) return false; return true; } - + /** Sets the directory to start browsing */ bool set_folder() { - auto g_application=g_application_get_default(); //TODO: Post issue that Gio::Application::get_default should return pointer and not Glib::RefPtr - auto gio_application=Glib::wrap(g_application, true); - auto application=Glib::RefPtr::cast_static(gio_application); - - auto current_path=application->window->notebook.get_current_folder(); + auto g_application = g_application_get_default(); //TODO: Post issue that Gio::Application::get_default should return pointer and not Glib::RefPtr + auto gio_application = Glib::wrap(g_application, true); + auto application = Glib::RefPtr::cast_static(gio_application); + + auto current_path = application->window->notebook.get_current_folder(); boost::system::error_code ec; - if(current_path.empty()) - current_path=boost::filesystem::current_path(ec); - if(ec) + if (current_path.empty()) + current_path = boost::filesystem::current_path(ec); + if (ec) return false; - - std::wstring path=current_path.native(); - size_t pos=0; - while((pos=path.find(L'/', pos))!=std::wstring::npos) {//TODO: issue bug report on boost::filesystem::path::native on MSYS2 + + std::wstring path = current_path.native(); + size_t pos = 0; + while ((pos = path.find(L'/', pos)) != + std::wstring::npos) {//TODO: issue bug report on boost::filesystem::path::native on MSYS2 path.replace(pos, 1, L"\\"); pos++; } - + IShellItem *folder = nullptr; - if(SHCreateItemFromParsingName(path.c_str(), nullptr, IID_PPV_ARGS(&folder))!=S_OK) + if (SHCreateItemFromParsingName(path.c_str(), nullptr, IID_PPV_ARGS(&folder)) != S_OK) return false; - if(dialog->SetFolder(folder)!=S_OK) + if (dialog->SetFolder(folder) != S_OK) return false; folder->Release(); return true; } - + std::string show() { - if(dialog->Show(nullptr)!=S_OK) + if (dialog->Show(nullptr) != S_OK) return ""; IShellItem *result = nullptr; - if(dialog->GetResult(&result)!=S_OK) + if (dialog->GetResult(&result) != S_OK) return ""; - LPWSTR file_path = nullptr; - auto hresult=result->GetDisplayName(SIGDN_FILESYSPATH, &file_path); + LPWSTR file_path = nullptr; + auto hresult = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path); result->Release(); - if(hresult!=S_OK) + if (hresult != S_OK) return ""; std::wstring file_path_wstring(file_path); std::string file_path_string(file_path_wstring.begin(), file_path_wstring.end()); @@ -150,12 +152,12 @@ std::string Dialog::new_file() { std::string Dialog::new_folder() { //Win32 (IFileDialog) does not support create folder... return gtk_dialog("New Folder", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Create", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Create", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); } std::string Dialog::open_file() { - return Win32Dialog().open(L"Open File"); + return Win32Dialog().open(L"Open File"); } std::string Dialog::save_file_as(const boost::filesystem::path &file_path) { diff --git a/src/directories.cc b/src/directories.cc index f8449493..3c14337a 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -12,7 +12,7 @@ bool Directories::TreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path bool Directories::TreeStore::drag_data_received_vfunc(const TreeModel::Path &path, const Gtk::SelectionData &selection_data) { auto &directories=Directories::get(); - + auto get_target_folder=[this, &directories](const TreeModel::Path &path) { if(path.size()==1) return directories.path; @@ -40,36 +40,36 @@ bool Directories::TreeStore::drag_data_received_vfunc(const TreeModel::Path &pat } return boost::filesystem::path(); }; - + auto it=directories.get_selection()->get_selected(); if(it) { auto source_path=it->get_value(directories.column_record.path); if(source_path.empty()) return false; - + auto target_path=get_target_folder(path); target_path/=source_path.filename(); - + if(source_path==target_path) return false; - + if(boost::filesystem::exists(target_path)) { Terminal::get().print("Error: could not move file: "+target_path.string()+" already exists\n", true); return false; } - + bool is_directory=boost::filesystem::is_directory(source_path); - + if(is_directory) Directories::get().remove_path(source_path); - + boost::system::error_code ec; boost::filesystem::rename(source_path, target_path, ec); if(ec) { Terminal::get().print("Error: could not move file: "+ec.message()+'\n', true); return false; } - + for(size_t c=0;cset_column_types(column_record); set_model(tree_store); - + get_column(0)->set_title(""); - + auto renderer=dynamic_cast(get_column(0)->get_first_cell()); get_column(0)->set_cell_data_func(*renderer, [this] (Gtk::CellRenderer *renderer, const Gtk::TreeModel::iterator &iter) { if(auto renderer_text=dynamic_cast(renderer)) renderer_text->property_markup()=iter->get_value(column_record.markup); }); - + get_style_context()->add_class("juci_directories"); - + tree_store->set_sort_column(column_record.id, Gtk::SortType::SORT_ASCENDING); set_enable_search(true); //TODO: why does this not work in OS X? set_search_column(column_record.name); - + signal_row_activated().connect([this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column){ auto iter = tree_store->get_iter(path); if (iter) { @@ -135,7 +135,7 @@ Directories::Directories() : Gtk::ListViewText(1) { } } }); - + signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path){ if(iter->children().begin()->get_value(column_record.path)=="") add_or_update_path(iter->get_value(column_record.path), *iter, true); @@ -144,10 +144,10 @@ Directories::Directories() : Gtk::ListViewText(1) { signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path){ this->remove_path(iter->get_value(column_record.path)); }); - + enable_model_drag_source(); enable_model_drag_dest(); - + auto new_file_label = "New File"; auto new_file_function = [this] { if(menu_popup_row_path.empty()) @@ -171,7 +171,7 @@ Directories::Directories() : Gtk::ListViewText(1) { Terminal::get().print("Error: could not create "+target_path.string()+": already exists\n", true); return; } - + EntryBox::get().hide(); }); auto entry_it=EntryBox::get().entries.begin(); @@ -181,15 +181,15 @@ Directories::Directories() : Gtk::ListViewText(1) { }); EntryBox::get().show(); }; - + menu_item_new_file.set_label(new_file_label); menu_item_new_file.signal_activate().connect(new_file_function); menu.append(menu_item_new_file); - + menu_root_item_new_file.set_label(new_file_label); menu_root_item_new_file.signal_activate().connect(new_file_function); menu_root.append(menu_root_item_new_file); - + auto new_folder_label = "New Folder"; auto new_folder_function = [this] { if(menu_popup_row_path.empty()) @@ -214,7 +214,7 @@ Directories::Directories() : Gtk::ListViewText(1) { Terminal::get().print("Error: could not create "+target_path.string()+": already exists\n", true); return; } - + EntryBox::get().hide(); }); auto entry_it=EntryBox::get().entries.begin(); @@ -224,17 +224,17 @@ Directories::Directories() : Gtk::ListViewText(1) { }); EntryBox::get().show(); }; - + menu_item_new_folder.set_label(new_folder_label); menu_item_new_folder.signal_activate().connect(new_folder_function); menu.append(menu_item_new_folder); - + menu_root_item_new_folder.set_label(new_folder_label); menu_root_item_new_folder.signal_activate().connect(new_folder_function); menu_root.append(menu_root_item_new_folder); - + menu.append(menu_item_separator); - + menu_item_rename.set_label("Rename"); menu_item_rename.signal_activate().connect([this] { if(menu_popup_row_path.empty()) @@ -242,17 +242,17 @@ Directories::Directories() : Gtk::ListViewText(1) { EntryBox::get().clear(); EntryBox::get().entries.emplace_back(menu_popup_row_path.filename().string(), [this, source_path=menu_popup_row_path](const std::string &content){ bool is_directory=boost::filesystem::is_directory(source_path); - + auto target_path=source_path.parent_path()/content; - + if(boost::filesystem::exists(target_path)) { Terminal::get().print("Error: could not rename to "+target_path.string()+": already exists\n", true); return; } - + if(is_directory) this->remove_path(source_path); - + boost::system::error_code ec; boost::filesystem::rename(source_path, target_path, ec); if(ec) { @@ -262,7 +262,7 @@ Directories::Directories() : Gtk::ListViewText(1) { update(); on_save_file(target_path); select(target_path); - + for(size_t c=0;cfile_path==source_path) { view->rename(target_path); - + std::string old_language_id; if(view->language) old_language_id=view->language->get_id(); @@ -290,7 +290,7 @@ Directories::Directories() : Gtk::ListViewText(1) { Terminal::get().print("Warning: language for "+target_path.string()+" has changed. Please reopen the file\n"); } } - + EntryBox::get().hide(); }); auto entry_it=EntryBox::get().entries.begin(); @@ -301,7 +301,7 @@ Directories::Directories() : Gtk::ListViewText(1) { EntryBox::get().show(); }); menu.append(menu_item_rename); - + menu_item_delete.set_label("Delete"); menu_item_delete.signal_activate().connect([this] { if(menu_popup_row_path.empty()) @@ -312,17 +312,17 @@ Directories::Directories() : Gtk::ListViewText(1) { int result = dialog.run(); if(result==Gtk::RESPONSE_YES) { bool is_directory=boost::filesystem::is_directory(menu_popup_row_path); - + boost::system::error_code ec; boost::filesystem::remove_all(menu_popup_row_path, ec); if(ec) Terminal::get().print("Error: could not delete "+menu_popup_row_path.string()+": "+ec.message()+"\n", true); else { update(); - + for(size_t c=0;cfile_path, menu_popup_row_path)) view->get_buffer()->set_modified(); @@ -334,13 +334,13 @@ Directories::Directories() : Gtk::ListViewText(1) { } }); menu.append(menu_item_delete); - + menu.show_all(); menu.accelerate(*this); - + menu_root.show_all(); menu_root.accelerate(*this); - + set_headers_clickable(); forall([this](Gtk::Widget &widget) { if(widget.get_name()=="GtkButton") { @@ -363,11 +363,11 @@ void Directories::open(const boost::filesystem::path &dir_path) { boost::system::error_code ec; if(dir_path.empty() || !boost::filesystem::exists(dir_path, ec) || ec) return; - + tree_store->clear(); - + path=filesystem::get_normal_path(dir_path); - + //TODO: report that set_title does not handle '_' correctly? auto title=path.filename().string(); size_t pos=0; @@ -382,7 +382,7 @@ void Directories::open(const boost::filesystem::path &dir_path) { directory.second.repository->clear_saved_status(); } directories.clear(); - + add_or_update_path(path, Gtk::TreeModel::Row(), true); } @@ -406,24 +406,24 @@ void Directories::on_save_file(const boost::filesystem::path &file_path) { void Directories::select(const boost::filesystem::path &select_path) { if(path=="") return; - + if(!filesystem::file_in_path(select_path, path)) return; - + //return if the select_path is already selected auto iter=get_selection()->get_selected(); if(iter) { if(iter->get_value(column_record.path)==select_path) return; } - + std::list paths; boost::filesystem::path parent_path; if(boost::filesystem::is_directory(select_path)) parent_path=select_path; else parent_path=select_path.parent_path(); - + //check if select_path is already expanded if(directories.find(parent_path.string())!=directories.end()) { //set cursor at select_path and return @@ -438,7 +438,7 @@ void Directories::select(const boost::filesystem::path &select_path) { }); return; } - + paths.emplace_front(parent_path); while(parent_path!=path) { parent_path=parent_path.parent_path(); @@ -455,7 +455,7 @@ void Directories::select(const boost::filesystem::path &select_path) { return false; }); } - + //set cursor at select_path tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter){ if(iter->get_value(column_record.path)==select_path) { @@ -493,7 +493,7 @@ bool Directories::on_button_press_event(GdkEventButton* event) { return true; } } - + return Gtk::TreeView::on_button_press_event(event); } @@ -504,19 +504,19 @@ void Directories::add_or_update_path(const boost::filesystem::path &dir_path, co directories.erase(path_it); return; } - + if(path_it==directories.end()) { auto g_file=Gio::File::create_for_path(dir_path.string()); auto monitor=g_file->monitor_directory(Gio::FileMonitorFlags::FILE_MONITOR_WATCH_MOVES); auto path_and_row=std::make_shared >(dir_path, row); auto connection=std::make_shared(); - + std::shared_ptr repository; try { repository=Git::get_repository(dir_path); } catch(const std::exception &) {} - + monitor->signal_changed().connect([this, connection, path_and_row, repository] (const Glib::RefPtr &file, const Glib::RefPtr&, Gio::FileMonitorEvent monitor_event) { @@ -531,12 +531,12 @@ void Directories::add_or_update_path(const boost::filesystem::path &dir_path, co }, 500); } }); - + std::shared_ptr repository_connection(new sigc::connection(), [](sigc::connection *connection) { connection->disconnect(); delete connection; }); - + if(repository) { auto connection=std::make_shared(); *repository_connection=repository->monitor->signal_changed().connect([this, connection, path_and_row](const Glib::RefPtr &file, @@ -554,7 +554,7 @@ void Directories::add_or_update_path(const boost::filesystem::path &dir_path, co } directories[dir_path.string()]={row, monitor, repository, repository_connection}; } - + Gtk::TreeNodeChildren children(row?row.children():tree_store->children()); if(children) { if(children.begin()->get_value(column_record.path)=="") @@ -589,7 +589,7 @@ void Directories::add_or_update_path(const boost::filesystem::path &dir_path, co } else { child->set_value(column_record.id, '2'+filename); - + auto language=Source::guess_language(it->path().filename()); if(!language) child->set_value(column_record.type, PathType::UNKNOWN); @@ -611,7 +611,7 @@ void Directories::add_or_update_path(const boost::filesystem::path &dir_path, co child->set_value(column_record.markup, Glib::Markup::escape_text("(empty)")); child->set_value(column_record.type, PathType::UNKNOWN); } - + colorize_path(dir_path, include_parent_paths); } @@ -620,14 +620,14 @@ void Directories::remove_path(const boost::filesystem::path &dir_path) { if(it==directories.end()) return; auto children=it->second.row->children(); - + for(auto it=directories.begin();it!=directories.end();) { if(filesystem::file_in_path(it->first, dir_path)) it=directories.erase(it); else it++; } - + if(children) { while(children) { tree_store->erase(children.begin()); @@ -644,7 +644,7 @@ void Directories::colorize_path(boost::filesystem::path dir_path_, bool include_ auto it=directories.find(dir_path->string()); if(it==directories.end()) return; - + if(it!=directories.end() && it->second.repository) { auto repository=it->second.repository; std::thread git_status_thread([this, dir_path, repository, include_parent_paths] { @@ -655,12 +655,12 @@ void Directories::colorize_path(boost::filesystem::path dir_path_, bool include_ catch(const std::exception &e) { Terminal::get().async_print(std::string("Error (git): ")+e.what()+'\n', true); } - + dispatcher.post([this, dir_path, include_parent_paths, status=std::move(status)] { auto it=directories.find(dir_path->string()); if(it==directories.end()) return; - + auto normal_color=get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL); Gdk::RGBA gray; gray.set_rgba(0.5, 0.5, 0.5); @@ -676,12 +676,12 @@ void Directories::colorize_path(boost::filesystem::path dir_path_, bool include_ green.set_red(normal_color.get_red()+factor*(green.get_red()-normal_color.get_red())); green.set_green(normal_color.get_green()+factor*(green.get_green()-normal_color.get_green())); green.set_blue(normal_color.get_blue()+factor*(green.get_blue()-normal_color.get_blue())); - + do { Gtk::TreeNodeChildren children(it->second.row?it->second.row.children():tree_store->children()); if(!children) return; - + for(auto &child: children) { auto name=Glib::Markup::escape_text(child.get_value(column_record.name)); auto path=child.get_value(column_record.path); @@ -692,22 +692,22 @@ void Directories::colorize_path(boost::filesystem::path dir_path_, bool include_ color=&green; else color=&normal_color; - + std::stringstream ss; ss << '#' << std::setfill('0') << std::hex; ss << std::setw(2) << std::hex << (color->get_red_u()>>8); ss << std::setw(2) << std::hex << (color->get_green_u()>>8); ss << std::setw(2) << std::hex << (color->get_blue_u()>>8); child.set_value(column_record.markup, ""+name+""); - + auto type=child.get_value(column_record.type); if(type==PathType::UNKNOWN) child.set_value(column_record.markup, ""+child.get_value(column_record.markup)+""); } - + if(!include_parent_paths) break; - + auto path=boost::filesystem::path(it->first); if(boost::filesystem::exists(path/".git")) break; diff --git a/src/directories.h b/src/directories.h index 73c28618..0bac8718 100644 --- a/src/directories.h +++ b/src/directories.h @@ -19,17 +19,17 @@ class Directories : public Gtk::ListViewText { std::shared_ptr repository; std::shared_ptr connection; }; - + enum class PathType {KNOWN, UNKNOWN}; - + class TreeStore : public Gtk::TreeStore { protected: TreeStore()=default; - + bool row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, const Gtk::SelectionData &selection_data) const override; bool drag_data_received_vfunc(const TreeModel::Path &path, const Gtk::SelectionData &selection_data) override; bool drag_data_delete_vfunc (const Gtk::TreeModel::Path &path) override; - + public: class ColumnRecord : public Gtk::TreeModel::ColumnRecord { public: @@ -46,7 +46,7 @@ class Directories : public Gtk::ListViewText { Gtk::TreeModelColumn path; Gtk::TreeModelColumn type; }; - + static Glib::RefPtr create() {return Glib::RefPtr(new TreeStore());} }; @@ -57,29 +57,29 @@ class Directories : public Gtk::ListViewText { return singleton; } ~Directories() override; - + void open(const boost::filesystem::path &dir_path=""); void update(); void on_save_file(const boost::filesystem::path &file_path); void select(const boost::filesystem::path &path); - + boost::filesystem::path path; - + protected: bool on_button_press_event(GdkEventButton *event) override; - + private: void add_or_update_path(const boost::filesystem::path &dir_path, const Gtk::TreeModel::Row &row, bool include_parent_paths); void remove_path(const boost::filesystem::path &dir_path); void colorize_path(boost::filesystem::path dir_path_, bool include_parent_paths); - + Glib::RefPtr tree_store; TreeStore::ColumnRecord column_record; - + std::unordered_map directories; - + Dispatcher dispatcher; - + Gtk::Menu menu; Gtk::MenuItem menu_item_new_file; Gtk::MenuItem menu_item_new_folder; diff --git a/src/dispatcher.cc b/src/dispatcher.cc index 5ad2d602..e4e7f1f4 100644 --- a/src/dispatcher.cc +++ b/src/dispatcher.cc @@ -2,21 +2,21 @@ #include Dispatcher::Dispatcher() { - connection=dispatcher.connect([this] { + connection = dispatcher.connect([this] { std::vector>::iterator> its; { std::unique_lock lock(functions_mutex); - if(functions.empty()) + if (functions.empty()) return; its.reserve(functions.size()); - for(auto it=functions.begin();it!=functions.end();++it) + for (auto it = functions.begin(); it != functions.end(); ++it) its.emplace_back(it); } - for(auto &it: its) + for (auto &it: its) (*it)(); { std::unique_lock lock(functions_mutex); - for(auto &it: its) + for (auto &it: its) functions.erase(it); } }); diff --git a/src/dispatcher.h b/src/dispatcher.h index 84766285..99302cf4 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -12,8 +13,9 @@ class Dispatcher { sigc::connection connection; public: Dispatcher(); + ~Dispatcher(); - + template void post(T &&function) { { @@ -22,6 +24,6 @@ class Dispatcher { } dispatcher(); } - + void disconnect(); }; diff --git a/src/documentation_cppreference.cc b/src/documentation_cppreference.cc index fe132ea6..ceec6225 100644 --- a/src/documentation_cppreference.cc +++ b/src/documentation_cppreference.cc @@ -12173,7 +12173,7 @@ std::experimental::filesystem::is_socket cpp/experimental/fs/is_socket std::experimental::filesystem::is_symlink cpp/experimental/fs/is_symlink std::experimental::filesystem::status_known cpp/experimental/fs/status_known"} )"; - + class SymbolToUrl { public: SymbolToUrl(const std::string &symbol_urls) { @@ -12197,7 +12197,7 @@ std::experimental::filesystem::status_known cpp/experimental/fs/status_known"} } std::unordered_map map; }; - + static SymbolToUrl symbol_to_url(symbol_urls); auto it=symbol_to_url.map.find(symbol); if(it==symbol_to_url.map.end()) diff --git a/src/documentation_cppreference.h b/src/documentation_cppreference.h index 5c0bd0c4..2c93c52a 100644 --- a/src/documentation_cppreference.h +++ b/src/documentation_cppreference.h @@ -1,4 +1,5 @@ #pragma once + #include namespace Documentation { diff --git a/src/entrybox.h b/src/entrybox.h index 7781e5c1..749ae7a6 100644 --- a/src/entrybox.h +++ b/src/entrybox.h @@ -30,7 +30,7 @@ class EntryBox : public Gtk::Box { Label(std::function update_=nullptr); std::function update; }; - + private: EntryBox(); public: @@ -38,7 +38,7 @@ class EntryBox : public Gtk::Box { static EntryBox singleton; return singleton; } - + Gtk::Box upper_box; Gtk::Box lower_box; void clear(); @@ -48,7 +48,7 @@ class EntryBox : public Gtk::Box { std::list