1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef PDFIUM_THIRD_PARTY_BASE_SAFE_CONVERSIONS_IMPL_H_ 6 #define PDFIUM_THIRD_PARTY_BASE_SAFE_CONVERSIONS_IMPL_H_ 7 8 #include <limits> 9 10 #include "../macros.h" 11 #include "../template_util.h" 12 13 namespace pdfium { 14 namespace base { 15 namespace internal { 16 17 // The std library doesn't provide a binary max_exponent for integers, however 18 // we can compute one by adding one to the number of non-sign bits. This allows 19 // for accurate range comparisons between floating point and integer types. 20 template <typename NumericType> 21 struct MaxExponent { 22 static const int value = std::numeric_limits<NumericType>::is_iec559 23 ? std::numeric_limits<NumericType>::max_exponent 24 : (sizeof(NumericType) * 8 + 1 - 25 std::numeric_limits<NumericType>::is_signed); 26 }; 27 28 enum IntegerRepresentation { 29 INTEGER_REPRESENTATION_UNSIGNED, 30 INTEGER_REPRESENTATION_SIGNED 31 }; 32 33 // A range for a given nunmeric Src type is contained for a given numeric Dst 34 // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and 35 // numeric_limits<Src>::min() >= numeric_limits<Dst>::min() are true. 36 // We implement this as template specializations rather than simple static 37 // comparisons to ensure type correctness in our comparisons. 38 enum NumericRangeRepresentation { 39 NUMERIC_RANGE_NOT_CONTAINED, 40 NUMERIC_RANGE_CONTAINED 41 }; 42 43 // Helper templates to statically determine if our destination type can contain 44 // maximum and minimum values represented by the source type. 45 46 template < 47 typename Dst, 48 typename Src, 49 IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed 50 ? INTEGER_REPRESENTATION_SIGNED 51 : INTEGER_REPRESENTATION_UNSIGNED, 52 IntegerRepresentation SrcSign = 53 std::numeric_limits<Src>::is_signed 54 ? INTEGER_REPRESENTATION_SIGNED 55 : INTEGER_REPRESENTATION_UNSIGNED > 56 struct StaticDstRangeRelationToSrcRange; 57 58 // Same sign: Dst is guaranteed to contain Src only if its range is equal or 59 // larger. 60 template <typename Dst, typename Src, IntegerRepresentation Sign> 61 struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> { 62 static const NumericRangeRepresentation value = 63 MaxExponent<Dst>::value >= MaxExponent<Src>::value 64 ? NUMERIC_RANGE_CONTAINED 65 : NUMERIC_RANGE_NOT_CONTAINED; 66 }; 67 68 // Unsigned to signed: Dst is guaranteed to contain source only if its range is 69 // larger. 70 template <typename Dst, typename Src> 71 struct StaticDstRangeRelationToSrcRange<Dst, 72 Src, 73 INTEGER_REPRESENTATION_SIGNED, 74 INTEGER_REPRESENTATION_UNSIGNED> { 75 static const NumericRangeRepresentation value = 76 MaxExponent<Dst>::value > MaxExponent<Src>::value 77 ? NUMERIC_RANGE_CONTAINED 78 : NUMERIC_RANGE_NOT_CONTAINED; 79 }; 80 81 // Signed to unsigned: Dst cannot be statically determined to contain Src. 82 template <typename Dst, typename Src> 83 struct StaticDstRangeRelationToSrcRange<Dst, 84 Src, 85 INTEGER_REPRESENTATION_UNSIGNED, 86 INTEGER_REPRESENTATION_SIGNED> { 87 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; 88 }; 89 90 enum RangeConstraint { 91 RANGE_VALID = 0x0, // Value can be represented by the destination type. 92 RANGE_UNDERFLOW = 0x1, // Value would overflow. 93 RANGE_OVERFLOW = 0x2, // Value would underflow. 94 RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). 95 }; 96 97 // Helper function for coercing an int back to a RangeContraint. 98 inline RangeConstraint GetRangeConstraint(int integer_range_constraint) { 99 assert(integer_range_constraint >= RANGE_VALID && 100 integer_range_constraint <= RANGE_INVALID); 101 return static_cast<RangeConstraint>(integer_range_constraint); 102 } 103 104 // This function creates a RangeConstraint from an upper and lower bound 105 // check by taking advantage of the fact that only NaN can be out of range in 106 // both directions at once. 107 inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, 108 bool is_in_lower_bound) { 109 return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) | 110 (is_in_lower_bound ? 0 : RANGE_UNDERFLOW)); 111 } 112 113 template < 114 typename Dst, 115 typename Src, 116 IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed 117 ? INTEGER_REPRESENTATION_SIGNED 118 : INTEGER_REPRESENTATION_UNSIGNED, 119 IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed 120 ? INTEGER_REPRESENTATION_SIGNED 121 : INTEGER_REPRESENTATION_UNSIGNED, 122 NumericRangeRepresentation DstRange = 123 StaticDstRangeRelationToSrcRange<Dst, Src>::value > 124 struct DstRangeRelationToSrcRangeImpl; 125 126 // The following templates are for ranges that must be verified at runtime. We 127 // split it into checks based on signedness to avoid confusing casts and 128 // compiler warnings on signed an unsigned comparisons. 129 130 // Dst range is statically determined to contain Src: Nothing to check. 131 template <typename Dst, 132 typename Src, 133 IntegerRepresentation DstSign, 134 IntegerRepresentation SrcSign> 135 struct DstRangeRelationToSrcRangeImpl<Dst, 136 Src, 137 DstSign, 138 SrcSign, 139 NUMERIC_RANGE_CONTAINED> { 140 static RangeConstraint Check(Src value) { return RANGE_VALID; } 141 }; 142 143 // Signed to signed narrowing: Both the upper and lower boundaries may be 144 // exceeded. 145 template <typename Dst, typename Src> 146 struct DstRangeRelationToSrcRangeImpl<Dst, 147 Src, 148 INTEGER_REPRESENTATION_SIGNED, 149 INTEGER_REPRESENTATION_SIGNED, 150 NUMERIC_RANGE_NOT_CONTAINED> { 151 static RangeConstraint Check(Src value) { 152 return std::numeric_limits<Dst>::is_iec559 153 ? GetRangeConstraint(value <= std::numeric_limits<Dst>::max(), 154 value >= -std::numeric_limits<Dst>::max()) 155 : GetRangeConstraint(value <= std::numeric_limits<Dst>::max(), 156 value >= std::numeric_limits<Dst>::min()); 157 } 158 }; 159 160 // Unsigned to unsigned narrowing: Only the upper boundary can be exceeded. 161 template <typename Dst, typename Src> 162 struct DstRangeRelationToSrcRangeImpl<Dst, 163 Src, 164 INTEGER_REPRESENTATION_UNSIGNED, 165 INTEGER_REPRESENTATION_UNSIGNED, 166 NUMERIC_RANGE_NOT_CONTAINED> { 167 static RangeConstraint Check(Src value) { 168 return GetRangeConstraint(value <= std::numeric_limits<Dst>::max(), true); 169 } 170 }; 171 172 // Unsigned to signed: The upper boundary may be exceeded. 173 template <typename Dst, typename Src> 174 struct DstRangeRelationToSrcRangeImpl<Dst, 175 Src, 176 INTEGER_REPRESENTATION_SIGNED, 177 INTEGER_REPRESENTATION_UNSIGNED, 178 NUMERIC_RANGE_NOT_CONTAINED> { 179 static RangeConstraint Check(Src value) { 180 return sizeof(Dst) > sizeof(Src) 181 ? RANGE_VALID 182 : GetRangeConstraint( 183 value <= static_cast<Src>(std::numeric_limits<Dst>::max()), 184 true); 185 } 186 }; 187 188 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, 189 // and any negative value exceeds the lower boundary. 190 template <typename Dst, typename Src> 191 struct DstRangeRelationToSrcRangeImpl<Dst, 192 Src, 193 INTEGER_REPRESENTATION_UNSIGNED, 194 INTEGER_REPRESENTATION_SIGNED, 195 NUMERIC_RANGE_NOT_CONTAINED> { 196 static RangeConstraint Check(Src value) { 197 return (MaxExponent<Dst>::value >= MaxExponent<Src>::value) 198 ? GetRangeConstraint(true, value >= static_cast<Src>(0)) 199 : GetRangeConstraint( 200 value <= static_cast<Src>(std::numeric_limits<Dst>::max()), 201 value >= static_cast<Src>(0)); 202 } 203 }; 204 205 template <typename Dst, typename Src> 206 inline RangeConstraint DstRangeRelationToSrcRange(Src value) { 207 COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized, 208 argument_must_be_numeric); 209 COMPILE_ASSERT(std::numeric_limits<Dst>::is_specialized, 210 result_must_be_numeric); 211 return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value); 212 } 213 214 } // namespace internal 215 } // namespace base 216 } // namespace pdfium 217 218 #endif // PDFIUM_THIRD_PARTY_BASE_SAFE_CONVERSIONS_IMPL_H_ 219 220