// 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 #include namespace dap { template 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 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 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 inline T& get() const; // is() returns true iff the contained value is of type T. template 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 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 any& any::operator=(const T& val) { if (!is()) { reset(); type = TypeOf::type(); alloc(type->size(), type->alignment()); type->copyConstruct(value, &val); } else { #ifdef __clang_analyzer__ assert(value != nullptr); #endif *reinterpret_cast(value) = val; } return *this; } any& any::operator=(const std::nullptr_t&) { reset(); return *this; } template T& any::get() const { static_assert(!std::is_same(), "Cannot get nullptr from 'any'."); assert(is()); return *reinterpret_cast(value); } template bool any::is() const { return type == TypeOf::type(); } template <> inline bool any::is() const { return value == nullptr; } void* any::alignUp(void* val, size_t alignment) { auto ptr = reinterpret_cast(val); return reinterpret_cast(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(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(heap); heap = nullptr; } value = nullptr; } bool any::isInBuffer(void* ptr) const { auto addr = reinterpret_cast(ptr); return addr >= reinterpret_cast(buffer) && addr < reinterpret_cast(buffer + sizeof(buffer)); } } // namespace dap #endif // dap_any_h