LCOV - code coverage report
Current view: top level - core/data - SPDataEncode.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 72 125 57.6 %
Date: 2024-05-12 00:16:13 Functions: 27 30 90.0 %

          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_ */

Generated by: LCOV version 1.14