diff --git a/include/ircd/db/cursor.h b/include/ircd/db/cursor.h index 73aa76ddb..1a0bfe77a 100644 --- a/include/ircd/db/cursor.h +++ b/include/ircd/db/cursor.h @@ -204,10 +204,25 @@ const if(stale) { for(const auto &cell : row) - if(cell.valid(idx->first)) - json::set(v, cell.col(), cell.val()); + { + const column &c{cell}; + const database::descriptor &desc{describe(c)}; + + if(desc.type.second == typeid(string_view)) + { + if(cell.valid(idx->first)) + json::set(v, cell.col(), cell.val()); + else + json::set(v, cell.col(), string_view{}); + } else - json::set(v, cell.col(), string_view{}); + { + if(cell.valid(idx->first)) + json::set(v, cell.col(), byte_view{cell.val()}); + else + json::set(v, cell.col(), byte_view{string_view{}}); + } + } stale = false; } diff --git a/include/ircd/json/property.h b/include/ircd/json/property.h index 7a661622e..96aa480ef 100644 --- a/include/ircd/json/property.h +++ b/include/ircd/json/property.h @@ -43,17 +43,13 @@ struct ircd::json::property operator const T &() const; operator T &(); - property(T&& value); + property(T&& value) + :value{value} + {} + property() = default; }; -template -ircd::json::property::property(T&& value) -:value{value} -{ -} - template ircd::json::property::operator diff --git a/include/ircd/json/tuple.h b/include/ircd/json/tuple.h index 06a441fde..7b7b43d42 100644 --- a/include/ircd/json/tuple.h +++ b/include/ircd/json/tuple.h @@ -257,7 +257,7 @@ at(const tuple &t) using value_type = tuple_value_type; - //TODO: does tcmalloc zero this or huh? + //TODO: undefined if(ret == value_type{}) throw not_found("%s", name); @@ -281,7 +281,7 @@ at(tuple &t) using value_type = tuple_value_type; - //TODO: does tcmalloc zero this or huh? + //TODO: undefined if(ret == value_type{}) throw not_found("%s", name); @@ -306,29 +306,32 @@ get(const tuple &t, using value_type = tuple_value_type, idx>; - //TODO: does tcmalloc zero this or huh? + //TODO: undefined return ret != value_type{}? ret : def; } +template +tuple_value_type, indexof>(name)> & +get(tuple &t) +{ + constexpr size_t idx + { + indexof>(name) + }; + + return val(t); +} + template tuple_value_type, indexof>(name)> & get(tuple &t, tuple_value_type, indexof>(name)> &def) { - constexpr size_t idx - { - indexof>(name) - }; - - auto &ret - { - val(t) - }; - - using value_type = tuple_value_type, idx>; - - //TODO: does tcmalloc zero this or huh? + //TODO: undefined + auto &ret{get(t)}; + using value_type = decltype(ret); return ret != value_type{}? ret : def; } @@ -544,82 +547,151 @@ at(const tuple &t, at(t, name, std::forward(f)); } +template +typename std::enable_if +< + std::is_convertible::value, +void>::type +_assign(dst &d, + src&& s) +{ + d = 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) +{ + 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 tuple & set(tuple &t, const string_view &key, - const V &val) + V&& val) +try { at(t, key, [&key, &val] (auto &target) { - using target_type = decltype(target); - using cast_type = typename std::remove_reference::type; - target = byte_view(val); + _assign(target, std::forward(val)); }); return t; } +catch(const std::exception &e) +{ + throw parse_error("failed to set member '%s': %s", + key, + 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) { - //TODO: is tcmalloc zero-initializing all tuple elements, or is something else doing that? std::for_each(std::begin(object), std::end(object), [this] (const auto &member) { - at(*this, member.first, [&member] - (auto &target) - { - using target_type = decltype(target); - using cast_type = typename std::remove_reference::type; try - { - target = lex_cast(member.second); - } - catch(const bad_lex_cast &e) - { - throw parse_error("member '%s' must convert to '%s'", - member.first, - typeid(target_type).name()); - } - }); + set(*this, member.first, member.second); }); } template tuple::tuple(const json::iov &iov) { - //TODO: is tcmalloc zero-initializing all tuple elements, or is something else doing that? std::for_each(std::begin(iov), std::end(iov), [this] (const auto &member) { - switch(type(member.second)) - { - case type::OBJECT: - case type::ARRAY: - if(unlikely(!member.second.serial)) - throw print_error("iov member '%s' must be JSON to be used by the tuple", - string_view{member.first}); - default: - break; - } - - at(*this, member.first, [&member] - (auto &target) - { - using target_type = decltype(target); - using cast_type = typename std::remove_reference::type; try - { - target = static_cast(member.second); - } - catch(const bad_lex_cast &e) - { - throw parse_error("member '%s' must convert to '%s'", - member.first, - typeid(target_type).name()); - } - }); + set(*this, member.first, member.second); }); } @@ -629,28 +701,7 @@ tuple::tuple(const std::initializer_list &members) std::for_each(std::begin(members), std::end(members), [this] (const auto &member) { - switch(type(member.second)) - { - case type::STRING: - case type::LITERAL: - set(*this, member.first, string_view{member.second}); - break; - - case type::NUMBER: - if(member.second.floats) - set(*this, member.first, member.second.floating); - else - set(*this, member.first, member.second.integer); - break; - - case type::ARRAY: - case type::OBJECT: - if(!member.second.serial) - throw parse_error("Unserialized value not supported yet"); - - set(*this, member.first, string_view{member.second}); - break; - } + set(*this, member.first, member.second); }); }