GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/json_document_write.h
Date: 2026-09-20 02:39:58
Exec Total Coverage
Lines: 70 92 76.1%
Branches: 43 101 42.6%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #ifndef CVMFS_JSON_DOCUMENT_WRITE_H_
6 #define CVMFS_JSON_DOCUMENT_WRITE_H_
7
8 #include <cstdio>
9 #include <string>
10 #include <vector>
11
12 #include "util/exception.h"
13 #include "util/string.h"
14
15 #ifdef CVMFS_NAMESPACE_GUARD
16 namespace CVMFS_NAMESPACE_GUARD {
17 #endif
18
19 /**
20 * This class is used for marshalling JSON objects to strings.
21 *
22 * When creating simple objects is sufficient to call the `Add()` methods to add
23 * new key - values to the final JSON.
24 *
25 * When creating complex objects, (an object that contains another object) is
26 * necessary to create first the nested object and then add it to the final
27 * object with the `AddJsonObject`. This will take care of all the escaping.
28 */
29 class JsonStringGenerator {
30 enum JsonVariant {
31 kString,
32 kInteger,
33 kFloat,
34 kJsonObject
35 };
36
37 struct JsonEntry {
38 JsonVariant variant;
39 std::string key_escaped;
40 std::string str_val_escaped;
41 int64_t int_val;
42 float float_val;
43
44 744 JsonEntry(const std::string &key_escaped, const std::string &val)
45 744 : variant(kString)
46 744 , key_escaped(key_escaped)
47
1/2
✓ Branch 1 taken 744 times.
✗ Branch 2 not taken.
744 , str_val_escaped(val)
48 744 , int_val(0)
49 744 , float_val(0.0) { }
50
51 133 JsonEntry(const std::string &key_escaped, const std::string &val,
52 const JsonVariant variant)
53 133 : variant(variant)
54 133 , key_escaped(key_escaped)
55
1/2
✓ Branch 1 taken 133 times.
✗ Branch 2 not taken.
133 , str_val_escaped(val)
56 133 , int_val(0)
57 133 , float_val(0.0) { }
58
59 1 JsonEntry(const std::string &key_escaped, const int val)
60 1 : variant(kInteger)
61 1 , key_escaped(key_escaped)
62 1 , str_val_escaped()
63 1 , int_val(val)
64 1 , float_val(0.0) { }
65
66 JsonEntry(const std::string &key_escaped, const float val)
67 : variant(kFloat)
68 , key_escaped(key_escaped)
69 , str_val_escaped()
70 , int_val(0)
71 , float_val(val) { }
72
73 218 JsonEntry(const std::string &key_escaped, const int64_t val)
74 218 : variant(kInteger)
75 218 , key_escaped(key_escaped)
76 218 , str_val_escaped()
77 218 , int_val(val)
78 218 , float_val(0.0) { }
79
80 1096 std::string Format() const {
81
3/5
✓ Branch 0 taken 744 times.
✓ Branch 1 taken 219 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 133 times.
✗ Branch 4 not taken.
1096 switch (variant) {
82 744 case kString:
83
3/6
✓ Branch 2 taken 744 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 744 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 744 times.
✗ Branch 9 not taken.
1488 return "\"" + key_escaped + "\":\"" + str_val_escaped + "\"";
84 219 case kInteger:
85
3/6
✓ Branch 2 taken 219 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 219 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 219 times.
✗ Branch 9 not taken.
438 return "\"" + key_escaped + "\":" + StringifyInt(int_val);
86 case kFloat:
87 return "\"" + key_escaped + "\":" + StringifyDouble(float_val);
88 133 case kJsonObject:
89
2/4
✓ Branch 2 taken 133 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 133 times.
✗ Branch 6 not taken.
266 return "\"" + key_escaped + "\":" + str_val_escaped;
90 default:
91 PANIC(kLogStdout | kLogStderr, "JSON creation failed");
92 }
93 }
94 };
95
96 public:
97 744 void Add(const std::string &key, const std::string &val) {
98
3/6
✓ Branch 1 taken 744 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 744 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 744 times.
✗ Branch 8 not taken.
1488 const JsonEntry entry(Escape(key), Escape(val));
99
1/2
✓ Branch 1 taken 744 times.
✗ Branch 2 not taken.
744 entries.push_back(entry);
100 744 }
101
102 1 void Add(const std::string &key, const int val) {
103
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 const JsonEntry entry(Escape(key), val);
104
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 entries.push_back(entry);
105 1 }
106
107 void Add(const std::string &key, const float val) {
108 const JsonEntry entry(Escape(key), val);
109 entries.push_back(entry);
110 }
111
112 218 void Add(const std::string &key, const int64_t val) {
113
2/4
✓ Branch 1 taken 218 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 218 times.
✗ Branch 5 not taken.
218 const JsonEntry entry(Escape(key), val);
114
1/2
✓ Branch 1 taken 218 times.
✗ Branch 2 not taken.
218 entries.push_back(entry);
115 218 }
116
117 133 void AddJsonObject(const std::string &key, const std::string &json) {
118 // we **do not escape** the value here
119
2/4
✓ Branch 1 taken 133 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 133 times.
✗ Branch 5 not taken.
133 const JsonEntry entry(Escape(key), json, kJsonObject);
120
1/2
✓ Branch 1 taken 133 times.
✗ Branch 2 not taken.
133 entries.push_back(entry);
121 133 }
122
123 526 std::string GenerateString() const {
124 526 std::string output;
125
126
1/2
✓ Branch 1 taken 526 times.
✗ Branch 2 not taken.
526 output += "{";
127
2/2
✓ Branch 1 taken 1096 times.
✓ Branch 2 taken 526 times.
1622 for (size_t i = 0u; i < this->entries.size(); ++i) {
128
2/4
✓ Branch 2 taken 1096 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1096 times.
✗ Branch 6 not taken.
1096 output += this->entries[i].Format();
129
2/2
✓ Branch 1 taken 655 times.
✓ Branch 2 taken 441 times.
1096 if (i < this->entries.size() - 1) {
130
1/2
✓ Branch 1 taken 655 times.
✗ Branch 2 not taken.
655 output += ',';
131 }
132 }
133
2/4
✓ Branch 2 taken 526 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 526 times.
✗ Branch 6 not taken.
526 output += std::string("}");
134 526 return output;
135 }
136
137 96 void Clear() { entries.clear(); }
138
139 private:
140 // this escape procedure is not as complete as it should be.
141 // we should manage ALL control chars from '\x00' to '\x1f'
142 // however this are the one that we can expect to happen
143 // More info: https://stackoverflow.com/a/33799784/869271
144 1840 const std::string Escape(const std::string &input) const {
145 1840 std::string result;
146
1/2
✓ Branch 2 taken 1840 times.
✗ Branch 3 not taken.
1840 result.reserve(input.size());
147
2/2
✓ Branch 1 taken 39788 times.
✓ Branch 2 taken 1840 times.
41628 for (size_t i = 0; i < input.size(); i++) {
148
2/8
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 39787 times.
39788 switch (input[i]) {
149 case '"':
150 result.append("\\\"");
151 break;
152 case '\\':
153 result.append("\\\\");
154 break;
155 case '\b':
156 result.append("\\b");
157 break;
158 case '\f':
159 result.append("\\f");
160 break;
161 1 case '\n':
162
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 result.append("\\n");
163 1 break;
164 case '\r':
165 result.append("\\r");
166 break;
167 case '\t':
168 result.append("\\t");
169 break;
170 39787 default:
171
1/2
✓ Branch 2 taken 39787 times.
✗ Branch 3 not taken.
39787 result.push_back(input[i]);
172 39787 break;
173 }
174 }
175 1840 return result;
176 }
177
178 std::vector<JsonEntry> entries;
179 };
180
181 #ifdef CVMFS_NAMESPACE_GUARD
182 } // namespace CVMFS_NAMESPACE_GUARD
183 #endif
184
185 #endif // CVMFS_JSON_DOCUMENT_WRITE_H_
186