1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef dap_any_h
16#define dap_any_h
17
18#include "typeinfo.h"
19
20#include <assert.h>
21#include <stdint.h>
22
23namespace dap {
24
25template <typename T>
26struct TypeOf;
27
28// any provides a type-safe container for values of any of dap type (boolean,
29// integer, number, array, variant, any, null, dap-structs).
30class any {
31 public:
32 // constructors
33 inline any() = default;
34 inline any(const any& other) noexcept;
35 inline any(any&& other) noexcept;
36
37 template <typename T>
38 inline any(const T& val);
39
40 // destructors
41 inline ~any();
42
43 // replaces the contained value with a null.
44 inline void reset();
45
46 // assignment
47 inline any& operator=(const any& rhs);
48 inline any& operator=(any&& rhs) noexcept;
49 template <typename T>
50 inline any& operator=(const T& val);
51
52 // get() returns the contained value of the type T.
53 // If the any does not contain a value of type T, then get() will assert.
54 template <typename T>
55 inline T& get() const;
56
57 // is() returns true iff the contained value is of type T.
58 template <typename T>
59 inline bool is() const;
60
61 private:
62 static inline void* alignUp(void* val, size_t alignment);
63 inline void alloc(size_t size, size_t align);
64 inline void free();
65 inline bool isInBuffer(void* ptr) const;
66
67 void* value = nullptr;
68 const TypeInfo* type = nullptr;
69 void* heap = nullptr; // heap allocation
70 uint8_t buffer[32]; // or internal allocation
71};
72
73inline any::~any() {
74 reset();
75}
76
77template <typename T>
78inline any::any(const T& val) {
79 *this = val;
80}
81
82any::any(const any& other) noexcept : type(other.type) {
83 if (other.value != nullptr) {
84 alloc(type->size(), type->alignment());
85 type->copyConstruct(value, other.value);
86 }
87}
88
89any::any(any&& other) noexcept : type(other.type) {
90 if (other.isInBuffer(other.value)) {
91 alloc(type->size(), type->alignment());
92 type->copyConstruct(value, other.value);
93 } else {
94 value = other.value;
95 }
96 other.value = nullptr;
97 other.type = nullptr;
98}
99
100void any::reset() {
101 if (value != nullptr) {
102 type->destruct(value);
103 free();
104 }
105 value = nullptr;
106 type = nullptr;
107}
108
109any& any::operator=(const any& rhs) {
110 reset();
111 type = rhs.type;
112 if (rhs.value != nullptr) {
113 alloc(type->size(), type->alignment());
114 type->copyConstruct(value, rhs.value);
115 }
116 return *this;
117}
118
119any& any::operator=(any&& rhs) noexcept {
120 reset();
121 type = rhs.type;
122 if (rhs.isInBuffer(rhs.value)) {
123 alloc(type->size(), type->alignment());
124 type->copyConstruct(value, rhs.value);
125 } else {
126 value = rhs.value;
127 }
128 rhs.value = nullptr;
129 rhs.type = nullptr;
130 return *this;
131}
132
133template <typename T>
134any& any::operator=(const T& val) {
135 if (!is<T>()) {
136 reset();
137 type = TypeOf<T>::type();
138 alloc(type->size(), type->alignment());
139 type->copyConstruct(value, &val);
140 } else {
141 *reinterpret_cast<T*>(value) = val;
142 }
143 return *this;
144}
145
146template <typename T>
147T& any::get() const {
148 assert(is<T>());
149 return *reinterpret_cast<T*>(value);
150}
151
152template <typename T>
153bool any::is() const {
154 return type == TypeOf<T>::type();
155}
156
157template <>
158inline bool any::is<std::nullptr_t>() const {
159 return value == nullptr;
160}
161
162void* any::alignUp(void* val, size_t alignment) {
163 auto ptr = reinterpret_cast<uintptr_t>(val);
164 return reinterpret_cast<void*>(alignment *
165 ((ptr + alignment - 1) / alignment));
166}
167
168void any::alloc(size_t size, size_t align) {
169 assert(value == nullptr);
170 value = alignUp(buffer, align);
171 if (isInBuffer(reinterpret_cast<uint8_t*>(value) + size - 1)) {
172 return;
173 }
174 heap = new uint8_t[size + align];
175 value = alignUp(heap, align);
176}
177
178void any::free() {
179 assert(value != nullptr);
180 if (heap != nullptr) {
181 delete[] reinterpret_cast<uint8_t*>(heap);
182 heap = nullptr;
183 }
184 value = nullptr;
185}
186
187bool any::isInBuffer(void* ptr) const {
188 auto addr = reinterpret_cast<uintptr_t>(ptr);
189 return addr >= reinterpret_cast<uintptr_t>(buffer) &&
190 addr < reinterpret_cast<uintptr_t>(buffer + sizeof(buffer));
191}
192
193} // namespace dap
194
195#endif // dap_any_h
196