You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
4.9 KiB
212 lines
4.9 KiB
2 years ago
|
// Copyright 2019 Google LLC
|
||
|
//
|
||
|
// 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
|
||
|
//
|
||
|
// https://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 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;
|
||
|
|
||
|
// any provides a type-safe container for values of any of dap type (boolean,
|
||
|
// integer, number, array, variant, any, null, dap-structs).
|
||
|
class any {
|
||
|
public:
|
||
|
// constructors
|
||
|
inline any() = default;
|
||
|
inline any(const any& other) noexcept;
|
||
|
inline any(any&& other) noexcept;
|
||
|
|
||
|
template <typename T>
|
||
|
inline any(const T& val);
|
||
|
|
||
|
// destructors
|
||
|
inline ~any();
|
||
|
|
||
|
// replaces the contained value with a null.
|
||
|
inline void reset();
|
||
|
|
||
|
// assignment
|
||
|
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);
|
||
|
|
||
|
// get() returns the contained value of the type T.
|
||
|
// If the any does not contain a value of type T, then get() will assert.
|
||
|
template <typename T>
|
||
|
inline T& get() const;
|
||
|
|
||
|
// is() returns true iff the contained value is of type T.
|
||
|
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; // heap allocation
|
||
|
uint8_t buffer[32]; // or internal allocation
|
||
|
};
|
||
|
|
||
|
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>(),
|
||
|
"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));
|
||
|
}
|
||
|
|
||
|
} // namespace dap
|
||
|
|
||
|
#endif // dap_any_h
|