1 // Copyright 2018 The Android Open Source Project
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 #pragma once
16 
17 #include "aemu/base/Compiler.h"
18 
19 #include <algorithm>
20 #include <atomic>
21 #include <cinttypes>
22 #include <cstdlib>
23 #include <cstring>
24 #include <type_traits>
25 #include <vector>
26 
27 #include <stdio.h>
28 
29 #ifdef _WIN32
30 #include <malloc.h>
31 #endif
32 
33 namespace gfxstream {
34 namespace guest {
35 
36 template <class T, size_t align>
37 class AlignedBuf {
38 public:
AlignedBuf(size_t size)39     explicit AlignedBuf(size_t size) {
40         static_assert(align && ((align & (align - 1)) == 0),
41                       "AlignedBuf only supports power-of-2 aligments.");
42         resizeImpl(size);
43     }
44 
AlignedBuf(const AlignedBuf & other)45     AlignedBuf(const AlignedBuf& other) : AlignedBuf(other.mSize) {
46         if (other.mBuffer) { // could have got moved out
47             std::copy(other.mBuffer, other.mBuffer + other.mSize, mBuffer);
48         }
49     }
50 
51     AlignedBuf& operator=(const AlignedBuf& other) {
52         if (this != &other) {
53             AlignedBuf tmp(other);
54             *this = std::move(tmp);
55         }
56         return *this;
57     }
58 
AlignedBuf(AlignedBuf && other)59     AlignedBuf(AlignedBuf&& other) { *this = std::move(other); }
60 
61     AlignedBuf& operator=(AlignedBuf&& other) {
62         mBuffer = other.mBuffer;
63         mSize = other.mSize;
64 
65         other.mBuffer = nullptr;
66         other.mSize = 0;
67 
68         return *this;
69     }
70 
~AlignedBuf()71     ~AlignedBuf() { if (mBuffer) freeImpl(mBuffer); } // account for getting moved out
72 
resize(size_t newSize)73     void resize(size_t newSize) {
74 #if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4) || \
75         defined(__OLD_STD_VERSION__)
76         // Older g++ doesn't support std::is_trivially_copyable.
77         constexpr bool triviallyCopyable =
78                 std::has_trivial_copy_constructor<T>::value;
79 #else
80         constexpr bool triviallyCopyable = std::is_trivially_copyable<T>::value;
81 #endif
82         static_assert(triviallyCopyable,
83                       "AlignedBuf can only resize trivially copyable values");
84 
85         resizeImpl(newSize);
86     }
87 
size()88     size_t size() const { return mSize; }
89 
data()90     T* data() { return mBuffer; }
91 
92     T& operator[](size_t index) { return mBuffer[index]; }
93 
94     const T& operator[](size_t index) const { return mBuffer[index]; }
95 
96     bool operator==(const AlignedBuf& other) const {
97         return 0 == std::memcmp(mBuffer, other.mBuffer, sizeof(T) * std::min(mSize, other.mSize));
98     }
99 
100 private:
101 
resizeImpl(size_t newSize)102     void resizeImpl(size_t newSize) {
103         if (newSize) {
104             size_t pad = std::max(align, sizeof(T));
105             size_t keepSize = std::min(newSize, mSize);
106             size_t newSizeBytes = ((align - 1 + newSize * sizeof(T) + pad) / align) * align;
107 
108             std::vector<T> temp(mBuffer, mBuffer + keepSize);
109             mBuffer = static_cast<T*>(reallocImpl(mBuffer, newSizeBytes));
110             std::copy(temp.data(), temp.data() + keepSize, mBuffer);
111         } else {
112             if (mBuffer) freeImpl(mBuffer);
113             mBuffer = nullptr;
114         }
115 
116         mSize = newSize;
117     }
118 
reallocImpl(void * oldPtr,size_t sizeBytes)119     void* reallocImpl(void* oldPtr, size_t sizeBytes) {
120         if (oldPtr) { freeImpl(oldPtr); }
121         // Platform aligned malloc might not behave right
122         // if we give it an alignemnt value smaller than sizeof(void*).
123         size_t actualAlign = std::max(align, sizeof(void*));
124 #ifdef _WIN32
125         return _aligned_malloc(sizeBytes, actualAlign);
126 #else
127         void* res;
128         if (posix_memalign(&res, actualAlign, sizeBytes)) {
129             fprintf(stderr, "%s: failed to alloc aligned memory\n", __func__);
130             abort();
131         }
132         return res;
133 #endif
134     }
135 
freeImpl(void * ptr)136     void freeImpl(void* ptr) {
137 #ifdef _WIN32
138         _aligned_free(ptr);
139 #else
140         free(ptr);
141 #endif
142 
143     }
144 
145     T* mBuffer = nullptr;
146     size_t mSize = 0;
147 };
148 
149 // Convenience function for aligned malloc across platforms
150 void* aligned_buf_alloc(size_t align, size_t size);
151 void aligned_buf_free(void* buf);
152 
153 }  // namespace guest
154 }  // namespace gfxstream
155