/* * Copyright (c) 2023-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef HISTREAMER_PLUGIN_COMMON_ANY_H #define HISTREAMER_PLUGIN_COMMON_ANY_H #ifndef HST_ANY_WITH_RTTI #ifndef HST_ANY_WITH_NO_RTTI #define HST_ANY_WITH_NO_RTTI #endif #else #ifdef HST_ANY_WITH_NO_RTTI #undef HST_ANY_WITH_NO_RTTI #endif #endif #ifndef MEDIA_NO_OHOS #ifndef MEDIA_OHOS #define MEDIA_OHOS #endif #else #ifdef MEDIA_OHOS #undef MEDIA_OHOS #endif #endif #if defined(__clang__) || defined(__GNUC__) #define CPP_STANDARD __cplusplus #elif defined(_MSC_VER) #define CPP_STANDARD _MSVC_LANG #endif #if CPP_STANDARD >= 201103L #include #include #include "cpp_ext/type_cast_ext.h" #include "securec.h" #include #include "message_parcel.h" namespace { #if CPP_STANDARD < 201402L template using decay_t = typename std::decay::type; template using enable_if_t = typename std::enable_if::type; template using conditional_t = typename std::conditional::type; template using remove_cv_t = typename std::remove_cv::type; template using remove_reference_t = typename std::remove_reference::type; #else using std::decay_t; using std::enable_if_t; using std::conditional_t; using std::remove_cv_t; using std::remove_reference_t; #endif constexpr size_t STACK_STORAGE_SIZE = 2 * sizeof(void*); // NOLINT: global var template struct IsTrivialStackStorable { static constexpr bool value = alignof(T) <= alignof(max_align_t) && std::is_trivially_copyable::value && sizeof(T) <= STACK_STORAGE_SIZE; }; template struct IsStackStorable { static constexpr bool value = alignof(T) <= alignof(max_align_t) && std::is_nothrow_move_constructible::value && sizeof(T) <= STACK_STORAGE_SIZE; }; template struct IsValidCast { static constexpr bool value = std::is_reference::value || std::is_copy_constructible::value; }; } // namespace namespace OHOS { namespace Media { enum struct AnyValueType : int32_t { INVALID_TYPE = 1, BOOL, INT8_T, UINT8_T, INT32_T, UINT32_T, INT64_T, UINT64_T, FLOAT, DOUBLE, VECTOR_UINT8, VECTOR_UINT32, STRING }; /** * @brief BadAnyCast exception, which is thrown when error occurs in AnyCast * * @since 1.0 * @version 1.0 */ class BadAnyCast : public std::bad_cast { public: const char* what() const noexcept override { return "bad any cast"; } }; class Any; template inline typename std::enable_if::value, bool>::type MakeAnyFromParcel(Any& value, MessageParcel& parcel); template inline typename std::enable_if::value, bool>::type MakeAnyFromParcel(Any& value, MessageParcel& parcel) { (void)value; (void)parcel; // current only support enum from parcel return false; } template inline typename std::enable_if::value, bool>::type WriteValueToParcel(const T &value, MessageParcel &parcel) { parcel.WriteInt32(static_cast(value)); return true; } template inline typename std::enable_if::value, bool>::type WriteValueToParcel(const T &value, MessageParcel &parcel) { (void)value; (void)parcel; // current only support write enum to parcel return false; } template inline typename std::enable_if::value, bool>::type WriteValueToParcelInt64(const T &value, MessageParcel &parcel) { parcel.WriteInt64(static_cast(value)); return true; } template inline typename std::enable_if::value, bool>::type WriteValueToParcelInt64(const T &value, MessageParcel &parcel) { (void)value; (void)parcel; // current only support write enum to parcel return false; } /** * @brief This class describes a type-safe container for arbitrary type values which are copy constructible. * * @since 1.0 * @version 1.0 */ class Any final { public: constexpr Any() noexcept { } __attribute__((no_sanitize("cfi"))) Any(const Any &other) : functionTable_(other.functionTable_) { if (other.HasValue()) { functionTable_->copy(storage_, other.storage_); } } __attribute__((no_sanitize("cfi"))) Any(Any &&other) noexcept : functionTable_(other.functionTable_) { if (other.HasValue()) { functionTable_->move(storage_, other.storage_); other.functionTable_ = nullptr; } } /** * constructor from right reference value with type of ValueType. * * @tparam Type ValueType is not the same as Any itself. The decay type of ValueType must be copy constructible. * @param value content */ template , Any>::value && std::is_copy_constructible>::value, bool> = true> Any(ValueType&& value) // NOLINT: explicit { DoEmplace>(std::forward(value)); } Any& operator=(const Any& other) { *this = Any(other); return *this; } Any& operator=(Any&& other) noexcept { Reset(); MoveFrom(std::forward(other)); return *this; } /** * Assigns contents to Any. * * @tparam ValueType Type ValueType is not the same as Any itself. The decay type of ValueType must be copy * constructible. * @param value content * @return */ template , Any>::value && std::is_copy_constructible>::value, bool> = true> Any& operator=(ValueType&& value) { *this = Any(std::forward(value)); return *this; } /** * Get TypeName From function info. * @return Name of Type T */ template static constexpr std::string_view GetTypeName() noexcept { const char* functionInfo = __PRETTY_FUNCTION__ ; return GetTypeNameFromFunctionInfo(functionInfo); } template static bool IsSameTypeWith(const Any& other) noexcept { #ifndef HST_ANY_WITH_NO_RTTI return other.SameTypeWith(typeid(T)); #else return other.SameTypeWith(Any::GetTypeName()); #endif } ~Any() { Reset(); } /** * Emplace one content with type of ValueType into object. The content is constructed by args. * * @tparam ValueType The decay type of ValueType must be constructible from args and copy constructible. * @tparam Args args type * @param args args * @return content with type of decay ValueType */ template , Args...>::value && std::is_copy_constructible>::value, bool> = true> decay_t& Emplace(Args&&... args) { Reset(); return DoEmplace>(std::forward(args)...); } /** * Emplace one content with type of ValueType into object. The content is constructed by il and args. * * @tparam ValueType type of conetent. The decay type of ValueType must be constructible from il and args and copy * constructible * @tparam U type of initializer list. * @tparam Args type of other args * @param il initializer list * @param args args * @return content with type of decay ValueType */ template , std::initializer_list&, Args...>::value && std::is_copy_constructible>::value, bool> = true> decay_t& Emplace(std::initializer_list il, Args&&... args) { Reset(); return DoEmplace>(il, std::forward(args)...); } /** * Destroy the inner content if exists. */ void __attribute__((no_sanitize("cfi"))) Reset() noexcept { if (HasValue()) { functionTable_->destroy(storage_); storage_.trivialStack_.fill(0); } functionTable_ = nullptr; } /** * swap contents of two any objects * * @param other object to swap with */ void Swap(Any& other) noexcept { Any tmp(std::move(*this)); *this = std::move(other); other = std::move(tmp); } /** * Checks whether the object has one content. * * @return true if object has one content, otherwise false. */ bool HasValue() const noexcept { return IsFunctionTableValid(); } #ifndef HST_ANY_WITH_NO_RTTI /** * Get tye type_info of object * * @return type info of object */ const std::type_info& Type() const noexcept { if (!HasValue()) { return typeid(void); } return functionTable_->type(); } #else std::string_view __attribute__((no_sanitize("cfi"))) TypeName() const noexcept { if (!HasValue()) { return "empty"; // no value } return functionTable_->type_name(); } bool __attribute__((no_sanitize("cfi"))) ToParcel(MessageParcel &parcel) const noexcept { if (!HasValue()) { return false; // no value } return functionTable_->toParcel(this, parcel); } bool __attribute__((no_sanitize("cfi"))) FromParcel(MessageParcel &parcel) const noexcept { return functionTable_->fromParcel(const_cast(this), parcel); } #endif #ifndef HST_ANY_WITH_NO_RTTI bool SameTypeWith(const std::type_info& otherInfo) const noexcept { if (functionTable_ == nullptr) { return false; } return IsSameType(functionTable_->type(), otherInfo); } #else bool __attribute__((no_sanitize("cfi"))) SameTypeWith(std::string_view otherTypeName) const noexcept { if (functionTable_ == nullptr) { return false; } return IsSameType(functionTable_->type_name(), otherTypeName); } #endif bool __attribute__((no_sanitize("cfi"))) SameTypeWith(const Any &other) const noexcept { #ifndef HST_ANY_WITH_NO_RTTI return IsSameType(functionTable_->type(), other.Type()); #else return IsSameType(functionTable_->type_name(), other.TypeName()); #endif } private: template friend const T* AnyCast(const Any* operand) noexcept; template friend T* AnyCast(Any* operand) noexcept; template friend bool AnyCast(const Any* operand, T& value) noexcept; union Storage { using Stack = std::aligned_storage::value>::type; using Heap = void*; std::array trivialStack_; Stack nonTrivialStack_; Heap heap_; }; struct FunctionTable { #ifndef HST_ANY_WITH_NO_RTTI const std::type_info& (*type)() noexcept; #else std::string_view (*type_name)() noexcept; bool (*toParcel)(const Any *operand, MessageParcel& parcel) noexcept; bool (*fromParcel)(Any *operand, MessageParcel& parcel) noexcept; #endif void (*destroy)(Storage&) noexcept; void (*copy)(Storage&, const Storage&) noexcept; void (*move)(Storage&, Storage&) noexcept; void* (*getPtr)(Storage&) noexcept; const void* (*getConstPtr)(const Storage&) noexcept; }; static bool BaseTypesToParcel(const Any *operand, MessageParcel& parcel) noexcept; // returnValue : 0 -- success; 1 -- retry enum; 2 -- failed no retry static int BaseTypesFromParcel(Any *operand, MessageParcel& parcel) noexcept; static std::string_view GetTypeNameFromFunctionInfo(const char* functionInfo) noexcept; template struct TrivialStackFunctionTable { #ifndef HST_ANY_WITH_NO_RTTI static const std::type_info& Type() noexcept { return typeid(T); } #else static std::string_view TypeName() noexcept { return GetTypeName(); } // Only support parcel enum value to int32_t except base types static bool ToParcel(const Any *operand, MessageParcel& parcel) noexcept { if (BaseTypesToParcel(operand, parcel)) { return true; } if (sizeof(T) > sizeof(int64_t)) { // Only support enum return false; } if (sizeof(T) == sizeof(int64_t)) { // support Int64 enum T value; if (AnyCast(operand, value)) { WriteValueToParcelInt64(value, parcel); return true; } return false; } T value; if (AnyCast(operand, value)) { WriteValueToParcel(value, parcel); return true; } return false; } static bool FromParcel(Any *operand, MessageParcel& parcel) noexcept { int ret = BaseTypesFromParcel(operand, parcel); if (ret == 0) { return true; } MakeAnyFromParcel(*operand, parcel); return true; } #endif static void Destroy(Storage& storage) noexcept { reinterpret_cast(storage.trivialStack_.data())->~T(); } static void Copy(Storage& dest, const Storage& source) noexcept { // memcpy_s will always success in this function (void)memcpy_s(GetPtr(dest), sizeof(Storage), GetConstPtr(source), sizeof(Storage)); } static void Move(Storage& dest, Storage& source) noexcept { Copy(dest, source); source.trivialStack_.fill(0); } static const void* GetConstPtr(const Storage& storage) noexcept { return reinterpret_cast(storage.trivialStack_.data()); } static void* GetPtr(Storage& storage) noexcept { return reinterpret_cast(storage.trivialStack_.data()); } }; template struct StackFunctionTable { #ifndef HST_ANY_WITH_NO_RTTI static const std::type_info& Type() noexcept { return typeid(T); } #else static std::string_view TypeName() noexcept { return GetTypeName(); } // Only support parcel enum value to int32_t except base types static bool ToParcel(const Any *operand, MessageParcel& parcel) noexcept { if (BaseTypesToParcel(operand, parcel)) { return true; } if (sizeof(T) > sizeof(int64_t)) { // Only support enum return false; } if (sizeof(T) == sizeof(int64_t)) { // support Int64 enum T value; if (AnyCast(operand, value)) { WriteValueToParcelInt64(value, parcel); return true; } return false; } T value; if (AnyCast(operand, value)) { WriteValueToParcel(value, parcel); return true; } return false; } static bool FromParcel(Any *operand, MessageParcel& parcel) noexcept { int ret = BaseTypesFromParcel(operand, parcel); if (ret == 0) { return true; } MakeAnyFromParcel(*operand, parcel); return true; } #endif static void Destroy(Storage& storage) noexcept { reinterpret_cast(GetPtr(storage))->~T(); // NOLINT: cast } static void Copy(Storage& dest, const Storage& source) noexcept { // NOLINTNEXTLINE: reinterpret_cast new (reinterpret_cast(GetPtr(dest))) T(*reinterpret_cast(GetConstPtr(source))); } static void Move(Storage& dest, Storage& source) noexcept { // NOLINTNEXTLINE: reinterpret_cast new (reinterpret_cast(GetPtr(dest))) T(std::move(*reinterpret_cast(GetPtr(source)))); Destroy(source); } static const void* GetConstPtr(const Storage& storage) noexcept { return reinterpret_cast(&storage.nonTrivialStack_); } static void* GetPtr(Storage& storage) noexcept { return reinterpret_cast(&storage.nonTrivialStack_); } }; template struct HeapFunctionTable { #ifndef HST_ANY_WITH_NO_RTTI static const std::type_info& Type() noexcept { return typeid(T); } #else static std::string_view TypeName() noexcept { return GetTypeName(); } // Only support parcel enum value to int32_t except base types static bool ToParcel(const Any *operand, MessageParcel& parcel) noexcept { if (BaseTypesToParcel(operand, parcel)) { return true; } if (sizeof(T) > sizeof(int64_t)) { // Only support enum return false; } if (sizeof(T) == sizeof(int64_t)) { // support Int64 enum T value; if (AnyCast(operand, value)) { WriteValueToParcelInt64(value, parcel); return true; } return false; } T value; if (AnyCast(operand, value)) { WriteValueToParcel(value, parcel); return true; } return false; } static bool FromParcel(Any *operand, MessageParcel& parcel) noexcept { int ret = BaseTypesFromParcel(operand, parcel); if (ret == 0) { return true; } MakeAnyFromParcel(*operand, parcel); return true; } #endif static void Destroy(Storage& storage) noexcept { delete reinterpret_cast(storage.heap_); // NOLINT: cast storage.heap_ = nullptr; } static void Copy(Storage& dest, const Storage& source) noexcept { dest.heap_ = new T(*reinterpret_cast(source.heap_)); // NOLINT: cast } static void Move(Storage& dest, Storage& source) noexcept { dest.heap_ = source.heap_; source.heap_ = nullptr; } static const void* GetConstPtr(const Storage& storage) noexcept { return storage.heap_; } static void* GetPtr(Storage& storage) noexcept { return storage.heap_; } }; template static FunctionTable* GetFunctionTable() { using DecayedValueType = decay_t; using DetailFunctionTable = conditional_t::value, TrivialStackFunctionTable, conditional_t::value, StackFunctionTable, HeapFunctionTable>>; static FunctionTable table = { #ifndef HST_ANY_WITH_NO_RTTI .type = DetailFunctionTable::Type, #else .type_name = DetailFunctionTable::TypeName, .toParcel = DetailFunctionTable::ToParcel, .fromParcel = DetailFunctionTable::FromParcel, #endif .destroy = DetailFunctionTable::Destroy, .copy = DetailFunctionTable::Copy, .move = DetailFunctionTable::Move, .getPtr = DetailFunctionTable::GetPtr, .getConstPtr = DetailFunctionTable::GetConstPtr, }; return &table; } bool IsFunctionTableValid() const noexcept { return functionTable_ != nullptr; } template DecayedValueType &__attribute__((no_sanitize("cfi"))) DoEmplace(Args &&...args) { functionTable_ = GetFunctionTable(); DecayedValueType* ptr = nullptr; if (IsTrivialStackStorable::value || IsStackStorable::value) { ptr = reinterpret_cast(functionTable_->getPtr(storage_)); new (ptr) DecayedValueType(std::forward(args)...); } else { storage_.heap_ = new DecayedValueType(std::forward(args)...); ptr = reinterpret_cast(storage_.heap_); } return *ptr; } void __attribute__((no_sanitize("cfi"))) MoveFrom(Any &&other) noexcept { if (other.HasValue()) { functionTable_ = other.functionTable_; functionTable_->move(storage_, other.storage_); other.Reset(); } } template ValueType* Cast() noexcept { using DecayedValueType = decay_t; if (!IsFunctionTableValid()) { return nullptr; } #ifndef HST_ANY_WITH_NO_RTTI if (!SameTypeWith(typeid(DecayedValueType))) { #else if (!SameTypeWith(Any::GetTypeName())) { #endif return nullptr; } return IsTrivialStackStorable::value ? reinterpret_cast(storage_.trivialStack_.data()) : (IsStackStorable::value ? reinterpret_cast(&storage_.nonTrivialStack_) : reinterpret_cast(storage_.heap_)); } template const ValueType* Cast() const noexcept { using DecayedValueType = decay_t; if (!IsFunctionTableValid()) { return nullptr; } #ifndef HST_ANY_WITH_NO_RTTI if (!SameTypeWith(typeid(DecayedValueType))) { #else if (!SameTypeWith(Any::GetTypeName())) { #endif return nullptr; } return IsTrivialStackStorable::value ? reinterpret_cast(storage_.trivialStack_.data()) : (IsStackStorable::value ? reinterpret_cast(&storage_.nonTrivialStack_) : reinterpret_cast(storage_.heap_)); } private: Storage storage_ {}; FunctionTable* functionTable_ {nullptr}; }; /** * cast one Any pointer into ValueType pointer * * @tparam ValueType target value type * @param operand any object * @return nullptr if type mismatch, operand is nullptr, or valueType is function/array. Otherwise, a pointer to the * const value contained by operand. */ template const ValueType* AnyCast(const Any* operand) noexcept { static_assert(!std::is_void::value, "ValueType of any_cast must not be void"); if (std::is_function::value || std::is_array::value || operand == nullptr) { return nullptr; } return operand->Cast(); } /** * cast one Any pointer into ValueType object * * @tparam ValueType target value type * @param operand any object * @param value ValueType * @return false if type mismatch, operand is nullptr, or valueType is function/array. Otherwise, true to the * value contained by operand. */ template bool AnyCast(const Any* operand, ValueType& value) noexcept { static_assert(!std::is_void::value, "ValueType of any_cast must not be void"); if (std::is_function::value || std::is_array::value || operand == nullptr) { return false; } #ifndef HST_ANY_WITH_NO_RTTI if (!operand->SameTypeWith(typeid(ValueType))) { #else if (!operand->SameTypeWith(Any::GetTypeName())) { #endif return false; } else { auto casted_value = operand->Cast(); if (casted_value != nullptr) { value = *casted_value; return true; } return false; } } /** * cast one Any pointer into ValueType pointer * * @tparam ValueType target value type * @param operand any object * @return nullptr if type mismatch, operand is nullptr, or valueType is function/array. Otherwise, a pointer to the * value contained by operand. */ template ValueType* AnyCast(Any* operand) noexcept { static_assert(!std::is_void::value, "ValueType of any_cast must not be void"); if (std::is_function::value || std::is_array::value || operand == nullptr) { return nullptr; } return operand->Cast(); } /** * cast one Any object into ValueType object * * @tparam ValueType target value type. It must match both conditions: * 1. ValueType must be reference or constructible * 2. Let U be remove_cv_t>, then std::is_constructible must be true * @param operand any object * @return throws BadAnyCast exception if type mismatch, operand is nullptr, or valueType is function/array. Otherwise, * one object of ValueType contained in Any. */ template ValueType AnyCast(const Any& other) { using U = remove_cv_t>; static_assert(IsValidCast::value, "template argument must be a reference or has copy constructors"); static_assert(std::is_constructible::value, "any_cast(const any&) requires ValueType constructable from const " "remove_cv_t>&"); auto ptr = AnyCast(&other); return static_cast(*ptr); } /** * cast one Any object into ValueType object * * @tparam ValueType target value type. It must match both conditions: * 1. ValueType must be reference or constructible * 2. Let U be remove_cv_t>, then std::is_constructible must be true * @param operand any object * @return throws BadAnyCast exception if type mismatch, operand is nullptr, or valueType is function/array. Otherwise, * one object of ValueType contained in Any. */ template ValueType AnyCast(Any& other) { using U = remove_cv_t>; static_assert(IsValidCast::value, "template argument must be a reference or has copy constructors"); static_assert(std::is_constructible::value, "any_cast(const any&) requires ValueType constructable from " "remove_cv_t>&"); auto ptr = AnyCast(&other); return static_cast(*ptr); } /** * cast one Any object into ValueType object * * @tparam ValueType target value type. It must match both conditions: * 1. ValueType must be reference or constructible * 2. Let U be remove_cv_t>, then std::is_constructible must be true * @param operand any object * @return throws BadAnyCast exception if type mismatch, operand is nullptr, or valueType is function/array. Otherwise, * one object of ValueType contained in Any. */ template ValueType AnyCast(Any&& other) { using U = remove_cv_t>; static_assert(IsValidCast::value, "template argument must be a reference or has copy constructors"); static_assert(std::is_constructible::value, "any_cast(const any&) requires ValueType constructable from " "remove_cv_t>"); auto ptr = AnyCast(&other); return static_cast(std::move(*ptr)); } /** * Constructs Any object, whose content is constructed by args. The content type is T. * * @tparam T type of Any's content * @tparam Args type of args * @param args args used to construct the content * @return Any object */ template Any MakeAny(Args&&... args) { Any tmp; tmp.Emplace(std::forward(args)...); return tmp; } /** * Constructs Any object, whose content is constructed by il and args. The content type is T. * * @tparam T type of Any's content * @tparam U type of initializer list * @tparam Args type of args * @param il initializer list * @param args args * @return Any object */ template Any MakeAny(std::initializer_list il, Args&&... args) { Any tmp; tmp.Emplace(il, std::forward(args)...); return tmp; } template inline typename std::enable_if::value, bool>::type MakeAnyFromParcel(Any& value, MessageParcel & parcel) { if (sizeof(T) == sizeof(int64_t)) { value.Emplace(static_cast(parcel.ReadInt64())); return true; } value.Emplace(static_cast(parcel.ReadInt32())); return true; } } // namespace Media } // namespace OHOS #endif namespace std { inline void swap(OHOS::Media::Any& lhs, OHOS::Media::Any& rhs) noexcept { lhs.Swap(rhs); } } // namespace std #endif // HISTREAMER_PLUGIN_COMMON_ANY_H