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 
298 template<typename X, typename F>
299 auto transform(const std::vector<X>& x, F f) {
300  using Y = typename std::result_of<F(X&)>::type;
301  std::vector<Y> y;
302  y.reserve(x.size());
303  for ( const auto& i : x )
304  y.emplace_back(f(i));
305  return y;
306 }
307 
311 template<typename X, typename F>
312 auto transform(const std::list<X>& x, F f) {
313  using Y = typename std::result_of<F(X&)>::type;
314  std::vector<Y> y;
315  y.reserve(x.size());
316  for ( const auto& i : x )
317  y.emplace_back(f(i));
318  return y;
319 }
320 
324 template<typename X, typename F>
325 auto transform(const std::set<X>& x, F f) {
326  using Y = typename std::result_of<F(X&)>::type;
327  std::set<Y> y;
328  for ( const auto& i : x )
329  y.insert(f(i));
330  return y;
331 }
332 
334 template<typename X, typename F>
335 auto transform(const Set<X>& x, F f) {
336  using Y = typename std::result_of<F(X&)>::type;
338  for ( const auto& i : x )
339  y.insert(f(i));
340  return y;
341 }
342 
344 template<typename X, typename Allocator, typename F>
345 auto transform(const Vector<X, Allocator>& x, F f) {
346  using Y = typename std::result_of<F(X&)>::type;
347  Vector<Y> y;
348  std::transform(x.begin(), x.end(), std::back_inserter(y), [&](const auto& value) { return f(value); });
349  return y;
350 }
351 
352 class OutOfRange;
353 
376 template<class Iter, typename Result>
377 inline Iter atoi_n(Iter s, Iter e, int base, Result* result) {
378  if ( base < 2 || base > 36 )
379  throw OutOfRange("base for numerical conversion must be between 2 and 36");
380 
381  if ( s == e )
382  throw InvalidArgument("cannot decode from empty range");
383 
384  std::optional<Result> n = std::nullopt;
385  bool neg = false;
386  auto it = s;
387 
388  if ( *it == '-' ) {
389  neg = true;
390  ++it;
391  }
392  else if ( *it == '+' ) {
393  neg = false;
394  ++it;
395  }
396 
397  for ( ; it != e; ++it ) {
398  auto c = *it;
399 
400  Result d;
401  if ( c >= '0' && c < '0' + base )
402  d = c - '0';
403  else if ( c >= 'a' && c < 'a' - 10 + base )
404  d = c - 'a' + 10;
405  else if ( c >= 'A' && c < 'A' - 10 + base )
406  d = c - 'A' + 10;
407  else
408  break;
409 
410  n = n.value_or(Result()) * base + d;
411  }
412 
413  if ( ! n )
414  return s;
415 
416  s = it;
417 
418  if ( neg )
419  *result = -*n;
420  else
421  *result = *n;
422 
423  return s;
424 }
425 
429 template<typename I1, typename I2>
430 inline I1 pow(I1 base, I2 exp) {
431  I1 x = 1;
432 
433  while ( exp ) {
434  if ( exp & 1 )
435  x *= base;
436 
437  exp >>= 1;
438  base *= base;
439  }
440 
441  return x;
442 }
443 
444 // Tuple for-each, from
445 // https://stackoverflow.com/questions/40212085/type-erasure-for-objects-containing-a-stdtuple-in-c11
446 namespace detail {
447 template<typename T, typename F, std::size_t... Is>
448 constexpr auto map_tuple(T&& tup, F& f, std::index_sequence<Is...> /*unused*/) {
449  return std::make_tuple(f(std::get<Is>(std::forward<T>(tup)))...);
450 }
451 
452 template<typename T, std::size_t... Is>
453 auto join_tuple(T&& tup, std::index_sequence<Is...> /*unused*/) {
454  std::vector<std::string> x = {rt::to_string(std::get<Is>(std::forward<T>(tup)))...};
455  return join(x, ", ");
456 }
457 
458 template<typename T, std::size_t... Is>
459 auto join_tuple_for_print(T&& tup, std::index_sequence<Is...> /*unused*/) {
460  std::vector<std::string> x = {rt::to_string_for_print(std::get<Is>(std::forward<T>(tup)))...};
461  return join(x, ", ");
462 }
463 } // namespace detail
464 
466 template<typename F, std::size_t I = 0, typename... Ts>
467 void tuple_for_each(const std::tuple<Ts...>& tup, F func) {
468  if constexpr ( I == sizeof...(Ts) )
469  return;
470  else {
471  func(std::get<I>(tup));
472  tuple_for_each<F, I + 1>(tup, func);
473  }
474 }
475 
480 template<typename T, typename F, std::size_t TupSize = std::tuple_size_v<std::decay_t<T>>>
481 constexpr auto map_tuple(T&& tup, F f) {
482  return detail::map_tuple(std::forward<T>(tup), f, std::make_index_sequence<TupSize>{});
483 }
484 
491 template<typename T, std::size_t TupSize = std::tuple_size_v<std::decay_t<T>>>
492 auto join_tuple(T&& tup) {
493  return detail::join_tuple(std::forward<T>(tup), std::make_index_sequence<TupSize>{});
494 }
495 
502 template<typename T, std::size_t TupSize = std::tuple_size_v<std::decay_t<T>>>
503 auto join_tuple_for_print(T&& tup) {
504  return detail::join_tuple_for_print(std::forward<T>(tup), std::make_index_sequence<TupSize>{});
505 }
506 
507 template<typename>
508 struct is_tuple : std::false_type {};
509 
511 template<typename... T>
512 struct is_tuple<std::tuple<T...>> : std::true_type {};
513 
515 enum class ByteOrder { Little, Big, Network, Host, Undef = -1 };
516 
521 extern ByteOrder systemByteOrder();
522 
523 namespace detail::adl {
524 std::string to_string(const ByteOrder& x, tag /*unused*/);
525 }
526 
539 std::string strftime(const std::string& format, const Time& time);
540 
553 Time strptime(const std::string& buf, const std::string& format);
554 
555 // RAII helper to create a temporary directory.
557 public:
559  const auto tmpdir = hilti::rt::filesystem::temp_directory_path();
560  auto template_ = (tmpdir / "hilti-rt-test-XXXXXX").native();
561  auto path = ::mkdtemp(template_.data());
562  if ( ! path )
563  throw RuntimeError("cannot create temporary directory");
564 
565  _path = path;
566  }
567 
568  TemporaryDirectory(const TemporaryDirectory& other) = delete;
569  TemporaryDirectory(TemporaryDirectory&& other) { _path = std::move(other._path); }
570 
571  ~TemporaryDirectory() {
572  if ( ! hilti::rt::filesystem::exists(_path) )
573  return;
574 
575  // Make sure we have permissions to remove the directory.
576  hilti::rt::filesystem::permissions(_path, hilti::rt::filesystem::perms::all);
577  for ( const auto& entry : hilti::rt::filesystem::recursive_directory_iterator(_path) )
578  hilti::rt::filesystem::permissions(entry, hilti::rt::filesystem::perms::all);
579 
580  std::error_code ec;
581  hilti::rt::filesystem::remove_all(_path, ec); // ignore errors
582  }
583 
584  const auto& path() const { return _path; }
585 
586  TemporaryDirectory& operator=(const TemporaryDirectory& other) = delete;
587  TemporaryDirectory& operator=(TemporaryDirectory&& other) {
588  _path = std::move(other._path);
589  return *this;
590  }
591 
592 private:
593  hilti::rt::filesystem::path _path;
594 };
595 
596 } // namespace hilti::rt
Definition: network.h:20
ByteOrder
Definition: util.h:515
std::string to_string(T &&x)
Definition: extension-points.h:26
bool isDebugVersion()
Definition: util.cc:34
Definition: util.h:508
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:18
Definition: optional.h:79
std::string replace(std::string s, std::string_view o, std::string_view n)
Definition: util.cc:379
auto transform(const std::vector< X > &x, F f)
Definition: util.h:299
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:377
std::pair< std::string, std::string > split1(std::string s)
Definition: util.cc:156
Definition: set.h:107
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:467
Definition: util.h:556
constexpr auto map_tuple(T &&tup, F f)
Definition: util.h:481
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:503
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:249
std::string version()
Definition: util.cc:22
ByteOrder systemByteOrder()
Definition: util.cc:392
auto join_tuple(T &&tup)
Definition: util.h:492
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:430
std::string linker_scope()
Definition: util.h:52
Definition: result.h:67