1 // Copyright 2020 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "tests/utils.h"
16 
17 #include <cstddef>
18 #include <cstdint>
19 #include <memory>
20 #include <new>
21 
22 #include "absl/base/config.h"
23 #include "gtest/gtest.h"
24 #include "src/utils/memory.h"
25 
26 #ifdef ABSL_HAVE_EXCEPTIONS
27 #include <exception>
28 #endif
29 
30 namespace libgav1 {
31 namespace test_utils {
32 namespace {
33 
34 constexpr size_t kMaxAllocableSize = 0x40000000;
35 
36 // Has a trivial default constructor that performs no action.
37 struct SmallMaxAligned : public MaxAlignedAllocable {
38   alignas(kMaxAlignment) uint8_t x;
39 };
40 
41 // Has a nontrivial default constructor that initializes the data member.
42 struct SmallMaxAlignedNontrivialConstructor : public MaxAlignedAllocable {
43   alignas(kMaxAlignment) uint8_t x = 0;
44 };
45 
46 // Has a trivial default constructor that performs no action.
47 struct HugeMaxAligned : public MaxAlignedAllocable {
48   alignas(kMaxAlignment) uint8_t x[kMaxAllocableSize + 1];
49 };
50 
51 // Has a nontrivial default constructor that initializes the data member.
52 struct HugeMaxAlignedNontrivialConstructor : public MaxAlignedAllocable {
53   alignas(kMaxAlignment) uint8_t x[kMaxAllocableSize + 1] = {};
54 };
55 
56 #ifdef ABSL_HAVE_EXCEPTIONS
57 struct MaxAlignedThrowingConstructor : public MaxAlignedAllocable {
MaxAlignedThrowingConstructorlibgav1::test_utils::__anon19a2a0450111::MaxAlignedThrowingConstructor58   MaxAlignedThrowingConstructor() { throw std::exception(); }
59 
60   uint8_t x;
61 };
62 #endif
63 
TEST(TestUtilsTest,TestMaxAlignedAllocable)64 TEST(TestUtilsTest, TestMaxAlignedAllocable) {
65   {
66     // MaxAlignedAllocable::operator new (std::nothrow) is called.
67     std::unique_ptr<SmallMaxAligned> small(new (std::nothrow) SmallMaxAligned);
68     EXPECT_NE(small, nullptr);
69     // Note this check doesn't guarantee conformance as a suitably aligned
70     // address may be returned from any allocator.
71     EXPECT_EQ(reinterpret_cast<uintptr_t>(small.get()) & (kMaxAlignment - 1),
72               0);
73     // MaxAlignedAllocable::operator delete is called.
74   }
75 
76   {
77     // MaxAlignedAllocable::operator new is called.
78     std::unique_ptr<SmallMaxAligned> small(new SmallMaxAligned);
79     EXPECT_NE(small, nullptr);
80     // Note this check doesn't guarantee conformance as a suitably aligned
81     // address may be returned from any allocator.
82     EXPECT_EQ(reinterpret_cast<uintptr_t>(small.get()) & (kMaxAlignment - 1),
83               0);
84     // MaxAlignedAllocable::operator delete is called.
85   }
86 
87   {
88     // MaxAlignedAllocable::operator new[] (std::nothrow) is called.
89     std::unique_ptr<SmallMaxAligned[]> small_array_of_smalls(
90         new (std::nothrow) SmallMaxAligned[10]);
91     EXPECT_NE(small_array_of_smalls, nullptr);
92     EXPECT_EQ(reinterpret_cast<uintptr_t>(small_array_of_smalls.get()) &
93                   (kMaxAlignment - 1),
94               0);
95     // MaxAlignedAllocable::operator delete[] is called.
96   }
97 
98   {
99     // MaxAlignedAllocable::operator new[] is called.
100     std::unique_ptr<SmallMaxAligned[]> small_array_of_smalls(
101         new SmallMaxAligned[10]);
102     EXPECT_NE(small_array_of_smalls, nullptr);
103     EXPECT_EQ(reinterpret_cast<uintptr_t>(small_array_of_smalls.get()) &
104                   (kMaxAlignment - 1),
105               0);
106     // MaxAlignedAllocable::operator delete[] is called.
107   }
108 
109   {
110     // MaxAlignedAllocable::operator new (std::nothrow) is called.
111     std::unique_ptr<HugeMaxAligned> huge(new (std::nothrow) HugeMaxAligned);
112     EXPECT_EQ(huge, nullptr);
113   }
114 
115   {
116     // MaxAlignedAllocable::operator new[] (std::nothrow) is called.
117     std::unique_ptr<SmallMaxAligned[]> huge_array_of_smalls(
118         new (std::nothrow)
119             SmallMaxAligned[kMaxAllocableSize / sizeof(SmallMaxAligned) + 1]);
120     EXPECT_EQ(huge_array_of_smalls, nullptr);
121   }
122 
123 #ifdef ABSL_HAVE_EXCEPTIONS
124   try {
125     // MaxAlignedAllocable::operator new (std::nothrow) is called.
126     // The constructor throws an exception.
127     // MaxAlignedAllocable::operator delete (std::nothrow) is called.
128     auto* always = new (std::nothrow) MaxAlignedThrowingConstructor;
129     static_cast<void>(always);
130   } catch (...) {
131   }
132 
133   try {
134     // MaxAlignedAllocable::operator new is called.
135     // The constructor throws an exception.
136     // MaxAlignedAllocable::operator delete is called.
137     auto* always = new MaxAlignedThrowingConstructor;
138     static_cast<void>(always);
139   } catch (...) {
140   }
141 
142   try {
143     // MaxAlignedAllocable::operator new[] (std::nothrow) is called.
144     // The constructor throws an exception.
145     // MaxAlignedAllocable::operator delete[] (std::nothrow) is called.
146     auto* always = new (std::nothrow) MaxAlignedThrowingConstructor[2];
147     static_cast<void>(always);
148   } catch (...) {
149   }
150 
151   try {
152     // MaxAlignedAllocable::operator new[] is called.
153     // The constructor throws an exception.
154     // MaxAlignedAllocable::operator delete[] is called.
155     auto* always = new MaxAlignedThrowingConstructor[2];
156     static_cast<void>(always);
157   } catch (...) {
158   }
159 
160   // Note these calls are only safe with exceptions enabled as if the throwing
161   // operator new returns the object is expected to be valid. In this case an
162   // attempt to invoke the object's constructor on a nullptr may be made which
163   // is undefined behavior.
164   try {
165     // MaxAlignedAllocable::operator new is called.
166     std::unique_ptr<HugeMaxAlignedNontrivialConstructor> huge(
167         new HugeMaxAlignedNontrivialConstructor);
168     ADD_FAILURE() << "huge allocation should fail.";
169   } catch (...) {
170     SUCCEED();
171   }
172 
173   try {
174     // MaxAlignedAllocable::operator new[] is called.
175     std::unique_ptr<SmallMaxAlignedNontrivialConstructor[]>
176         huge_array_of_smalls(
177             new SmallMaxAlignedNontrivialConstructor
178                 [kMaxAllocableSize /
179                      sizeof(SmallMaxAlignedNontrivialConstructor) +
180                  1]);
181     ADD_FAILURE() << "huge_array_of_smalls allocation should fail.";
182   } catch (...) {
183     SUCCEED();
184   }
185 #endif  // ABSL_HAVE_EXCEPTIONS
186 }
187 
188 }  // namespace
189 }  // namespace test_utils
190 }  // namespace libgav1
191