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;
applySkTOutOfRange_False29     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;
applySkTOutOfRange_LT_MinD40     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;
applySkTOutOfRange_LT_Zero52     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;
applySkTOutOfRange_GT_MaxD63     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;
applySkTOutOfRange_Either77     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. */
OutOfRangeSkTRangeChecker103     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'. */
SkTFitsIn(S s)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