Commit e0b9d4f7 by Gert Wollny Committed by Commit Bot

libAngle: Add Json context serializer library

Bug: angleproject:5715 Change-Id: I42319fe30e42d49d7e817b14c211b5ba82a94a42 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2760324Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 51886f57
......@@ -914,8 +914,22 @@ group("angle_compression") {
[ "$angle_zlib_compression_utils_dir:compression_utils_portable" ]
}
angle_source_set("libjson_serializer") {
public_deps = [
":libANGLE_base",
"$angle_root/third_party/rapidjson",
]
sources = [
"src/libANGLE/serializer/JsonSerializer.cpp",
"src/libANGLE/serializer/JsonSerializer.h",
]
}
angle_source_set("libANGLE_with_capture") {
public_deps = [ ":libANGLE_base" ]
public_deps = [
":libANGLE_base",
":libjson_serializer",
]
deps = [ ":angle_compression" ]
public_configs = [ ":angle_frame_capture_enabled" ]
sources = libangle_capture_sources
......
......@@ -39,6 +39,7 @@
#include "libANGLE/capture/gl_enum_utils.h"
#include "libANGLE/queryconversions.h"
#include "libANGLE/queryutils.h"
#include "libANGLE/serializer/JsonSerializer.h"
#define USE_SYSTEM_ZLIB
#include "compression_utils_portable.h"
......
......@@ -29,6 +29,7 @@
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/FramebufferImpl.h"
#include "libANGLE/renderer/RenderbufferImpl.h"
#include "libANGLE/serializer/JsonSerializer.h"
// Note: when diagnosing serialization comparison failures, you can disable the unused function
// compiler warning to allow bisecting the comparison function. One first check is to disable
......
//
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// JsonSerializer.cpp: Implementation of a JSON based serializer
// Note that for binary blob data only a checksum is stored so that
// a lossless deserialization is not supported.
#include "JsonSerializer.h"
#include "common/debug.h"
#include <rapidjson/document.h>
#include <rapidjson/filewritestream.h>
#include <rapidjson/ostreamwrapper.h>
#include <rapidjson/prettywriter.h>
#include <anglebase/sha1.h>
namespace angle
{
namespace js = rapidjson;
JsonSerializer::JsonSerializer() : mDoc(js::kObjectType), mAllocator(mDoc.GetAllocator()) {}
JsonSerializer::~JsonSerializer() {}
void JsonSerializer::startDocument(const std::string &name)
{
startGroup(name);
}
void JsonSerializer::startGroup(const std::string &name)
{
auto group = std::make_unique<js::Value>(js::kObjectType);
mGroupValueStack.push(std::move(group));
mGroupNameStack.push(name);
}
void JsonSerializer::endGroup()
{
ValuePointer group = std::move(mGroupValueStack.top());
std::string name = std::move(mGroupNameStack.top());
mGroupValueStack.pop();
mGroupNameStack.pop();
rapidjson::Value name_value(name.c_str(), mAllocator);
mGroupValueStack.top()->AddMember(name_value, *group, mAllocator);
}
void JsonSerializer::addBlob(const std::string &name, const uint8_t *blob, size_t length)
{
rapidjson::Value tag(name.c_str(), mAllocator);
unsigned char hash[angle::base::kSHA1Length];
angle::base::SHA1HashBytes(blob, length, hash);
std::ostringstream os;
// Since we don't want to de-serialize the data we just store a checksume
// of the blob
os << "SHA1:";
static constexpr char kASCII[] = "0123456789ABCDEF";
for (size_t i = 0; i < angle::base::kSHA1Length; ++i)
{
os << kASCII[hash[i] & 0xf] << kASCII[hash[i] >> 4];
}
addString(name, os.str());
}
void JsonSerializer::addCString(const std::string &name, const char *value)
{
rapidjson::Value tag(name.c_str(), mAllocator);
rapidjson::Value val(value, mAllocator);
mGroupValueStack.top()->AddMember(tag, val, mAllocator);
}
void JsonSerializer::addString(const std::string &name, const std::string &value)
{
addCString(name, value.c_str());
}
const char *JsonSerializer::data() const
{
return mResult.c_str();
}
std::vector<uint8_t> JsonSerializer::getData() const
{
return std::vector<uint8_t>(mResult.begin(), mResult.end());
}
void JsonSerializer::endDocument()
{
// finalize last group
ASSERT(!mGroupValueStack.empty());
ASSERT(!mGroupNameStack.empty());
rapidjson::Value name_value(mGroupNameStack.top().c_str(), mAllocator);
mDoc.AddMember(name_value, *mGroupValueStack.top(), mAllocator);
mGroupValueStack.pop();
mGroupNameStack.pop();
ASSERT(mGroupValueStack.empty());
ASSERT(mGroupNameStack.empty());
std::stringstream os;
js::OStreamWrapper osw(os);
js::PrettyWriter<js::OStreamWrapper> pretty_os(osw);
mDoc.Accept(pretty_os);
mResult = os.str();
}
size_t JsonSerializer::length() const
{
return mResult.length();
}
} // namespace angle
//
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// JsonSerializer.h: Implementation of a JSON based serializer
//
#ifndef LIBANGLE_JSONSERIALIZER_H_
#define LIBANGLE_JSONSERIALIZER_H_
#include "common/angleutils.h"
#include <rapidjson/document.h>
#include <memory>
#include <sstream>
#include <stack>
#include <type_traits>
namespace angle
{
// Rapidjson has problems picking the right AddMember template for long
// integer types, so let's just make these values use 64 bit variants
template <typename T>
struct StoreAs
{
using Type = T;
};
template <>
struct StoreAs<unsigned long>
{
using Type = uint64_t;
};
template <>
struct StoreAs<signed long>
{
using Type = int64_t;
};
class JsonSerializer : public angle::NonCopyable
{
public:
JsonSerializer();
~JsonSerializer();
void startDocument(const std::string &name);
void endDocument();
template <typename T>
void addScalar(const std::string &name, T value)
{
rapidjson::Value tag(name.c_str(), mAllocator);
typename StoreAs<T>::Type v = value;
mGroupValueStack.top()->AddMember(tag, v, mAllocator);
}
template <typename T>
void addVector(const std::string &name, const std::vector<T> &value)
{
rapidjson::Value tag(name.c_str(), mAllocator);
rapidjson::Value array(rapidjson::kArrayType);
array.SetArray();
for (typename StoreAs<T>::Type v : value)
array.PushBack(v, mAllocator);
mGroupValueStack.top()->AddMember(tag, array, mAllocator);
}
void addCString(const std::string &name, const char *value);
void addString(const std::string &name, const std::string &value);
void addBlob(const std::string &name, const uint8_t *value, size_t length);
void startGroup(const std::string &name);
void endGroup();
const char *data() const;
std::vector<uint8_t> getData() const;
size_t length() const;
private:
using ValuePointer = std::unique_ptr<rapidjson::Value>;
rapidjson::Document mDoc;
rapidjson::Document::AllocatorType &mAllocator;
std::stack<std::string> mGroupNameStack;
std::stack<ValuePointer> mGroupValueStack;
std::string mResult;
};
} // namespace angle
#endif // JSONSERIALIZER_H
//
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// JsonSerializer_unittests-cpp: Unit tests for the JSON based serializer
//
#include "JsonSerializer.h"
#include <gtest/gtest.h>
class JsonSerializerTest : public ::testing::Test
{
protected:
void SetUp() override;
void check(const std::string &expect);
angle::JsonSerializer js;
};
// Test writing one integer value
TEST_F(JsonSerializerTest, NamedIntValue1)
{
js.addScalar("test1", 1);
const std::string expect =
R"({
"context": {
"test1": 1
}
})";
check(expect);
}
// Test writing one long value
TEST_F(JsonSerializerTest, NamedLongValue)
{
long v = -12;
js.addScalar("test1", v);
const std::string expect =
R"({
"context": {
"test1": -12
}
})";
check(expect);
}
// Test writing one unsigned long value
TEST_F(JsonSerializerTest, NamedULongValue)
{
unsigned long v = 12;
js.addScalar("test1", v);
const std::string expect =
R"({
"context": {
"test1": 12
}
})";
check(expect);
}
// Test writing another integer value
TEST_F(JsonSerializerTest, NamedIntValue2)
{
js.addScalar("test2", 2);
const std::string expect =
R"({
"context": {
"test2": 2
}
})";
check(expect);
}
// Test writing one string value
TEST_F(JsonSerializerTest, NamedStringValue)
{
js.addCString("test2", "value");
const std::string expect =
R"({
"context": {
"test2": "value"
}
})";
check(expect);
}
// Test writing one byte array
// Since he serialiter is only used for testing we don't store
// the whole byte array, but only it's SHA1 checksum
TEST_F(JsonSerializerTest, ByteArrayValue)
{
const uint8_t value[5] = {10, 0, 0xcc, 0xff, 0xaa};
js.addBlob("test2", value, 5);
const std::string expect =
R"({
"context": {
"test2": "SHA1:4315724B1AB1EB2C0128E8E9DAD6D76254BA711D"
}
})";
check(expect);
}
// Test writing one vector of integer values
TEST_F(JsonSerializerTest, IntVectorValue)
{
std::vector<int> v = {0, 1, -1};
js.addVector("test2", v);
const std::string expect =
R"({
"context": {
"test2": [
0,
1,
-1
]
}
})";
check(expect);
}
// Test writing boolean values
TEST_F(JsonSerializerTest, NamedBoolValues)
{
js.addScalar("test_false", false);
js.addScalar("test_true", true);
const std::string expect =
R"({
"context": {
"test_false": false,
"test_true": true
}
})";
check(expect);
}
// test writing two values in a sub-group
TEST_F(JsonSerializerTest, GroupedIntValue)
{
js.startGroup("group");
js.addScalar("test1", 1);
js.addScalar("test2", 2);
js.endGroup();
const std::string expect =
R"({
"context": {
"group": {
"test1": 1,
"test2": 2
}
}
})";
check(expect);
}
void JsonSerializerTest::SetUp()
{
js.startDocument("context");
}
void JsonSerializerTest::check(const std::string &expect)
{
js.endDocument();
EXPECT_EQ(js.data(), expect);
EXPECT_EQ(js.length(), expect.length());
std::vector<uint8_t> expect_as_ubyte(expect.begin(), expect.end());
EXPECT_EQ(js.getData(), expect_as_ubyte);
}
......@@ -144,6 +144,7 @@ angle_test("angle_unittests") {
"$angle_root:libEGL_static",
"$angle_root:libGLESv2_static",
"$angle_root:libfeature_support",
"$angle_root:libjson_serializer",
"$angle_root:preprocessor",
"$angle_root:translator",
"$angle_root/util:angle_util_static",
......
......@@ -48,6 +48,7 @@ angle_unittests_sources = [
"../libANGLE/renderer/TextureImpl_mock.h",
"../libANGLE/renderer/TransformFeedbackImpl_mock.h",
"../libANGLE/renderer/serial_utils_unittest.cpp",
"../libANGLE/serializer/JsonSerializer_unittest.cpp",
"angle_unittests_utils.h",
"preprocessor_tests/MockDiagnostics.h",
"preprocessor_tests/MockDirectiveHandler.h",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment