|
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 |