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