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