diff options
Diffstat (limited to 'third_party/libwebrtc/rtc_base/untyped_function.h')
-rw-r--r-- | third_party/libwebrtc/rtc_base/untyped_function.h | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/third_party/libwebrtc/rtc_base/untyped_function.h b/third_party/libwebrtc/rtc_base/untyped_function.h new file mode 100644 index 0000000000..c1f59458b9 --- /dev/null +++ b/third_party/libwebrtc/rtc_base/untyped_function.h @@ -0,0 +1,324 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_UNTYPED_FUNCTION_H_ +#define RTC_BASE_UNTYPED_FUNCTION_H_ + +#include <cstddef> +#include <cstring> +#include <memory> +#include <type_traits> +#include <utility> + +#include "rtc_base/system/assume.h" + +namespace webrtc { +namespace webrtc_function_impl { + +using FunVoid = void(); + +// Inline storage size is this many machine words. +enum : size_t { kInlineStorageWords = 4 }; + +union VoidUnion { + void* void_ptr; + FunVoid* fun_ptr; + typename std::aligned_storage<kInlineStorageWords * sizeof(uintptr_t)>::type + inline_storage; +}; + +// Returns the number of elements of the `inline_storage` array required to +// store an object of type T. +template <typename T> +constexpr size_t InlineStorageSize() { + // sizeof(T) / sizeof(uintptr_t), but rounded up. + return (sizeof(T) + sizeof(uintptr_t) - 1) / sizeof(uintptr_t); +} + +template <typename T> +struct CallHelpers; +template <typename RetT, typename... ArgT> +struct CallHelpers<RetT(ArgT...)> { + // Return type of the three helpers below. + using return_type = RetT; + // Complete function type of the three helpers below. + using function_type = RetT(VoidUnion*, ArgT...); + // Helper for calling the `void_ptr` case of VoidUnion. + template <typename F> + static RetT CallVoidPtr(VoidUnion* vu, ArgT... args) { + return (*static_cast<F*>(vu->void_ptr))(std::forward<ArgT>(args)...); + } + // Helper for calling the `fun_ptr` case of VoidUnion. + static RetT CallFunPtr(VoidUnion* vu, ArgT... args) { + return (reinterpret_cast<RetT (*)(ArgT...)>(vu->fun_ptr))( + std::forward<ArgT>(args)...); + } + // Helper for calling the `inline_storage` case of VoidUnion. + template <typename F> + static RetT CallInlineStorage(VoidUnion* vu, ArgT... args) { + return (*reinterpret_cast<F*>(&vu->inline_storage))( + std::forward<ArgT>(args)...); + } +}; + +} // namespace webrtc_function_impl + +// A class that holds (and owns) any callable. The same function call signature +// must be provided when constructing and calling the object. +// +// The point of not having the call signature as a class template parameter is +// to have one single concrete type for all signatures; this reduces binary +// size. +class UntypedFunction final { + public: + // Callables of at most this size can be stored inline, if they are trivial. + // (Useful in tests and benchmarks; avoid using this in production code.) + enum : size_t { + kInlineStorageSize = sizeof(webrtc_function_impl::VoidUnion::inline_storage) + }; + static_assert(kInlineStorageSize == + webrtc_function_impl::kInlineStorageWords * + sizeof(uintptr_t), + ""); + + // The *UntypedFunctionArgs structs are used to transfer arguments from + // PrepareArgs() to Create(). They are trivial, but may own heap allocations, + // so make sure to pass them to Create() exactly once! + // + // The point of doing Create(PrepareArgs(foo)) instead of just Create(foo) is + // to separate the code that has to be inlined (PrepareArgs) from the code + // that can be noninlined (Create); the *UntypedFunctionArgs types are + // designed to efficiently carry the required information from one to the + // other. + template <size_t N> + struct TrivialUntypedFunctionArgs { + static_assert(N >= 1, ""); + static_assert(N <= webrtc_function_impl::kInlineStorageWords, ""); + // We use an uintptr_t array here instead of std::aligned_storage, because + // the former can be efficiently passed in registers when using + // TrivialUntypedFunctionArgs as a function argument. (We can't do the same + // in VoidUnion, because std::aligned_storage but not uintptr_t can be + // legally reinterpret_casted to arbitrary types. + // TrivialUntypedFunctionArgs, on the other hand, only needs to handle + // placement new and memcpy.) + alignas(std::max_align_t) uintptr_t inline_storage[N]; + webrtc_function_impl::FunVoid* call; + }; + struct NontrivialUntypedFunctionArgs { + void* void_ptr; + webrtc_function_impl::FunVoid* call; + void (*del)(webrtc_function_impl::VoidUnion*); + }; + struct FunctionPointerUntypedFunctionArgs { + webrtc_function_impl::FunVoid* fun_ptr; + webrtc_function_impl::FunVoid* call; + }; + + // Create function for lambdas and other callables that are trivial and small; + // it accepts every type of argument except those noted in its enable_if call. + template < + typename Signature, + typename F, + typename F_deref = typename std::remove_reference<F>::type, + typename std::enable_if< + // Not for function pointers; we have another overload for that below. + !std::is_function< + typename std::remove_pointer<F_deref>::type>::value && + + // Not for nullptr; we have a constructor for that below. + !std::is_same<std::nullptr_t, + typename std::remove_cv<F>::type>::value && + + // Not for UntypedFunction objects; use move construction or + // assignment. + !std::is_same<UntypedFunction, + typename std::remove_cv<F_deref>::type>::value && + + // Only for trivial callables that will fit in inline storage. + std::is_trivially_move_constructible<F_deref>::value && + std::is_trivially_destructible<F_deref>::value && + sizeof(F_deref) <= kInlineStorageSize>::type* = nullptr, + size_t InlineSize = webrtc_function_impl::InlineStorageSize<F_deref>()> + static TrivialUntypedFunctionArgs<InlineSize> PrepareArgs(F&& f) { + // The callable is trivial and small enough, so we just store its bytes + // in the inline storage. + TrivialUntypedFunctionArgs<InlineSize> args; + new (&args.inline_storage) F_deref(std::forward<F>(f)); + args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>( + webrtc_function_impl::CallHelpers< + Signature>::template CallInlineStorage<F_deref>); + return args; + } + template <size_t InlineSize> + static UntypedFunction Create(TrivialUntypedFunctionArgs<InlineSize> args) { + webrtc_function_impl::VoidUnion vu; + std::memcpy(&vu.inline_storage, args.inline_storage, + sizeof(args.inline_storage)); + return UntypedFunction(vu, args.call, nullptr); + } + + // Create function for lambdas and other callables that are nontrivial or + // large; it accepts every type of argument except those noted in its + // enable_if call. + template <typename Signature, + typename F, + typename F_deref = typename std::remove_reference<F>::type, + typename std::enable_if< + // Not for function pointers; we have another overload for that + // below. + !std::is_function< + typename std::remove_pointer<F_deref>::type>::value && + + // Not for nullptr; we have a constructor for that below. + !std::is_same<std::nullptr_t, + typename std::remove_cv<F>::type>::value && + + // Not for UntypedFunction objects; use move construction or + // assignment. + !std::is_same<UntypedFunction, + typename std::remove_cv<F_deref>::type>::value && + + // Only for nontrivial callables, or callables that won't fit in + // inline storage. + !(std::is_trivially_move_constructible<F_deref>::value && + std::is_trivially_destructible<F_deref>::value && + sizeof(F_deref) <= kInlineStorageSize)>::type* = nullptr> + static NontrivialUntypedFunctionArgs PrepareArgs(F&& f) { + // The callable is either nontrivial or too large, so we can't keep it + // in the inline storage; use the heap instead. + NontrivialUntypedFunctionArgs args; + args.void_ptr = new F_deref(std::forward<F>(f)); + args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>( + webrtc_function_impl::CallHelpers<Signature>::template CallVoidPtr< + F_deref>); + args.del = static_cast<void (*)(webrtc_function_impl::VoidUnion*)>( + [](webrtc_function_impl::VoidUnion* vu) { + // Assuming that this pointer isn't null allows the + // compiler to eliminate a null check in the (inlined) + // delete operation. + RTC_ASSUME(vu->void_ptr != nullptr); + delete reinterpret_cast<F_deref*>(vu->void_ptr); + }); + return args; + } + static UntypedFunction Create(NontrivialUntypedFunctionArgs args) { + webrtc_function_impl::VoidUnion vu; + vu.void_ptr = args.void_ptr; + return UntypedFunction(vu, args.call, args.del); + } + + // Create function that accepts function pointers. If the argument is null, + // the result is an empty UntypedFunction. + template <typename Signature> + static FunctionPointerUntypedFunctionArgs PrepareArgs(Signature* f) { + FunctionPointerUntypedFunctionArgs args; + args.fun_ptr = reinterpret_cast<webrtc_function_impl::FunVoid*>(f); + args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>( + webrtc_function_impl::CallHelpers<Signature>::CallFunPtr); + return args; + } + static UntypedFunction Create(FunctionPointerUntypedFunctionArgs args) { + webrtc_function_impl::VoidUnion vu; + vu.fun_ptr = args.fun_ptr; + return UntypedFunction(vu, args.fun_ptr == nullptr ? nullptr : args.call, + nullptr); + } + + // Prepares arguments and creates an UntypedFunction in one go. + template <typename Signature, typename F> + static UntypedFunction Create(F&& f) { + return Create(PrepareArgs<Signature>(std::forward<F>(f))); + } + + // Default constructor. Creates an empty UntypedFunction. + UntypedFunction() : call_(nullptr), delete_(nullptr) {} + + // Nullptr constructor and assignment. Creates an empty UntypedFunction. + UntypedFunction(std::nullptr_t) // NOLINT(runtime/explicit) + : call_(nullptr), delete_(nullptr) {} + UntypedFunction& operator=(std::nullptr_t) { + call_ = nullptr; + if (delete_) { + delete_(&f_); + delete_ = nullptr; + } + return *this; + } + + // Not copyable. + UntypedFunction(const UntypedFunction&) = delete; + UntypedFunction& operator=(const UntypedFunction&) = delete; + + // Move construction and assignment. + UntypedFunction(UntypedFunction&& other) + : f_(other.f_), call_(other.call_), delete_(other.delete_) { + other.delete_ = nullptr; + } + UntypedFunction& operator=(UntypedFunction&& other) { + if (delete_) { + delete_(&f_); + } + f_ = other.f_; + call_ = other.call_; + delete_ = other.delete_; + other.delete_ = nullptr; + return *this; + } + + ~UntypedFunction() { + if (delete_) { + delete_(&f_); + } + } + + friend void swap(UntypedFunction& a, UntypedFunction& b) { + using std::swap; + swap(a.f_, b.f_); + swap(a.call_, b.call_); + swap(a.delete_, b.delete_); + } + + // Returns true if we have a function, false if we don't (i.e., we're null). + explicit operator bool() const { return call_ != nullptr; } + + template <typename Signature, typename... ArgT> + typename webrtc_function_impl::CallHelpers<Signature>::return_type Call( + ArgT&&... args) { + return reinterpret_cast< + typename webrtc_function_impl::CallHelpers<Signature>::function_type*>( + call_)(&f_, std::forward<ArgT>(args)...); + } + + // Returns true iff we don't need to call a destructor. This is guaranteed + // to hold for a moved-from object. + bool IsTriviallyDestructible() { return delete_ == nullptr; } + + private: + UntypedFunction(webrtc_function_impl::VoidUnion f, + webrtc_function_impl::FunVoid* call, + void (*del)(webrtc_function_impl::VoidUnion*)) + : f_(f), call_(call), delete_(del) {} + + // The callable thing, or a pointer to it. + webrtc_function_impl::VoidUnion f_; + + // Pointer to a dispatch function that knows the type of the callable thing + // that's stored in f_, and how to call it. An UntypedFunction object is empty + // (null) iff call_ is null. + webrtc_function_impl::FunVoid* call_; + + // Pointer to a function that knows how to delete the callable thing that's + // stored in f_. Null if `f_` is trivially deletable. + void (*delete_)(webrtc_function_impl::VoidUnion*); +}; + +} // namespace webrtc + +#endif // RTC_BASE_UNTYPED_FUNCTION_H_ |