]>
Commit | Line | Data |
---|---|---|
1 | // | |
2 | // Tests for | |
3 | // bounded_ptr& operator+=(std::ptrdiff_t n); | |
4 | // | |
5 | ||
6 | #include <libkern/c++/bounded_ptr.h> | |
7 | #include <array> | |
8 | #include <cstddef> | |
9 | #include <cstdint> | |
10 | #include <limits> | |
11 | #include <darwintest.h> | |
12 | #include <darwintest_utils.h> | |
13 | #include "test_utils.h" | |
14 | ||
15 | #define _assert(...) T_ASSERT_TRUE((__VA_ARGS__), # __VA_ARGS__) | |
16 | ||
17 | struct T { int i; }; | |
18 | ||
19 | namespace { | |
20 | struct tracking_policy { | |
21 | static bool did_trap; | |
22 | static void | |
23 | trap(char const*) | |
24 | { | |
25 | did_trap = true; | |
26 | } | |
27 | }; | |
28 | bool tracking_policy::did_trap = false; | |
29 | } | |
30 | ||
31 | template <typename T, typename QualT> | |
32 | static void | |
33 | tests() | |
34 | { | |
35 | std::array<T, 5> array = {T{0}, T{1}, T{2}, T{3}, T{4}}; | |
36 | ||
37 | // Add-assign positive offsets | |
38 | // T{0} T{1} T{2} T{3} T{4} <one-past-last> | |
39 | // ^ ^ | |
40 | // | | | |
41 | // begin,ptr end | |
42 | { | |
43 | test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end()); | |
44 | auto& ref = ptr += 0; | |
45 | _assert(&ref == &ptr); | |
46 | _assert(&*ptr == &array[0]); | |
47 | } | |
48 | { | |
49 | test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end()); | |
50 | auto& ref = ptr += 1; | |
51 | _assert(&ref == &ptr); | |
52 | _assert(&*ptr == &array[1]); | |
53 | } | |
54 | { | |
55 | test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end()); | |
56 | auto& ref = ptr += 2; | |
57 | _assert(&ref == &ptr); | |
58 | _assert(&*ptr == &array[2]); | |
59 | } | |
60 | { | |
61 | test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end()); | |
62 | auto& ref = ptr += 3; | |
63 | _assert(&ref == &ptr); | |
64 | _assert(&*ptr == &array[3]); | |
65 | } | |
66 | { | |
67 | test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end()); | |
68 | auto& ref = ptr += 4; | |
69 | _assert(&ref == &ptr); | |
70 | _assert(&*ptr == &array[4]); | |
71 | } | |
72 | { | |
73 | test_bounded_ptr<QualT> ptr(array.begin(), array.begin(), array.end()); | |
74 | auto& ref = ptr += 5; | |
75 | _assert(&ref == &ptr); | |
76 | _assert(ptr == array.end()); | |
77 | } | |
78 | ||
79 | // Add-assign negative offsets | |
80 | // T{0} T{1} T{2} T{3} T{4} <one-past-last> | |
81 | // ^ ^ | |
82 | // | | | |
83 | // begin end,ptr | |
84 | { | |
85 | test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end()); | |
86 | auto& ref = ptr += 0; | |
87 | _assert(&ref == &ptr); | |
88 | _assert(ptr == array.end()); | |
89 | } | |
90 | { | |
91 | test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end()); | |
92 | auto& ref = ptr += -1; | |
93 | _assert(&ref == &ptr); | |
94 | _assert(&*ptr == &array[4]); | |
95 | } | |
96 | { | |
97 | test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end()); | |
98 | auto& ref = ptr += -2; | |
99 | _assert(&ref == &ptr); | |
100 | _assert(&*ptr == &array[3]); | |
101 | } | |
102 | { | |
103 | test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end()); | |
104 | auto& ref = ptr += -3; | |
105 | _assert(&ref == &ptr); | |
106 | _assert(&*ptr == &array[2]); | |
107 | } | |
108 | { | |
109 | test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end()); | |
110 | auto& ref = ptr += -4; | |
111 | _assert(&ref == &ptr); | |
112 | _assert(&*ptr == &array[1]); | |
113 | } | |
114 | { | |
115 | test_bounded_ptr<QualT> ptr(array.end(), array.begin(), array.end()); | |
116 | auto& ref = ptr += -5; | |
117 | _assert(&ref == &ptr); | |
118 | _assert(&*ptr == &array[0]); | |
119 | } | |
120 | ||
121 | // Make sure we trap on arithmetic overflow in the number of bytes calculation | |
122 | { | |
123 | std::ptrdiff_t sizeof_T = sizeof(T); // avoid promotion to unsigned in calculations | |
124 | ||
125 | // largest (most positive) n for the number of bytes `n * sizeof(T)` not to overflow ptrdiff_t | |
126 | std::ptrdiff_t max_n = std::numeric_limits<std::ptrdiff_t>::max() / sizeof_T; | |
127 | ||
128 | // smallest (most negative) n for the number of bytes `n * sizeof(T)` not to overflow ptrdiff_t | |
129 | std::ptrdiff_t min_n = std::numeric_limits<std::ptrdiff_t>::min() / sizeof_T; | |
130 | ||
131 | // Overflow with a positive offset | |
132 | { | |
133 | libkern::bounded_ptr<QualT, tracking_policy> ptr(array.begin(), array.begin(), array.end()); | |
134 | tracking_policy::did_trap = false; | |
135 | ptr += max_n + 1; | |
136 | _assert(tracking_policy::did_trap); | |
137 | } | |
138 | ||
139 | // Overflow with a negative offset | |
140 | { | |
141 | libkern::bounded_ptr<QualT, tracking_policy> ptr(array.begin(), array.begin(), array.end()); | |
142 | tracking_policy::did_trap = false; | |
143 | ptr += min_n - 1; | |
144 | _assert(tracking_policy::did_trap); | |
145 | } | |
146 | } | |
147 | ||
148 | // Make sure we trap on arithmetic overflow in the offset calculation | |
149 | // | |
150 | // To avoid running into the overflow of `n * sizeof(T)` when ptrdiff_t | |
151 | // is the same size as int32_t, we test the offset overflow check by | |
152 | // successive addition of smaller offsets. | |
153 | // | |
154 | // We basically push the offset right to its limit, and then push it | |
155 | // past its limit to watch it overflow. | |
156 | { | |
157 | std::int64_t sizeof_T = sizeof(T); // avoid promotion to unsigned in calculations | |
158 | ||
159 | // largest (most positive) n for the number of bytes `n * sizeof(T)` not to overflow the int32_t offset | |
160 | std::int64_t max_n = std::numeric_limits<std::int32_t>::max() / sizeof_T; | |
161 | ||
162 | // smallest (most negative) n for the number of bytes `n * sizeof(T)` not to overflow the int32_t offset | |
163 | std::int64_t min_n = std::numeric_limits<std::int32_t>::min() / sizeof_T; | |
164 | ||
165 | // Add positive offsets | |
166 | { | |
167 | libkern::bounded_ptr<QualT, tracking_policy> ptr(array.begin(), array.begin(), array.end()); | |
168 | tracking_policy::did_trap = false; | |
169 | ptr += static_cast<ptrdiff_t>(max_n / 2); | |
170 | _assert(!tracking_policy::did_trap); | |
171 | ptr += static_cast<ptrdiff_t>(max_n / 2); | |
172 | _assert(!tracking_policy::did_trap); | |
173 | ptr += (max_n % 2); | |
174 | _assert(!tracking_policy::did_trap); // offset is now right at its positive limit | |
175 | ptr += 1; | |
176 | _assert(tracking_policy::did_trap); | |
177 | } | |
178 | ||
179 | // Add negative offsets | |
180 | { | |
181 | libkern::bounded_ptr<QualT, tracking_policy> ptr(array.begin(), array.begin(), array.end()); | |
182 | tracking_policy::did_trap = false; | |
183 | ptr += static_cast<ptrdiff_t>(min_n / 2); | |
184 | _assert(!tracking_policy::did_trap); | |
185 | ptr += static_cast<ptrdiff_t>(min_n / 2); | |
186 | _assert(!tracking_policy::did_trap); | |
187 | ptr += (min_n % 2); | |
188 | _assert(!tracking_policy::did_trap); // offset is now right at its negative limit | |
189 | ptr += -1; | |
190 | _assert(tracking_policy::did_trap); | |
191 | } | |
192 | } | |
193 | } | |
194 | ||
195 | T_DECL(arith_add_assign, "bounded_ptr.arith.add_assign") { | |
196 | tests<T, T>(); | |
197 | tests<T, T const>(); | |
198 | tests<T, T volatile>(); | |
199 | tests<T, T const volatile>(); | |
200 | } |