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