1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef MINIKIN_BUFFER_H 18 #define MINIKIN_BUFFER_H 19 20 #include <cstring> 21 #include <string_view> 22 #include <type_traits> 23 #include <utility> 24 25 namespace minikin { 26 27 // This is a helper class to read data from a memory buffer. 28 // This class does not copy memory, and may return pointers to parts of the memory buffer. 29 // Thus the memory buffer should outlive objects created using this class. 30 // 31 // Note on alignment: 32 // Some CPU archs (e.g. arm32) do not allow misaligned memory access. 33 // Therefore, BufferReader and BufferWriter automatically insert paddings 34 // to align data records. 35 // For the padding to be deterministic, the following conditions must be met: 36 // (1) Alignment and size of each data record must be fixed regardless of 37 // CPU arch. 38 // (2) Alignment for each data record must be a power of 2 (2^n) and 39 // must be less than or equal to kMaxAlignment. 40 // (3) The head address of the buffer must be aligned at kMaxAlignment. 41 // 42 // The condition (2) and (3) ensures that 'headAddress % align == 0' 43 // and the padding is determined only by the current position. 44 // I.e. mCurrent % align == (mCurrent - headAddress) % align. 45 class BufferReader { 46 public: 47 static constexpr size_t kMaxAlignment = 8; 48 BufferReader(const void * buffer)49 explicit BufferReader(const void* buffer) : BufferReader(buffer, 0) {} BufferReader(const void * buffer,uint32_t pos)50 BufferReader(const void* buffer, uint32_t pos) 51 : mCurrent(reinterpret_cast<const uint8_t*>(buffer) + pos) {} 52 53 // align() adds padding if necessary so that the returned pointer is aligned 54 // at 'align' template parameter (i.e. align<T, _align>(p) % _align == 0). 55 // 56 // By default we align to sizeof(T) instead of alignof(T), because the 57 // buffer may be shared between 32-bit processes and 64-bit processes. 58 // The value of alignof(T) may change between the two. 59 // 60 // If T is a large struct or class, you would need to specify 'align' 61 // template parameter manually. 62 template <typename T, size_t AlignT = sizeof(T)> align(const uint8_t * p)63 static const uint8_t* align(const uint8_t* p) { 64 static_assert(AlignT <= kMaxAlignment); 65 static_assert(__builtin_popcount(AlignT) == 1, "align must be a power of 2"); 66 constexpr size_t mask = AlignT - 1; 67 intptr_t i = reinterpret_cast<intptr_t>(p); 68 intptr_t aligned = (i + mask) & ~mask; 69 return reinterpret_cast<const uint8_t*>(aligned); 70 } 71 72 template <typename T, size_t align = sizeof(T)> read()73 const T& read() { 74 const T* data = map<T, align>(sizeof(T)); 75 return *data; 76 } 77 78 template <typename T, size_t align = sizeof(T)> map(uint32_t size)79 const T* map(uint32_t size) { 80 static_assert(std::is_pod<T>::value, "T must be a POD"); 81 mCurrent = BufferReader::align<T, align>(mCurrent); 82 const T* data = reinterpret_cast<const T*>(mCurrent); 83 mCurrent += size; 84 return data; 85 } 86 87 template <typename T, size_t align = sizeof(T)> skip()88 void skip() { 89 static_assert(std::is_pod<T>::value, "T must be a POD"); 90 mCurrent = BufferReader::align<T, align>(mCurrent); 91 mCurrent += sizeof(T); 92 } 93 94 // Return a pointer to an array and its number of elements. 95 template <typename T, size_t align = sizeof(T)> readArray()96 std::pair<const T*, uint32_t> readArray() { 97 static_assert(std::is_pod<T>::value, "T must be a POD"); 98 static_assert(sizeof(T) % align == 0); 99 uint32_t size = read<uint32_t>(); 100 mCurrent = BufferReader::align<T, align>(mCurrent); 101 const T* data = reinterpret_cast<const T*>(mCurrent); 102 mCurrent += size * sizeof(T); 103 return std::make_pair(data, size); 104 } 105 106 template <typename T, size_t align = sizeof(T)> skipArray()107 void skipArray() { 108 static_assert(std::is_pod<T>::value, "T must be a POD"); 109 uint32_t size = read<uint32_t>(); 110 mCurrent = BufferReader::align<T, align>(mCurrent); 111 mCurrent += size * sizeof(T); 112 } 113 readString()114 std::string_view readString() { 115 auto [data, size] = readArray<char>(); 116 return std::string_view(data, size); 117 } 118 skipString()119 void skipString() { skipArray<char>(); } 120 current()121 const void* current() const { return mCurrent; } 122 123 private: 124 const uint8_t* mCurrent; 125 }; 126 127 // This is a helper class to write data to a memory buffer. 128 // 129 // BufferWriter does NOT allocate the memory. 130 // The typical usage is to use BufferWriter twice; in the first pass, write 131 // data with a fake BufferWriter (BufferWriter(nullptr)) to calculate the buffer 132 // size. In the second pass, allocate a memory buffer and use a real 133 // BufferWriter to write the data. 134 // Pseudo code: 135 // BufferWriter fakeWriter(nullptr); 136 // myData.writeTo(&fakeWriter); 137 // void* buffer = malloc(fakeWriter.size()); 138 // BufferWriter realWriter(buffer); 139 // myData.writeTo(&realWriter); 140 class BufferWriter { 141 public: 142 // Create a buffer writer. Passing nullptr creates a fake writer, 143 // which can be used to measure the buffer size needed. BufferWriter(void * buffer)144 explicit BufferWriter(void* buffer) : BufferWriter(buffer, 0) {} BufferWriter(void * buffer,uint32_t pos)145 BufferWriter(void* buffer, uint32_t pos) 146 : mData(reinterpret_cast<uint8_t*>(buffer)), mPos(pos) {} 147 148 BufferWriter(BufferWriter&&) = default; 149 BufferWriter& operator=(BufferWriter&&) = default; 150 151 // Write a single data of type T. 152 // Please always specify T explicitly using <>. std::common_type_t<T> resolves to T, but 153 // disables template argument deduction. 154 // TODO: use std::type_identity_t when C++20 is available. 155 template <typename T, size_t align = sizeof(T)> write(const std::common_type_t<T> & data)156 void write(const std::common_type_t<T>& data) { 157 T* buf = reserve<T, align>(sizeof(T)); 158 if (buf != nullptr) { 159 memcpy(buf, &data, sizeof(T)); 160 } 161 } 162 163 // Reserve a region and return a pointer to the reserved region. 164 // The reserved region is not initialized. 165 template <typename T, size_t align = sizeof(T)> reserve(uint32_t size)166 T* reserve(uint32_t size) { 167 static_assert(std::is_pod<T>::value, "T must be a POD"); 168 mPos = BufferWriter::align<T, align>(mPos); 169 uint32_t pos = mPos; 170 mPos += size; 171 return mData == nullptr ? nullptr : reinterpret_cast<T*>(mData + pos); 172 } 173 174 // Write an array of type T. 175 // Please always specify T explicitly using <>. std::common_type_t<T> resolves to T, but 176 // disables template argument deduction. 177 // TODO: use std::type_identity_t when C++20 is available. 178 template <typename T, size_t align = sizeof(T)> writeArray(const std::common_type_t<T> * data,uint32_t size)179 void writeArray(const std::common_type_t<T>* data, uint32_t size) { 180 static_assert(std::is_pod<T>::value, "T must be a POD"); 181 static_assert(sizeof(T) % align == 0); 182 write<uint32_t>(size); 183 mPos = BufferWriter::align<T, align>(mPos); 184 if (mData != nullptr) { 185 memcpy(mData + mPos, data, size * sizeof(T)); 186 } 187 mPos += size * sizeof(T); 188 } 189 writeString(std::string_view string)190 void writeString(std::string_view string) { writeArray<char>(string.data(), string.size()); } 191 192 // Return the number of bytes written. size()193 size_t size() const { return mPos; } 194 195 private: 196 uint8_t* mData; 197 size_t mPos; 198 199 template <typename T, size_t AlignT> align(size_t pos)200 size_t align(size_t pos) const { 201 return BufferReader::align<T, AlignT>(mData + pos) - mData; 202 } 203 204 // Forbid copy and assign. 205 BufferWriter(const BufferWriter&) = delete; 206 void operator=(const BufferWriter&) = delete; 207 }; 208 209 } // namespace minikin 210 211 #endif // MINIKIN_BUFFER_H 212