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