#ifndef dap_any_h
#define dap_any_h
#include "typeinfo.h"
#include <assert.h>
#include <stdint.h>
namespace dap {
template <typename T>
struct TypeOf;
class Deserializer;
class Serializer;
class any {
public:
inline any() = default;
inline any(const any& other) noexcept;
inline any(any&& other) noexcept;
template <typename T>
inline any(const T& val);
inline ~any();
inline void reset();
inline any& operator=(const any& rhs);
inline any& operator=(any&& rhs) noexcept;
template <typename T>
inline any& operator=(const T& val);
inline any& operator=(const std::nullptr_t& val);
template <typename T>
inline T& get() const;
template <typename T>
inline bool is() const;
private:
friend class Deserializer;
friend class Serializer;
static inline void* alignUp(void* val, size_t alignment);
inline void alloc(size_t size, size_t align);
inline void free();
inline bool isInBuffer(void* ptr) const;
void* value = nullptr;
const TypeInfo* type = nullptr;
void* heap = nullptr;
uint8_t buffer[32];
};
inline any::~any() {
reset();
}
template <typename T>
inline any::any(const T& val) {
*this = val;
}
any::any(const any& other) noexcept : type(other.type) {
if (other.value != nullptr) {
alloc(type->size(), type->alignment());
type->copyConstruct(value, other.value);
}
}
any::any(any&& other) noexcept : type(other.type) {
if (other.isInBuffer(other.value)) {
alloc(type->size(), type->alignment());
type->copyConstruct(value, other.value);
} else {
value = other.value;
}
other.value = nullptr;
other.type = nullptr;
}
void any::reset() {
if (value != nullptr) {
type->destruct(value);
free();
}
value = nullptr;
type = nullptr;
}
any& any::operator=(const any& rhs) {
reset();
type = rhs.type;
if (rhs.value != nullptr) {
alloc(type->size(), type->alignment());
type->copyConstruct(value, rhs.value);
}
return *this;
}
any& any::operator=(any&& rhs) noexcept {
reset();
type = rhs.type;
if (rhs.isInBuffer(rhs.value)) {
alloc(type->size(), type->alignment());
type->copyConstruct(value, rhs.value);
} else {
value = rhs.value;
}
rhs.value = nullptr;
rhs.type = nullptr;
return *this;
}
template <typename T>
any& any::operator=(const T& val) {
if (!is<T>()) {
reset();
type = TypeOf<T>::type();
alloc(type->size(), type->alignment());
type->copyConstruct(value, &val);
} else {
#ifdef __clang_analyzer__
assert(value != nullptr);
#endif
*reinterpret_cast<T*>(value) = val;
}
return *this;
}
any& any::operator=(const std::nullptr_t&) {
reset();
return *this;
}
template <typename T>
T& any::get() const {
static_assert(!std::is_same<T, std::nullptr_t>::value,
"Cannot get nullptr from 'any'.");
assert(is<T>());
return *reinterpret_cast<T*>(value);
}
template <typename T>
bool any::is() const {
return type == TypeOf<T>::type();
}
template <>
inline bool any::is<std::nullptr_t>() const {
return value == nullptr;
}
void* any::alignUp(void* val, size_t alignment) {
auto ptr = reinterpret_cast<uintptr_t>(val);
return reinterpret_cast<void*>(alignment *
((ptr + alignment - 1) / alignment));
}
void any::alloc(size_t size, size_t align) {
assert(value == nullptr);
value = alignUp(buffer, align);
if (isInBuffer(reinterpret_cast<uint8_t*>(value) + size - 1)) {
return;
}
heap = new uint8_t[size + align];
value = alignUp(heap, align);
}
void any::free() {
assert(value != nullptr);
if (heap != nullptr) {
delete[] reinterpret_cast<uint8_t*>(heap);
heap = nullptr;
}
value = nullptr;
}
bool any::isInBuffer(void* ptr) const {
auto addr = reinterpret_cast<uintptr_t>(ptr);
return addr >= reinterpret_cast<uintptr_t>(buffer) &&
addr < reinterpret_cast<uintptr_t>(buffer + sizeof(buffer));
}
}
#endif