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