1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker * Copyright 2021 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker *
4*38e8c45fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker *
8*38e8c45fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker *
10*38e8c45fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker */
16*38e8c45fSAndroid Build Coastguard Worker
17*38e8c45fSAndroid Build Coastguard Worker #pragma once
18*38e8c45fSAndroid Build Coastguard Worker
19*38e8c45fSAndroid Build Coastguard Worker #include <limits>
20*38e8c45fSAndroid Build Coastguard Worker #include <type_traits>
21*38e8c45fSAndroid Build Coastguard Worker
22*38e8c45fSAndroid Build Coastguard Worker #include <ftl/details/cast.h>
23*38e8c45fSAndroid Build Coastguard Worker
24*38e8c45fSAndroid Build Coastguard Worker namespace android::ftl {
25*38e8c45fSAndroid Build Coastguard Worker
26*38e8c45fSAndroid Build Coastguard Worker enum class CastSafety { kSafe, kUnderflow, kOverflow };
27*38e8c45fSAndroid Build Coastguard Worker
28*38e8c45fSAndroid Build Coastguard Worker // Returns whether static_cast<R>(v) is safe, or would result in underflow or overflow.
29*38e8c45fSAndroid Build Coastguard Worker //
30*38e8c45fSAndroid Build Coastguard Worker // static_assert(ftl::cast_safety<uint8_t>(-1) == ftl::CastSafety::kUnderflow);
31*38e8c45fSAndroid Build Coastguard Worker // static_assert(ftl::cast_safety<int8_t>(128u) == ftl::CastSafety::kOverflow);
32*38e8c45fSAndroid Build Coastguard Worker //
33*38e8c45fSAndroid Build Coastguard Worker // static_assert(ftl::cast_safety<uint32_t>(-.1f) == ftl::CastSafety::kUnderflow);
34*38e8c45fSAndroid Build Coastguard Worker // static_assert(ftl::cast_safety<int32_t>(static_cast<float>(INT32_MAX)) ==
35*38e8c45fSAndroid Build Coastguard Worker // ftl::CastSafety::kOverflow);
36*38e8c45fSAndroid Build Coastguard Worker //
37*38e8c45fSAndroid Build Coastguard Worker // static_assert(ftl::cast_safety<float>(-DBL_MAX) == ftl::CastSafety::kUnderflow);
38*38e8c45fSAndroid Build Coastguard Worker //
39*38e8c45fSAndroid Build Coastguard Worker template <typename R, typename T>
cast_safety(T v)40*38e8c45fSAndroid Build Coastguard Worker constexpr CastSafety cast_safety(T v) {
41*38e8c45fSAndroid Build Coastguard Worker static_assert(std::is_arithmetic_v<T>);
42*38e8c45fSAndroid Build Coastguard Worker static_assert(std::is_arithmetic_v<R>);
43*38e8c45fSAndroid Build Coastguard Worker
44*38e8c45fSAndroid Build Coastguard Worker constexpr bool kFromSigned = std::is_signed_v<T>;
45*38e8c45fSAndroid Build Coastguard Worker constexpr bool kToSigned = std::is_signed_v<R>;
46*38e8c45fSAndroid Build Coastguard Worker
47*38e8c45fSAndroid Build Coastguard Worker using details::max_exponent;
48*38e8c45fSAndroid Build Coastguard Worker
49*38e8c45fSAndroid Build Coastguard Worker // If the R range contains the T range, then casting is always safe.
50*38e8c45fSAndroid Build Coastguard Worker if constexpr ((kFromSigned == kToSigned && max_exponent<R> >= max_exponent<T>) ||
51*38e8c45fSAndroid Build Coastguard Worker (!kFromSigned && kToSigned && max_exponent<R> > max_exponent<T>)) {
52*38e8c45fSAndroid Build Coastguard Worker return CastSafety::kSafe;
53*38e8c45fSAndroid Build Coastguard Worker }
54*38e8c45fSAndroid Build Coastguard Worker
55*38e8c45fSAndroid Build Coastguard Worker using C = std::common_type_t<R, T>;
56*38e8c45fSAndroid Build Coastguard Worker
57*38e8c45fSAndroid Build Coastguard Worker if constexpr (kFromSigned) {
58*38e8c45fSAndroid Build Coastguard Worker using L = details::safe_limits<R, T>;
59*38e8c45fSAndroid Build Coastguard Worker
60*38e8c45fSAndroid Build Coastguard Worker if constexpr (kToSigned) {
61*38e8c45fSAndroid Build Coastguard Worker // Signed to signed.
62*38e8c45fSAndroid Build Coastguard Worker if (v < L::lowest()) return CastSafety::kUnderflow;
63*38e8c45fSAndroid Build Coastguard Worker return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow;
64*38e8c45fSAndroid Build Coastguard Worker } else {
65*38e8c45fSAndroid Build Coastguard Worker // Signed to unsigned.
66*38e8c45fSAndroid Build Coastguard Worker if (v < 0) return CastSafety::kUnderflow;
67*38e8c45fSAndroid Build Coastguard Worker return static_cast<C>(v) <= static_cast<C>(L::max()) ? CastSafety::kSafe
68*38e8c45fSAndroid Build Coastguard Worker : CastSafety::kOverflow;
69*38e8c45fSAndroid Build Coastguard Worker }
70*38e8c45fSAndroid Build Coastguard Worker } else {
71*38e8c45fSAndroid Build Coastguard Worker using L = std::numeric_limits<R>;
72*38e8c45fSAndroid Build Coastguard Worker
73*38e8c45fSAndroid Build Coastguard Worker if constexpr (kToSigned) {
74*38e8c45fSAndroid Build Coastguard Worker // Unsigned to signed.
75*38e8c45fSAndroid Build Coastguard Worker return static_cast<C>(v) <= static_cast<C>(L::max()) ? CastSafety::kSafe
76*38e8c45fSAndroid Build Coastguard Worker : CastSafety::kOverflow;
77*38e8c45fSAndroid Build Coastguard Worker } else {
78*38e8c45fSAndroid Build Coastguard Worker // Unsigned to unsigned.
79*38e8c45fSAndroid Build Coastguard Worker return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow;
80*38e8c45fSAndroid Build Coastguard Worker }
81*38e8c45fSAndroid Build Coastguard Worker }
82*38e8c45fSAndroid Build Coastguard Worker }
83*38e8c45fSAndroid Build Coastguard Worker
84*38e8c45fSAndroid Build Coastguard Worker } // namespace android::ftl
85