| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_DEVICES_SYSMEM_DRIVERS_SYSMEM_UTILS_H_ |
| #define SRC_DEVICES_SYSMEM_DRIVERS_SYSMEM_UTILS_H_ |
| |
| #include <fidl/fuchsia.sysmem/cpp/fidl.h> |
| #include <fidl/fuchsia.sysmem2/cpp/fidl.h> |
| #include <lib/sysmem-version/sysmem-version.h> |
| |
| #include <type_traits> |
| |
| #include <safemath/safe_conversions.h> |
| |
| bool IsWriteUsage(const fuchsia_sysmem2::BufferUsage& buffer_usage); |
| |
| bool IsCpuUsage(const fuchsia_sysmem2::BufferUsage& buffer_usage); |
| |
| bool IsAnyUsage(const fuchsia_sysmem2::BufferUsage& buffer_usage); |
| |
| namespace internal { |
| |
| // Partial template specialization only works on classes / structs not functions, so impl is a |
| // struct. |
| // |
| // Base case lacks "do_cast" so intentionally won't compile. |
| template <typename TypeOut, typename TypeIn, typename Enable = void> |
| struct SafeCastImpl {}; |
| template <typename TypeOut, typename TypeIn> |
| struct SafeCastImpl< |
| TypeOut, TypeIn, |
| std::enable_if_t<std::is_arithmetic_v<TypeOut> && std::is_arithmetic_v<TypeIn>>> { |
| static constexpr TypeOut do_cast(TypeIn in) { return safemath::checked_cast<TypeOut>(in); } |
| }; |
| template <typename TypeOut, typename TypeIn> |
| struct SafeCastImpl< |
| TypeOut, TypeIn, |
| std::enable_if_t<!std::is_arithmetic_v<TypeOut> || !std::is_arithmetic_v<TypeIn>>> { |
| // We explicitly require any casting to/from fidl enum to be converting from/to the exact |
| // underlying type, respectively. Any additional conversion on top of that can be achieved with a |
| // separate safe_cast<>(). |
| static_assert(!sysmem::IsFidlEnum_v<TypeOut> || |
| std::is_same_v<TypeIn, sysmem::FidlUnderlyingTypeOrType_t<TypeOut>>); |
| static_assert(!sysmem::IsFidlEnum_v<TypeIn> || |
| std::is_same_v<TypeOut, sysmem::FidlUnderlyingTypeOrType_t<TypeIn>>); |
| |
| static constexpr TypeOut do_cast(TypeIn in) { |
| if constexpr (sysmem::IsFidlEnum_v<TypeOut> || sysmem::IsFidlEnum_v<TypeIn>) { |
| // LLCPP natural enums can be converted to/from their underlying type, but only explicitly. |
| // Thanks to the static_assert()s above, we know that this static_cast<>() won't be narrowing |
| // (or widening). |
| return static_cast<TypeOut>(in); |
| } else { |
| // The -Wno-conversion cflags can only catch narrowing if the conversion is an implicit |
| // conversion, so we need this to be an implicit conversion to cause any narrowing under this |
| // template specialization to be a compile warning (treated as error). |
| // |
| // If hitting a compile warning about narrowing here, ensure that all narrowing safe_cast<>()s |
| // are done with the source and destination types both is_arithmetic_v<> true. This way, all |
| // narrowing goes through safemath::checked_cast<>() above. In other words, avoid doing both |
| // a narrowing and a conversion to/from non-is_arithmetic_v<> in the same safe_cast<>(). |
| // Instead, do those two conversion steps separately, with separate safe_cast<>()s. |
| return in; |
| } |
| } |
| }; |
| |
| } // namespace internal |
| |
| template <typename TypeOut, typename TypeIn> |
| constexpr TypeOut safe_cast(TypeIn in) { |
| return internal::SafeCastImpl<TypeOut, TypeIn>::do_cast(in); |
| } |
| |
| // debug_safe_cast<>() is _only_ for use in situations where we are very sure that |
| // safemath::checked_cast<>() would not ever complain. The "safe"-ness is disabled in --release to |
| // avoid completely unnecessary checking in situations where the checking would be both very |
| // unnecessary and at least plausibly a performance concern. Use safe_cast<>() instead in nearly |
| // all situations. Buffer pattern filling is an example where debug_safe_cast<>() can be |
| // appropriate. |
| #if ZX_DEBUG_ASSERT_IMPLEMENTED |
| // This is "safe" in debug. This way we can double-check when not --release that we're not messing |
| // up in a situation where we are "very sure" won't annoy safemath::checked_cast<>(). |
| template <typename TypeOut, typename TypeIn> |
| constexpr TypeOut debug_safe_cast(TypeIn in) { |
| return internal::SafeCastImpl<TypeOut, TypeIn>::do_cast(in); |
| } |
| #else |
| // Not "safe" in --release, but we only use debug_safe_cast<> when we're quite sure |
| // safemath::checked_cast<> can't possibly complain. |
| template <typename TypeOut, typename TypeIn> |
| constexpr TypeOut debug_safe_cast(TypeIn in) { |
| return static_cast<TypeOut>(in); |
| } |
| #endif |
| |
| // Ensure we get/permit a constexpr cast when possible. |
| static_assert(4u == safe_cast<uint32_t>(4ull)); |
| |
| template <typename FidlType, typename enable = void> |
| class IsNaturalFidlTable : public std::false_type {}; |
| template <typename FidlType> |
| class IsNaturalFidlTable< |
| FidlType, std::enable_if_t<fidl::IsTable<FidlType>::value && !fidl::IsWire<FidlType>::value>> |
| : public std::true_type {}; |
| |
| static_assert(IsNaturalFidlTable<fuchsia_sysmem2::BufferUsage>::value); |
| static_assert(!IsNaturalFidlTable<fuchsia_sysmem2::wire::BufferUsage>::value); |
| static_assert(!IsNaturalFidlTable<uint32_t>::value); |
| static_assert(!IsNaturalFidlTable<std::vector<fuchsia_sysmem2::BufferUsage>>::value); |
| static_assert(!IsNaturalFidlTable<std::vector<fuchsia_sysmem::BufferUsage>>::value); |
| static_assert(!IsNaturalFidlTable<fidl::VectorView<fuchsia_sysmem::BufferUsage>>::value); |
| static_assert(!IsNaturalFidlTable<fidl::VectorView<fuchsia_sysmem2::BufferUsage>>::value); |
| static_assert(!IsNaturalFidlTable<std::string>::value); |
| static_assert(!IsNaturalFidlTable<fidl::StringView>::value); |
| |
| template <typename T> |
| void ignore_result(const T& to_ignore) {} |
| |
| template <typename TypeOut, typename TypeIn> |
| TypeOut static_down_cast(TypeIn in) { |
| TypeOut result = static_cast<TypeOut>(in); |
| // This doesn't guarantee that |in| is actually an instance of sub-class TypeOut, but it does at |
| // least check that TypeOut is a sub-class of TypeIn. |
| static_assert(std::is_base_of_v<std::remove_reference_t<decltype(*in)>, |
| std::remove_reference_t<decltype(*result)>>); |
| return result; |
| } |
| |
| #endif // SRC_DEVICES_SYSMEM_DRIVERS_SYSMEM_UTILS_H_ |