1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkTFitsIn_DEFINED 9 #define SkTFitsIn_DEFINED 10 11 #include "SkTypes.h" 12 #include "SkTLogic.h" 13 #include <limits> 14 #include <type_traits> 15 16 namespace sktfitsin { 17 namespace Private { 18 19 /** SkTMux::type = (a && b) ? Both : (a) ? A : (b) ? B : Neither; */ 20 template <bool a, bool b, typename Both, typename A, typename B, typename Neither> 21 struct SkTMux { 22 using type = skstd::conditional_t<a, skstd::conditional_t<b, Both, A>, 23 skstd::conditional_t<b, B, Neither>>; 24 }; 25 26 /** SkTHasMoreDigits = (digits(A) >= digits(B)) ? true_type : false_type. */ 27 template<typename A, typename B> struct SkTHasMoreDigits 28 : skstd::bool_constant<std::numeric_limits<A>::digits >= std::numeric_limits<B>::digits> 29 { }; 30 31 /** A high or low side predicate which is used when it is statically known 32 * that source values are in the range of the Destination. 33 */ 34 template <typename S> struct SkTOutOfRange_False { 35 typedef std::false_type can_be_true; 36 typedef S source_type; 37 static bool apply(S s) { 38 return false; 39 } 40 }; 41 42 /** A low side predicate which tests if the source value < Min(D). 43 * Assumes that Min(S) <= Min(D). 44 */ 45 template <typename D, typename S> struct SkTOutOfRange_LT_MinD { 46 typedef std::true_type can_be_true; 47 typedef S source_type; 48 static bool apply(S s) { 49 typedef SkTHasMoreDigits<S, D> precondition; 50 static_assert(precondition::value, "SkTOutOfRange_LT_MinD__minS_gt_minD"); 51 52 return s < static_cast<S>((std::numeric_limits<D>::min)()); 53 } 54 }; 55 56 /** A low side predicate which tests if the source value is less than 0. */ 57 template <typename D, typename S> struct SkTOutOfRange_LT_Zero { 58 typedef std::true_type can_be_true; 59 typedef S source_type; 60 static bool apply(S s) { 61 return s < static_cast<S>(0); 62 } 63 }; 64 65 /** A high side predicate which tests if the source value > Max(D). 66 * Assumes that Max(S) >= Max(D). 67 */ 68 template <typename D, typename S> struct SkTOutOfRange_GT_MaxD { 69 typedef std::true_type can_be_true; 70 typedef S source_type; 71 static bool apply(S s) { 72 typedef SkTHasMoreDigits<S, D> precondition; 73 static_assert(precondition::value, "SkTOutOfRange_GT_MaxD__maxS_lt_maxD"); 74 75 return s > static_cast<S>((std::numeric_limits<D>::max)()); 76 } 77 }; 78 79 /** Composes two SkTOutOfRange predicates. 80 * First checks OutOfRange_Low then, if in range, OutOfRange_High. 81 */ 82 template<class OutOfRange_Low, class OutOfRange_High> struct SkTOutOfRange_Either { 83 typedef std::true_type can_be_true; 84 typedef typename OutOfRange_Low::source_type source_type; 85 static bool apply(source_type s) { 86 bool outOfRange = OutOfRange_Low::apply(s); 87 if (!outOfRange) { 88 outOfRange = OutOfRange_High::apply(s); 89 } 90 return outOfRange; 91 } 92 }; 93 94 /** SkTCombineOutOfRange::type is an SkTOutOfRange_XXX type which is the 95 * optimal combination of OutOfRange_Low and OutOfRange_High. 96 */ 97 template<class OutOfRange_Low, class OutOfRange_High> struct SkTCombineOutOfRange { 98 typedef SkTOutOfRange_Either<OutOfRange_Low, OutOfRange_High> Both; 99 typedef SkTOutOfRange_False<typename OutOfRange_Low::source_type> Neither; 100 101 typedef typename OutOfRange_Low::can_be_true apply_low; 102 typedef typename OutOfRange_High::can_be_true apply_high; 103 104 typedef typename SkTMux<apply_low::value, apply_high::value, 105 Both, OutOfRange_Low, OutOfRange_High, Neither>::type type; 106 }; 107 108 template<typename D, typename S, class OutOfRange_Low, class OutOfRange_High> 109 struct SkTRangeChecker { 110 /** This is the method which is called at runtime to do the range check. */ 111 static bool OutOfRange(S s) { 112 typedef typename SkTCombineOutOfRange<OutOfRange_Low, OutOfRange_High>::type Combined; 113 return Combined::apply(s); 114 } 115 }; 116 117 /** SkTFitsIn_Unsigned2Unsiged::type is an SkTRangeChecker with an OutOfRange(S s) method 118 * the implementation of which is tailored for the source and destination types. 119 * Assumes that S and D are unsigned integer types. 120 */ 121 template<typename D, typename S> struct SkTFitsIn_Unsigned2Unsiged { 122 typedef SkTOutOfRange_False<S> OutOfRange_Low; 123 typedef SkTOutOfRange_GT_MaxD<D, S> OutOfRange_High; 124 125 typedef SkTRangeChecker<D, S, OutOfRange_Low, OutOfRange_High> HighSideOnlyCheck; 126 typedef SkTRangeChecker<D, S, SkTOutOfRange_False<S>, SkTOutOfRange_False<S> > NoCheck; 127 128 // If std::numeric_limits<D>::digits >= std::numeric_limits<S>::digits, nothing to check. 129 // This also protects the precondition of SkTOutOfRange_GT_MaxD. 130 typedef SkTHasMoreDigits<D, S> sourceFitsInDesitination; 131 typedef skstd::conditional_t<sourceFitsInDesitination::value, NoCheck, HighSideOnlyCheck> type; 132 }; 133 134 /** SkTFitsIn_Signed2Signed::type is an SkTRangeChecker with an OutOfRange(S s) method 135 * the implementation of which is tailored for the source and destination types. 136 * Assumes that S and D are signed integer types. 137 */ 138 template<typename D, typename S> struct SkTFitsIn_Signed2Signed { 139 typedef SkTOutOfRange_LT_MinD<D, S> OutOfRange_Low; 140 typedef SkTOutOfRange_GT_MaxD<D, S> OutOfRange_High; 141 142 typedef SkTRangeChecker<D, S, OutOfRange_Low, OutOfRange_High> FullCheck; 143 typedef SkTRangeChecker<D, S, SkTOutOfRange_False<S>, SkTOutOfRange_False<S> > NoCheck; 144 145 // If std::numeric_limits<D>::digits >= std::numeric_limits<S>::digits, nothing to check. 146 // This also protects the precondition of SkTOutOfRange_LT_MinD and SkTOutOfRange_GT_MaxD. 147 typedef SkTHasMoreDigits<D, S> sourceFitsInDesitination; 148 typedef skstd::conditional_t<sourceFitsInDesitination::value, NoCheck, FullCheck> type; 149 }; 150 151 /** SkTFitsIn_Signed2Unsigned::type is an SkTRangeChecker with an OutOfRange(S s) method 152 * the implementation of which is tailored for the source and destination types. 153 * Assumes that S is a signed integer type and D is an unsigned integer type. 154 */ 155 template<typename D, typename S> struct SkTFitsIn_Signed2Unsigned { 156 typedef SkTOutOfRange_LT_Zero<D, S> OutOfRange_Low; 157 typedef SkTOutOfRange_GT_MaxD<D, S> OutOfRange_High; 158 159 typedef SkTRangeChecker<D, S, OutOfRange_Low, OutOfRange_High> FullCheck; 160 typedef SkTRangeChecker<D, S, OutOfRange_Low, SkTOutOfRange_False<S> > LowSideOnlyCheck; 161 162 // If std::numeric_limits<D>::max() >= std::numeric_limits<S>::max(), 163 // no need to check the high side. (Until C++11, assume more digits means greater max.) 164 // This also protects the precondition of SkTOutOfRange_GT_MaxD. 165 typedef SkTHasMoreDigits<D, S> sourceCannotExceedDest; 166 typedef skstd::conditional_t<sourceCannotExceedDest::value, LowSideOnlyCheck, FullCheck> type; 167 }; 168 169 /** SkTFitsIn_Unsigned2Signed::type is an SkTRangeChecker with an OutOfRange(S s) method 170 * the implementation of which is tailored for the source and destination types. 171 * Assumes that S is an usigned integer type and D is a signed integer type. 172 */ 173 template<typename D, typename S> struct SkTFitsIn_Unsigned2Signed { 174 typedef SkTOutOfRange_False<S> OutOfRange_Low; 175 typedef SkTOutOfRange_GT_MaxD<D, S> OutOfRange_High; 176 177 typedef SkTRangeChecker<D, S, OutOfRange_Low, OutOfRange_High> HighSideOnlyCheck; 178 typedef SkTRangeChecker<D, S, SkTOutOfRange_False<S>, SkTOutOfRange_False<S> > NoCheck; 179 180 // If std::numeric_limits<D>::max() >= std::numeric_limits<S>::max(), nothing to check. 181 // (Until C++11, assume more digits means greater max.) 182 // This also protects the precondition of SkTOutOfRange_GT_MaxD. 183 typedef SkTHasMoreDigits<D, S> sourceCannotExceedDest; 184 typedef skstd::conditional_t<sourceCannotExceedDest::value, NoCheck, HighSideOnlyCheck> type; 185 }; 186 187 /** SkTFitsIn::type is an SkTRangeChecker with an OutOfRange(S s) method 188 * the implementation of which is tailored for the source and destination types. 189 * Assumes that S and D are integer types. 190 */ 191 template<typename D, typename S> struct SkTFitsIn { 192 // One of the following will be the 'selector' type. 193 typedef SkTFitsIn_Signed2Signed<D, S> S2S; 194 typedef SkTFitsIn_Signed2Unsigned<D, S> S2U; 195 typedef SkTFitsIn_Unsigned2Signed<D, S> U2S; 196 typedef SkTFitsIn_Unsigned2Unsiged<D, S> U2U; 197 198 typedef skstd::bool_constant<std::numeric_limits<S>::is_signed> S_is_signed; 199 typedef skstd::bool_constant<std::numeric_limits<D>::is_signed> D_is_signed; 200 201 typedef typename SkTMux<S_is_signed::value, D_is_signed::value, 202 S2S, S2U, U2S, U2U>::type selector; 203 // This type is an SkTRangeChecker. 204 typedef typename selector::type type; 205 }; 206 207 } // namespace Private 208 } // namespace sktfitsin 209 210 /** Returns true if the integer source value 's' will fit in the integer destination type 'D'. */ 211 template <typename D, typename S> inline bool SkTFitsIn(S s) { 212 static_assert(std::numeric_limits<S>::is_integer, "SkTFitsIn_source_must_be_integer"); 213 static_assert(std::numeric_limits<D>::is_integer, "SkTFitsIn_destination_must_be_integer"); 214 215 return !sktfitsin::Private::SkTFitsIn<D, S>::type::OutOfRange(s); 216 } 217 218 #endif 219