blob: a4d01044772cb90c9ded2013f646c62ba619279c [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MINIKIN_BUFFER_H
#define MINIKIN_BUFFER_H
#include <cstring>
#include <string_view>
#include <type_traits>
#include <utility>
namespace minikin {
// This is a helper class to read data from a memory buffer.
// This class does not copy memory, and may return pointers to parts of the memory buffer.
// Thus the memory buffer should outlive objects created using this class.
//
// Note on alignment:
// Some CPU archs (e.g. arm32) do not allow misaligned memory access.
// Therefore, BufferReader and BufferWriter automatically insert paddings
// to align data records.
// For the padding to be deterministic, the following conditions must be met:
// (1) Alignment and size of each data record must be fixed regardless of
// CPU arch.
// (2) Alignment for each data record must be a power of 2 (2^n) and
// must be less than or equal to kMaxAlignment.
// (3) The head address of the buffer must be aligned at kMaxAlignment.
//
// The condition (2) and (3) ensures that 'headAddress % align == 0'
// and the padding is determined only by the current position.
// I.e. mCurrent % align == (mCurrent - headAddress) % align.
class BufferReader {
public:
static constexpr size_t kMaxAlignment = 8;
explicit BufferReader(const void* buffer) : BufferReader(buffer, 0) {}
BufferReader(const void* buffer, uint32_t pos)
: mCurrent(reinterpret_cast<const uint8_t*>(buffer) + pos) {}
// align() adds padding if necessary so that the returned pointer is aligned
// at 'align' template parameter (i.e. align<T, _align>(p) % _align == 0).
//
// By default we align to sizeof(T) instead of alignof(T), because the
// buffer may be shared between 32-bit processes and 64-bit processes.
// The value of alignof(T) may change between the two.
//
// If T is a large struct or class, you would need to specify 'align'
// template parameter manually.
template <typename T, size_t align = sizeof(T)>
static const uint8_t* align(const uint8_t* p) {
static_assert(align <= kMaxAlignment);
static_assert(__builtin_popcount(align) == 1, "align must be a power of 2");
constexpr size_t mask = align - 1;
intptr_t i = reinterpret_cast<intptr_t>(p);
intptr_t aligned = (i + mask) & ~mask;
return reinterpret_cast<const uint8_t*>(aligned);
}
template <typename T, size_t align = sizeof(T)>
const T& read() {
const T* data = map<T, align>(sizeof(T));
return *data;
}
template <typename T, size_t align = sizeof(T)>
const T* map(uint32_t size) {
static_assert(std::is_pod<T>::value, "T must be a POD");
mCurrent = BufferReader::align<T, align>(mCurrent);
const T* data = reinterpret_cast<const T*>(mCurrent);
mCurrent += size;
return data;
}
template <typename T, size_t align = sizeof(T)>
void skip() {
static_assert(std::is_pod<T>::value, "T must be a POD");
mCurrent = BufferReader::align<T, align>(mCurrent);
mCurrent += sizeof(T);
}
// Return a pointer to an array and its number of elements.
template <typename T, size_t align = sizeof(T)>
std::pair<const T*, uint32_t> readArray() {
static_assert(std::is_pod<T>::value, "T must be a POD");
static_assert(sizeof(T) % align == 0);
uint32_t size = read<uint32_t>();
mCurrent = BufferReader::align<T, align>(mCurrent);
const T* data = reinterpret_cast<const T*>(mCurrent);
mCurrent += size * sizeof(T);
return std::make_pair(data, size);
}
template <typename T, size_t align = sizeof(T)>
void skipArray() {
static_assert(std::is_pod<T>::value, "T must be a POD");
uint32_t size = read<uint32_t>();
mCurrent = BufferReader::align<T, align>(mCurrent);
mCurrent += size * sizeof(T);
}
std::string_view readString() {
auto [data, size] = readArray<char>();
return std::string_view(data, size);
}
void skipString() { skipArray<char>(); }
const void* current() const { return mCurrent; }
private:
const uint8_t* mCurrent;
};
// This is a helper class to write data to a memory buffer.
//
// BufferWriter does NOT allocate the memory.
// The typical usage is to use BufferWriter twice; in the first pass, write
// data with a fake BufferWriter (BufferWriter(nullptr)) to calculate the buffer
// size. In the second pass, allocate a memory buffer and use a real
// BufferWriter to write the data.
// Pseudo code:
// BufferWriter fakeWriter(nullptr);
// myData.writeTo(&fakeWriter);
// void* buffer = malloc(fakeWriter.size());
// BufferWriter realWriter(buffer);
// myData.writeTo(&realWriter);
class BufferWriter {
public:
// Create a buffer writer. Passing nullptr creates a fake writer,
// which can be used to measure the buffer size needed.
explicit BufferWriter(void* buffer) : BufferWriter(buffer, 0) {}
BufferWriter(void* buffer, uint32_t pos)
: mData(reinterpret_cast<uint8_t*>(buffer)), mPos(pos) {}
BufferWriter(BufferWriter&&) = default;
BufferWriter& operator=(BufferWriter&&) = default;
// Write a single data of type T.
// Please always specify T explicitly using <>. std::common_type_t<T> resolves to T, but
// disables template argument deduction.
// TODO: use std::type_identity_t when C++20 is available.
template <typename T, size_t align = sizeof(T)>
void write(const std::common_type_t<T>& data) {
T* buf = reserve<T, align>(sizeof(T));
if (buf != nullptr) {
memcpy(buf, &data, sizeof(T));
}
}
// Reserve a region and return a pointer to the reserved region.
// The reserved region is not initialized.
template <typename T, size_t align = sizeof(T)>
T* reserve(uint32_t size) {
static_assert(std::is_pod<T>::value, "T must be a POD");
mPos = BufferWriter::align<T, align>(mPos);
uint32_t pos = mPos;
mPos += size;
return mData == nullptr ? nullptr : reinterpret_cast<T*>(mData + pos);
}
// Write an array of type T.
// Please always specify T explicitly using <>. std::common_type_t<T> resolves to T, but
// disables template argument deduction.
// TODO: use std::type_identity_t when C++20 is available.
template <typename T, size_t align = sizeof(T)>
void writeArray(const std::common_type_t<T>* data, uint32_t size) {
static_assert(std::is_pod<T>::value, "T must be a POD");
static_assert(sizeof(T) % align == 0);
write<uint32_t>(size);
mPos = BufferWriter::align<T, align>(mPos);
if (mData != nullptr) {
memcpy(mData + mPos, data, size * sizeof(T));
}
mPos += size * sizeof(T);
}
void writeString(std::string_view string) { writeArray<char>(string.data(), string.size()); }
// Return the number of bytes written.
size_t size() const { return mPos; }
private:
uint8_t* mData;
size_t mPos;
template <typename T, size_t align>
size_t align(size_t pos) const {
return BufferReader::align<T, align>(mData + pos) - mData;
}
// Forbid copy and assign.
BufferWriter(const BufferWriter&) = delete;
void operator=(const BufferWriter&) = delete;
};
} // namespace minikin
#endif // MINIKIN_BUFFER_H