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 <climits>
9 #include <cmath>
10 #include <functional>
11 #include <initializer_list>
12 #include <iostream>
13 #include <list>
14 #include <map>
15 #include <memory>
16 #include <optional>
17 #include <set>
18 #include <stdexcept>
19 #include <string>
20 #include <type_traits>
21 #include <unordered_map>
22 #include <utility>
23 #include <vector>
24 
25 #include <hilti/rt/filesystem.h>
26 #include <hilti/rt/unpack.h>
27 #include <hilti/rt/util.h>
28 
29 #include <hilti/autogen/config.h>
30 #include <hilti/base/result.h>
31 
32 namespace hilti::util::detail {
34 void __internal_error(const std::string& s);
35 } // namespace hilti::util::detail
36 
37 #undef TINYFORMAT_ERROR
38 #define TINYFORMAT_ERROR(reason) ::util::detail::__internal_error(reason)
39 #include <hilti/rt/3rdparty/tinyformat/tinyformat.h>
40 
41 namespace hilti {
42 
43 struct Configuration;
44 
50 #define _UNUSED(x) ((void)(x));
51 
53 #define IF_DERIVED_FROM(t, cls) typename std::enable_if_t<std::is_base_of<cls, t>::value>* = nullptr
54 
56 #define IF_NOT_DERIVED_FROM(t, cls) typename std::enable_if_t<! std::is_base_of<cls, t>::value>* = nullptr
57 
59 #define IF_SAME(t, cls) typename std::enable_if_t<std::is_same<cls, t>::value>* = nullptr
60 
62 #define IF_NOT_SAME(t, cls) typename std::enable_if_t<! std::is_same<cls, t>::value>* = nullptr
63 
64 namespace util {
65 
68 
70 extern void cannot_be_reached() __attribute__((noreturn));
71 
73 template<typename T>
74 std::string typename_() {
75  return demangle(typeid(T).name());
76 }
77 
79 template<typename... Args>
80 std::string fmt(const char* fmt, const Args&... args) {
81  return tfm::format(fmt, args...);
82 }
83 
84 using hilti::rt::transform; // NOLINT(misc-unused-using-decls)
85 
87 template<typename X, typename F>
88 auto transform_to_vector(const std::set<X>& x, F f) {
89  using Y = typename std::invoke_result_t<F, X&>;
90  std::vector<Y> y;
91  y.reserve(x.size());
92  for ( const auto& i : x )
93  y.push_back(f(i));
94  return y;
95 }
96 
98 template<typename C, typename F>
99 auto filter(const C& x, F f) {
100  C y;
101  std::copy_if(std::begin(x), std::end(x), std::inserter(y, std::end(y)), f);
102  return y;
103 }
104 
109 template<typename T, typename TIter = decltype(std::begin(std::declval<T>())),
110  typename = decltype(std::end(std::declval<T>()))>
111 constexpr auto enumerate(T&& iterable) {
112  struct iterator {
113  size_t i;
114  TIter iter;
115  bool operator!=(const iterator& other) const { return iter != other.iter; }
116  void operator++() {
117  ++i;
118  ++iter;
119  }
120  auto operator*() const { return std::tie(i, *iter); }
121  };
122  struct iterable_wrapper {
123  T iterable;
124  auto begin() { return iterator{0, std::begin(iterable)}; }
125  auto end() { return iterator{0, std::end(iterable)}; }
126  };
127  return iterable_wrapper{std::forward<T>(iterable)};
128 }
129 
131 extern std::vector<std::string> split(std::string s, const std::string& delim = " ");
132 
137 extern std::pair<std::string, std::string> split1(std::string s, const std::string& delim = " ");
138 
143 extern std::pair<std::string, std::string> rsplit1(std::string s, const std::string& delim = " ");
144 
153 template<typename T>
154 std::vector<T> slice(const std::vector<T>& v, int begin, int end = -1) {
155  if ( begin < 0 )
156  begin = v.size() + begin;
157 
158  if ( static_cast<size_t>(begin) > v.size() )
159  return {};
160 
161  if ( end < 0 )
162  end = v.size() + end + 1;
163 
164  if ( begin < 0 )
165  begin = 0;
166 
167  if ( end < 0 )
168  end = 0;
169 
170  if ( static_cast<size_t>(end) > v.size() )
171  end = v.size();
172 
173  return std::vector<T>(v.begin() + begin, v.begin() + end);
174 }
175 
180 template<typename T>
181 std::string join(const T& l, const std::string& delim = "") {
182  std::string result;
183  bool first = true;
184 
185  for ( const auto& i : l ) {
186  if ( not first )
187  result += delim;
188 
189  result += std::string(i);
190  first = false;
191  }
192 
193  return result;
194 }
195 
200 template<typename T>
201 std::string join(const std::initializer_list<T>& l, const std::string& delim = "") {
202  std::string result;
203  bool first = true;
204 
205  for ( const auto& i : l ) {
206  if ( not first )
207  result += delim;
208  result += std::string(i);
209  first = false;
210  }
211 
212  return result;
213 }
214 
219 template<typename iterator>
220 std::string join(const iterator& begin, const iterator& end, const std::string& delim = "") {
221  std::string result;
222  bool first = true;
223 
224  for ( iterator i = begin; i != end; i++ ) {
225  if ( not first )
226  result += delim;
227  result += std::string(*i);
228  first = false;
229  }
230 
231  return result;
232 }
233 
234 
252 extern std::string prefixParts(const std::string& in, const std::string& prefix, const std::string& include_tag = "");
253 
264 extern std::vector<std::string> flattenParts(const std::vector<std::string>& in);
265 
267 extern std::string replace(const std::string& s, const std::string& o, const std::string& n);
268 
270 extern std::string tolower(const std::string& s);
271 
273 extern std::string toupper(const std::string& s);
274 
276 extern std::string trim(const std::string& s);
277 
279 extern std::string rtrim(const std::string& s);
280 
282 extern std::string ltrim(const std::string& s);
283 
285 inline bool startsWith(const std::string& s, const std::string& prefix) { return s.find(prefix) == 0; }
286 
288 extern bool endsWith(const std::string& s, const std::string& suffix);
289 
291 extern uint64_t hash(const std::string& str);
292 
294 extern uint64_t hash(const char* data, size_t len);
295 
300 constexpr std::pair<intmax_t, intmax_t> signed_integer_range(int width) {
301  switch ( width ) {
302  case 8: return std::make_pair(INT8_MIN, INT8_MAX);
303  case 16: return std::make_pair(INT16_MIN, INT16_MAX);
304  case 32: return std::make_pair(INT32_MIN, INT32_MAX);
305  case 64: return std::make_pair(INT64_MIN, INT64_MAX);
306  default: throw std::out_of_range("unsupported integer width");
307  }
308 }
309 
314 constexpr std::pair<uintmax_t, uintmax_t> unsigned_integer_range(int width) {
315  switch ( width ) {
316  case 8: return std::make_pair(0, UINT8_MAX);
317  case 16: return std::make_pair(0, UINT16_MAX);
318  case 32: return std::make_pair(0, UINT32_MAX);
319  case 64: return std::make_pair(0, UINT64_MAX);
320  default: throw std::out_of_range("unsupported integer width");
321  }
322 }
323 
331 template<typename Error>
332 uint64_t chars_to_uint64(const char* dgts, int base, Error handler) {
333  errno = 0;
334  char* cp;
335  auto u = strtoull(dgts, &cp, base);
336  if ( cp == dgts || *cp != '\0' || (u == ULONG_MAX && errno == ERANGE) ) {
337  errno = 0;
338  handler();
339  }
340  return u;
341 };
342 
349 template<typename Error>
350 double chars_to_double(const char* dgts, Error handler) {
351  errno = 0;
352  char* cp;
353  auto d = strtod(dgts, &cp);
354  if ( cp == dgts || *cp != '\0' || (d == HUGE_VAL && errno == ERANGE) ) {
355  errno = 0;
356  handler();
357  }
358 
359  return d;
360 };
361 
372 extern std::string uitoa_n(uint64_t value, unsigned int base, int n = -1);
373 
374 using hilti::rt::escapeBytes;
375 using hilti::rt::escapeUTF8; // NOLINT(misc-unused-using-decls)
376 using hilti::rt::expandEscapes; // NOLINT(misc-unused-using-decls)
377 
385 inline std::string escapeBytesForCxx(std::string_view s) { return escapeBytes(s, true, true); }
386 
396 extern std::string toIdentifier(const std::string& s, bool ensure_non_keyword = false);
397 
399 extern double currentTime();
400 
402 extern hilti::Result<hilti::rt::filesystem::path> findInPaths(const hilti::rt::filesystem::path& file,
403  const std::vector<hilti::rt::filesystem::path>& paths);
404 
406 using hilti::rt::normalizePath; // NOLINT(misc-unused-using-decls)
407 
414 using hilti::rt::createTemporaryFile; // NOLINT(misc-unused-using-decls)
415 
417 hilti::rt::filesystem::path currentExecutable();
418 
420 [[noreturn]] extern void abort_with_backtrace();
421 
423 template<class Iter, typename Result>
424 inline auto atoi_n(Iter s, Iter e, int base, Result* result) {
425  return hilti::rt::atoi_n(s, e, base, result);
426 }
427 
433 template<typename A, typename B>
434 std::list<std::pair<A, B>> zip2(const std::list<A>& lhs, const std::list<B>& rhs) {
435  std::list<std::pair<A, B>> result;
436  for ( std::pair<typename std::list<A>::const_iterator, typename std::list<B>::const_iterator> iter =
437  std::pair<typename std::list<A>::const_iterator, typename std::list<B>::const_iterator>(lhs.cbegin(),
438  rhs.cbegin());
439  iter.first != lhs.end() and iter.second != rhs.end(); ++iter.first, ++iter.second )
440  result.emplace_back(*iter.first, *iter.second);
441  return result;
442 }
443 
449 template<typename A, typename B>
450 std::vector<std::pair<A, B>> zip2(const std::vector<A>& lhs, const std::vector<B>& rhs) {
451  std::vector<std::pair<A, B>> result;
452  for ( std::pair<typename std::vector<A>::const_iterator, typename std::vector<B>::const_iterator> iter =
453  std::pair<typename std::vector<A>::const_iterator, typename std::vector<B>::const_iterator>(lhs.cbegin(),
454  rhs.cbegin());
455  iter.first != lhs.end() and iter.second != rhs.end(); ++iter.first, ++iter.second )
456  result.emplace_back(*iter.first, *iter.second);
457  return result;
458 }
459 
461 template<typename A, typename B>
462 std::set<A> map_keys(const std::map<A, B>& m) {
463  std::set<A> l;
464 
465  for ( const auto& i : m )
466  l.insert(i.first);
467 
468  return l;
469 }
470 
472 template<typename A, typename B>
473 std::set<B> map_values(const std::map<A, B>& m) {
474  std::set<B> l;
475 
476  for ( const auto& i : m )
477  l.insert(i.second);
478 
479  return l;
480 }
481 
483 template<typename A, typename B>
484 std::set<A> map_keys(const std::unordered_map<A, B>& m) {
485  std::set<A> l;
486 
487  for ( const auto& i : m )
488  l.insert(i.first);
489 
490  return l;
491 }
492 
494 template<typename A, typename B>
495 std::set<B> map_values(const std::unordered_map<A, B>& m) {
496  std::set<B> l;
497 
498  for ( const auto& i : m )
499  l.insert(i.second);
500 
501  return l;
502 }
503 
505 template<typename A, typename Compare = std::less<A>>
506 std::set<A, Compare> set_difference(const std::set<A, Compare>& a, const std::set<A, Compare>& b) {
507  std::set<A, Compare> r;
508  std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(r, r.end()), Compare());
509  return r;
510 }
511 
513 template<typename A, typename Compare = std::less<A>>
514 std::set<A, Compare> set_intersection(std::set<A, Compare>& a, std::set<A, Compare>& b) {
515  std::set<A, Compare> r;
516  std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), std::inserter(r, r.end()), Compare());
517  return r;
518 }
519 
521 template<typename A, typename Compare = std::less<A>>
522 std::set<A, Compare> set_union(const std::set<A, Compare>& a, const std::set<A, Compare>& b) {
523  std::set<A, Compare> r;
524  std::set_union(a.begin(), a.end(), b.begin(), b.end(), std::inserter(r, r.end()), Compare());
525  return r;
526 }
527 
529 template<typename T>
530 std::vector<T> concat(std::vector<T> v1, const std::vector<T>& v2) {
531  v1.reserve(v1.size() + v2.size());
532  v1.insert(v1.end(), v2.begin(), v2.end());
533  return v1;
534 }
535 
537 template<typename T>
538 std::vector<T>& append(std::vector<T>& v1, const std::vector<T>& v2) {
539  v1.reserve(v1.size() + v2.size());
540  v1.insert(v1.end(), v2.begin(), v2.end());
541  return v1;
542 }
543 
545 template<typename T>
546 std::vector<T> remove_duplicates(std::vector<T> v) {
547  std::set<T> seen;
548  std::vector<T> out;
549 
550  for ( auto&& i : v ) {
551  if ( seen.find(i) != seen.end() )
552  continue;
553 
554  seen.insert(i);
555  out.emplace_back(std::move(i));
556  }
557 
558  return out;
559 }
560 
566 template<typename T>
567 std::string uniqueIndex(const T& c, std::string hint) {
568  if ( c.find(hint) == c.end() )
569  return hint;
570 
571  std::string idx;
572  int cnt = 1;
573 
574  while ( true ) {
575  std::string idx = fmt("%s.%d", hint, ++cnt);
576  if ( c.find(idx) == c.end() )
577  return idx;
578  }
579 }
580 
582 inline bool copyStream(std::istream& in, std::ostream& out) {
583  char buffer[4096];
584  while ( in.good() ) {
585  in.read(buffer, sizeof(buffer));
586  out.write(buffer, sizeof(buffer));
587  }
588 
589  return in.eof();
590 }
591 
592 namespace enum_ {
593 
595 template<typename E>
596 struct Value {
597  E value;
598  const char* name;
599 };
600 
611 template<typename Enum, std::size_t Size>
612 constexpr auto from_string(const std::string_view name, const Value<Enum> (&values)[Size]) {
613  for ( const auto& v : values )
614  if ( v.name == name )
615  return v.value;
616 
617  throw std::out_of_range(name.data());
618 };
619 
630 template<typename Enum, std::size_t Size>
631 constexpr auto to_string(Enum value, const Value<Enum> (&values)[Size]) {
632  for ( const auto& v : values )
633  if ( v.value == value )
634  return v.name;
635 
636  throw std::out_of_range(std::to_string(static_cast<int>(value)));
637 };
638 
639 } // namespace enum_
640 
647 std::optional<hilti::rt::filesystem::path> cacheDirectory(const hilti::Configuration& configuration);
648 
649 } // namespace util
650 
651 } // namespace hilti
auto transform(const C &x, F f)
Definition: util.h:362
Definition: util.h:596
auto filter(const hilti::node::Range< X > &x, F f)
Definition: node.h:884
std::string_view rtrim(std::string_view s, const std::string &chars) noexcept
Definition: util.h:121
std::string_view ltrim(std::string_view s, const std::string &chars) noexcept
Definition: util.h:132
Definition: optional.h:79
std::string demangle(const std::string &symbol)
Definition: backtrace.h:40
std::string replace(std::string s, std::string_view o, std::string_view n)
Definition: util.cc:367
hilti::rt::filesystem::path normalizePath(const hilti::rt::filesystem::path &p)
Definition: util.cc:92
std::pair< std::string, std::string > split1(std::string s)
Definition: util.cc:146
bool startsWith(const std::string &s, const std::string &prefix)
Definition: util.cc:380
hilti::rt::Result< hilti::rt::filesystem::path > createTemporaryFile(const std::string &prefix="")
Definition: util.cc:74
void cannot_be_reached() __attribute__((noreturn))
Definition: util.cc:42
Definition: util.h:32
std::vector< std::string_view > split(std::string_view s, std::string_view delim)
Definition: util.cc:102
constexpr auto enumerate(T &&iterable)
Definition: util.h:239
Iter atoi_n(Iter s, Iter e, uint8_t base, Result *result)
Definition: util.h:396
std::pair< std::string, std::string > rsplit1(std::string s)
Definition: util.cc:153
std::string join(const T &l, const std::string &delim="")
Definition: util.h:312
void abort_with_backtrace() __attribute__((noreturn))
Definition: util.cc:34
std::string_view trim(std::string_view s, const std::string &chars) noexcept
Definition: util.h:143
Definition: result.h:67
std::string fmt(const char *fmt, const Args &... args)
Definition: fmt.h:13