Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2025-01-31 02:19:34

0001 #ifndef FWCore_Utilities_interface_compactStringSerializer_h
0002 #define FWCore_Utilities_interface_compactStringSerializer_h
0003 
0004 #include <cassert>
0005 #include <iterator>
0006 #include <numeric>
0007 #include <ranges>
0008 #include <string>
0009 #include <string_view>
0010 
0011 namespace edm::compactString {
0012   namespace detail {
0013     constexpr std::string_view kDelimiters = "\x1d\x1e";
0014     constexpr char kContainerDelimiter = kDelimiters[0];  // "group separator" in ASCII
0015     constexpr char kElementDelimiter = kDelimiters[1];    // "record separator" in ASCII
0016 
0017     void throwIfContainsDelimiters(std::string const& str);
0018   }  // namespace detail
0019 
0020   /**
0021    * Following three functions serialize a sequence of strings and containers of strings
0022    *
0023    * Each top-level string or container of strings is separated with kContainerDelimeter
0024    * In case of container of strings, each element is separated with kElementDelimeter
0025    * The serialized string will end with kContainerDelimeter and a null character
0026    *
0027    * The functions throw an exception if the serialized strings
0028    * contain any of the delimeter characters. The underlying string
0029    * operations may also throw exceptions.
0030    */
0031   inline std::string serialize(std::string arg) noexcept(false) {
0032     detail::throwIfContainsDelimiters(arg);
0033     arg += detail::kContainerDelimiter;
0034     return arg;
0035   }
0036 
0037   template <typename R>
0038     requires std::ranges::input_range<R> and std::is_same_v<std::ranges::range_value_t<R>, std::string>
0039   std::string serialize(R const& arg) noexcept(false) {
0040     std::string ret;
0041 
0042     if (not std::ranges::empty(arg)) {
0043       for (std::string const& elem : arg) {
0044         ret.reserve(ret.size() + elem.size() + 1);
0045         detail::throwIfContainsDelimiters(elem);
0046         ret += elem;
0047         ret += detail::kElementDelimiter;
0048       }
0049     }
0050 
0051     ret += detail::kContainerDelimiter;
0052     return ret;
0053   }
0054 
0055   template <typename T, typename... Args>
0056     requires(sizeof...(Args) >= 1)
0057   std::string serialize(T&& arg0, Args&&... args) noexcept(false) {
0058     return serialize(std::forward<T>(arg0)) + serialize(std::forward<Args>(args)...);
0059   }
0060 
0061   /**
0062    * Following three functions deserialize a string 'input' into a
0063    * sequence of strings and containers of strings
0064    *
0065    * The 'input' string is assumed to be serialized with the
0066    * serialize() functions above.
0067    *
0068    * The output arguments following the 'input' define the schema of
0069    * the deserialization.
0070    * - std::string& for strings
0071    * - output iterator for containers of strings (e.g. std::back_inserter(vector))
0072    *
0073    * Upon success, the return value is the position in `input` for the
0074    * next possible element (i.e. the position after the
0075    * kContainerDelimiter), that is also the number of characters
0076    * consumed by the deserializatiom..
0077    *
0078    * Upon failure, returns 0 to denote the beginning of `input`. The
0079    * output arguments may have been modified.
0080    *
0081    * The functions do not explicitly throw exceptions, but underlying
0082    * operations may throw exceptions.
0083    */
0084   inline std::string_view::size_type deserialize(std::string_view input, std::string& arg) {
0085     auto const pos = input.find_first_of(detail::kDelimiters);
0086     if (pos == std::string_view::npos or input[pos] != detail::kContainerDelimiter) {
0087       return 0;
0088     }
0089     arg = input.substr(0, pos);
0090     return pos + 1;  // skip delimiter
0091   }
0092 
0093   template <std::output_iterator<std::string> I>
0094   inline std::string_view::size_type deserialize(std::string_view input, I arg) {
0095     auto pos = input.find_first_of(detail::kDelimiters);
0096     // invalid input
0097     if (pos == std::string_view::npos) {
0098       return 0;
0099     }
0100     // no elements
0101     if (input[pos] == detail::kContainerDelimiter) {
0102       // invalid input for empty container
0103       if (pos != 0) {
0104         return 0;
0105       }
0106       // skip delimiter
0107       return pos + 1;
0108     }
0109 
0110     std::string_view::size_type prev = 0;
0111     while (pos != std::string_view::npos and input[pos] == detail::kElementDelimiter) {
0112       *arg = std::string(input.substr(prev, pos - prev));
0113       ++arg;
0114       prev = pos + 1;  //skip delimiter
0115       pos = input.find_first_of(detail::kDelimiters, prev);
0116     }
0117 
0118     // every container must end with kContainerDelimiter
0119     // reaching npos is an error
0120     if (pos == std::string_view::npos) {
0121       return 0;
0122     }
0123     assert(input[pos] == detail::kContainerDelimiter);
0124 
0125     return pos + 1;  // skip delimiter
0126   }
0127 
0128   template <typename T, typename... Args>
0129     requires(sizeof...(Args) >= 1)
0130   std::string_view::size_type deserialize(std::string_view input, T&& arg0, Args&&... args) {
0131     auto pos = deserialize(input, std::forward<T>(arg0));
0132     if (pos != 0) {
0133       auto const ret = deserialize(input.substr(pos), std::forward<Args>(args)...);
0134       pos = (ret == 0) ? 0 : pos + ret;
0135     }
0136     return pos;
0137   }
0138 }  // namespace edm::compactString
0139 
0140 #endif