• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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