GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/json_document_write.h
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 72 94 76.6%
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 { kString, kInteger, kFloat, kJsonObject };
31
32 struct JsonEntry {
33 JsonVariant variant;
34 std::string key_escaped;
35 std::string str_val_escaped;
36 int64_t int_val;
37 float float_val;
38
39 17 JsonEntry(const std::string& key_escaped, const std::string& val)
40 17 : variant(kString),
41 17 key_escaped(key_escaped),
42
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
17 str_val_escaped(val),
43 17 int_val(0),
44 17 float_val(0.0) {}
45
46 3 JsonEntry(const std::string& key_escaped, const std::string& val,
47 const JsonVariant variant)
48 3 : variant(variant),
49 3 key_escaped(key_escaped),
50
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 str_val_escaped(val),
51 3 int_val(0),
52 3 float_val(0.0) {}
53
54 1 JsonEntry(const std::string& key_escaped, const int val)
55 1 : variant(kInteger),
56 1 key_escaped(key_escaped),
57 1 str_val_escaped(),
58 1 int_val(val),
59 1 float_val(0.0) {}
60
61 JsonEntry(const std::string& key_escaped, const float val)
62 : variant(kFloat),
63 key_escaped(key_escaped),
64 str_val_escaped(),
65 int_val(0),
66 float_val(val) {}
67
68 3 JsonEntry(const std::string& key_escaped, const int64_t val)
69 3 : variant(kInteger),
70 3 key_escaped(key_escaped),
71 3 str_val_escaped(),
72 3 int_val(val),
73 3 float_val(0.0) {}
74
75 24 std::string Format() const {
76
3/5
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
24 switch (variant) {
77 17 case kString:
78
3/6
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 17 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 17 times.
✗ Branch 9 not taken.
34 return "\"" + key_escaped + "\":\"" + str_val_escaped + "\"";
79 4 case kInteger:
80
3/6
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
8 return "\"" + key_escaped + "\":" + StringifyInt(int_val);
81 case kFloat:
82 return "\"" + key_escaped + "\":" + StringifyDouble(float_val);
83 3 case kJsonObject:
84
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
6 return "\"" + key_escaped + "\":" + str_val_escaped;
85 default:
86 PANIC(kLogStdout | kLogStderr, "JSON creation failed");
87 }
88 }
89 };
90
91 public:
92 17 void Add(const std::string& key, const std::string& val) {
93
3/6
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
34 const JsonEntry entry(Escape(key), Escape(val));
94
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
17 entries.push_back(entry);
95 17 }
96
97 1 void Add(const std::string& key, const int val) {
98
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);
99
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 entries.push_back(entry);
100 1 }
101
102 void Add(const std::string& key, const float val) {
103 const JsonEntry entry(Escape(key), val);
104 entries.push_back(entry);
105 }
106
107 3 void Add(const std::string& key, const int64_t val) {
108
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 const JsonEntry entry(Escape(key), val);
109
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 entries.push_back(entry);
110 3 }
111
112 3 void AddJsonObject(const std::string& key, const std::string& json) {
113 // we **do not escape** the value here
114
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 const JsonEntry entry(Escape(key), json, kJsonObject);
115
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 entries.push_back(entry);
116 3 }
117
118 12 std::string GenerateString() const {
119 12 std::string output;
120
121
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 output += "{";
122
2/2
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 12 times.
36 for (size_t i = 0u; i < this->entries.size(); ++i) {
123
2/4
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
24 output += this->entries[i].Format();
124
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 10 times.
24 if (i < this->entries.size() - 1) {
125
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 output += ',';
126 }
127 }
128
2/4
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
12 output += std::string("}");
129 12 return output;
130 }
131
132 2 void Clear() {
133 2 entries.clear();
134 2 }
135
136 private:
137 // this escape procedure is not as complete as it should be.
138 // we should manage ALL control chars from '\x00' to '\x1f'
139 // however this are the one that we can expect to happen
140 // More info: https://stackoverflow.com/a/33799784/869271
141 41 const std::string Escape(const std::string& input) const {
142 41 std::string result;
143
1/2
✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
41 result.reserve(input.size());
144
2/2
✓ Branch 1 taken 869 times.
✓ Branch 2 taken 41 times.
910 for (size_t i = 0; i < input.size(); i++) {
145
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 868 times.
869 switch (input[i]) {
146 case '"':
147 result.append("\\\"");
148 break;
149 case '\\':
150 result.append("\\\\");
151 break;
152 case '\b':
153 result.append("\\b");
154 break;
155 case '\f':
156 result.append("\\f");
157 break;
158 1 case '\n':
159
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 result.append("\\n");
160 1 break;
161 case '\r':
162 result.append("\\r");
163 break;
164 case '\t':
165 result.append("\\t");
166 break;
167 868 default:
168
1/2
✓ Branch 2 taken 868 times.
✗ Branch 3 not taken.
868 result.push_back(input[i]);
169 868 break;
170 }
171 }
172 41 return result;
173 }
174
175 std::vector<JsonEntry> entries;
176 };
177
178 #ifdef CVMFS_NAMESPACE_GUARD
179 } // namespace CVMFS_NAMESPACE_GUARD
180 #endif
181
182 #endif // CVMFS_JSON_DOCUMENT_WRITE_H_
183