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