tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

diplomat_runtime.hpp (12048B)


      1 #ifndef DIPLOMAT_RUNTIME_CPP_H
      2 #define DIPLOMAT_RUNTIME_CPP_H
      3 
      4 #include <optional>
      5 #include <string>
      6 #include <string_view>
      7 #include <type_traits>
      8 #include <variant>
      9 #include <cstdint>
     10 #include <functional>
     11 #include <memory>
     12 #include <limits>
     13 
     14 
     15 #if __cplusplus >= 202002L
     16 #include <span>
     17 #else
     18 #include <array>
     19 #endif
     20 
     21 namespace diplomat {
     22 
     23 namespace capi {
     24 extern "C" {
     25 
     26 static_assert(sizeof(char) == sizeof(uint8_t), "your architecture's `char` is not 8 bits");
     27 static_assert(sizeof(char16_t) == sizeof(uint16_t), "your architecture's `char16_t` is not 16 bits");
     28 static_assert(sizeof(char32_t) == sizeof(uint32_t), "your architecture's `char32_t` is not 32 bits");
     29 
     30 typedef struct DiplomatWrite {
     31    void* context;
     32    char* buf;
     33    size_t len;
     34    size_t cap;
     35    bool grow_failed;
     36    void (*flush)(struct DiplomatWrite*);
     37    bool (*grow)(struct DiplomatWrite*, size_t);
     38 } DiplomatWrite;
     39 
     40 bool diplomat_is_str(const char* buf, size_t len);
     41 
     42 #define MAKE_SLICES(name, c_ty) \
     43    typedef struct Diplomat##name##View { \
     44        const c_ty* data; \
     45        size_t len; \
     46    } Diplomat##name##View; \
     47    typedef struct Diplomat##name##ViewMut { \
     48        c_ty* data; \
     49        size_t len; \
     50    } Diplomat##name##ViewMut; \
     51    typedef struct Diplomat##name##Array { \
     52        const c_ty* data; \
     53        size_t len; \
     54    } Diplomat##name##Array;
     55 
     56 #define MAKE_SLICES_AND_OPTIONS(name, c_ty) \
     57    MAKE_SLICES(name, c_ty) \
     58    typedef struct Option##name {union { c_ty ok; }; bool is_ok; } Option##name; \
     59    typedef struct Option##name##View {union { Diplomat##name##View ok; }; bool is_ok; } Option##name##View; \
     60    typedef struct Option##name##ViewMut {union { Diplomat##name##ViewMut ok; }; bool is_ok; } Option##name##ViewMut; \
     61    typedef struct Option##name##Array {union { Diplomat##name##Array ok; }; bool is_ok; } Option##name##Array; \
     62 
     63 MAKE_SLICES_AND_OPTIONS(I8, int8_t)
     64 MAKE_SLICES_AND_OPTIONS(U8, uint8_t)
     65 MAKE_SLICES_AND_OPTIONS(I16, int16_t)
     66 MAKE_SLICES_AND_OPTIONS(U16, uint16_t)
     67 MAKE_SLICES_AND_OPTIONS(I32, int32_t)
     68 MAKE_SLICES_AND_OPTIONS(U32, uint32_t)
     69 MAKE_SLICES_AND_OPTIONS(I64, int64_t)
     70 MAKE_SLICES_AND_OPTIONS(U64, uint64_t)
     71 MAKE_SLICES_AND_OPTIONS(Isize, intptr_t)
     72 MAKE_SLICES_AND_OPTIONS(Usize, size_t)
     73 MAKE_SLICES_AND_OPTIONS(F32, float)
     74 MAKE_SLICES_AND_OPTIONS(F64, double)
     75 MAKE_SLICES_AND_OPTIONS(Bool, bool)
     76 MAKE_SLICES_AND_OPTIONS(Char, char32_t)
     77 MAKE_SLICES_AND_OPTIONS(String, char)
     78 MAKE_SLICES_AND_OPTIONS(String16, char16_t)
     79 MAKE_SLICES_AND_OPTIONS(Strings, DiplomatStringView)
     80 MAKE_SLICES_AND_OPTIONS(Strings16, DiplomatString16View)
     81 
     82 } // extern "C"
     83 } // namespace capi
     84 
     85 extern "C" inline void _flush(capi::DiplomatWrite* w) {
     86  std::string* string = reinterpret_cast<std::string*>(w->context);
     87  string->resize(w->len);
     88 }
     89 
     90 extern "C" inline bool _grow(capi::DiplomatWrite* w, uintptr_t requested) {
     91  std::string* string = reinterpret_cast<std::string*>(w->context);
     92  string->resize(requested);
     93  w->cap = string->length();
     94  w->buf = &(*string)[0];
     95  return true;
     96 }
     97 
     98 inline capi::DiplomatWrite WriteFromString(std::string& string) {
     99  capi::DiplomatWrite w;
    100  w.context = &string;
    101  w.buf = &string[0];
    102  w.len = string.length();
    103  w.cap = string.length();
    104  // Will never become true, as _grow is infallible.
    105  w.grow_failed = false;
    106  w.flush = _flush;
    107  w.grow = _grow;
    108  return w;
    109 }
    110 
    111 template<class T> struct Ok {
    112  T inner;
    113  Ok(T&& i): inner(std::forward<T>(i)) {}
    114  // We don't want to expose an lvalue-capable constructor in general
    115  // however there is no problem doing this for trivially copyable types
    116  template<typename X = T, typename = typename std::enable_if<std::is_trivially_copyable<X>::value>::type>
    117  Ok(T i): inner(i) {}
    118  Ok() = default;
    119  Ok(Ok&&) noexcept = default;
    120  Ok(const Ok &) = default;
    121  Ok& operator=(const Ok&) = default;
    122  Ok& operator=(Ok&&) noexcept = default;
    123 };
    124 
    125 template<class T> struct Err {
    126  T inner;
    127  Err(T&& i): inner(std::forward<T>(i)) {}
    128  // We don't want to expose an lvalue-capable constructor in general
    129  // however there is no problem doing this for trivially copyable types
    130  template<typename X = T, typename = typename std::enable_if<std::is_trivially_copyable<X>::value>::type>
    131  Err(T i): inner(i) {}
    132  Err() = default;
    133  Err(Err&&) noexcept = default;
    134  Err(const Err &) = default;
    135  Err& operator=(const Err&) = default;
    136  Err& operator=(Err&&) noexcept = default;
    137 };
    138 
    139 template<class T, class E>
    140 class result {
    141 private:
    142    std::variant<Ok<T>, Err<E>> val;
    143 public:
    144  result(Ok<T>&& v): val(std::move(v)) {}
    145  result(Err<E>&& v): val(std::move(v)) {}
    146  result() = default;
    147  result(const result &) = default;
    148  result& operator=(const result&) = default;
    149  result& operator=(result&&) noexcept = default;
    150  result(result &&) noexcept = default;
    151  ~result() = default;
    152  bool is_ok() const {
    153    return std::holds_alternative<Ok<T>>(this->val);
    154  }
    155  bool is_err() const {
    156    return std::holds_alternative<Err<E>>(this->val);
    157  }
    158 
    159  template<typename U = T, typename std::enable_if_t<!std::is_reference_v<U>, std::nullptr_t> = nullptr>
    160  std::optional<T> ok() && {
    161    if (!this->is_ok()) {
    162      return std::nullopt;
    163    }
    164    return std::make_optional(std::move(std::get<Ok<T>>(std::move(this->val)).inner));
    165  }
    166 
    167  template<typename U = E, typename std::enable_if_t<!std::is_reference_v<U>, std::nullptr_t> = nullptr>
    168  std::optional<E> err() && {
    169    if (!this->is_err()) {
    170      return std::nullopt;
    171    }
    172    return std::make_optional(std::move(std::get<Err<E>>(std::move(this->val)).inner));
    173  }
    174 
    175  // std::optional does not work with reference types directly, so wrap them if present
    176  template<typename U = T, typename std::enable_if_t<std::is_reference_v<U>, std::nullptr_t> = nullptr>
    177  std::optional<std::reference_wrapper<std::remove_reference_t<T>>> ok() && {
    178    if (!this->is_ok()) {
    179      return std::nullopt;
    180    }
    181    return std::make_optional(std::reference_wrapper(std::forward<T>(std::get<Ok<T>>(std::move(this->val)).inner)));
    182  }
    183 
    184  template<typename U = E, typename std::enable_if_t<std::is_reference_v<U>, std::nullptr_t> = nullptr>
    185  std::optional<std::reference_wrapper<std::remove_reference_t<E>>> err() && {
    186    if (!this->is_err()) {
    187      return std::nullopt;
    188    }
    189    return std::make_optional(std::reference_wrapper(std::forward<E>(std::get<Err<E>>(std::move(this->val)).inner)));
    190  }
    191 
    192  void set_ok(T&& t) {
    193    this->val = Ok<T>(std::move(t));
    194  }
    195 
    196  void set_err(E&& e) {
    197    this->val = Err<E>(std::move(e));
    198  }
    199 
    200  template<typename T2>
    201  result<T2, E> replace_ok(T2&& t) {
    202    if (this->is_err()) {
    203      return result<T2, E>(Err<E>(std::get<Err<E>>(std::move(this->val))));
    204    } else {
    205      return result<T2, E>(Ok<T2>(std::move(t)));
    206    }
    207  }
    208 };
    209 
    210 class Utf8Error {};
    211 
    212 // Use custom std::span on C++17, otherwise use std::span
    213 #if __cplusplus >= 202002L
    214 
    215 constexpr std::size_t dynamic_extent = std::dynamic_extent;
    216 template<class T, std::size_t E = dynamic_extent> using span = std::span<T, E>;
    217 
    218 #else // __cplusplus < 202002L
    219 
    220 // C++-17-compatible-ish std::span
    221 constexpr size_t dynamic_extent = std::numeric_limits<std::size_t>::max();
    222 template <class T, std::size_t Extent = dynamic_extent>
    223 class span {
    224 public:
    225  constexpr span(T *data = nullptr, size_t size = Extent)
    226    : data_(data), size_(size) {}
    227 
    228  constexpr span(const span<T> &o)
    229    : data_(o.data_), size_(o.size_) {}
    230  template <size_t N>
    231  constexpr span(std::array<typename std::remove_const_t<T>, N> &arr)
    232    : data_(const_cast<T *>(arr.data())), size_(N) {}
    233 
    234  constexpr T* data() const noexcept {
    235    return this->data_;
    236  }
    237  constexpr size_t size() const noexcept {
    238    return this->size_;
    239  }
    240 
    241  constexpr T *begin() const noexcept { return data(); }
    242  constexpr T *end() const noexcept { return data() + size(); }
    243 
    244  void operator=(span<T> o) {
    245    data_ = o.data_;
    246    size_ = o.size_;
    247  }
    248 
    249 private:
    250  T* data_;
    251  size_t size_;
    252 };
    253 
    254 #endif // __cplusplus >= 202002L
    255 
    256 // Interop between std::function & our C Callback wrapper type
    257 
    258 template <typename T, typename = void>
    259 struct as_ffi {
    260  using type = T;
    261 };
    262 
    263 template <typename T>
    264 struct as_ffi<T, std::void_t<decltype(std::declval<std::remove_pointer_t<T>>().AsFFI())>> {
    265  using type = decltype(std::declval<std::remove_pointer_t<T>>().AsFFI());
    266 };
    267 
    268 template<typename T>
    269 using as_ffi_t = typename as_ffi<T>::type;
    270 
    271 template<typename T>
    272 using replace_string_view_t = std::conditional_t<std::is_same_v<T, std::string_view>, capi::DiplomatStringView, T>;
    273 
    274 template<typename T>
    275 using replace_ref_with_ptr_t = std::conditional_t<std::is_reference_v<T>, std::add_pointer_t<std::remove_reference_t<T>>, T>;
    276 
    277 /// Replace the argument types from the std::function with the argument types for th function pointer
    278 template<typename T>
    279 using replace_fn_t = replace_string_view_t<as_ffi_t<T>>;
    280 
    281 template <typename T> struct fn_traits;
    282 template <typename Ret, typename... Args> struct fn_traits<std::function<Ret(Args...)>> {
    283    using fn_ptr_t = Ret(Args...);
    284    using function_t = std::function<fn_ptr_t>;
    285    using ret = Ret;
    286 
    287    // For a given T, creates a function that take in the C ABI version & return the C++ type.
    288    template<typename T>
    289    static T replace(replace_fn_t<T> val) {
    290      if constexpr(std::is_same_v<T, std::string_view>)   {
    291          return std::string_view{val.data, val.len};
    292      } else if constexpr (!std::is_same_v<T, as_ffi_t<T>>) {
    293        if constexpr (std::is_lvalue_reference_v<T>) {
    294          return *std::remove_reference_t<T>::FromFFI(val);
    295        }
    296        else {
    297          return T::FromFFI(val);
    298        }
    299      }
    300      else {
    301          return val;
    302      }
    303    }
    304 
    305    static Ret c_run_callback(const void *cb, replace_fn_t<Args>... args) {
    306        return (*reinterpret_cast<const function_t *>(cb))(replace<Args>(args)...);
    307    }
    308 
    309    static void c_delete(const void *cb) {
    310        delete reinterpret_cast<const function_t *>(cb);
    311    }
    312 
    313    fn_traits(function_t) {} // Allows less clunky construction (avoids decltype)
    314 };
    315 
    316 // additional deduction guide required
    317 template<class T>
    318 fn_traits(T) -> fn_traits<T>;
    319 
    320 // Trait for extracting inner types from either std::optional or std::unique_ptr.
    321 // These are the two potential types returned by next() functions
    322 template<typename T> struct inner { using type = T; };
    323 template<typename T> struct inner<std::unique_ptr<T>> { using type = T; };
    324 template<typename T> struct inner<std::optional<T>>{ using type = T; };
    325 
    326 template<typename T, typename U = typename inner<T>::type>
    327 inline const U get_inner_if_present(T v) {
    328  if constexpr(std::is_same_v<T,U>) {
    329    return std::move(v);
    330  } else {
    331    return *std::move(v);
    332  }
    333 }
    334 
    335 // Adapter for iterator types
    336 template<typename T, typename U = void> struct has_next : std::false_type {};
    337 template<typename T> struct has_next < T, std::void_t<decltype(std::declval<T>().next())>> : std::true_type {};
    338 template<typename T> constexpr bool has_next_v = has_next<T>::value;
    339 
    340 /// Helper template enabling native iteration over unique ptrs to objects which implement next()
    341 template<typename T>
    342 struct next_to_iter_helper {
    343  static_assert(has_next_v<T>, "next_to_iter_helper may only be used with types implementing next()");
    344  using next_type = decltype(std::declval<T>().next());
    345 
    346  // STL Iterator trait definitions
    347  using value_type = typename inner<next_type>::type;
    348  using difference_type = void;
    349  using reference = std::add_lvalue_reference_t<value_type>;
    350  using iterator_category = std::input_iterator_tag;
    351 
    352  next_to_iter_helper(std::unique_ptr<T>&& ptr) : _ptr(std::move(ptr)), _curr(_ptr->next()) {}
    353 
    354  // https://en.cppreference.com/w/cpp/named_req/InputIterator requires that the type be copyable
    355  next_to_iter_helper(const next_to_iter_helper& o) : _ptr(o._ptr), _curr(o._curr) {}
    356 
    357  void operator++() { _curr = _ptr->next(); }
    358  void operator++(int) { ++(*this); }
    359  const value_type& operator*() const { return *_curr; }
    360 
    361  bool operator!=(std::nullopt_t) {
    362    return (bool)_curr;
    363  }
    364 
    365  std::shared_ptr<T> _ptr; // shared to satisfy the copyable requirement
    366  next_type _curr;
    367 };
    368 
    369 } // namespace diplomat
    370 
    371 #endif