mutable
A Database System for Research and Fast Prototyping
Loading...
Searching...
No Matches
Tuple.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <mutable/mutable-config.hpp>
6#include <functional>
7#include <iomanip>
8#include <iostream>
9#include <type_traits>
10
11
12namespace m {
13
14struct Schema;
15struct Type;
16
18struct M_EXPORT Value
19{
20 friend std::hash<Value>;
21
22 using val_t = union {
23 bool b;
24 int64_t i;
25 float f;
26 double d;
27 void *p;
28 };
29
30#ifdef M_ENABLE_SANITY_FIELDS
32 enum value_type {
33 VNone,
34 Vb,
35 Vi,
36 Vf,
37 Vd,
38 Vp,
39 } type = VNone;
40#endif
41
42 private:
44
45 public:
47 memset(&val_, 0, sizeof(val_)); // initialize with 0 bytes
48#ifdef M_ENABLE_SANITY_FIELDS
49 type = VNone;
50#endif
51 }
52
53 template<typename T>
54 Value(T val) : Value() {
55 static_assert(std::is_fundamental_v<T> or std::is_pointer_v<T> or std::is_same_v<T, m::ThreadSafePooledString>,
56 "type T must be either a fundamental, a pointer, or a pooled string type");
57#ifdef M_ENABLE_SANITY_FIELDS
58#define SET_TYPE(TY) this->type = V##TY
59#else
60#define SET_TYPE(TY)
61#endif
62#define SET(TY) { val_.TY = val; SET_TYPE(TY); }
63 if constexpr (std::is_same_v<T, bool>) SET(b)
64 else if constexpr (std::is_integral_v<T>) SET(i)
65 else if constexpr (std::is_same_v<T, float>) SET(f)
66 else if constexpr (std::is_same_v<T, double>) SET(d)
67 else if constexpr (std::is_same_v<T, m::ThreadSafePooledString>) {
68 val_.p = (void*)(const char*)(val); SET_TYPE(p); // TODO: This is a hack. `Value` must *own* the string.
69 }
70 else if constexpr (std::is_pointer_v<T>) { val_.p = (void*)(val); SET_TYPE(p); }
71 else static_assert(not std::is_same_v<T, T>, "unspoorted type T");
72#undef SET
73#undef SET_TYPE
74 }
75
76 /*----- Access methods -------------------------------------------------------------------------------------------*/
78 template<typename T>
79 std::conditional_t<std::is_pointer_v<T>, T, T&> as() {
80#ifdef M_ENABLE_SANITY_FIELDS
81#define VALIDATE_TYPE(TY) M_insist(this->type == V##TY)
82#else
83#define VALIDATE_TYPE(TY)
84#endif
85#define GET(TY) { VALIDATE_TYPE(TY); return val_.TY; }
86 if constexpr (std::is_same_v<T, bool>) GET(b)
87 else if constexpr (std::is_same_v<T, int64_t>) GET(i)
88 else if constexpr (std::is_same_v<T, float>) GET(f)
89 else if constexpr (std::is_same_v<T, double>) GET(d)
90 else if constexpr (std::is_pointer_v<T>) { VALIDATE_TYPE(p); return reinterpret_cast<T>(val_.p); }
91 else static_assert(not std::is_same_v<T, T>, "unsupported type");
92#undef GET
93#undef VALIDATE_TYPE
94 }
95
97 template<typename T>
98 T as() const { return const_cast<Value*>(this)->as<T>(); }
99
101 auto & as_b() { return as<bool>(); }
103 auto & as_i() { return as<int64_t>(); }
105 auto & as_f() { return as<float>(); }
107 auto & as_d() { return as<double>(); }
109 auto as_p() { return as<void*>(); }
110
112 auto as_b() const { return as<bool>(); }
114 auto as_i() const { return as<int64_t>(); }
116 auto as_f() const { return as<float>(); }
118 auto as_d() const { return as<double>(); }
120 auto as_p() const { return as<void*>(); }
121
122 explicit operator bool() const { return as_b(); }
123 explicit operator int32_t() const { return as_i(); }
124 explicit operator int64_t() const { return as_i(); }
125 explicit operator float() const { return as_f(); }
126 explicit operator double() const { return as_d(); }
127
128 template<typename T>
129 explicit operator T*() const { return reinterpret_cast<T*>(as_p()); }
130
131 /*----- Print ----------------------------------------------------------------------------------------------------*/
134 friend std::ostream & operator<<(std::ostream &out, Value val) {
135#ifdef M_ENABLE_SANITY_FIELDS
136 switch (val.type) {
137 case VNone: return out << "<none>";
138 case Vb: return out << (val.as_b() ? "TRUE" : "FALSE");
139 case Vi: return out << val.as_i();
140 case Vf: return out << val.as_f();
141 case Vd: return out << val.as_d();
142 case Vp: return out << val.as_p();
143 }
144#else
145 out << "0x" << std::hex;
146 auto prev_fill = out.fill('0');
147 for (uint8_t *end = reinterpret_cast<uint8_t*>(&val.val_), *ptr = end + sizeof(val.val_); ptr != end; )
148 out << std::setw(2) << uint32_t(*--ptr);
149 out.fill(prev_fill);
150#endif
151 return out << std::dec;
152 }
154
156 void print(std::ostream &out, const Type &ty) const;
157
160 bool operator==(Value other) const {
161#ifdef M_ENABLE_SANITY_FIELDS
162 M_insist(this->type == other.type, "comparing values of different type");
163#endif
164 return memcmp(&this->val_, &other.val_, sizeof(this->val_)) == 0;
165 }
168 bool operator!=(Value other) const { return not operator==(other); }
169
170 void dump(std::ostream &out) const;
171 void dump() const;
172};
173
174static_assert(std::is_move_constructible_v<Value>, "Value must be move constructible");
175static_assert(std::is_trivially_destructible_v<Value>, "Value must be trivially destructible");
176#ifndef M_ENABLE_SANITY_FIELDS
177static_assert(sizeof(Value) == 8, "Value exceeds expected size");
178#endif
179
180struct M_EXPORT Tuple
181{
182 friend std::hash<Tuple>;
183
184 friend void swap(Tuple &first, Tuple &second) {
185 using std::swap;
186 swap(first.values_, second.values_);
187 swap(first.null_mask_, second.null_mask_);
188#ifdef M_ENABLE_SANITY_FIELDS
189 swap(first.num_values_, second.num_values_);
190#endif
191 }
192
193 private:
194 Value *values_ = nullptr;
195 SmallBitset null_mask_ = SmallBitset(-1UL);
196#ifdef M_ENABLE_SANITY_FIELDS
197 std::size_t num_values_ = 0;
198#define INBOUNDS(VAR) M_insist((VAR) < num_values_, "index out of bounds")
199#else
200#define INBOUNDS(VAR)
201#endif
202
203 public:
206 explicit Tuple(const Schema &S);
207 explicit Tuple(std::vector<const Type*> types);
208
209 Tuple() { }
210 Tuple(const Tuple&) = delete;
211 Tuple(Tuple &&other) { swap(*this, other); }
212
213 ~Tuple() { free(values_); }
214
215 Tuple & operator=(const Tuple&) = delete;
216 Tuple & operator=(Tuple &&other) { swap(*this, other); return *this; }
217
219 bool is_null(std::size_t idx) const {
220 INBOUNDS(idx);
221 return null_mask_(idx);
222 }
223
225 void null(std::size_t idx) {
226 INBOUNDS(idx);
227 null_mask_(idx) = true;
228 }
229
231 void clear() { null_mask_ = SmallBitset(~0UL); }
232
234 void not_null(std::size_t idx) {
235 INBOUNDS(idx);
236 null_mask_(idx) = false;
237 }
238
240 void set(std::size_t idx, Value val) {
241 INBOUNDS(idx);
242 not_null(idx);
243 values_[idx] = val;
244 }
245
247 void set(std::size_t idx, Value val, bool is_null) {
248 INBOUNDS(idx);
249 null_mask_(idx) = is_null;
250 values_[idx] = val;
251 }
252
254 Value & operator[](std::size_t idx) {
255 INBOUNDS(idx);
256 return values_[idx];
257 }
258
260 const Value & operator[](std::size_t idx) const { return const_cast<Tuple*>(this)->operator[](idx); }
261
263 Value & get(std::size_t idx) {
264 INBOUNDS(idx);
265 M_insist(not is_null(idx), "Value must not be NULL");
266 return values_[idx];
267 }
268#undef INBOUNDS
269
271 const Value & get(std::size_t idx) const { return const_cast<Tuple*>(this)->get(idx); }
272
274 void insert(const Tuple &other, std::size_t pos, std::size_t len) {
275 for (std::size_t i = 0; i != len; ++i)
276 set(pos + i, other[i], other.is_null(i));
277 }
278
280 Tuple clone(const Schema &S) const;
281
283 friend std::ostream & operator<<(std::ostream &out, const Tuple &tup) {
284#ifdef M_ENABLE_SANITY_FIELDS
285 out << "(";
286 for (std::size_t i = 0; i != tup.num_values_; ++i) {
287 if (i != 0) out << ", ";
288 if (tup.is_null(i)) out << "NULL";
289 else out << tup[i];
290 }
291 return out << ')';
292#else
293 out << "Tuple:";
294 SmallBitset alive(~tup.null_mask_);
295 for (auto i : alive)
296 out << "\n [" << std::setw(2) << i << "]: " << tup.values_[i];
297 return out;
298#endif
299 }
301
302 bool operator==(const Tuple &other) const {
303 if (this->null_mask_ != other.null_mask_) return false;
304 SmallBitset alive(~null_mask_);
305 for (auto idx : alive) {
306 if (this->get(idx) != other.get(idx))
307 return false;
308 }
309 return true;
310 }
311 bool operator!=(const Tuple &other) const { return not operator==(other); }
312
314 void print(std::ostream &out, const Schema &schema) const;
315
316 void dump(std::ostream &out) const;
317 void dump() const;
318};
319
320}
321
322namespace std {
323
324template<>
325struct hash<m::Value>
326{
327 uint64_t operator()(m::Value val) const {
328 /* Inspired by FNV-1a 64 bit. */
329 uint64_t hash = 0xcbf29ce484222325;
330 for (uint64_t *p = reinterpret_cast<uint64_t*>(&val.val_), *end = p + sizeof(val.val_) / sizeof(uint64_t);
331 p != end; ++p)
332 {
333 hash ^= *p;
334 hash *= 1099511628211;
335 }
336 return hash;
337 }
338};
339
340template<>
341struct hash<m::Tuple>
342{
343 uint64_t operator()(const m::Tuple &tup) const {
344 std::hash<m::Value> h;
345 auto mask = m::SmallBitset(~tup.null_mask_);
346 uint64_t hash = 0;
347 for (auto idx : mask) {
348 hash ^= h(tup[idx]);
349 hash *= 1099511628211;
350 }
351 return hash;
352 }
353};
354
355}
#define SET_TYPE(TY)
#define GET(TY)
#define INBOUNDS(VAR)
Definition: Tuple.hpp:200
#define VALIDATE_TYPE(TY)
#define SET(TY)
#define M_insist(...)
Definition: macro.hpp:129
‍mutable namespace
Definition: Backend.hpp:10
void swap(PlanTableBase< Actual > &first, PlanTableBase< Actual > &second)
Definition: PlanTable.hpp:394
const Type
Definition: Type.hpp:498
T(x)
STL namespace.
A Schema represents a sequence of identifiers, optionally with a prefix, and their associated types.
Definition: Schema.hpp:39
Implements a small and efficient set over integers in the range of 0 to 63 (including).
Definition: ADT.hpp:26
~Tuple()
Definition: Tuple.hpp:213
Tuple()
Definition: Tuple.hpp:209
const Value & get(std::size_t idx) const
Returns a reference to the Value at index idx.
Definition: Tuple.hpp:271
Value & get(std::size_t idx)
Returns a reference to the Value at index idx.
Definition: Tuple.hpp:263
Value * values_
the Values in this Tuple
Definition: Tuple.hpp:194
void clear()
Sets all Values of this Tuple to NULL.
Definition: Tuple.hpp:231
void insert(const Tuple &other, std::size_t pos, std::size_t len)
Inserts the Tuple other of length len into this Tuple starting at index pos.
Definition: Tuple.hpp:274
bool operator!=(const Tuple &other) const
Definition: Tuple.hpp:311
M_LCOV_EXCL_STOP bool operator==(const Tuple &other) const
Definition: Tuple.hpp:302
Tuple(Tuple &&other)
Definition: Tuple.hpp:211
Tuple & operator=(Tuple &&other)
Definition: Tuple.hpp:216
const Value & operator[](std::size_t idx) const
Returns the Value at index idx.
Definition: Tuple.hpp:260
Tuple(const Tuple &)=delete
Tuple & operator=(const Tuple &)=delete
SmallBitset null_mask_
a bit mask for the NULL values; 1 represents NULL
Definition: Tuple.hpp:195
friend void swap(Tuple &first, Tuple &second)
Definition: Tuple.hpp:184
void null(std::size_t idx)
Sets the Value at index idx to NULL.
Definition: Tuple.hpp:225
Value & operator[](std::size_t idx)
Returns a reference to the Value at index idx.
Definition: Tuple.hpp:254
void set(std::size_t idx, Value val, bool is_null)
Assigns the Value val to this Tuple at index idx and sets the respective NULL bit to is_null.
Definition: Tuple.hpp:247
M_LCOV_EXCL_START friend std::ostream & operator<<(std::ostream &out, const Tuple &tup)
Definition: Tuple.hpp:283
bool is_null(std::size_t idx) const
Returns true iff the Value at index idx is NULL.
Definition: Tuple.hpp:219
void not_null(std::size_t idx)
Sets the Value at index idx to not-NULL.
Definition: Tuple.hpp:234
void set(std::size_t idx, Value val)
Assigns the Value val to this Tuple at index idx and clears the respective NULL bit.
Definition: Tuple.hpp:240
This class represents types in the SQL type system.
Definition: Type.hpp:46
This class holds a SQL attribute value.
Definition: Tuple.hpp:19
Value(T val)
Definition: Tuple.hpp:54
void * p
Definition: Tuple.hpp:27
Value()
Definition: Tuple.hpp:46
auto & as_f()
Returns a reference to the value interpreted as of type float.
Definition: Tuple.hpp:105
val_t val_
Definition: Tuple.hpp:43
bool operator!=(Value other) const
Checks whether this Value is not equal to other.
Definition: Tuple.hpp:168
bool operator==(Value other) const
Checks whether this Value is equal to other.
Definition: Tuple.hpp:160
auto as_f() const
Returns the value interpreted as of type float.
Definition: Tuple.hpp:116
auto as_p()
Returns a reference to the value interpreted as of type void*.
Definition: Tuple.hpp:109
auto & as_b()
Returns a reference to the value interpreted as of type bool.
Definition: Tuple.hpp:101
auto & as_d()
Returns a reference to the value interpreted as of type double.
Definition: Tuple.hpp:107
auto as_i() const
Returns the value interpreted as of type int64_t.
Definition: Tuple.hpp:114
auto & as_i()
Returns a reference to the value interpreted as of type int64_t.
Definition: Tuple.hpp:103
auto as_d() const
Returns the value interpreted as of type double.
Definition: Tuple.hpp:118
T as() const
Returns the value interpreted as of type T.
Definition: Tuple.hpp:98
float f
Definition: Tuple.hpp:25
std::conditional_t< std::is_pointer_v< T >, T, T & > as()
Returns a reference to the value interpreted as of type T.
Definition: Tuple.hpp:79
int64_t i
Definition: Tuple.hpp:24
M_LCOV_EXCL_START friend std::ostream & operator<<(std::ostream &out, Value val)
Print a hexdump of val to out.
Definition: Tuple.hpp:134
double d
Definition: Tuple.hpp:26
auto as_p() const
Returns the value interpreted as of type void*.
Definition: Tuple.hpp:120
auto as_b() const
Returns the value interpreted as of type bool.
Definition: Tuple.hpp:112
union { bool b val_t
Definition: Tuple.hpp:23
uint64_t operator()(const m::Tuple &tup) const
Definition: Tuple.hpp:343
uint64_t operator()(m::Value val) const
Definition: Tuple.hpp:327