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