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