// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. #pragma once #define HAVE_IRCD_JSON_TUPLE_H #include "property.h" namespace ircd { namespace json { /// All tuple templates inherit from this non-template type for tagging. struct tuple_base { // EBO tag }; /// A compile-time construct to describe a JSON object's members and types. /// /// Member access by name is O(1) because of recursive constexpr function /// inlining when translating a name to the index number which is then used /// as the template argument to std::get() for the value. /// /// Here we represent a JSON object with a named tuple, allowing the programmer /// to create a structure specifying all of the potentially valid members of the /// object. Thus at runtime, the tuple only carries around its values like a /// `struct`. Unlike a `struct`, the tuple is abstractly iterable and we have /// implemented logic operating on all JSON tuples regardless of their makeup /// without any effort from a developer when creating a new tuple. /// /// The member structure for the tuple is called `property` because json::member /// is already used to pair together runtime oriented json::values. /// /// Create and use a tuple to efficiently extract members from a json::object. /// The tuple will populate its own members during a single-pass iteration of /// the JSON input. /// /// But remember, the tuple carries very little information for you at runtime /// which may make it difficult to represent all JS phenomena like "undefined" /// and "null". /// template struct tuple :std::tuple ,tuple_base { struct keys; using tuple_type = std::tuple; using super_type = tuple; static constexpr size_t size(); operator json::value() const; operator crh::sha256::buf() const; template tuple(const tuple &); tuple(const json::object &); tuple(const json::iov &); tuple(const std::initializer_list &); tuple() = default; }; template constexpr bool is_tuple() { return std::is_base_of::value; } template using enable_if_tuple = typename std::enable_if(), R>::type; template using enable_if_tuple_and = typename std::enable_if() && test(), R>::type; template using tuple_type = typename tuple::tuple_type; template using tuple_size = std::tuple_size>; template using tuple_element = typename std::tuple_element>::type; template using tuple_value_type = typename tuple_element::value_type; template auto & stdcast(const tuple &o) { return static_cast(o); } template auto & stdcast(tuple &o) { return static_cast(o); } template constexpr enable_if_tuple size() { return tuple_size::value; } template constexpr enable_if_tuple key() { return tuple_element::key; } template enable_if_tuple key(const tuple &t) { return std::get(t).key; } template constexpr typename std::enable_if(), size_t>::type indexof() { return size(); } template constexpr typename std::enable_if(), size_t>::type indexof() { constexpr auto equal { ircd::hash(key()) == hash }; return equal? i : indexof(); } template constexpr typename std::enable_if(), size_t>::type indexof(const char *const &name) { return size(); } template constexpr typename std::enable_if(), size_t>::type indexof(const char *const &name) { constexpr auto equal { _constexpr_equal(key(), name) }; return equal? i : indexof(name); } template constexpr typename std::enable_if(), size_t>::type indexof(const string_view &name) { return size(); } template constexpr typename std::enable_if(), size_t>::type indexof(const string_view &name) { const auto equal { name == key() }; return equal? i : indexof(name); } template constexpr bool key_exists(const string_view &key) { return indexof(key) < size(); } template enable_if_tuple &> val(tuple &t) { return static_cast &>(std::get(t)); } template enable_if_tuple &> val(const tuple &t) { return static_cast &>(std::get(t)); } template typename std::enable_if(), size_t>::type serialized(T&& t) { return lex_cast(t).size(); } template typename std::enable_if(), bool>::type defined(T&& t) { return !is_zero{}(t); } template typename std::enable_if < std::is_base_of() && std::is_convertible(), void>::type _assign(dst &d, src&& s) { d = unquote(string_view{std::forward(s)}); } template typename std::enable_if < !std::is_base_of() && std::is_convertible() && !ircd::json::is_tuple() && !std::is_same(), void>::type _assign(dst &d, src&& s) { d = std::forward(s); } template typename std::enable_if < !std::is_base_of() && std::is_convertible() && !ircd::json::is_tuple() && std::is_same(), void>::type _assign(dst &d, src&& s) { static const is_zero test{}; d = test(std::forward(s)); } template typename std::enable_if < std::is_arithmetic() && std::is_base_of::type>() && !std::is_base_of, typename std::remove_reference::type>(), void>::type _assign(dst &d, src&& s) try { d = lex_cast(std::forward(s)); } catch(const bad_lex_cast &e) { throw parse_error("cannot convert '%s' to '%s'", demangle(), demangle()); } template typename std::enable_if < std::is_arithmetic() && std::is_base_of, typename std::remove_reference::type>(), void>::type _assign(dst &d, src&& s) { assert(!s.empty()); d = byte_view(std::forward(s)); } template typename std::enable_if < std::is_base_of() && std::is_pod::type>(), void>::type _assign(dst &d, src&& s) { d = byte_view(std::forward(s)); } template typename std::enable_if < ircd::json::is_tuple(), void>::type _assign(dst &d, src&& s) { d = dst{std::forward(s)}; } template enable_if_tuple()> &> get(const tuple &t) { constexpr size_t idx { indexof() }; return val(t); } template enable_if_tuple()>> get(const tuple &t, const tuple_value_type()> &def) { constexpr size_t idx { indexof() }; const auto &ret { val(t) }; using value_type = tuple_value_type; //TODO: undefined return defined(ret)? ret : def; } template enable_if_tuple()> &> get(tuple &t) { constexpr size_t idx { indexof() }; return val(t); } template enable_if_tuple()> &> get(tuple &t, tuple_value_type()> &def) { //TODO: undefined auto &ret{get(t)}; using value_type = decltype(ret); return defined(ret)? ret : def; } template enable_if_tuple()> &> at(const tuple &t) { constexpr size_t idx { indexof() }; auto &ret { val(t) }; using value_type = tuple_value_type; //TODO: XXX if(!defined(ret)) throw not_found("%s", key(t)); return ret; } template enable_if_tuple()> &> at(tuple &t) { constexpr size_t idx { indexof() }; auto &ret { val(t) }; using value_type = tuple_value_type; //TODO: XXX if(!defined(ret)) throw not_found("%s", key(t)); return ret; } template typename std::enable_if(), void>::type at(tuple &t, const string_view &name, function&& f) { } template typename std::enable_if(), void>::type at(tuple &t, const string_view &name, function&& f) { if(indexof(name) == i) f(val(t)); else at(t, name, std::forward(f)); } template typename std::enable_if(), void>::type at(const tuple &t, const string_view &name, function&& f) { } template typename std::enable_if(), void>::type at(const tuple &t, const string_view &name, function&& f) { if(indexof(name) == i) f(val(t)); else at(t, name, std::forward(f)); } template typename std::enable_if(), void>::type for_each(const tuple &t, function&& f) {} template typename std::enable_if(), void>::type for_each(tuple &t, function&& f) {} template typename std::enable_if(), void>::type for_each(const tuple &t, function&& f) { f(key(t), val(t)); for_each(t, std::forward(f)); } template typename std::enable_if(), void>::type for_each(tuple &t, function&& f) { f(key(t), val(t)); for_each(t, std::forward(f)); } template void for_each(const tuple &t, const vector_view &mask, function&& f) { std::for_each(std::begin(mask), std::end(mask), [&t, &f] (const auto &key) { at(t, key, [&f, &key] (auto&& val) { f(key, val); }); }); } template void for_each(tuple &t, const vector_view &mask, function&& f) { std::for_each(std::begin(mask), std::end(mask), [&t, &f] (const auto &key) { at(t, key, [&f, &key] (auto&& val) { f(key, val); }); }); } template typename std::enable_if<(i < 0), void>::type rfor_each(const tuple &t, function&& f) {} template typename std::enable_if<(i < 0), void>::type rfor_each(tuple &t, function&& f) {} template() - 1> typename std::enable_if(), void>::type rfor_each(const tuple &t, function&& f) { f(key(t), val(t)); rfor_each(t, std::forward(f)); } template() - 1> typename std::enable_if(), void>::type rfor_each(tuple &t, function&& f) { f(key(t), val(t)); rfor_each(t, std::forward(f)); } template typename std::enable_if(), bool>::type until(const tuple &t, function&& f) { return true; } template typename std::enable_if(), bool>::type until(tuple &t, function&& f) { return true; } template typename std::enable_if(), bool>::type until(const tuple &t, function&& f) { return f(key(t), val(t))? until(t, std::forward(f)): false; } template typename std::enable_if(), bool>::type until(tuple &t, function&& f) { return f(key(t), val(t))? until(t, std::forward(f)): false; } template typename std::enable_if(), bool>::type until(const tuple &a, const tuple &b, function&& f) { return true; } template typename std::enable_if(), bool>::type until(const tuple &a, const tuple &b, function&& f) { return f(key(a), val(a), val(b))? until(a, b, std::forward(f)): false; } template typename std::enable_if<(i < 0), bool>::type runtil(const tuple &t, function&& f) { return true; } template typename std::enable_if<(i < 0), bool>::type runtil(tuple &t, function&& f) { return true; } template() - 1> typename std::enable_if(), bool>::type runtil(const tuple &t, function&& f) { return f(key(t), val(t))? runtil(t, std::forward(f)): false; } template() - 1> typename std::enable_if(), bool>::type runtil(tuple &t, function&& f) { return f(key(t), val(t))? runtil(t, std::forward(f)): false; } template tuple & set(tuple &t, const string_view &key, V&& val) try { at(t, key, [&key, &val] (auto &target) { _assign(target, std::forward(val)); }); return t; } catch(const std::exception &e) { throw parse_error("failed to set member '%s' (from %s): %s", key, demangle(), e.what()); } template tuple & set(tuple &t, const string_view &key, const json::value &value) { switch(type(value)) { case type::STRING: case type::LITERAL: set(t, key, string_view{value}); break; case type::NUMBER: if(value.floats) set(t, key, value.floating); else set(t, key, value.integer); break; case type::OBJECT: case type::ARRAY: if(unlikely(!value.serial)) throw print_error("Type %s must be JSON to be used by tuple member '%s'", reflect(type(value)), key); set(t, key, string_view{value}); break; } return t; } template tuple::tuple(const json::object &object) { std::for_each(std::begin(object), std::end(object), [this] (const auto &member) { set(*this, member.first, member.second); }); } template tuple::tuple(const json::iov &iov) { std::for_each(std::begin(iov), std::end(iov), [this] (const auto &member) { set(*this, member.first, member.second); }); } template tuple::tuple(const std::initializer_list &members) { std::for_each(std::begin(members), std::end(members), [this] (const auto &member) { set(*this, member.first, member.second); }); } template template tuple::tuple(const tuple &t) { for_each(t, [this] (const auto &key, const auto &val) { set(*this, key, val); }); } template constexpr size_t tuple::size() { return std::tuple_size(); } template constexpr auto _key_transform(it_a it, const it_b end, closure&& lambda) { for(size_t i(0); i < tuple::size() && it != end; ++i) { *it = lambda(key()); ++it; } return it; } template constexpr auto _key_transform(it_a it, const it_b end) { for(size_t i(0); i < tuple::size() && it != end; ++i) { *it = key(); ++it; } return it; } template auto _key_transform(const tuple &tuple, it_a it, const it_b end) { for_each(tuple, [&it, &end] (const auto &key, const auto &val) { if(it != end) { *it = key; ++it; } }); return it; } template struct tuple::keys :std::array::size()> { struct selection; struct include; struct exclude; constexpr keys() { _key_transform>(this->begin(), this->end()); } }; template struct tuple::keys::selection :std::bitset::size()> { template constexpr bool until(closure &&function) const { for(size_t i(0); i < this->size(); ++i) if(this->test(i)) if(!function(key, i>())) return false; return true; } template constexpr void for_each(closure &&function) const { this->until([&function](auto&& key) { function(key); return true; }); } template constexpr auto transform(it_a it, const it_b end) const { this->until([&it, &end](auto&& key) { if(it == end) return false; *it = key; ++it; return true; }); } }; template struct tuple::keys::include :selection { constexpr include(const std::initializer_list &list) { for(const auto &key : list) this->set(indexof>(key), true); } }; template struct tuple::keys::exclude :selection { constexpr exclude(const std::initializer_list &list) { this->set(); for(const auto &key : list) this->set(indexof>(key), false); } }; template auto _member_transform_if(const tuple &tuple, it_a it, const it_b end, closure&& lambda) { until(tuple, [&it, &end, &lambda] (const auto &key, const auto &val) { if(it == end) return false; if(lambda(*it, key, val)) ++it; return true; }); return it; } template auto _member_transform(const tuple &tuple, it_a it, const it_b end, closure&& lambda) { return _member_transform_if(tuple, it, end, [&lambda] (auto&& ret, const auto &key, const auto &val) { ret = lambda(key, val); return true; }); } template auto _member_transform(const tuple &tuple, it_a it, const it_b end) { return _member_transform_if(tuple, it, end, [] (auto&& ret, const auto &key, const auto &val) { ret = member { key, val }; return true; }); } template size_t serialized(const tuple &t) { constexpr const size_t member_count { tuple::size() }; std::array sizes {0}; const auto e{_member_transform_if(t, begin(sizes), end(sizes), [] (auto &ret, const string_view &key, auto&& val) { if(!defined(val)) return false; // " " : , ret = 1 + key.size() + 1 + 1 + serialized(val) + 1; return true; })}; // Subtract one to get the final size when an extra comma is // accumulated on non-empty objects. const auto overhead { 1 + std::all_of(begin(sizes), e, is_zero{}) }; return std::accumulate(begin(sizes), e, size_t(overhead)); } template string_view stringify(mutable_buffer &buf, const tuple &tuple) { std::array members; std::sort(begin(members), end(members), [] (const auto &a, const auto &b) { return a.first < b.first; }); const auto e{_member_transform_if(tuple, begin(members), end(members), [] (auto &ret, const string_view &key, auto&& val) { if(!defined(val)) return false; ret = member { key, val }; return true; })}; return stringify(buf, begin(members), e); } template std::ostream & operator<<(std::ostream &s, const tuple &t) { s << json::strung(t); return s; } template tuple::operator crh::sha256::buf() const { //TODO: XXX const auto preimage { json::strung(*this) }; return crh::sha256::buf { [&preimage](auto &buf) { sha256{buf, const_buffer{preimage}}; } }; } template enable_if_tuple sign(const tuple &t, const ed25519::sk &sk) { //TODO: XXX const auto preimage { json::strung(t) }; return ed25519::sig { [&sk, &preimage](auto &buf) { sk.sign(buf, const_buffer{preimage}); } }; } template enable_if_tuple verify(const tuple &t, const ed25519::pk &pk, const ed25519::sig &sig, std::nothrow_t) noexcept try { //TODO: XXX const auto preimage { json::strung(t) }; return pk.verify(const_buffer{preimage}, sig); } catch(const std::exception &e) { log::error("Verification of json::tuple unexpected failure: %s", e.what()); return false; } template enable_if_tuple verify(const tuple &t, const ed25519::pk &pk, const ed25519::sig &sig) { if(!verify(t, pk, sig, std::nothrow)) throw ed25519::bad_sig{"Verification failed"}; } template enable_if_tuple verify(const tuple &t, const ed25519::pk &pk) { const ed25519::sig sig { [&t](auto &buf) { b64decode(buf, at<"signatures"_>(t)); } }; auto copy(t); at<"signatures"_>(copy) = string_view{}; if(!verify(copy, pk, sig, std::nothrow)) throw ed25519::bad_sig{"Verification failed"}; } template tuple::operator json::value() const { json::value ret; ret.type = OBJECT; ret.create_string(serialized(*this), [this] (mutable_buffer buffer) { stringify(buffer, *this); }); return ret; } } // namespace json } // namespace ircd