| // | |
| // 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 <array> | |
| #include <sstream> | |
| #include <string_view> | |
| #include "common/debug.h" | |
| #include "common/unsafe_buffers.h" | |
| #include <anglebase/sha1.h> | |
| #include <rapidjson/document.h> | |
| #include <rapidjson/filewritestream.h> | |
| #include <rapidjson/ostreamwrapper.h> | |
| #include <rapidjson/prettywriter.h> | |
| namespace angle | |
| { | |
| namespace js = rapidjson; | |
| JsonSerializer::JsonSerializer() : mDoc(js::kObjectType), mAllocator(mDoc.GetAllocator()) {} | |
| JsonSerializer::~JsonSerializer() {} | |
| void JsonSerializer::startGroup(const std::string &name) | |
| { | |
| mGroupValueStack.push(SortedValueGroup()); | |
| mGroupNameStack.push(name); | |
| } | |
| void JsonSerializer::endGroup() | |
| { | |
| ASSERT(!mGroupValueStack.empty()); | |
| ASSERT(!mGroupNameStack.empty()); | |
| rapidjson::Value group = makeValueGroup(mGroupValueStack.top()); | |
| std::string name = mGroupNameStack.top(); | |
| mGroupValueStack.pop(); | |
| mGroupNameStack.pop(); | |
| addValue(name, std::move(group)); | |
| } | |
| void JsonSerializer::addBlob(const std::string &name, angle::Span<const uint8_t> blob) | |
| { | |
| addBlobWithMax(name, blob, 16); | |
| } | |
| void JsonSerializer::addBlobWithMax(const std::string &name, | |
| angle::Span<const uint8_t> blob, | |
| size_t maxSerializedLength) | |
| { | |
| std::array<unsigned char, angle::base::kSHA1Length> hash; | |
| angle::base::SHA1HashBytes(blob.data(), blob.size(), hash.data()); | |
| std::ostringstream os; | |
| // Since we don't want to de-serialize the data we just store a checksum of the blob | |
| os << "SHA1:"; | |
| static constexpr std::string_view kASCII = "0123456789ABCDEF"; | |
| for (size_t i = 0; i < angle::base::kSHA1Length; ++i) | |
| { | |
| os << kASCII[hash[i] & 0xf] << kASCII[hash[i] >> 4]; | |
| } | |
| std::ostringstream hashName; | |
| hashName << name << "-hash"; | |
| addString(hashName.str(), os.str()); | |
| if (blob.size() > maxSerializedLength) | |
| { | |
| blob = blob.first(maxSerializedLength); | |
| } | |
| std::vector<uint8_t> data(blob.begin(), blob.end()); | |
| std::ostringstream rawName; | |
| rawName << name << "-raw[0-" << data.size() - 1 << ']'; | |
| addVector(rawName.str(), data); | |
| } | |
| void JsonSerializer::addCString(const std::string &name, const char *value) | |
| { | |
| rapidjson::Value val(value, mAllocator); | |
| addValue(name, std::move(val)); | |
| } | |
| void JsonSerializer::addString(const std::string &name, const std::string &value) | |
| { | |
| addCString(name, value.c_str()); | |
| } | |
| void JsonSerializer::addVectorOfStrings(const std::string &name, | |
| const std::vector<std::string> &value) | |
| { | |
| rapidjson::Value arrayValue(rapidjson::kArrayType); | |
| arrayValue.SetArray(); | |
| for (const std::string &v : value) | |
| { | |
| rapidjson::Value str(v.c_str(), mAllocator); | |
| arrayValue.PushBack(str, mAllocator); | |
| } | |
| addValue(name, std::move(arrayValue)); | |
| } | |
| void JsonSerializer::addBool(const std::string &name, bool value) | |
| { | |
| rapidjson::Value boolValue(value); | |
| addValue(name, std::move(boolValue)); | |
| } | |
| void JsonSerializer::addHexValue(const std::string &name, int value) | |
| { | |
| // JSON doesn't support hex values, so write it as a string | |
| std::stringstream hexStream; | |
| hexStream << "0x" << std::uppercase << std::setfill('0') << std::setw(4) << std::hex << value; | |
| addCString(name, hexStream.str().c_str()); | |
| } | |
| const char *JsonSerializer::data() | |
| { | |
| ensureEndDocument(); | |
| return mResult.c_str(); | |
| } | |
| std::vector<uint8_t> JsonSerializer::getData() | |
| { | |
| ensureEndDocument(); | |
| return std::vector<uint8_t>(mResult.begin(), mResult.end()); | |
| } | |
| void JsonSerializer::ensureEndDocument() | |
| { | |
| if (!mResult.empty()) | |
| { | |
| return; | |
| } | |
| std::stringstream os; | |
| js::OStreamWrapper osw(os); | |
| js::PrettyWriter<js::OStreamWrapper> prettyOs(osw); | |
| mDoc.Accept(prettyOs); | |
| mResult = os.str(); | |
| } | |
| size_t JsonSerializer::length() | |
| { | |
| ensureEndDocument(); | |
| return mResult.length(); | |
| } | |
| rapidjson::Value JsonSerializer::makeValueGroup(SortedValueGroup &group) | |
| { | |
| rapidjson::Value valueGroup(js::kObjectType); | |
| for (auto &it : group) | |
| { | |
| rapidjson::Value tag(it.first.c_str(), mAllocator); | |
| valueGroup.AddMember(tag, it.second, mAllocator); | |
| } | |
| return valueGroup; | |
| } | |
| void JsonSerializer::addValue(const std::string &name, rapidjson::Value &&value) | |
| { | |
| if (!mGroupValueStack.empty()) | |
| { | |
| mGroupValueStack.top().insert(std::make_pair(name, std::move(value))); | |
| } | |
| else | |
| { | |
| rapidjson::Value nameValue(name, mAllocator); | |
| mDoc.AddMember(nameValue, std::move(value), mAllocator); | |
| } | |
| } | |
| } // namespace angle |