GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/json_document.cc
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 115 141 81.6%
Branches: 101 202 50.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "json_document.h"
6
7 #include <cassert>
8 #include <cstdlib>
9 #include <cstring>
10
11 #include "util/exception.h"
12 #include "util/logging.h"
13 #include "util/pointer.h"
14 #include "util/string.h"
15
16 using namespace std; // NOLINT
17
18 52 JsonDocument *JsonDocument::Create(const string &text) {
19
3/6
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 52 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 52 times.
✗ Branch 8 not taken.
52 UniquePtr<JsonDocument> json(new JsonDocument());
20
1/2
✓ Branch 2 taken 52 times.
✗ Branch 3 not taken.
52 bool retval = json->Parse(text);
21
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 47 times.
52 if (!retval) return NULL;
22 47 return json.Release();
23 52 }
24
25 14 string JsonDocument::EscapeString(const string &input) {
26 14 string escaped;
27
1/2
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 escaped.reserve(input.length());
28
29
2/2
✓ Branch 1 taken 102 times.
✓ Branch 2 taken 14 times.
116 for (unsigned i = 0, s = input.length(); i < s; ++i) {
30
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 102 times.
102 if (input[i] == '\\') {
31 escaped.push_back('\\');
32 escaped.push_back('\\');
33
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 100 times.
102 } else if (input[i] == '"') {
34
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 escaped.push_back('\\');
35
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 escaped.push_back('"');
36 } else {
37
1/2
✓ Branch 2 taken 100 times.
✗ Branch 3 not taken.
100 escaped.push_back(input[i]);
38 }
39 }
40 14 return escaped;
41 }
42
43 52 JsonDocument::JsonDocument()
44 52 : allocator_(kDefaultBlockSize), root_(NULL), raw_text_(NULL) {}
45
46 52 JsonDocument::~JsonDocument() {
47
1/2
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
52 if (raw_text_) free(raw_text_);
48 52 }
49
50 /**
51 * Parses a JSON string in text.
52 *
53 * @return true if parsing was successful
54 */
55 52 bool JsonDocument::Parse(const string &text) {
56
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 assert(root_ == NULL);
57
58 // The used JSON library 'vjson' is a destructive parser and therefore
59 // alters the content of the provided buffer. The buffer must persist as
60 // name and string values from JSON nodes just point into it.
61 52 raw_text_ = strdup(text.c_str());
62
63 52 char *error_pos = 0;
64 52 char *error_desc = 0;
65 52 int error_line = 0;
66 JSON *root =
67
1/2
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
52 json_parse(raw_text_, &error_pos, &error_desc, &error_line, &allocator_);
68
69 // check if the json string was parsed successfully
70
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 47 times.
52 if (!root) {
71
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 LogCvmfs(kLogUtility, kLogDebug,
72 "Failed to parse json string. Error at line %d: %s (%s)",
73 error_line, error_desc, error_pos);
74 5 return false;
75 }
76
77 47 root_ = root;
78 47 return true;
79 }
80
81 3 string JsonDocument::PrintArray(JSON *first_child, PrintOptions print_options) {
82
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 string result = "[";
83
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (print_options.with_whitespace) {
84 result += "\n";
85 print_options.num_indent += 2;
86 }
87 3 JSON *value = first_child;
88
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (value != NULL) {
89
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 result += PrintValue(value, print_options);
90 2 value = value->next_sibling;
91 }
92
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 3 times.
13 while (value != NULL) {
93
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
10 result += print_options.with_whitespace ? ",\n" : ",";
94
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 result += PrintValue(value, print_options);
95 10 value = value->next_sibling;
96 }
97
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (print_options.with_whitespace) {
98 result += "\n";
99 for (unsigned i = 2; i < print_options.num_indent; ++i)
100 result.push_back(' ');
101 }
102
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 return result + "]";
103 3 }
104
105 /**
106 * JSON string in a canonical format:
107 * - No whitespaces
108 * - Variable names and strings in quotes
109 *
110 * Can be used as a canonical representation to sign or encrypt a JSON text.
111 */
112 3 string JsonDocument::PrintCanonical() {
113
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3 if (!root_) return "";
114 3 PrintOptions print_options;
115
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 return PrintObject(root_->first_child, print_options);
116 }
117
118 7 string JsonDocument::PrintObject(JSON *first_child,
119 PrintOptions print_options) {
120
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 string result = "{";
121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (print_options.with_whitespace) {
122 result += "\n";
123 print_options.num_indent += 2;
124 }
125 7 JSON *value = first_child;
126
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
7 if (value != NULL) {
127
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 result += PrintValue(value, print_options);
128 4 value = value->next_sibling;
129 }
130
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 7 times.
16 while (value != NULL) {
131
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
9 result += print_options.with_whitespace ? ",\n" : ",";
132
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
9 result += PrintValue(value, print_options);
133 9 value = value->next_sibling;
134 }
135
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (print_options.with_whitespace) {
136 result += "\n";
137 for (unsigned i = 2; i < print_options.num_indent; ++i)
138 result.push_back(' ');
139 }
140
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
14 return result + "}";
141 7 }
142
143 /**
144 * JSON string for humans.
145 */
146 string JsonDocument::PrintPretty() {
147 if (!root_) return "";
148 PrintOptions print_options;
149 print_options.with_whitespace = true;
150 return PrintObject(root_->first_child, print_options);
151 }
152
153 25 std::string JsonDocument::PrintValue(JSON *value, PrintOptions print_options) {
154
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 assert(value);
155
156 25 string result;
157
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 25 times.
25 for (unsigned i = 0; i < print_options.num_indent; ++i) result.push_back(' ');
158
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 14 times.
25 if (value->name) {
159
5/10
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 11 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 11 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 11 times.
✗ Branch 15 not taken.
11 result += "\"" + EscapeString(value->name) + "\":";
160
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
11 if (print_options.with_whitespace) result += " ";
161 }
162
7/8
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
25 switch (value->type) {
163 2 case JSON_NULL:
164
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 result += "null";
165 2 break;
166 4 case JSON_OBJECT:
167
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 result += PrintObject(value->first_child, print_options);
168 4 break;
169 3 case JSON_ARRAY:
170
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 result += PrintArray(value->first_child, print_options);
171 3 break;
172 3 case JSON_STRING:
173
5/10
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 3 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 3 times.
✗ Branch 15 not taken.
3 result += "\"" + EscapeString(value->string_value) + "\"";
174 3 break;
175 8 case JSON_INT:
176
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 result += StringifyInt(value->int_value);
177 8 break;
178 2 case JSON_FLOAT:
179
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 result += StringifyDouble(value->float_value);
180 2 break;
181 3 case JSON_BOOL:
182
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 result += value->int_value ? "true" : "false";
183 3 break;
184 default:
185 PANIC(NULL);
186 }
187 25 return result;
188 }
189
190 119 JSON *JsonDocument::SearchInObject(const JSON *json_object, const string &name,
191 const json_type type) {
192
4/4
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 117 times.
119 if (!json_object || (json_object->type != JSON_OBJECT)) return NULL;
193
194 117 JSON *walker = json_object->first_child;
195
2/2
✓ Branch 0 taken 225 times.
✓ Branch 1 taken 14 times.
239 while (walker != NULL) {
196
3/6
✓ Branch 2 taken 225 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 103 times.
✓ Branch 8 taken 122 times.
225 if (string(walker->name) == name) {
197
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 2 times.
103 return (walker->type == type) ? walker : NULL;
198 }
199 122 walker = walker->next_sibling;
200 }
201 14 return NULL;
202 }
203
204 template <>
205 6 bool GetFromJSON<std::string>(const JSON *object, const std::string &name,
206 std::string *value) {
207 6 const JSON *o = JsonDocument::SearchInObject(object, name, JSON_STRING);
208
209
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (o == NULL) {
210 1 return false;
211 }
212
213
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (value) {
214 5 *value = o->string_value;
215 }
216
217 5 return true;
218 }
219
220 template <>
221 2 bool GetFromJSON<int>(const JSON *object, const std::string &name, int *value) {
222 2 const JSON *o = JsonDocument::SearchInObject(object, name, JSON_INT);
223
224
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (o == NULL || value == NULL) {
225 return false;
226 }
227
228 2 *value = o->int_value;
229
230 2 return true;
231 }
232
233 template <>
234 bool GetFromJSON<float>(const JSON *object, const std::string &name,
235 float *value) {
236 const JSON *o = JsonDocument::SearchInObject(object, name, JSON_FLOAT);
237
238 if (o == NULL || value == NULL) {
239 return false;
240 }
241
242 *value = o->float_value;
243
244 return true;
245 }
246