/* * Copyright 2021 The Android Open Source Project * * 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. */ #pragma once #include #include #include namespace android::ftl { enum class CastSafety { kSafe, kUnderflow, kOverflow }; // Returns whether static_cast(v) is safe, or would result in underflow or overflow. // // static_assert(ftl::cast_safety(-1) == ftl::CastSafety::kUnderflow); // static_assert(ftl::cast_safety(128u) == ftl::CastSafety::kOverflow); // // static_assert(ftl::cast_safety(-.1f) == ftl::CastSafety::kUnderflow); // static_assert(ftl::cast_safety(static_cast(INT32_MAX)) == // ftl::CastSafety::kOverflow); // // static_assert(ftl::cast_safety(-DBL_MAX) == ftl::CastSafety::kUnderflow); // template constexpr CastSafety cast_safety(T v) { static_assert(std::is_arithmetic_v); static_assert(std::is_arithmetic_v); constexpr bool kFromSigned = std::is_signed_v; constexpr bool kToSigned = std::is_signed_v; using details::max_exponent; // If the R range contains the T range, then casting is always safe. if constexpr ((kFromSigned == kToSigned && max_exponent >= max_exponent) || (!kFromSigned && kToSigned && max_exponent > max_exponent)) { return CastSafety::kSafe; } using C = std::common_type_t; if constexpr (kFromSigned) { using L = details::safe_limits; if constexpr (kToSigned) { // Signed to signed. if (v < L::lowest()) return CastSafety::kUnderflow; return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow; } else { // Signed to unsigned. if (v < 0) return CastSafety::kUnderflow; return static_cast(v) <= static_cast(L::max()) ? CastSafety::kSafe : CastSafety::kOverflow; } } else { using L = std::numeric_limits; if constexpr (kToSigned) { // Unsigned to signed. return static_cast(v) <= static_cast(L::max()) ? CastSafety::kSafe : CastSafety::kOverflow; } else { // Unsigned to unsigned. return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow; } } } } // namespace android::ftl