Merge pull request #12606 from vitlibar/improve-json-internal-interface

Improve JSON internal interface.
This commit is contained in:
Vitaly Baranov 2020-07-20 23:35:09 +03:00 committed by GitHub
commit e3ee555c57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 105 additions and 70 deletions

View File

@ -17,6 +17,8 @@ struct DummyJSONParser
class Array; class Array;
class Object; class Object;
/// References an element in a JSON document, representing a JSON null, boolean, string, number,
/// array or object.
class Element class Element
{ {
public: public:
@ -39,6 +41,7 @@ struct DummyJSONParser
Object getObject() const; Object getObject() const;
}; };
/// References an array in a JSON document.
class Array class Array
{ {
public: public:
@ -46,10 +49,10 @@ struct DummyJSONParser
{ {
public: public:
Element operator*() const { return {}; } Element operator*() const { return {}; }
Iterator & operator ++() { return *this; } Iterator & operator++() { return *this; }
Iterator operator ++(int) { return *this; } Iterator operator++(int) { return *this; }
friend bool operator ==(const Iterator &, const Iterator &) { return true; } friend bool operator==(const Iterator &, const Iterator &) { return true; }
friend bool operator !=(const Iterator &, const Iterator &) { return false; } friend bool operator!=(const Iterator &, const Iterator &) { return false; }
}; };
Iterator begin() const { return {}; } Iterator begin() const { return {}; }
@ -58,29 +61,40 @@ struct DummyJSONParser
Element operator[](size_t) const { return {}; } Element operator[](size_t) const { return {}; }
}; };
using KeyValuePair = std::pair<std::string_view, Element>;
/// References an object in a JSON document.
class Object class Object
{ {
public: public:
using KeyValuePair = std::pair<std::string_view, Element>;
class Iterator class Iterator
{ {
public: public:
KeyValuePair operator *() const { return {}; } KeyValuePair operator*() const { return {}; }
Iterator & operator ++() { return *this; } Iterator & operator++() { return *this; }
Iterator operator ++(int) { return *this; } Iterator operator++(int) { return *this; }
friend bool operator ==(const Iterator &, const Iterator &) { return true; } friend bool operator==(const Iterator &, const Iterator &) { return true; }
friend bool operator !=(const Iterator &, const Iterator &) { return false; } friend bool operator!=(const Iterator &, const Iterator &) { return false; }
}; };
Iterator begin() const { return {}; } Iterator begin() const { return {}; }
Iterator end() const { return {}; } Iterator end() const { return {}; }
size_t size() const { return 0; } size_t size() const { return 0; }
KeyValuePair operator[](size_t) const { return {}; }
bool find(const std::string_view &, Element &) const { return false; } bool find(const std::string_view &, Element &) const { return false; }
#if 0
/// Optional: Provides access to an object's element by index.
KeyValuePair operator[](size_t) const { return {}; }
#endif
}; };
/// Parses a JSON document, returns the reference to its root element if succeeded.
bool parse(const std::string_view &, Element &) { throw Exception{"Functions JSON* are not supported", ErrorCodes::NOT_IMPLEMENTED}; } bool parse(const std::string_view &, Element &) { throw Exception{"Functions JSON* are not supported", ErrorCodes::NOT_IMPLEMENTED}; }
#if 0
/// Optional: Allocates memory to parse JSON documents faster.
void reserve(size_t max_size);
#endif
}; };
} }

View File

@ -139,6 +139,12 @@ private:
BOOST_TTI_HAS_MEMBER_FUNCTION(reserve) BOOST_TTI_HAS_MEMBER_FUNCTION(reserve)
BOOST_TTI_HAS_MEMBER_FUNCTION(prepare) BOOST_TTI_HAS_MEMBER_FUNCTION(prepare)
template <class T, class = void>
struct has_index_operator : std::false_type {};
template <class T>
struct has_index_operator<T, std::void_t<decltype(std::declval<T>()[0])>> : std::true_type {};
/// Represents a move of a JSON iterator described by a single argument passed to a JSON function. /// Represents a move of a JSON iterator described by a single argument passed to a JSON function.
/// For example, the call JSONExtractInt('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 1) /// For example, the call JSONExtractInt('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 1)
/// contains two moves: {MoveType::ConstKey, "b"} and {MoveType::ConstIndex, 1}. /// contains two moves: {MoveType::ConstKey, "b"} and {MoveType::ConstIndex, 1}.
@ -217,38 +223,32 @@ private:
{ {
auto array = element.getArray(); auto array = element.getArray();
if (index >= 0) if (index >= 0)
{
--index; --index;
if (static_cast<size_t>(index) >= array.size()) else
return false; index += array.size();
element = array[index];
out_key = {}; if (static_cast<size_t>(index) >= array.size())
return true;
}
index += array.size();
if (index < 0)
return false; return false;
element = array[index]; element = array[index];
out_key = {}; out_key = {};
return true; return true;
} }
if (element.isObject()) if constexpr (has_index_operator<typename JSONParser::Object>::value)
{ {
auto object = element.getObject(); if (element.isObject())
if (index >= 0)
{ {
--index; auto object = element.getObject();
if (index >= 0)
--index;
else
index += object.size();
if (static_cast<size_t>(index) >= object.size()) if (static_cast<size_t>(index) >= object.size())
return false; return false;
std::tie(out_key, element) = object[index]; std::tie(out_key, element) = object[index];
return true; return true;
} }
index += object.size();
if (index < 0)
return false;
std::tie(out_key, element) = object[index];
return true;
} }
return {}; return {};

View File

@ -20,6 +20,8 @@ struct RapidJSONParser
class Array; class Array;
class Object; class Object;
/// References an element in a JSON document, representing a JSON null, boolean, string, number,
/// array or object.
class Element class Element
{ {
public: public:
@ -47,6 +49,7 @@ struct RapidJSONParser
const rapidjson::Value * ptr = nullptr; const rapidjson::Value * ptr = nullptr;
}; };
/// References an array in a JSON document.
class Array class Array
{ {
public: public:
@ -67,17 +70,18 @@ struct RapidJSONParser
ALWAYS_INLINE Iterator begin() const { return ptr->Begin(); } ALWAYS_INLINE Iterator begin() const { return ptr->Begin(); }
ALWAYS_INLINE Iterator end() const { return ptr->End(); } ALWAYS_INLINE Iterator end() const { return ptr->End(); }
ALWAYS_INLINE size_t size() const { return ptr->Size(); } ALWAYS_INLINE size_t size() const { return ptr->Size(); }
ALWAYS_INLINE Element operator[](size_t index) const { return *(ptr->Begin() + index); } ALWAYS_INLINE Element operator[](size_t index) const { assert(index < size()); return *(ptr->Begin() + index); }
private: private:
const rapidjson::Value * ptr = nullptr; const rapidjson::Value * ptr = nullptr;
}; };
using KeyValuePair = std::pair<std::string_view, Element>;
/// References an object in a JSON document.
class Object class Object
{ {
public: public:
using KeyValuePair = std::pair<std::string_view, Element>;
class Iterator class Iterator
{ {
public: public:
@ -96,14 +100,7 @@ struct RapidJSONParser
ALWAYS_INLINE Iterator end() const { return ptr->MemberEnd(); } ALWAYS_INLINE Iterator end() const { return ptr->MemberEnd(); }
ALWAYS_INLINE size_t size() const { return ptr->MemberCount(); } ALWAYS_INLINE size_t size() const { return ptr->MemberCount(); }
ALWAYS_INLINE KeyValuePair operator[](size_t index) const bool find(const std::string_view & key, Element & result) const
{
auto it = ptr->MemberBegin() + index;
std::string_view key{it->name.GetString(), it->name.GetStringLength()};
return KeyValuePair{key, it->value};
}
ALWAYS_INLINE bool find(const std::string_view & key, Element & result) const
{ {
auto it = ptr->FindMember(rapidjson::StringRef(key.data(), key.length())); auto it = ptr->FindMember(rapidjson::StringRef(key.data(), key.length()));
if (it == ptr->MemberEnd()) if (it == ptr->MemberEnd())
@ -113,10 +110,20 @@ struct RapidJSONParser
return true; return true;
} }
/// Optional: Provides access to an object's element by index.
ALWAYS_INLINE KeyValuePair operator[](size_t index) const
{
assert (index < size());
auto it = ptr->MemberBegin() + index;
std::string_view key{it->name.GetString(), it->name.GetStringLength()};
return {key, it->value};
}
private: private:
const rapidjson::Value * ptr = nullptr; const rapidjson::Value * ptr = nullptr;
}; };
/// Parses a JSON document, returns the reference to its root element if succeeded.
bool parse(const std::string_view & json, Element & result) bool parse(const std::string_view & json, Element & result)
{ {
rapidjson::MemoryStream ms(json.data(), json.size()); rapidjson::MemoryStream ms(json.data(), json.size());
@ -128,6 +135,11 @@ struct RapidJSONParser
return true; return true;
} }
#if 0
/// Optional: Allocates memory to parse JSON documents faster.
void reserve(size_t max_size);
#endif
private: private:
rapidjson::Document document; rapidjson::Document document;
}; };

View File

@ -25,6 +25,8 @@ struct SimdJSONParser
class Array; class Array;
class Object; class Object;
/// References an element in a JSON document, representing a JSON null, boolean, string, number,
/// array or object.
class Element class Element
{ {
public: public:
@ -52,6 +54,7 @@ struct SimdJSONParser
simdjson::dom::element element; simdjson::dom::element element;
}; };
/// References an array in a JSON document.
class Array class Array
{ {
public: public:
@ -59,11 +62,11 @@ struct SimdJSONParser
{ {
public: public:
ALWAYS_INLINE Iterator(const simdjson::dom::array::iterator & it_) : it(it_) {} ALWAYS_INLINE Iterator(const simdjson::dom::array::iterator & it_) : it(it_) {}
ALWAYS_INLINE Element operator *() const { return *it; } ALWAYS_INLINE Element operator*() const { return *it; }
ALWAYS_INLINE Iterator & operator ++() { ++it; return *this; } ALWAYS_INLINE Iterator & operator++() { ++it; return *this; }
ALWAYS_INLINE Iterator operator ++(int) { auto res = *this; ++it; return res; } ALWAYS_INLINE Iterator operator++(int) { auto res = *this; ++it; return res; }
ALWAYS_INLINE friend bool operator !=(const Iterator & left, const Iterator & right) { return left.it != right.it; } ALWAYS_INLINE friend bool operator!=(const Iterator & left, const Iterator & right) { return left.it != right.it; }
ALWAYS_INLINE friend bool operator ==(const Iterator & left, const Iterator & right) { return !(left != right); } ALWAYS_INLINE friend bool operator==(const Iterator & left, const Iterator & right) { return !(left != right); }
private: private:
simdjson::dom::array::iterator it; simdjson::dom::array::iterator it;
}; };
@ -72,26 +75,27 @@ struct SimdJSONParser
ALWAYS_INLINE Iterator begin() const { return array.begin(); } ALWAYS_INLINE Iterator begin() const { return array.begin(); }
ALWAYS_INLINE Iterator end() const { return array.end(); } ALWAYS_INLINE Iterator end() const { return array.end(); }
ALWAYS_INLINE size_t size() const { return array.size(); } ALWAYS_INLINE size_t size() const { return array.size(); }
ALWAYS_INLINE Element operator[](size_t index) const { return array.at(index).first; } ALWAYS_INLINE Element operator[](size_t index) const { assert(index < size()); return array.at(index).first; }
private: private:
simdjson::dom::array array; simdjson::dom::array array;
}; };
using KeyValuePair = std::pair<std::string_view, Element>;
/// References an object in a JSON document.
class Object class Object
{ {
public: public:
using KeyValuePair = std::pair<std::string_view, Element>;
class Iterator class Iterator
{ {
public: public:
ALWAYS_INLINE Iterator(const simdjson::dom::object::iterator & it_) : it(it_) {} ALWAYS_INLINE Iterator(const simdjson::dom::object::iterator & it_) : it(it_) {}
ALWAYS_INLINE KeyValuePair operator *() const { const auto & res = *it; return {res.key, res.value}; } ALWAYS_INLINE KeyValuePair operator*() const { const auto & res = *it; return {res.key, res.value}; }
ALWAYS_INLINE Iterator & operator ++() { ++it; return *this; } ALWAYS_INLINE Iterator & operator++() { ++it; return *this; }
ALWAYS_INLINE Iterator operator ++(int) { auto res = *this; ++it; return res; } ALWAYS_INLINE Iterator operator++(int) { auto res = *this; ++it; return res; }
ALWAYS_INLINE friend bool operator !=(const Iterator & left, const Iterator & right) { return left.it != right.it; } ALWAYS_INLINE friend bool operator!=(const Iterator & left, const Iterator & right) { return left.it != right.it; }
ALWAYS_INLINE friend bool operator ==(const Iterator & left, const Iterator & right) { return !(left != right); } ALWAYS_INLINE friend bool operator==(const Iterator & left, const Iterator & right) { return !(left != right); }
private: private:
simdjson::dom::object::iterator it; simdjson::dom::object::iterator it;
}; };
@ -101,15 +105,7 @@ struct SimdJSONParser
ALWAYS_INLINE Iterator end() const { return object.end(); } ALWAYS_INLINE Iterator end() const { return object.end(); }
ALWAYS_INLINE size_t size() const { return object.size(); } ALWAYS_INLINE size_t size() const { return object.size(); }
KeyValuePair operator [](size_t index) const bool find(const std::string_view & key, Element & result) const
{
Iterator it = begin();
while (index--)
++it;
return *it;
}
ALWAYS_INLINE bool find(const std::string_view & key, Element & result) const
{ {
auto x = object.at_key(key); auto x = object.at_key(key);
if (x.error()) if (x.error())
@ -119,17 +115,22 @@ struct SimdJSONParser
return true; return true;
} }
/// Optional: Provides access to an object's element by index.
KeyValuePair operator[](size_t index) const
{
assert(index < size());
auto it = object.begin();
while (index--)
++it;
const auto & res = *it;
return {res.key, res.value};
}
private: private:
simdjson::dom::object object; simdjson::dom::object object;
}; };
void reserve(size_t max_size) /// Parses a JSON document, returns the reference to its root element if succeeded.
{
if (parser.allocate(max_size) != simdjson::error_code::SUCCESS)
throw Exception{"Couldn't allocate " + std::to_string(max_size) + " bytes when parsing JSON",
ErrorCodes::CANNOT_ALLOCATE_MEMORY};
}
bool parse(const std::string_view & json, Element & result) bool parse(const std::string_view & json, Element & result)
{ {
auto document = parser.parse(json.data(), json.size()); auto document = parser.parse(json.data(), json.size());
@ -140,6 +141,14 @@ struct SimdJSONParser
return true; return true;
} }
/// Optional: Allocates memory to parse JSON documents faster.
void reserve(size_t max_size)
{
if (parser.allocate(max_size) != simdjson::error_code::SUCCESS)
throw Exception{"Couldn't allocate " + std::to_string(max_size) + " bytes when parsing JSON",
ErrorCodes::CANNOT_ALLOCATE_MEMORY};
}
private: private:
simdjson::dom::parser parser; simdjson::dom::parser parser;
}; };