blob: 378f37f077c05d2b3970c4001e9a30b584682cd0 [file] [log] [blame]
/*
* Copyright 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.
*/
#pragma once
#include <numeric>
#include <optional>
#include <type_traits>
#include <vector>
#include <utils/Flattenable.h>
#define RETURN_IF_ERROR(op) \
if (const status_t status = (op); status != OK) return status;
namespace android {
struct FlattenableHelpers {
// Helpers for reading and writing POD structures which are not LightFlattenable.
template <class T,
typename = std::enable_if_t<
std::conjunction_v<std::is_trivially_copyable<T>,
std::negation<std::is_base_of<LightFlattenable<T>, T>>>>>
static constexpr size_t getFlattenedSize(const T&) {
return sizeof(T);
}
template <class T,
typename = std::enable_if_t<
std::conjunction_v<std::is_trivially_copyable<T>,
std::negation<std::is_base_of<LightFlattenable<T>, T>>>>>
static status_t flatten(void** buffer, size_t* size, const T& value) {
if (*size < sizeof(T)) return NO_MEMORY;
FlattenableUtils::write(*buffer, *size, value);
return OK;
}
template <class T,
typename = std::enable_if_t<
std::conjunction_v<std::is_trivially_copyable<T>,
std::negation<std::is_base_of<LightFlattenable<T>, T>>>>>
static status_t unflatten(const void** buffer, size_t* size, T* value) {
if (*size < sizeof(T)) return NO_MEMORY;
FlattenableUtils::read(*buffer, *size, *value);
return OK;
}
// Helpers for reading and writing std::string
static size_t getFlattenedSize(const std::string& str) {
return sizeof(uint64_t) + str.length();
}
static status_t flatten(void** buffer, size_t* size, const std::string& str) {
if (*size < getFlattenedSize(str)) return NO_MEMORY;
flatten(buffer, size, (uint64_t)str.length());
memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length());
FlattenableUtils::advance(*buffer, *size, str.length());
return OK;
}
static status_t unflatten(const void** buffer, size_t* size, std::string* str) {
uint64_t length;
RETURN_IF_ERROR(unflatten(buffer, size, &length));
if (*size < length) return NO_MEMORY;
str->assign(reinterpret_cast<const char*>(*buffer), length);
FlattenableUtils::advance(*buffer, *size, length);
return OK;
}
// Helpers for reading and writing LightFlattenable
template <class T>
static size_t getFlattenedSize(const LightFlattenable<T>& value) {
return value.getFlattenedSize();
}
template <class T>
static status_t flatten(void** buffer, size_t* size, const LightFlattenable<T>& value) {
RETURN_IF_ERROR(value.flatten(*buffer, *size));
FlattenableUtils::advance(*buffer, *size, value.getFlattenedSize());
return OK;
}
template <class T>
static status_t unflatten(const void** buffer, size_t* size, LightFlattenable<T>* value) {
RETURN_IF_ERROR(value->unflatten(*buffer, *size));
FlattenableUtils::advance(*buffer, *size, value->getFlattenedSize());
return OK;
}
// Helpers for reading and writing std::optional
template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
static size_t getFlattenedSize(const std::optional<T>& value) {
return sizeof(bool) + (value ? getFlattenedSize(*value) : 0);
}
template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
static status_t flatten(void** buffer, size_t* size, const std::optional<T>& value) {
if (value) {
RETURN_IF_ERROR(flatten(buffer, size, true));
RETURN_IF_ERROR(flatten(buffer, size, *value));
} else {
RETURN_IF_ERROR(flatten(buffer, size, false));
}
return OK;
}
template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
static status_t unflatten(const void** buffer, size_t* size, std::optional<T>* value) {
bool isPresent;
RETURN_IF_ERROR(unflatten(buffer, size, &isPresent));
if (isPresent) {
*value = T();
RETURN_IF_ERROR(unflatten(buffer, size, &(**value)));
} else {
value->reset();
}
return OK;
}
// Helpers for reading and writing std::vector
template <class T>
static size_t getFlattenedSize(const std::vector<T>& value) {
return std::accumulate(value.begin(), value.end(), sizeof(uint64_t),
[](size_t sum, const T& element) {
return sum + getFlattenedSize(element);
});
}
template <class T>
static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) {
RETURN_IF_ERROR(flatten(buffer, size, (uint64_t)value.size()));
for (const auto& element : value) {
RETURN_IF_ERROR(flatten(buffer, size, element));
}
return OK;
}
template <class T>
static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) {
uint64_t numElements;
RETURN_IF_ERROR(unflatten(buffer, size, &numElements));
// We don't need an extra size check since each iteration of the loop does that
std::vector<T> elements;
for (size_t i = 0; i < numElements; i++) {
T element;
RETURN_IF_ERROR(unflatten(buffer, size, &element));
elements.push_back(element);
}
*value = std::move(elements);
return OK;
}
};
} // namespace android
#undef RETURN_IF_ERROR