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