1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include <mozilla/Saturate.h>
8 
9 #include <mozilla/Assertions.h>
10 
11 #include <limits>
12 
13 using mozilla::detail::Saturate;
14 
15 #define A(a) MOZ_RELEASE_ASSERT(a, "Test \'" #a "\'  failed.")
16 
17 static const unsigned long sNumOps = 32;
18 
19 template <typename T>
StartValue()20 static T StartValue() {
21   // Specialize |StartValue| for the given type.
22   A(false);
23 }
24 
25 template <>
StartValue()26 int8_t StartValue<int8_t>() {
27   return 0;
28 }
29 
30 template <>
StartValue()31 int16_t StartValue<int16_t>() {
32   return 0;
33 }
34 
35 template <>
StartValue()36 int32_t StartValue<int32_t>() {
37   return 0;
38 }
39 
40 template <>
StartValue()41 uint8_t StartValue<uint8_t>() {
42   // Picking a value near middle of uint8_t's range.
43   return static_cast<uint8_t>(std::numeric_limits<int8_t>::max());
44 }
45 
46 template <>
StartValue()47 uint16_t StartValue<uint16_t>() {
48   // Picking a value near middle of uint16_t's range.
49   return static_cast<uint8_t>(std::numeric_limits<int16_t>::max());
50 }
51 
52 template <>
StartValue()53 uint32_t StartValue<uint32_t>() {
54   // Picking a value near middle of uint32_t's range.
55   return static_cast<uint8_t>(std::numeric_limits<int32_t>::max());
56 }
57 
58 // Add
59 //
60 
61 template <typename T>
TestPrefixIncr()62 static void TestPrefixIncr() {
63   T value = StartValue<T>();
64   Saturate<T> satValue(value);
65 
66   for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
67     A(++value == ++satValue);
68   }
69 }
70 
71 template <typename T>
TestPostfixIncr()72 static void TestPostfixIncr() {
73   T value = StartValue<T>();
74   Saturate<T> satValue(value);
75 
76   for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
77     A(value++ == satValue++);
78   }
79 }
80 
81 template <typename T>
TestAdd()82 static void TestAdd() {
83   T value = StartValue<T>();
84   Saturate<T> satValue(value);
85 
86   for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
87     A((value + i) == (satValue + i));
88   }
89 }
90 
91 // Subtract
92 //
93 
94 template <typename T>
TestPrefixDecr()95 static void TestPrefixDecr() {
96   T value = StartValue<T>();
97   Saturate<T> satValue(value);
98 
99   for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
100     A(--value == --satValue);
101   }
102 }
103 
104 template <typename T>
TestPostfixDecr()105 static void TestPostfixDecr() {
106   T value = StartValue<T>();
107   Saturate<T> satValue(value);
108 
109   for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
110     A(value-- == satValue--);
111   }
112 }
113 
114 template <typename T>
TestSub()115 static void TestSub() {
116   T value = StartValue<T>();
117   Saturate<T> satValue(value);
118 
119   for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
120     A((value - i) == (satValue - i));
121   }
122 }
123 
124 // Corner cases near bounds
125 //
126 
127 template <typename T>
TestUpperBound()128 static void TestUpperBound() {
129   Saturate<T> satValue(std::numeric_limits<T>::max());
130 
131   A(--satValue == (std::numeric_limits<T>::max() - 1));
132   A(++satValue == (std::numeric_limits<T>::max()));
133   A(++satValue == (std::numeric_limits<T>::max()));      // don't overflow here
134   A(++satValue == (std::numeric_limits<T>::max()));      // don't overflow here
135   A(--satValue == (std::numeric_limits<T>::max() - 1));  // back at (max - 1)
136   A(--satValue == (std::numeric_limits<T>::max() - 2));
137 }
138 
139 template <typename T>
TestLowerBound()140 static void TestLowerBound() {
141   Saturate<T> satValue(std::numeric_limits<T>::min());
142 
143   A(++satValue == (std::numeric_limits<T>::min() + 1));
144   A(--satValue == (std::numeric_limits<T>::min()));
145   A(--satValue == (std::numeric_limits<T>::min()));      // don't overflow here
146   A(--satValue == (std::numeric_limits<T>::min()));      // don't overflow here
147   A(++satValue == (std::numeric_limits<T>::min() + 1));  // back at (max + 1)
148   A(++satValue == (std::numeric_limits<T>::min() + 2));
149 }
150 
151 // Framework
152 //
153 
154 template <typename T>
TestAll()155 static void TestAll() {
156   // Assert that we don't accidently hit type's range limits in tests.
157   const T value = StartValue<T>();
158   A(std::numeric_limits<T>::min() + static_cast<T>(sNumOps) <= value);
159   A(std::numeric_limits<T>::max() - static_cast<T>(sNumOps) >= value);
160 
161   TestPrefixIncr<T>();
162   TestPostfixIncr<T>();
163   TestAdd<T>();
164 
165   TestPrefixDecr<T>();
166   TestPostfixDecr<T>();
167   TestSub<T>();
168 
169   TestUpperBound<T>();
170   TestLowerBound<T>();
171 }
172 
main()173 int main() {
174   TestAll<int8_t>();
175   TestAll<int16_t>();
176   TestAll<int32_t>();
177   TestAll<uint8_t>();
178   TestAll<uint16_t>();
179   TestAll<uint32_t>();
180   return 0;
181 }
182