Line data Source code
1 : /**
2 : Copyright (c) 2017-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #ifndef STAPPLER_DATA_SPDATAENCODEJSON_H_
25 : #define STAPPLER_DATA_SPDATAENCODEJSON_H_
26 :
27 : #include "SPDataValue.h"
28 : #include "SPFilesystem.h"
29 :
30 : namespace STAPPLER_VERSIONIZED stappler::data::json {
31 :
32 : template <typename StringType>
33 14618450 : inline void encodeString(const Callback<void(StringView)> &stream, const StringType &str) {
34 14618450 : stream << '"';
35 842064500 : for (auto &i : str) {
36 827446050 : switch (i) {
37 85800 : case '\n' : stream << "\\n"; break;
38 0 : case '\r' : stream << "\\r"; break;
39 0 : case '\t' : stream << "\\t"; break;
40 0 : case '\f' : stream << "\\f"; break;
41 0 : case '\b' : stream << "\\b"; break;
42 25 : case '\\' : stream << "\\\\"; break;
43 1053025 : case '\"' : stream << "\\\""; break;
44 51577900 : case ' ' : stream << ' '; break;
45 774729300 : default:
46 774729300 : if (i >= 0 && i <= 0x20) {
47 0 : stream << "\\u00" << base16::charToHex(i, true);
48 : } else {
49 774729300 : stream << i;
50 : }
51 774729300 : break;
52 : }
53 : }
54 14618450 : stream << '"';
55 14618450 : }
56 :
57 : template <typename Interface>
58 : struct RawEncoder : public Interface::AllocBaseType {
59 : using InterfaceType = Interface;
60 : using ValueType = ValueTemplate<Interface>;
61 :
62 13350 : inline RawEncoder(const Callback<void(StringView)> *s) : stream(s) { }
63 :
64 932100 : inline void writeData(const char *data, size_t size) {
65 932100 : (*stream) << StringView(data, size);
66 932100 : }
67 :
68 927825 : inline void writeData(const char *data) {
69 927825 : writeData(data, strlen(data));
70 927825 : }
71 :
72 : inline void writeChar(char c) {
73 : (*stream) << c;
74 : }
75 :
76 4275 : inline void write(nullptr_t) { writeData("null", "null"_len); }
77 927825 : inline void write(bool value) { writeData((value)?"true":"false"); }
78 556125 : inline void write(int64_t value) { (*stream) << value; }
79 6425 : inline void write(double value) {
80 6425 : (*stream) << value;
81 6425 : }
82 :
83 12617575 : inline void write(const typename ValueType::StringType &str) {
84 12617575 : encodeString(*stream, str);
85 12617575 : }
86 :
87 4900 : inline void write(const typename ValueType::BytesType &data) {
88 4900 : (*stream) << '"' << "BASE64:";
89 158250 : base64url::encode([&] (char c) {
90 77975 : *stream << c;
91 : }, data);
92 4900 : (*stream) << '"';
93 4900 : }
94 481775 : inline void onBeginArray(const typename ValueType::ArrayType &arr) { (*stream) << '['; }
95 481775 : inline void onEndArray(const typename ValueType::ArrayType &arr) { (*stream) << ']'; }
96 1176025 : inline void onBeginDict(const typename ValueType::DictionaryType &dict) { (*stream) << '{'; }
97 1176025 : inline void onEndDict(const typename ValueType::DictionaryType &dict) { (*stream) << '}'; }
98 7084700 : inline void onKey(const typename ValueType::StringType &str) { write(str); (*stream) << ':'; }
99 7025425 : inline void onNextValue() { (*stream) << ','; }
100 :
101 : const Callback<void(StringView)> *stream = nullptr;
102 : };
103 :
104 : template <typename Interface>
105 : struct PrettyEncoder : public Interface::AllocBaseType {
106 : using InterfaceType = Interface;
107 : using ValueType = ValueTemplate<Interface>;
108 :
109 675 : PrettyEncoder(const Callback<void(StringView)> *s, bool tM = false) : timeMarkers(tM), stream(s) { }
110 :
111 675 : void write(nullptr_t) { (*stream) << "null"; offsetted = false; }
112 147425 : void write(bool value) { (*stream) << ((value)?"true":"false"); offsetted = false; }
113 83400 : void write(int64_t value) {
114 83400 : (*stream) << value; offsetted = false;
115 166800 : if (timeMarkers
116 0 : && (lastKey.find("time") != maxOf<size_t>()
117 0 : || lastKey.find("Time") != maxOf<size_t>()
118 0 : || lastKey.find("TIME") != maxOf<size_t>()
119 0 : || lastKey.find("date") != maxOf<size_t>()
120 0 : || lastKey.find("Date") != maxOf<size_t>())
121 83400 : && (value > 1000000000000000 && value < 10000000000000000)) {
122 0 : (*stream) << " /* " << Time::microseconds(value).toHttp<Interface>() << " */";
123 : }
124 83400 : }
125 0 : void write(double value) {
126 0 : (*stream) << value;
127 0 : offsetted = false;
128 0 : }
129 :
130 2000875 : void write(const typename ValueType::StringType &str) {
131 2000875 : encodeString(*stream, str);
132 2000875 : offsetted = false;
133 2000875 : }
134 :
135 0 : void write(const typename ValueType::BytesType &data) {
136 0 : (*stream) << '"' << "BASE64:";
137 0 : base64url::encode([&] (char c) {
138 0 : *stream << c;
139 : }, data);
140 0 : (*stream) << '"';
141 0 : offsetted = false;
142 0 : }
143 :
144 76050 : bool isObjectArray(const typename ValueType::ArrayType &arr) {
145 158125 : for (auto &it : arr) {
146 95125 : if (!it.isDictionary()) {
147 13050 : return false;
148 : }
149 : }
150 63000 : return true;
151 : }
152 :
153 76050 : void onBeginArray(const typename ValueType::ArrayType &arr) {
154 76050 : (*stream) << '[';
155 76050 : if (!isObjectArray(arr)) {
156 13050 : ++ depth;
157 13050 : bstack.push_back(false);
158 13050 : offsetted = false;
159 : } else {
160 63000 : bstack.push_back(true);
161 : }
162 76050 : }
163 :
164 76050 : void onEndArray(const typename ValueType::ArrayType &arr) {
165 76050 : if (!bstack.empty()) {
166 76050 : if (!bstack.back()) {
167 13050 : -- depth;
168 13050 : (*stream) << '\n';
169 39200 : for (size_t i = 0; i < depth; i++) {
170 26150 : (*stream) << '\t';
171 : }
172 : }
173 76050 : bstack.pop_back();
174 : } else {
175 0 : -- depth;
176 0 : (*stream) << '\n';
177 0 : for (size_t i = 0; i < depth; i++) {
178 0 : (*stream) << '\t';
179 : }
180 : }
181 76050 : (*stream) << ']';
182 76050 : popComplex = true;
183 76050 : }
184 :
185 185425 : void onBeginDict(const typename ValueType::DictionaryType &dict) {
186 185425 : lastKey = StringView();
187 185425 : (*stream) << '{';
188 185425 : ++ depth;
189 185425 : }
190 :
191 185425 : void onEndDict(const typename ValueType::DictionaryType &dict) {
192 185425 : lastKey = StringView();
193 185425 : -- depth;
194 185425 : (*stream) << '\n';
195 2282225 : for (size_t i = 0; i < depth; i++) {
196 2096800 : (*stream) << '\t';
197 : }
198 185425 : (*stream) << '}';
199 185425 : popComplex = true;
200 185425 : }
201 :
202 1120950 : void onKey(const typename ValueType::StringType &str) {
203 1120950 : lastKey = str;
204 1120950 : (*stream) << '\n';
205 14600200 : for (size_t i = 0; i < depth; i++) {
206 13479250 : (*stream) << '\t';
207 : }
208 1120950 : write(str);
209 1120950 : offsetted = true;
210 1120950 : (*stream) << ':' << ' ';
211 1120950 : }
212 :
213 1111750 : void onNextValue() {
214 1111750 : lastKey = StringView();
215 1111750 : (*stream) << ',';
216 1111750 : }
217 :
218 1372900 : void onValue(const ValueType &val) {
219 1372900 : if (depth > 0) {
220 1371900 : if (popComplex && (val.isArray() || val.isDictionary())) {
221 36500 : (*stream) << ' ';
222 : } else {
223 1335400 : if (!offsetted) {
224 169200 : (*stream) << '\n';
225 676900 : for (size_t i = 0; i < depth; i++) {
226 507700 : (*stream) << '\t';
227 : }
228 169200 : offsetted = true;
229 : }
230 : }
231 1371900 : popComplex = false;
232 : }
233 1372900 : }
234 :
235 : size_t depth = 0;
236 : bool popComplex = false;
237 : bool offsetted = false;
238 : bool timeMarkers = false;
239 : const Callback<void(StringView)> *stream = nullptr;
240 : StringView lastKey;
241 : typename Interface::template ArrayType<bool> bstack;
242 : };
243 :
244 : template <typename Interface>
245 14025 : inline void write(const Callback<void(StringView)> &stream, const ValueTemplate<Interface> &val, bool pretty, bool timeMarkers = false) {
246 14025 : if (pretty) {
247 675 : PrettyEncoder<Interface> encoder(&stream, timeMarkers);
248 675 : val.encode(encoder);
249 675 : } else {
250 13350 : RawEncoder<Interface> encoder(&stream);
251 13350 : val.encode(encoder);
252 : }
253 14025 : }
254 :
255 : template <typename Interface>
256 9950 : inline auto write(const ValueTemplate<Interface> &val, bool pretty = false, bool timeMarkers = false) -> typename Interface::StringType {
257 9950 : typename Interface::StringType stream;
258 986918050 : write<Interface>([&] (StringView str) {
259 896099300 : stream.append(str.data(), str.size());
260 : }, val, pretty, timeMarkers);
261 9950 : return stream;
262 0 : }
263 :
264 : template <typename Interface>
265 0 : bool save(const ValueTemplate<Interface> &val, StringView ipath, bool pretty, bool timeMarkers = false) {
266 0 : auto path = filesystem::native::posixToNative<Interface>(ipath);
267 0 : std::ofstream stream(path.data());
268 0 : if (stream.is_open()) {
269 0 : write([&] (StringView str) {
270 0 : stream.write(str.data(), str.size());
271 : }, val, pretty, timeMarkers);
272 0 : stream.flush();
273 0 : stream.close();
274 0 : return true;
275 : }
276 0 : return false;
277 0 : }
278 :
279 : template <typename Interface>
280 : auto toString(const ValueTemplate<Interface> &data, bool pretty) -> typename Interface::StringType {
281 : return write(data, pretty);
282 : }
283 :
284 : }
285 :
286 : #endif /* STAPPLER_DATA_SPDATAENCODEJSON_H_ */
|