blob: fc2b8dd6307acc6518e53d1007b8fd0e247b8868 [file] [log] [blame]
bungeman@google.comf5cc5b12013-07-12 18:22:49 +00001/*
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>
bungeman221524d2016-01-05 14:59:40 -080014#include <type_traits>
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000015
16namespace sktfitsin {
17namespace Private {
18
bungeman761cf612015-08-28 07:09:20 -070019/** SkTMux::type = (a && b) ? Both : (a) ? A : (b) ? B : Neither; */
20template <bool a, bool b, typename Both, typename A, typename B, typename Neither>
21struct SkTMux {
22 using type = skstd::conditional_t<a, skstd::conditional_t<b, Both, A>,
23 skstd::conditional_t<b, B, Neither>>;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000024};
25
bungeman761cf612015-08-28 07:09:20 -070026/** SkTHasMoreDigits = (digits(A) >= digits(B)) ? true_type : false_type. */
27template<typename A, typename B> struct SkTHasMoreDigits
28 : skstd::bool_constant<std::numeric_limits<A>::digits >= std::numeric_limits<B>::digits>
29{ };
30
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000031/** 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 */
34template <typename S> struct SkTOutOfRange_False {
bungeman221524d2016-01-05 14:59:40 -080035 typedef std::false_type can_be_true;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000036 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 */
45template <typename D, typename S> struct SkTOutOfRange_LT_MinD {
bungeman221524d2016-01-05 14:59:40 -080046 typedef std::true_type can_be_true;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000047 typedef S source_type;
48 static bool apply(S s) {
bungeman761cf612015-08-28 07:09:20 -070049 typedef SkTHasMoreDigits<S, D> precondition;
bungeman99fe8222015-08-20 07:57:51 -070050 static_assert(precondition::value, "SkTOutOfRange_LT_MinD__minS_gt_minD");
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000051
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. */
57template <typename D, typename S> struct SkTOutOfRange_LT_Zero {
bungeman221524d2016-01-05 14:59:40 -080058 typedef std::true_type can_be_true;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000059 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 */
68template <typename D, typename S> struct SkTOutOfRange_GT_MaxD {
bungeman221524d2016-01-05 14:59:40 -080069 typedef std::true_type can_be_true;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000070 typedef S source_type;
71 static bool apply(S s) {
bungeman761cf612015-08-28 07:09:20 -070072 typedef SkTHasMoreDigits<S, D> precondition;
bungeman99fe8222015-08-20 07:57:51 -070073 static_assert(precondition::value, "SkTOutOfRange_GT_MaxD__maxS_lt_maxD");
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000074
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 */
82template<class OutOfRange_Low, class OutOfRange_High> struct SkTOutOfRange_Either {
bungeman221524d2016-01-05 14:59:40 -080083 typedef std::true_type can_be_true;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +000084 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 */
97template<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
bungeman761cf612015-08-28 07:09:20 -0700104 typedef typename SkTMux<apply_low::value, apply_high::value,
bungeman@google.comf5cc5b12013-07-12 18:22:49 +0000105 Both, OutOfRange_Low, OutOfRange_High, Neither>::type type;
106};
107
108template<typename D, typename S, class OutOfRange_Low, class OutOfRange_High>
109struct 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 */
121template<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.
bungeman761cf612015-08-28 07:09:20 -0700130 typedef SkTHasMoreDigits<D, S> sourceFitsInDesitination;
131 typedef skstd::conditional_t<sourceFitsInDesitination::value, NoCheck, HighSideOnlyCheck> type;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +0000132};
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 */
138template<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.
bungeman761cf612015-08-28 07:09:20 -0700147 typedef SkTHasMoreDigits<D, S> sourceFitsInDesitination;
148 typedef skstd::conditional_t<sourceFitsInDesitination::value, NoCheck, FullCheck> type;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +0000149};
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 */
155template<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.
bungeman761cf612015-08-28 07:09:20 -0700165 typedef SkTHasMoreDigits<D, S> sourceCannotExceedDest;
166 typedef skstd::conditional_t<sourceCannotExceedDest::value, LowSideOnlyCheck, FullCheck> type;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +0000167};
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 */
173template<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.
bungeman761cf612015-08-28 07:09:20 -0700183 typedef SkTHasMoreDigits<D, S> sourceCannotExceedDest;
184 typedef skstd::conditional_t<sourceCannotExceedDest::value, NoCheck, HighSideOnlyCheck> type;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +0000185};
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 */
191template<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
bungeman761cf612015-08-28 07:09:20 -0700198 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;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +0000200
bungeman761cf612015-08-28 07:09:20 -0700201 typedef typename SkTMux<S_is_signed::value, D_is_signed::value,
202 S2S, S2U, U2S, U2U>::type selector;
bungeman@google.comf5cc5b12013-07-12 18:22:49 +0000203 // 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'. */
211template <typename D, typename S> inline bool SkTFitsIn(S s) {
bungeman99fe8222015-08-20 07:57:51 -0700212 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");
bungeman@google.comf5cc5b12013-07-12 18:22:49 +0000214
215 return !sktfitsin::Private::SkTFitsIn<D, S>::type::OutOfRange(s);
216}
217
218#endif