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_SPDATAENCODE_H_
25 : #define STAPPLER_DATA_SPDATAENCODE_H_
26 :
27 : #include "SPDataEncodeCbor.h"
28 : #include "SPDataEncodeJson.h"
29 : #include "SPDataEncodeSerenity.h"
30 :
31 : #ifdef MODULE_STAPPLER_FILESYSTEM
32 : #include "SPFilesystem.h"
33 : #endif
34 :
35 : namespace STAPPLER_VERSIONIZED stappler::data {
36 :
37 : struct EncodeFormat {
38 : static int EncodeStreamIndex;
39 :
40 : enum Format {
41 : Json = 0b0000, // Raw JSON data, with no whitespace
42 : Pretty = 0b0001, // Pretty-printed JSON data
43 : Cbor = 0b0010, // CBOR data (http://cbor.io/, http://tools.ietf.org/html/rfc7049)
44 : DefaultFormat = 0b0011,
45 : Serenity = 0b0100,
46 : SerenityPretty = 0b0101,
47 : PrettyTime = 0b1001, // Pretty-printed JSON data (with time markers comment)
48 : };
49 :
50 : // We use LZ4 for compression, it's very fast to decode
51 : enum Compression {
52 : NoCompression = 0b0000 << 4,
53 : LZ4Compression = 0b0001 << 4,
54 : LZ4HCCompression = 0b0011 << 4,
55 :
56 : #ifdef MODULE_STAPPLER_BROTLI_LIB
57 : Brotli = 0b0100 << 4,
58 : #endif
59 :
60 : DefaultCompress = NoCompression
61 : };
62 :
63 : enum Encryption {
64 : Unencrypted = 0b0000 << 8,
65 : Encrypted = 0b0001 << 8
66 : };
67 :
68 : static EncodeFormat CborCompressed;
69 : static EncodeFormat JsonCompressed;
70 :
71 15925 : constexpr EncodeFormat(Format fmt = DefaultFormat, Compression cmp = DefaultCompress, Encryption enc = Unencrypted, StringView = StringView())
72 15925 : : format(fmt), compression(cmp), encryption(enc) { }
73 :
74 1375 : constexpr explicit EncodeFormat(int flag)
75 1375 : : format((Format)(flag & 0x0F)), compression((Compression)(flag & 0xF0))
76 1375 : , encryption((Encryption)(flag &0xF00)) { }
77 :
78 34300 : EncodeFormat(const EncodeFormat & other) : format(other.format), compression(other.compression)
79 34300 : , encryption(other.encryption) { }
80 :
81 0 : EncodeFormat & operator=(const EncodeFormat & other) {
82 0 : format = other.format;
83 0 : compression = other.compression;
84 0 : encryption = other.encryption;
85 0 : return *this;
86 : }
87 :
88 4075 : bool isRaw() const {
89 4075 : return compression == NoCompression && encryption == Unencrypted;
90 : }
91 :
92 25 : bool isTextual() const {
93 25 : return isRaw() && (format == Json || format == Pretty);
94 : }
95 :
96 225 : int flag() const {
97 225 : return (int)format | (int)compression | (int)encryption;
98 : }
99 :
100 : Format format;
101 : Compression compression;
102 : Encryption encryption;
103 : };
104 :
105 : uint8_t *getLZ4EncodeState();
106 : size_t compressData(const uint8_t *src, size_t srcSize, uint8_t *dest, size_t destSize, EncodeFormat::Compression c);
107 : void writeCompressionMark(uint8_t *data, size_t sourceSize, EncodeFormat::Compression c, uint8_t padding = 0);
108 :
109 : template <typename Interface>
110 : auto compress(const uint8_t *, size_t, EncodeFormat::Compression, bool conditional) -> typename Interface::BytesType;
111 :
112 : size_t getCompressBounds(size_t, EncodeFormat::Compression);
113 :
114 : template <typename Interface>
115 : struct EncodeTraits {
116 : using InterfaceType = Interface;
117 : using ValueType = ValueTemplate<Interface>;
118 : using BytesType = typename ValueType::BytesType;
119 : using StringType = typename ValueType::StringType;
120 :
121 18650 : static BytesType write(const ValueType &data, EncodeFormat fmt, size_t reserve = 0) {
122 18650 : BytesType ret;
123 18650 : switch (fmt.format) {
124 3950 : case EncodeFormat::Json:
125 : case EncodeFormat::Pretty:
126 : case EncodeFormat::PrettyTime: {
127 3950 : StringType s = json::write(data, (fmt.format == EncodeFormat::Pretty), (fmt.format == EncodeFormat::PrettyTime));
128 3950 : ret.reserve(s.length());
129 3950 : ret.assign(s.begin(), s.end());
130 3950 : break;
131 3950 : }
132 14700 : case EncodeFormat::Cbor:
133 : case EncodeFormat::DefaultFormat:
134 14700 : ret = cbor::write(data, reserve);
135 14700 : break;
136 :
137 0 : case EncodeFormat::Serenity:
138 : case EncodeFormat::SerenityPretty: {
139 0 : StringType s = serenity::write(data, (fmt.format == EncodeFormat::SerenityPretty));
140 0 : ret.reserve(s.length());
141 0 : ret.assign(s.begin(), s.end());
142 0 : break;
143 0 : }
144 : }
145 :
146 18650 : if (fmt.compression != EncodeFormat::NoCompression) {
147 2750 : auto tmp = compress<Interface>(ret.data(), ret.size(), fmt.compression, true);
148 2750 : if (!tmp.empty()) {
149 2475 : return tmp;
150 : }
151 2750 : }
152 16175 : return ret;
153 18650 : }
154 :
155 3975 : static bool write(const Callback<void(StringView)> &stream, const ValueType &data, EncodeFormat fmt) {
156 3975 : if (fmt.isRaw()) {
157 3975 : switch (fmt.format) {
158 3775 : case EncodeFormat::Json: json::write(stream, data, false); return true; break;
159 200 : case EncodeFormat::Pretty: json::write(stream, data, true); return true; break;
160 0 : case EncodeFormat::PrettyTime: json::write(stream, data, true, true); return true; break;
161 0 : case EncodeFormat::Cbor:
162 : case EncodeFormat::DefaultFormat:
163 0 : return cbor::write([&] (BytesView bytes) {
164 0 : StringView str; str.set((const char *)bytes.data(), bytes.size());
165 0 : stream(str);
166 0 : }, data);
167 : break;
168 0 : case EncodeFormat::Serenity: serenity::write(stream, data, false); return true; break;
169 0 : case EncodeFormat::SerenityPretty: serenity::write(stream, data, true); return true; break;
170 : }
171 : } else {
172 0 : auto ret = write(data, fmt);
173 0 : if (!ret.empty()) {
174 0 : StringView str; str.set((const char *)ret.data(), ret.size());
175 0 : stream << str;
176 0 : return true;
177 : }
178 0 : return false;
179 0 : }
180 0 : return false;
181 : }
182 :
183 : #ifdef MODULE_STAPPLER_FILESYSTEM
184 75 : static bool save(const ValueType &data, const StringView &file, EncodeFormat fmt) {
185 75 : auto path = filepath::absolute<Interface>(file, true);
186 75 : if (fmt.format == EncodeFormat::DefaultFormat) {
187 25 : auto ext = filepath::lastExtension(path);
188 25 : if (ext == "json") {
189 0 : fmt.format = EncodeFormat::Json;
190 : } else {
191 25 : fmt.format = EncodeFormat::Cbor;
192 : }
193 : }
194 75 : if (fmt.isRaw()) {
195 75 : switch (fmt.format) {
196 0 : case EncodeFormat::Json: return json::save(data, path, false); break;
197 0 : case EncodeFormat::Pretty: return json::save(data, path, true); break;
198 0 : case EncodeFormat::PrettyTime: return json::save(data, path, true, true); break;
199 75 : case EncodeFormat::Cbor:
200 : case EncodeFormat::DefaultFormat:
201 75 : return cbor::save(data, path);
202 : break;
203 0 : case EncodeFormat::Serenity: return serenity::save(data, path, false); break;
204 0 : case EncodeFormat::SerenityPretty: return serenity::save(data, path, true); break;
205 : }
206 : } else {
207 0 : auto ret = write(data, fmt);
208 0 : if (!ret.empty()) {
209 0 : filesystem::write(path, ret);
210 0 : return true;
211 : }
212 0 : return false;
213 0 : }
214 0 : return false;
215 75 : }
216 : #endif
217 : };
218 :
219 : template <typename Interface> inline auto
220 18650 : write(const ValueTemplate<Interface> &data, EncodeFormat fmt = EncodeFormat(), size_t reserve = 0) -> typename ValueTemplate<Interface>::BytesType {
221 18650 : return EncodeTraits<Interface>::write(data, fmt, reserve);
222 : }
223 :
224 : template <typename Interface> inline bool
225 3975 : write(const Callback<void(StringView)> &stream, const ValueTemplate<Interface> &data, EncodeFormat fmt = EncodeFormat()) {
226 3975 : return EncodeTraits<Interface>::write(stream, data, fmt);
227 : }
228 :
229 : template <typename Interface> inline bool
230 75 : save(const ValueTemplate<Interface> &data, const StringView &file, EncodeFormat fmt = EncodeFormat()) {
231 75 : return EncodeTraits<Interface>::save(data, file, fmt);
232 : }
233 :
234 : template <typename Interface> inline auto
235 5875 : toString(const ValueTemplate<Interface> &data, bool pretty = false) -> typename ValueTemplate<Interface>::StringType {
236 5875 : return json::write<Interface>(data, pretty);
237 : }
238 : template <typename Interface> inline auto
239 125 : toString(const ValueTemplate<Interface> &data, EncodeFormat::Format fmt) -> typename ValueTemplate<Interface>::StringType {
240 125 : switch (fmt) {
241 125 : case EncodeFormat::Json:
242 125 : return json::write<Interface>(data, false);
243 : break;
244 0 : case EncodeFormat::Pretty:
245 0 : return json::write<Interface>(data, true);
246 : break;
247 0 : case EncodeFormat::PrettyTime:
248 0 : return json::write<Interface>(data, true, true);
249 : break;
250 0 : case EncodeFormat::Cbor:
251 0 : return base64::encode<Interface>(cbor::write(data));
252 : break;
253 0 : case EncodeFormat::DefaultFormat:
254 0 : return json::write<Interface>(data, false);
255 : break;
256 0 : case EncodeFormat::Serenity:
257 0 : return serenity::write<Interface>(data, false);
258 : break;
259 0 : case EncodeFormat::SerenityPretty:
260 0 : return serenity::write<Interface>(data, true);
261 : break;
262 : }
263 0 : return typename ValueTemplate<Interface>::StringType();
264 : }
265 :
266 : template<typename CharT, typename Traits> inline std::basic_ostream<CharT, Traits>&
267 225 : operator<<(std::basic_ostream<CharT, Traits> & stream, EncodeFormat f) {
268 225 : stream.iword( EncodeFormat::EncodeStreamIndex ) = f.flag();
269 225 : return stream;
270 : }
271 :
272 : template<typename CharT, typename Traits> inline std::basic_ostream<CharT, Traits>&
273 200 : operator<<(std::basic_ostream<CharT, Traits> & stream, EncodeFormat::Format f) {
274 200 : EncodeFormat fmt(f);
275 200 : stream << fmt;
276 200 : return stream;
277 : }
278 :
279 : template<typename CharT, typename Traits, typename Interface> inline std::basic_ostream<CharT, Traits>&
280 1375 : operator<<(std::basic_ostream<CharT, Traits> & stream, const ValueTemplate<Interface> &val) {
281 1375 : EncodeFormat fmt(stream.iword( EncodeFormat::EncodeStreamIndex ));
282 1375 : write<Interface>([&] (StringViewBase<CharT> str) {
283 26600 : stream << str;
284 : }, val, fmt);
285 1375 : return stream;
286 : }
287 :
288 : }
289 :
290 : #endif /* STAPPLER_DATA_SPDATAENCODE_H_ */
|