| // 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>::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)); | |
| } | |
| } // namespace dap | |
| #endif // dap_any_h |