1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef CHRE_UTIL_MEMORY_IMPL_H_
18 #define CHRE_UTIL_MEMORY_IMPL_H_
19
20 // IWYU pragma: private
21 #include <cstddef>
22 #include <cstring>
23 #include <new>
24 #include <type_traits>
25 #include <utility>
26
27 #include "chre/util/container_support.h"
28
29 namespace chre {
30
31 namespace util::internal {
32
33 // Backport of C++20's std::is_unbounded_array_v
34 template <typename>
35 constexpr bool is_unbounded_array_v = false;
36 template <typename T>
37 constexpr bool is_unbounded_array_v<T[]> = true;
38
39 } // namespace util::internal
40
41 template <typename ElementType>
destroy(ElementType * first,size_t count)42 inline void destroy(ElementType *first, size_t count) {
43 for (size_t i = 0; i < count; i++) {
44 first[i].~ElementType();
45 }
46 }
47
48 //! Overload used when the type is move assignable
49 template <typename ElementType>
moveOrCopyAssign(ElementType & dest,ElementType & source,std::true_type)50 inline void moveOrCopyAssign(ElementType &dest, ElementType &source,
51 std::true_type) {
52 dest = std::move(source);
53 }
54
55 //! Overload used when the type is not move assignable
56 template <typename ElementType>
moveOrCopyAssign(ElementType & dest,ElementType & source,std::false_type)57 inline void moveOrCopyAssign(ElementType &dest, ElementType &source,
58 std::false_type) {
59 dest = source;
60 }
61
62 template <typename ElementType>
moveOrCopyAssign(ElementType & dest,ElementType & source)63 inline void moveOrCopyAssign(ElementType &dest, ElementType &source) {
64 moveOrCopyAssign(dest, source,
65 typename std::is_move_assignable<ElementType>::type());
66 }
67
68 //! Overload used when type is trivially copy constructible
69 template <typename ElementType>
uninitializedMoveOrCopy(ElementType * source,size_t count,ElementType * dest,std::true_type)70 inline void uninitializedMoveOrCopy(ElementType *source, size_t count,
71 ElementType *dest, std::true_type) {
72 std::memcpy(dest, source, count * sizeof(ElementType));
73 }
74
75 //! Overload used when type is not trivially copy constructible, but is move
76 //! constructible
77 template <typename ElementType>
uninitializedMoveOrCopy(ElementType * source,size_t count,ElementType * dest,std::false_type,std::true_type)78 inline void uninitializedMoveOrCopy(ElementType *source, size_t count,
79 ElementType *dest, std::false_type,
80 std::true_type) {
81 for (size_t i = 0; i < count; i++) {
82 new (&dest[i]) ElementType(std::move(source[i]));
83 }
84 }
85
86 //! Overload used when type is not trivially copy constructible or move
87 //! constructible
88 template <typename ElementType>
uninitializedMoveOrCopy(ElementType * source,size_t count,ElementType * dest,std::false_type,std::false_type)89 inline void uninitializedMoveOrCopy(ElementType *source, size_t count,
90 ElementType *dest, std::false_type,
91 std::false_type) {
92 for (size_t i = 0; i < count; i++) {
93 new (&dest[i]) ElementType(source[i]);
94 }
95 }
96
97 //! Overload used when type is not trivially copy constructible
98 template <typename ElementType>
uninitializedMoveOrCopy(ElementType * source,size_t count,ElementType * dest,std::false_type)99 inline void uninitializedMoveOrCopy(ElementType *source, size_t count,
100 ElementType *dest, std::false_type) {
101 // Check the assumption that if is_move_constructible is false, then
102 // is_copy_constructible is true
103 static_assert(std::is_move_constructible<ElementType>() ||
104 std::is_copy_constructible<ElementType>(),
105 "Object must be copy- or move- constructible to use "
106 "uninitializedMoveOrCopy");
107 uninitializedMoveOrCopy(
108 source, count, dest, std::false_type(),
109 typename std::is_move_constructible<ElementType>::type());
110 }
111
112 template <typename ElementType>
uninitializedMoveOrCopy(ElementType * source,size_t count,ElementType * dest)113 inline void uninitializedMoveOrCopy(ElementType *source, size_t count,
114 ElementType *dest) {
115 // TODO: we should be able to use std::is_trivially_copy_constructible here,
116 // but it's not found in the linux x86 build, because our build uses GCC 4.8's
117 // C++ standard library, which doesn't support it. Works in the SLPI build,
118 // though...
119 uninitializedMoveOrCopy(source, count, dest,
120 typename std::is_trivial<ElementType>::type());
121 // typename std::is_trivially_copy_constructible<ElementType>::type());
122 }
123
124 template <typename T, typename... Args>
memoryAlloc(Args &&...args)125 inline T *memoryAlloc(Args &&... args) {
126 T *storage = nullptr;
127 if constexpr (alignof(T) > alignof(std::max_align_t)) {
128 storage = memoryAlignedAlloc<T>();
129 } else {
130 storage = static_cast<T *>(memoryAlloc(sizeof(T)));
131 }
132
133 if (storage != nullptr) {
134 new (storage) T(std::forward<Args>(args)...);
135 }
136
137 return storage;
138 }
139
140 template <typename T>
memoryAllocArray(size_t count)141 typename std::remove_extent<T>::type *memoryAllocArray(size_t count) {
142 static_assert(util::internal::is_unbounded_array_v<T>,
143 "memoryAllocArray is only supported for unbounded array types, "
144 "e.g. int[]");
145 // If this limitation becomes an issue, the solution is just to create a
146 // version of memoryAlignedAlloc() that accepts size_t as a parameter
147 static_assert(alignof(T) <= alignof(std::max_align_t),
148 "Additional alignment in memoryAllocArray isn't supported");
149 using BaseType = typename std::remove_extent<T>::type;
150 auto *ptr = static_cast<BaseType *>(memoryAlloc(count * sizeof(BaseType)));
151 if (ptr != nullptr) {
152 new (ptr) BaseType[count];
153 }
154 return ptr;
155 }
156
157 template <typename T>
memoryFreeAndDestroy(T * element)158 void memoryFreeAndDestroy(T *element) {
159 if (element != nullptr) {
160 element->~T();
161 memoryFree(element);
162 }
163 }
164
165 } // namespace chre
166
167 #endif // CHRE_UTIL_MEMORY_IMPL_H_
168