xref: /aosp_15_r20/system/chre/util/include/chre/util/memory_impl.h (revision 84e339476a462649f82315436d70fd732297a399)
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