1 //
2 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // JsonSerializer.cpp: Implementation of a JSON based serializer
7 // Note that for binary blob data only a checksum is stored so that
8 // a lossless deserialization is not supported.
9
10 #include "JsonSerializer.h"
11
12 #include "common/debug.h"
13
14 #include <anglebase/sha1.h>
15 #include <rapidjson/document.h>
16 #include <rapidjson/filewritestream.h>
17 #include <rapidjson/ostreamwrapper.h>
18 #include <rapidjson/prettywriter.h>
19
20 namespace angle
21 {
22
23 namespace js = rapidjson;
24
JsonSerializer()25 JsonSerializer::JsonSerializer() : mDoc(js::kObjectType), mAllocator(mDoc.GetAllocator()) {}
26
~JsonSerializer()27 JsonSerializer::~JsonSerializer() {}
28
startGroup(const std::string & name)29 void JsonSerializer::startGroup(const std::string &name)
30 {
31 mGroupValueStack.push(SortedValueGroup());
32 mGroupNameStack.push(name);
33 }
34
endGroup()35 void JsonSerializer::endGroup()
36 {
37 ASSERT(!mGroupValueStack.empty());
38 ASSERT(!mGroupNameStack.empty());
39
40 rapidjson::Value group = makeValueGroup(mGroupValueStack.top());
41 std::string name = mGroupNameStack.top();
42
43 mGroupValueStack.pop();
44 mGroupNameStack.pop();
45
46 addValue(name, std::move(group));
47 }
48
addBlob(const std::string & name,const uint8_t * blob,size_t length)49 void JsonSerializer::addBlob(const std::string &name, const uint8_t *blob, size_t length)
50 {
51 unsigned char hash[angle::base::kSHA1Length];
52 angle::base::SHA1HashBytes(blob, length, hash);
53 std::ostringstream os;
54
55 // Since we don't want to de-serialize the data we just store a checksum of the blob
56 os << "SHA1:";
57 static constexpr char kASCII[] = "0123456789ABCDEF";
58 for (size_t i = 0; i < angle::base::kSHA1Length; ++i)
59 {
60 os << kASCII[hash[i] & 0xf] << kASCII[hash[i] >> 4];
61 }
62
63 std::ostringstream hashName;
64 hashName << name << "-hash";
65 addString(hashName.str(), os.str());
66
67 std::vector<uint8_t> data((length < 16) ? length : static_cast<size_t>(16));
68 std::copy(blob, blob + data.size(), data.begin());
69
70 std::ostringstream rawName;
71 rawName << name << "-raw[0-" << data.size() - 1 << ']';
72 addVector(rawName.str(), data);
73 }
74
addCString(const std::string & name,const char * value)75 void JsonSerializer::addCString(const std::string &name, const char *value)
76 {
77 rapidjson::Value tag(name.c_str(), mAllocator);
78 rapidjson::Value val(value, mAllocator);
79 addValue(name, std::move(val));
80 }
81
addString(const std::string & name,const std::string & value)82 void JsonSerializer::addString(const std::string &name, const std::string &value)
83 {
84 addCString(name, value.c_str());
85 }
86
addVectorOfStrings(const std::string & name,const std::vector<std::string> & value)87 void JsonSerializer::addVectorOfStrings(const std::string &name,
88 const std::vector<std::string> &value)
89 {
90 rapidjson::Value arrayValue(rapidjson::kArrayType);
91 arrayValue.SetArray();
92
93 for (const std::string &v : value)
94 {
95 rapidjson::Value str(v.c_str(), mAllocator);
96 arrayValue.PushBack(str, mAllocator);
97 }
98
99 addValue(name, std::move(arrayValue));
100 }
101
addBool(const std::string & name,bool value)102 void JsonSerializer::addBool(const std::string &name, bool value)
103 {
104 rapidjson::Value boolValue(value);
105 addValue(name, std::move(boolValue));
106 }
107
addHexValue(const std::string & name,int value)108 void JsonSerializer::addHexValue(const std::string &name, int value)
109 {
110 // JSON doesn't support hex values, so write it as a string
111 std::stringstream hexStream;
112 hexStream << "0x" << std::uppercase << std::setfill('0') << std::setw(4) << std::hex << value;
113 addCString(name, hexStream.str().c_str());
114 }
115
data()116 const char *JsonSerializer::data()
117 {
118 ensureEndDocument();
119 return mResult.c_str();
120 }
121
getData()122 std::vector<uint8_t> JsonSerializer::getData()
123 {
124 ensureEndDocument();
125 return std::vector<uint8_t>(mResult.begin(), mResult.end());
126 }
127
ensureEndDocument()128 void JsonSerializer::ensureEndDocument()
129 {
130 if (!mResult.empty())
131 {
132 return;
133 }
134
135 std::stringstream os;
136 js::OStreamWrapper osw(os);
137 js::PrettyWriter<js::OStreamWrapper> prettyOs(osw);
138 mDoc.Accept(prettyOs);
139 mResult = os.str();
140 }
141
length()142 size_t JsonSerializer::length()
143 {
144 ensureEndDocument();
145 return mResult.length();
146 }
147
makeValueGroup(SortedValueGroup & group)148 rapidjson::Value JsonSerializer::makeValueGroup(SortedValueGroup &group)
149 {
150 rapidjson::Value valueGroup(js::kObjectType);
151 for (auto &it : group)
152 {
153 rapidjson::Value tag(it.first.c_str(), mAllocator);
154 valueGroup.AddMember(tag, it.second, mAllocator);
155 }
156 return valueGroup;
157 }
158
addValue(const std::string & name,rapidjson::Value && value)159 void JsonSerializer::addValue(const std::string &name, rapidjson::Value &&value)
160 {
161 if (!mGroupValueStack.empty())
162 {
163 mGroupValueStack.top().insert(std::make_pair(name, std::move(value)));
164 }
165 else
166 {
167 rapidjson::Value nameValue(name, mAllocator);
168 mDoc.AddMember(nameValue, std::move(value), mAllocator);
169 }
170 }
171 } // namespace angle
172