summaryrefslogtreecommitdiffstats
path: root/include/dap/any.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/dap/any.h')
-rw-r--r--include/dap/any.h211
1 files changed, 211 insertions, 0 deletions
diff --git a/include/dap/any.h b/include/dap/any.h
new file mode 100644
index 0000000..b05f03d
--- /dev/null
+++ b/include/dap/any.h
@@ -0,0 +1,211 @@
+// 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