SkTFitsIn.h revision 58190644c30e1c4aa8e527f3503c58f841e0fcf3
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
15namespace sktfitsin {
16namespace Private {
17
18/** SkTHasMoreDigits::type = (digits(A) >= digits(B)) ? SkTrue : SkFalse. */
19template<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 */
26template <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 */
37template <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. */
49template <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 */
60template <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 */
74template<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 */
89template<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
100template<typename D, typename S, class OutOfRange_Low, class OutOfRange_High>
101struct 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 */
113template<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 */
130template<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 */
147template<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 */
165template<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 */
183template<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'. */
202template <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