GCC Code Coverage Report


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