Improve performance for both in-place and copy parsing (faster than AZ::JsonSerializationUtils::ReadJsonString now!)

Signed-off-by: Nicholas Van Sickle <nvsickle@amazon.com>
This commit is contained in:
Nicholas Van Sickle
2021-12-01 16:22:48 -08:00
parent e0508ddefc
commit d519d4cb03
2 changed files with 66 additions and 62 deletions
@@ -413,57 +413,6 @@ namespace AZ::Dom::Json
return true;
}
//
// struct AzStringStream
//
// rapidjson stream wrapper for AZStd::string suitable for in-situ parsing
AzStringStream::AzStringStream(AZStd::string& buffer)
{
m_cursor = buffer.data();
m_begin = m_cursor;
}
char AzStringStream::Peek() const
{
return *m_cursor;
}
char AzStringStream::Take()
{
return *m_cursor++;
}
size_t AzStringStream::Tell() const
{
return static_cast<size_t>(m_cursor - m_begin);
}
char* AzStringStream::PutBegin()
{
m_write = m_cursor;
return m_cursor;
}
void AzStringStream::Put(char c)
{
(*m_write++) = c;
}
void AzStringStream::Flush()
{
}
size_t AzStringStream::PutEnd(char* begin)
{
return m_write - begin;
}
const char* AzStringStream::Peek4() const
{
AZ_Assert(false, "Not implemented, encoding is hard-coded to UTF-8");
return m_cursor;
}
//
// Serialized JSON util functions
//
@@ -113,19 +113,65 @@ namespace AZ::Dom::Json
};
//! rapidjson stream wrapper for AZStd::string suitable for in-situ parsing
//! Faster than rapidjson::MemoryStream for reading from AZStd::string / AZStd::string_view (because it requires a null terminator)
//! \note This needs to be inlined for performance reasons.
struct AzStringStream
{
using Ch = char; //<! Denotes the string character storage type for rapidjson
AzStringStream(AZStd::string& buffer);
char Peek() const;
char Take();
size_t Tell() const;
char* PutBegin();
void Put(char c);
void Flush();
size_t PutEnd(char* begin);
const char* Peek4() const;
AZ_FORCE_INLINE AzStringStream(AZStd::string& buffer)
{
m_cursor = buffer.data();
m_begin = m_cursor;
}
AZ_FORCE_INLINE AzStringStream(AZStd::string_view buffer)
{
// rapidjson won't actually call PutBegin or Put unless kParseInSituFlag is set, so this is safe
m_cursor = const_cast<char*>(buffer.data());
m_begin = m_cursor;
}
AZ_FORCE_INLINE char Peek() const
{
return *m_cursor;
}
AZ_FORCE_INLINE char Take()
{
return *m_cursor++;
}
AZ_FORCE_INLINE size_t Tell() const
{
return static_cast<size_t>(m_cursor - m_begin);
}
AZ_FORCE_INLINE char* PutBegin()
{
m_write = m_cursor;
return m_cursor;
}
AZ_FORCE_INLINE void Put(char c)
{
(*m_write++) = c;
}
AZ_FORCE_INLINE void Flush()
{
}
AZ_FORCE_INLINE size_t PutEnd(char* begin)
{
return m_write - begin;
}
AZ_FORCE_INLINE const char* Peek4() const
{
AZ_Assert(false, "Not implemented, encoding is hard-coded to UTF-8");
return m_cursor;
}
char* m_cursor; //!< Current read position.
char* m_write; //!< Current write position.
@@ -180,10 +226,19 @@ namespace AZ::Dom::Json
Visitor::Result VisitSerializedJson(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor)
{
rapidjson::Reader reader;
rapidjson::MemoryStream stream(buffer.data(), buffer.size());
RapidJsonReadHandler handler(&visitor, lifetime);
reader.Parse<static_cast<int>(parseFlags)>(stream, handler);
// If the string is null terminated, we can use the faster AzStringStream path - otherwise we fall back on rapidjson::MemoryStream
if (buffer.data()[buffer.size()] == '\0')
{
AzStringStream stream(buffer);
reader.Parse<static_cast<int>(parseFlags)>(stream, handler);
}
else
{
rapidjson::MemoryStream stream(buffer.data(), buffer.size());
reader.Parse<static_cast<int>(parseFlags)>(stream, handler);
}
return handler.TakeOutcome();
}