Spicy
util.h
1 // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <cxxabi.h>
6 
7 #include <algorithm>
8 #include <list>
9 #include <memory>
10 #include <numeric>
11 #include <optional>
12 #include <set>
13 #include <string>
14 #include <string_view>
15 #include <tuple>
16 #include <utility>
17 #include <vector>
18 
19 #include <hilti/rt/autogen/config.h>
20 #include <hilti/rt/exception.h>
21 #include <hilti/rt/filesystem.h>
22 #include <hilti/rt/result.h>
23 #include <hilti/rt/types/set_fwd.h>
24 #include <hilti/rt/types/time.h>
25 #include <hilti/rt/types/vector_fwd.h>
26 
27 namespace hilti::rt {
28 
29 void internalError(const std::string& msg) __attribute__((noreturn));
30 
31 } // namespace hilti::rt
32 
33 #undef TINYFORMAT_ERROR
34 #define TINYFORMAT_ERROR(reason) throw ::hilti::rt::FormattingError(reason)
35 #include <hilti/rt/3rdparty/tinyformat/tinyformat.h>
36 #include <hilti/rt/extension-points.h>
37 #include <hilti/rt/fmt.h>
38 
39 extern const char* __hlto_scope; // defined by linker code in HLTO file
40 
41 namespace hilti::rt {
42 
44 extern std::string version();
45 
47 extern bool isDebugVersion();
48 
50 extern void abort_with_backtrace() __attribute__((noreturn));
51 
53 inline std::string linker_scope() { return __hlto_scope; }
54 
56 extern void cannot_be_reached() __attribute__((noreturn));
57 
59 struct ResourceUsage {
60  // Note when changing this, update `resource_usage()`.
61  double user_time; //< user time since runtime initialization
62  double system_time; //< system time since runtime initialization
63  uint64_t memory_heap; //< current size of heap in bytes
64  uint64_t num_fibers; //< number of fibers currently in use
65  uint64_t max_fibers; //< high-water mark for number of fibers in use
66  uint64_t cached_fibers; //< number of fibers currently cached for reuse
67 };
68 
71 
73 extern std::optional<std::string> getenv(const std::string& name);
74 
82 
84 hilti::rt::filesystem::path normalizePath(const hilti::rt::filesystem::path& p);
85 
91 inline std::string_view rtrim(std::string_view s, const std::string& chars) noexcept {
92  s.remove_suffix(s.size() -
93  [](size_t pos) { return pos != std::string_view::npos ? pos + 1 : 0; }(s.find_last_not_of(chars)));
94  return s;
95 }
96 
102 inline std::string_view ltrim(std::string_view s, const std::string& chars) noexcept {
103  s.remove_prefix(std::min(s.find_first_not_of(chars), s.size()));
104  return s;
105 }
106 
113 inline std::string_view trim(std::string_view s, const std::string& chars) noexcept {
114  return ltrim(rtrim(s, chars), chars);
115 }
116 
117 namespace detail {
118 constexpr char whitespace_chars[] = " \t\f\v\n\r";
119 } // namespace detail
120 
126 inline std::string_view rtrim(std::string_view s) noexcept { return rtrim(s, detail::whitespace_chars); }
127 
133 inline std::string_view ltrim(std::string_view s) noexcept { return ltrim(s, detail::whitespace_chars); }
134 
140 inline std::string_view trim(std::string_view s) noexcept { return trim(s, detail::whitespace_chars); }
141 
148 std::vector<std::string_view> split(std::string_view s, std::string_view delim);
149 
155 std::vector<std::string_view> split(std::string_view s);
156 
163 extern std::pair<std::string, std::string> split1(std::string s);
164 
171 extern std::pair<std::string, std::string> rsplit1(std::string s);
172 
179 extern std::pair<std::string, std::string> split1(std::string s, const std::string& delim);
180 
187 extern std::pair<std::string, std::string> rsplit1(std::string s, const std::string& delim);
188 
194 std::string replace(std::string s, std::string_view o, std::string_view n);
195 
201 inline bool startsWith(const std::string& s, const std::string& prefix) { return s.find(prefix) == 0; }
202 
207 template<typename T, typename TIter = decltype(std::begin(std::declval<T>())),
208  typename = decltype(std::end(std::declval<T>()))>
209 constexpr auto enumerate(T&& iterable) {
210  struct iterator {
211  size_t i;
212  TIter iter;
213  bool operator!=(const iterator& other) const { return iter != other.iter; }
214  void operator++() {
215  ++i;
216  ++iter;
217  }
218  auto operator*() const { return std::tie(i, *iter); }
219  };
220  struct iterable_wrapper {
221  T iterable;
222  auto begin() { return iterator{0, std::begin(iterable)}; }
223  auto end() { return iterator{0, std::end(iterable)}; }
224  };
225  return iterable_wrapper{std::forward<T>(iterable)};
226 }
227 
228 /*
229  * Expands escape sequences in a UTF8 string. The following escape sequences
230  * are supported:
231  *
232  * ============ ============================
233  * Escape Result
234  * ============ ============================
235  * \\ Backslash
236  * \\n Line feed
237  * \\r Carriage return
238  * \\t Tabulator
239  * \\uXXXX 16-bit Unicode codepoint
240  * \\UXXXXXXXX 32-bit Unicode codepoint
241  * \\xXX 8-bit hex value
242  * ============ ============================
243  *
244  * @param str string to expand
245  * @return A UTF8 string with escape sequences expanded
246  */
247 std::string expandEscapes(std::string s);
248 
249 /*
250  * Escapes non-printable characters in a raw string. This produces a new
251  * string that can be reverted by expandEscapes().
252  *
253  * @param str string to escape
254  * @param escape_quotes if true, also escapes quotes characters
255  * @param use_octal use `\NNN` instead of `\XX` (needed for C++)
256  * @return escaped string
257  *
258  * \todo This is getting messy; should use enums instead of booleans.
259  */
260 std::string escapeBytes(std::string_view s, bool escape_quotes = false, bool use_octal = false);
261 
262 /*
263  * Escapes non-printable and control characters in an UTF8 string. This
264  * produces a new string that can be reverted by expandEscapes().
265  *
266  * @param str string to escape
267  * @param escape_quotes if true, also escapes quotes characters
268  * @param escape_control if false, do not escape control characters
269  * @param keep_hex if true, do not escape our custom "\xYY" escape codes
270  * @return escaped std::string
271  *
272  * \todo This is getting messy; should use enums instead of booleans.
273  */
274 std::string escapeUTF8(std::string_view s, bool escape_quotes = false, bool escape_control = true,
275  bool keep_hex = false);
276 
281 template<typename T>
282 std::string join(const T& l, const std::string& delim = "") {
283  std::string result;
284  bool first = true;
285 
286  for ( const auto& i : l ) {
287  if ( not first )
288  result += delim;
289  result += std::string(i);
290  first = false;
291  }
292 
293  return result;
294 }
295 
296 namespace detail {
297 
299 template<typename T>
300 struct is_Vector : std::false_type {};
301 
302 template<typename T, typename Allocator>
303 struct is_Vector<Vector<T, Allocator>> : std::true_type {};
304 
307 template<typename C, typename Y>
308 constexpr auto transform_result_value(const C&) {
309  using X = typename C::value_type;
310 
311  if constexpr ( std::is_same_v<C, std::vector<X>> ) {
312  return std::vector<Y>();
313  }
314  else if constexpr ( std::is_same_v<C, std::set<X>> ) {
315  return std::set<Y>();
316  }
317  else if constexpr ( is_Vector<C>::value ) {
318  // We do not preserve the allocator since a proper custom one could depend on `Y`.
319  return Vector<Y>();
320  }
321  else if constexpr ( std::is_same_v<C, Set<X>> ) {
322  return Set<Y>();
323  }
324 
325  // No default value defined for type.
326 }
327 
328 } // namespace detail
329 
331 template<typename C, typename F>
332 auto transform(const C& x, F f) {
333  using Y = typename std::invoke_result_t<F, typename C::value_type&>;
334 
335  auto y = detail::transform_result_value<C, Y>(x);
336  std::transform(std::begin(x), std::end(x), std::inserter(y, std::end(y)), f);
337 
338  return y;
339 }
340 
341 class OutOfRange;
342 
365 template<class Iter, typename Result>
366 inline Iter atoi_n(Iter s, Iter e, uint8_t base, Result* result) {
367  if ( base < 2 || base > 36 )
368  throw OutOfRange("base for numerical conversion must be between 2 and 36");
369 
370  if ( s == e )
371  throw InvalidArgument("cannot decode from empty range");
372 
373  std::optional<Result> n = std::nullopt;
374  bool neg = false;
375  auto it = s;
376 
377  if ( *it == '-' ) {
378  neg = true;
379  ++it;
380  }
381  else if ( *it == '+' ) {
382  neg = false;
383  ++it;
384  }
385 
386  for ( ; it != e; ++it ) {
387  auto c = *it;
388 
389  Result d;
390  if ( c >= '0' && c < '0' + base )
391  d = c - '0';
392  else if ( c >= 'a' && c < 'a' - 10 + base )
393  d = c - 'a' + 10;
394  else if ( c >= 'A' && c < 'A' - 10 + base )
395  d = c - 'A' + 10;
396  else
397  break;
398 
399  n = n.value_or(Result()) * base + d;
400  }
401 
402  if ( ! n )
403  return s;
404 
405  s = it;
406 
407  if ( neg )
408  *result = -*n;
409  else
410  *result = *n;
411 
412  return s;
413 }
414 
418 template<typename I1, typename I2>
419 inline I1 pow(I1 base, I2 exp) {
420  I1 x = 1;
421 
422  while ( true ) {
423  if ( exp & 1 )
424  x *= base;
425 
426  exp >>= 1;
427  if ( ! exp )
428  break;
429  base *= base;
430  }
431 
432  return x;
433 }
434 
435 // Tuple for-each, from
436 // https://stackoverflow.com/questions/40212085/type-erasure-for-objects-containing-a-stdtuple-in-c11
437 namespace detail {
438 template<typename T, typename F, std::size_t... Is>
439 constexpr auto map_tuple(T&& tup, F& f, std::index_sequence<Is...> /*unused*/) {
440  return std::make_tuple(f(std::get<Is>(std::forward<T>(tup)))...);
441 }
442 
443 template<typename T, std::size_t... Is>
444 auto join_tuple(T&& tup, std::index_sequence<Is...> /*unused*/) {
445  std::vector<std::string> x = {rt::to_string(std::get<Is>(std::forward<T>(tup)))...};
446  return join(x, ", ");
447 }
448 
449 template<typename T, std::size_t... Is>
450 auto join_tuple_for_print(T&& tup, std::index_sequence<Is...> /*unused*/) {
451  std::vector<std::string> x = {rt::to_string_for_print(std::get<Is>(std::forward<T>(tup)))...};
452  return join(x, ", ");
453 }
454 } // namespace detail
455 
457 template<typename F, std::size_t I = 0, typename... Ts>
458 void tuple_for_each(const std::tuple<Ts...>& tup, F func) {
459  if constexpr ( I == sizeof...(Ts) )
460  return;
461  else {
462  func(std::get<I>(tup));
463  tuple_for_each<F, I + 1>(tup, func);
464  }
465 }
466 
471 template<typename T, typename F, std::size_t TupSize = std::tuple_size_v<std::decay_t<T>>>
472 constexpr auto map_tuple(T&& tup, F f) {
473  return detail::map_tuple(std::forward<T>(tup), f, std::make_index_sequence<TupSize>{});
474 }
475 
482 template<typename T, std::size_t TupSize = std::tuple_size_v<std::decay_t<T>>>
483 auto join_tuple(T&& tup) {
484  return detail::join_tuple(std::forward<T>(tup), std::make_index_sequence<TupSize>{});
485 }
486 
493 template<typename T, std::size_t TupSize = std::tuple_size_v<std::decay_t<T>>>
494 auto join_tuple_for_print(T&& tup) {
495  return detail::join_tuple_for_print(std::forward<T>(tup), std::make_index_sequence<TupSize>{});
496 }
497 
498 template<typename>
499 struct is_tuple : std::false_type {};
500 
502 template<typename... T>
503 struct is_tuple<std::tuple<T...>> : std::true_type {};
504 
506 enum class ByteOrder : int64_t { Little, Big, Network, Host, Undef = -1 };
507 
512 extern ByteOrder systemByteOrder();
513 
514 namespace detail::adl {
515 std::string to_string(const ByteOrder& x, tag /*unused*/);
516 }
517 
530 std::string strftime(const std::string& format, const Time& time);
531 
544 Time strptime(const std::string& buf, const std::string& format);
545 
546 // RAII helper to create a temporary directory.
548 public:
550  const auto tmpdir = hilti::rt::filesystem::temp_directory_path();
551  auto template_ = (tmpdir / "hilti-rt-test-XXXXXX").native();
552  auto path = ::mkdtemp(template_.data());
553  if ( ! path )
554  throw RuntimeError("cannot create temporary directory");
555 
556  _path = path;
557  }
558 
559  TemporaryDirectory(const TemporaryDirectory& other) = delete;
560  TemporaryDirectory(TemporaryDirectory&& other) noexcept { _path = std::move(other._path); }
561 
562  ~TemporaryDirectory() {
563  // In general, ignore errors in this function.
564  std::error_code ec;
565 
566  if ( ! hilti::rt::filesystem::exists(_path, ec) )
567  return;
568 
569  // Make sure we have permissions to remove the directory.
570  hilti::rt::filesystem::permissions(_path, hilti::rt::filesystem::perms::all, ec);
571 
572  // The desugared loop contains an iterator increment which could throw (no automagic call of
573  // `std::filesystem::recursive_directory_iterator::increment`), see LWG3013 for the "fix".
574  // Ignore errors from that.
575  try {
576  for ( const auto& entry : hilti::rt::filesystem::recursive_directory_iterator(_path, ec) )
577  hilti::rt::filesystem::permissions(entry, hilti::rt::filesystem::perms::all, ec);
578  } catch ( ... ) {
579  ; // Ignore error.
580  }
581 
582  hilti::rt::filesystem::remove_all(_path, ec); // ignore errors
583  }
584 
585  const auto& path() const { return _path; }
586 
587  TemporaryDirectory& operator=(const TemporaryDirectory& other) = delete;
588  TemporaryDirectory& operator=(TemporaryDirectory&& other) noexcept {
589  _path = std::move(other._path);
590  return *this;
591  }
592 
593 private:
594  hilti::rt::filesystem::path _path;
595 };
596 
597 // Combine two or more hashes.
598 template<typename... Hashes>
599 constexpr std::size_t hashCombine(std::size_t hash1, std::size_t hash2, Hashes... hashes) {
600  auto result = hash1 ^ (hash2 << 1);
601 
602  if constexpr ( sizeof...(hashes) > 0 )
603  return hashCombine(result, hashes...);
604  else
605  return result;
606 }
607 
608 } // namespace hilti::rt
Definition: network.h:20
std::string to_string(T &&x)
Definition: extension-points.h:26
auto transform(const C &x, F f)
Definition: util.h:332
bool isDebugVersion()
Definition: util.cc:34
Definition: util.h:499
Definition: util.h:300
Definition: any.h:7
std::string_view rtrim(std::string_view s, const std::string &chars) noexcept
Definition: util.h:91
std::string_view ltrim(std::string_view s, const std::string &chars) noexcept
Definition: util.h:102
void internalError(const std::string &msg) __attribute__((noreturn))
Definition: logging.cc:17
Definition: optional.h:79
std::string replace(std::string s, std::string_view o, std::string_view n)
Definition: util.cc:377
hilti::rt::filesystem::path normalizePath(const hilti::rt::filesystem::path &p)
Definition: util.cc:102
std::pair< std::string, std::string > split1(std::string s)
Definition: util.cc:156
Definition: set.h:108
bool startsWith(const std::string &s, const std::string &prefix)
Definition: util.h:201
hilti::rt::Result< hilti::rt::filesystem::path > createTemporaryFile(const std::string &prefix="")
Definition: util.cc:84
void cannot_be_reached() __attribute__((noreturn))
Definition: util.cc:52
std::string strftime(const std::string &format, const Time &time)
Definition: util.cc:412
void tuple_for_each(const std::tuple< Ts... > &tup, F func)
Definition: util.h:458
Definition: util.h:547
constexpr auto map_tuple(T &&tup, F f)
Definition: util.h:472
std::vector< std::string_view > split(std::string_view s, std::string_view delim)
Definition: util.cc:112
Time strptime(const std::string &buf, const std::string &format)
Definition: util.cc:438
auto join_tuple_for_print(T &&tup)
Definition: util.h:494
std::optional< std::string > getenv(const std::string &name)
Definition: util.cc:77
ByteOrder
Definition: util.h:506
Definition: util.h:59
constexpr auto enumerate(T &&iterable)
Definition: util.h:209
Iter atoi_n(Iter s, Iter e, uint8_t base, Result *result)
Definition: util.h:366
std::pair< std::string, std::string > rsplit1(std::string s)
Definition: util.cc:163
std::string join(const T &l, const std::string &delim="")
Definition: util.h:282
Definition: vector.h:251
std::string version()
Definition: util.cc:22
ByteOrder systemByteOrder()
Definition: util.cc:390
auto join_tuple(T &&tup)
Definition: util.h:483
void abort_with_backtrace() __attribute__((noreturn))
Definition: util.cc:44
ResourceUsage resource_usage()
Definition: util.cc:54
std::string_view trim(std::string_view s, const std::string &chars) noexcept
Definition: util.h:113
Definition: time.h:20
I1 pow(I1 base, I2 exp)
Definition: util.h:419
std::string linker_scope()
Definition: util.h:53
Definition: result.h:67