| // 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_SYSMEM_SERVER_UTILS_H_ | |
| #define SRC_SYSMEM_SERVER_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> | |
| #include <safemath/safe_math.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); | |
| bool IsNoneUsagePermitAllocation(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; | |
| } | |
| // Leaves the source optional with has_value() false instead of leaving source.has_value() true with | |
| // a moved-out contained value. This is a nice way of dealing with an optional that's getting moved | |
| // into a lambda's captures. | |
| template <typename T> | |
| std::optional<T> TakeOptional(std::optional<T>& source) { | |
| auto tmp = std::move(source); | |
| source.reset(); | |
| return tmp; | |
| } | |
| namespace sysmem_service { | |
| template <typename T, typename U> | |
| auto CheckRoundDown(T val, U multiple) { | |
| using safemath::CheckDiv; | |
| using safemath::CheckMul; | |
| return CheckMul(CheckDiv(val, multiple), multiple); | |
| } | |
| template <typename T, typename U> | |
| auto CheckRoundUp(T val, U multiple) { | |
| using safemath::CheckAdd; | |
| using safemath::CheckDiv; | |
| using safemath::CheckMin; | |
| using safemath::CheckMod; | |
| using safemath::CheckMul; | |
| auto div = CheckDiv(val, multiple); | |
| auto mod = CheckMod(val, multiple); | |
| auto has_remainder = CheckMin(mod, decltype(div)(1)); | |
| return CheckMul(CheckAdd(div, has_remainder), multiple); | |
| } | |
| } // namespace sysmem_service | |
| #endif // SRC_SYSMEM_SERVER_UTILS_H_ |