added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / mixed.hpp
1 /*************************************************************************
2  *
3  * Copyright 2016 Realm Inc.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  **************************************************************************/
18
19 #ifndef REALM_MIXED_HPP
20 #define REALM_MIXED_HPP
21
22 #include <cstdint> // int64_t - not part of C++03, not even required by C++11 (see C++11 section 18.4.1)
23
24 #include <cstddef> // size_t
25 #include <cstring>
26
27 #include <realm/binary_data.hpp>
28 #include <realm/data_type.hpp>
29 #include <realm/olddatetime.hpp>
30 #include <realm/string_data.hpp>
31 #include <realm/timestamp.hpp>
32 #include <realm/util/assert.hpp>
33 #include <realm/utilities.hpp>
34
35 namespace realm {
36
37
38 /// This class represents a polymorphic Realm value.
39 ///
40 /// At any particular moment an instance of this class stores a
41 /// definite value of a definite type. If, for instance, that is an
42 /// integer value, you may call get_int() to extract that value. You
43 /// may call get_type() to discover what type of value is currently
44 /// stored. Calling get_int() on an instance that does not store an
45 /// integer, has undefined behavior, and likewise for all the other
46 /// types that can be stored.
47 ///
48 /// It is crucial to understand that the act of extracting a value of
49 /// a particular type requires definite knowledge about the stored
50 /// type. Calling a getter method for any particular type, that is not
51 /// the same type as the stored value, has undefined behavior.
52 ///
53 /// While values of numeric types are contained directly in a Mixed
54 /// instance, character and binary data are merely referenced. A Mixed
55 /// instance never owns the referenced data, nor does it in any other
56 /// way attempt to manage its lifetime.
57 ///
58 /// For compatibility with C style strings, when a string (character
59 /// data) is stored in a Realm database, it is always followed by a
60 /// terminating null character. This is also true when strings are
61 /// stored in a mixed type column. This means that in the following
62 /// code, if the 'mixed' value of the 8th row stores a string, then \c
63 /// c_str will always point to a null-terminated string:
64 ///
65 /// \code{.cpp}
66 ///
67 ///   const char* c_str = my_table[7].mixed.data(); // Always null-terminated
68 ///
69 /// \endcode
70 ///
71 /// Note that this assumption does not hold in general for strings in
72 /// instances of Mixed. Indeed there is nothing stopping you from
73 /// constructing a new Mixed instance that refers to a string without
74 /// a terminating null character.
75 ///
76 /// At the present time no soultion has been found that would allow
77 /// for a Mixed instance to directly store a reference to a table. The
78 /// problem is roughly as follows: From most points of view, the
79 /// desirable thing to do, would be to store the table reference in a
80 /// Mixed instance as a plain pointer without any ownership
81 /// semantics. This would have no negative impact on the performance
82 /// of copying and destroying Mixed instances, and it would serve just
83 /// fine for passing a table as argument when setting the value of an
84 /// entry in a mixed column. In that case a copy of the referenced
85 /// table would be inserted into the mixed column.
86 ///
87 /// On the other hand, when retrieving a table reference from a mixed
88 /// column, storing it as a plain pointer in a Mixed instance is no
89 /// longer an acceptable option. The complex rules for managing the
90 /// lifetime of a Table instance, that represents a subtable,
91 /// necessitates the use of a "smart pointer" such as
92 /// TableRef. Enhancing the Mixed class to be able to act as a
93 /// TableRef would be possible, but would also lead to several new
94 /// problems. One problem is the risk of a Mixed instance outliving a
95 /// stack allocated Table instance that it references. This would be a
96 /// fatal error. Another problem is the impact that the nontrivial
97 /// table reference has on the performance of copying and destroying
98 /// Mixed instances.
99 ///
100 /// \sa StringData
101 class Mixed {
102 public:
103     Mixed() noexcept;
104
105     Mixed(bool) noexcept;
106     Mixed(int64_t) noexcept;
107     Mixed(float) noexcept;
108     Mixed(double) noexcept;
109     Mixed(StringData) noexcept;
110     Mixed(BinaryData) noexcept;
111     Mixed(OldDateTime) noexcept;
112     Mixed(Timestamp) noexcept;
113
114     // These are shortcuts for Mixed(StringData(c_str)), and are
115     // needed to avoid unwanted implicit conversion of char* to bool.
116     Mixed(char* c_str) noexcept
117     {
118         set_string(c_str);
119     }
120     Mixed(const char* c_str) noexcept
121     {
122         set_string(c_str);
123     }
124
125     struct subtable_tag {
126     };
127     Mixed(subtable_tag) noexcept
128         : m_type(type_Table)
129     {
130     }
131
132     ~Mixed() noexcept
133     {
134     }
135
136     DataType get_type() const noexcept
137     {
138         return m_type;
139     }
140
141     int64_t get_int() const noexcept;
142     bool get_bool() const noexcept;
143     float get_float() const noexcept;
144     double get_double() const noexcept;
145     StringData get_string() const noexcept;
146     BinaryData get_binary() const noexcept;
147     OldDateTime get_olddatetime() const noexcept;
148     Timestamp get_timestamp() const noexcept;
149
150     void set_int(int64_t) noexcept;
151     void set_bool(bool) noexcept;
152     void set_float(float) noexcept;
153     void set_double(double) noexcept;
154     void set_string(StringData) noexcept;
155     void set_binary(BinaryData) noexcept;
156     void set_binary(const char* data, size_t size) noexcept;
157     void set_olddatetime(OldDateTime) noexcept;
158     void set_timestamp(Timestamp) noexcept;
159
160     template <class Ch, class Tr>
161     friend std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>&, const Mixed&);
162
163 private:
164     DataType m_type;
165     union {
166         int64_t m_int;
167         bool m_bool;
168         float m_float;
169         double m_double;
170         const char* m_data;
171         int_fast64_t m_date;
172         Timestamp m_timestamp;
173     };
174     size_t m_size = 0;
175 };
176
177 // Note: We cannot compare two mixed values, since when the type of
178 // both is type_Table, we would have to compare the two tables, but
179 // the mixed values do not provide access to those tables.
180
181 // Note: The mixed values are specified as Wrap<Mixed>. If they were
182 // not, these operators would apply to simple comparisons, such as int
183 // vs int64_t, and cause ambiguity. This is because the constructors
184 // of Mixed are not explicit.
185
186 // Compare mixed with integer
187 template <class T>
188 bool operator==(Wrap<Mixed>, const T&) noexcept;
189 template <class T>
190 bool operator!=(Wrap<Mixed>, const T&) noexcept;
191 template <class T>
192 bool operator==(const T&, Wrap<Mixed>) noexcept;
193 template <class T>
194 bool operator!=(const T&, Wrap<Mixed>) noexcept;
195
196 // Compare mixed with boolean
197 bool operator==(Wrap<Mixed>, bool) noexcept;
198 bool operator!=(Wrap<Mixed>, bool) noexcept;
199 bool operator==(bool, Wrap<Mixed>) noexcept;
200 bool operator!=(bool, Wrap<Mixed>) noexcept;
201
202 // Compare mixed with float
203 bool operator==(Wrap<Mixed>, float);
204 bool operator!=(Wrap<Mixed>, float);
205 bool operator==(float, Wrap<Mixed>);
206 bool operator!=(float, Wrap<Mixed>);
207
208 // Compare mixed with double
209 bool operator==(Wrap<Mixed>, double);
210 bool operator!=(Wrap<Mixed>, double);
211 bool operator==(double, Wrap<Mixed>);
212 bool operator!=(double, Wrap<Mixed>);
213
214 // Compare mixed with string
215 bool operator==(Wrap<Mixed>, StringData) noexcept;
216 bool operator!=(Wrap<Mixed>, StringData) noexcept;
217 bool operator==(StringData, Wrap<Mixed>) noexcept;
218 bool operator!=(StringData, Wrap<Mixed>) noexcept;
219 bool operator==(Wrap<Mixed>, const char* c_str) noexcept;
220 bool operator!=(Wrap<Mixed>, const char* c_str) noexcept;
221 bool operator==(const char* c_str, Wrap<Mixed>) noexcept;
222 bool operator!=(const char* c_str, Wrap<Mixed>) noexcept;
223 bool operator==(Wrap<Mixed>, char* c_str) noexcept;
224 bool operator!=(Wrap<Mixed>, char* c_str) noexcept;
225 bool operator==(char* c_str, Wrap<Mixed>) noexcept;
226 bool operator!=(char* c_str, Wrap<Mixed>) noexcept;
227
228 // Compare mixed with binary data
229 bool operator==(Wrap<Mixed>, BinaryData) noexcept;
230 bool operator!=(Wrap<Mixed>, BinaryData) noexcept;
231 bool operator==(BinaryData, Wrap<Mixed>) noexcept;
232 bool operator!=(BinaryData, Wrap<Mixed>) noexcept;
233
234 // Compare mixed with date
235 bool operator==(Wrap<Mixed>, OldDateTime) noexcept;
236 bool operator!=(Wrap<Mixed>, OldDateTime) noexcept;
237 bool operator==(OldDateTime, Wrap<Mixed>) noexcept;
238 bool operator!=(OldDateTime, Wrap<Mixed>) noexcept;
239
240 // Compare mixed with Timestamp
241 bool operator==(Wrap<Mixed>, Timestamp) noexcept;
242 bool operator!=(Wrap<Mixed>, Timestamp) noexcept;
243 bool operator==(Timestamp, Wrap<Mixed>) noexcept;
244 bool operator!=(Timestamp, Wrap<Mixed>) noexcept;
245
246 // Implementation:
247
248 inline Mixed::Mixed() noexcept
249 {
250     m_type = type_Int;
251     m_int = 0;
252 }
253
254 inline Mixed::Mixed(int64_t v) noexcept
255 {
256     m_type = type_Int;
257     m_int = v;
258 }
259
260 inline Mixed::Mixed(bool v) noexcept
261 {
262     m_type = type_Bool;
263     m_bool = v;
264 }
265
266 inline Mixed::Mixed(float v) noexcept
267 {
268     m_type = type_Float;
269     m_float = v;
270 }
271
272 inline Mixed::Mixed(double v) noexcept
273 {
274     m_type = type_Double;
275     m_double = v;
276 }
277
278 inline Mixed::Mixed(StringData v) noexcept
279 {
280     m_type = type_String;
281     m_data = v.data();
282     m_size = v.size();
283 }
284
285 inline Mixed::Mixed(BinaryData v) noexcept
286 {
287     m_type = type_Binary;
288     m_data = v.data();
289     m_size = v.size();
290 }
291
292 inline Mixed::Mixed(OldDateTime v) noexcept
293 {
294     m_type = type_OldDateTime;
295     m_date = v.get_olddatetime();
296 }
297
298 inline Mixed::Mixed(Timestamp v) noexcept
299 {
300     m_type = type_Timestamp;
301     m_timestamp = v;
302 }
303
304 inline int64_t Mixed::get_int() const noexcept
305 {
306     REALM_ASSERT(m_type == type_Int);
307     return m_int;
308 }
309
310 inline bool Mixed::get_bool() const noexcept
311 {
312     REALM_ASSERT(m_type == type_Bool);
313     return m_bool;
314 }
315
316 inline float Mixed::get_float() const noexcept
317 {
318     REALM_ASSERT(m_type == type_Float);
319     return m_float;
320 }
321
322 inline double Mixed::get_double() const noexcept
323 {
324     REALM_ASSERT(m_type == type_Double);
325     return m_double;
326 }
327
328 inline StringData Mixed::get_string() const noexcept
329 {
330     REALM_ASSERT(m_type == type_String);
331     return StringData(m_data, m_size);
332 }
333
334 inline BinaryData Mixed::get_binary() const noexcept
335 {
336     REALM_ASSERT(m_type == type_Binary);
337     return BinaryData(m_data, m_size);
338 }
339
340 inline OldDateTime Mixed::get_olddatetime() const noexcept
341 {
342     REALM_ASSERT(m_type == type_OldDateTime);
343     return m_date;
344 }
345
346 inline Timestamp Mixed::get_timestamp() const noexcept
347 {
348     REALM_ASSERT(m_type == type_Timestamp);
349     return m_timestamp;
350 }
351
352 inline void Mixed::set_int(int64_t v) noexcept
353 {
354     m_type = type_Int;
355     m_int = v;
356 }
357
358 inline void Mixed::set_bool(bool v) noexcept
359 {
360     m_type = type_Bool;
361     m_bool = v;
362 }
363
364 inline void Mixed::set_float(float v) noexcept
365 {
366     m_type = type_Float;
367     m_float = v;
368 }
369
370 inline void Mixed::set_double(double v) noexcept
371 {
372     m_type = type_Double;
373     m_double = v;
374 }
375
376 inline void Mixed::set_string(StringData v) noexcept
377 {
378     m_type = type_String;
379     m_data = v.data();
380     m_size = v.size();
381 }
382
383 inline void Mixed::set_binary(BinaryData v) noexcept
384 {
385     set_binary(v.data(), v.size());
386 }
387
388 inline void Mixed::set_binary(const char* data, size_t size) noexcept
389 {
390     m_type = type_Binary;
391     m_data = data;
392     m_size = size;
393 }
394
395 inline void Mixed::set_olddatetime(OldDateTime v) noexcept
396 {
397     m_type = type_OldDateTime;
398     m_date = v.get_olddatetime();
399 }
400
401 inline void Mixed::set_timestamp(Timestamp v) noexcept
402 {
403     m_type = type_Timestamp;
404     m_timestamp = v;
405 }
406
407 // LCOV_EXCL_START
408 template <class Ch, class Tr>
409 inline std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& out, const Mixed& m)
410 {
411     out << "Mixed(";
412     switch (m.m_type) {
413         case type_Int:
414             out << m.m_int;
415             break;
416         case type_Bool:
417             out << m.m_bool;
418             break;
419         case type_Float:
420             out << m.m_float;
421             break;
422         case type_Double:
423             out << m.m_double;
424             break;
425         case type_String:
426             out << StringData(m.m_data, m.m_size);
427             break;
428         case type_Binary:
429             out << BinaryData(m.m_data, m.m_size);
430             break;
431         case type_OldDateTime:
432             out << OldDateTime(m.m_date);
433             break;
434         case type_Timestamp:
435             out << Timestamp(m.m_timestamp);
436             break;
437         case type_Table:
438             out << "subtable";
439             break;
440         case type_Mixed:
441         case type_Link:
442         case type_LinkList:
443             REALM_ASSERT(false);
444     }
445     out << ")";
446     return out;
447 }
448 // LCOV_EXCL_STOP
449
450
451 // Compare mixed with integer
452
453 template <class T>
454 inline bool operator==(Wrap<Mixed> a, const T& b) noexcept
455 {
456     return Mixed(a).get_type() == type_Int && Mixed(a).get_int() == b;
457 }
458
459 template <class T>
460 inline bool operator!=(Wrap<Mixed> a, const T& b) noexcept
461 {
462     return Mixed(a).get_type() != type_Int || Mixed(a).get_int() != b;
463 }
464
465 template <class T>
466 inline bool operator==(const T& a, Wrap<Mixed> b) noexcept
467 {
468     return type_Int == Mixed(b).get_type() && a == Mixed(b).get_int();
469 }
470
471 template <class T>
472 inline bool operator!=(const T& a, Wrap<Mixed> b) noexcept
473 {
474     return type_Int != Mixed(b).get_type() || a != Mixed(b).get_int();
475 }
476
477
478 // Compare mixed with boolean
479
480 inline bool operator==(Wrap<Mixed> a, bool b) noexcept
481 {
482     return Mixed(a).get_type() == type_Bool && Mixed(a).get_bool() == b;
483 }
484
485 inline bool operator!=(Wrap<Mixed> a, bool b) noexcept
486 {
487     return Mixed(a).get_type() != type_Bool || Mixed(a).get_bool() != b;
488 }
489
490 inline bool operator==(bool a, Wrap<Mixed> b) noexcept
491 {
492     return type_Bool == Mixed(b).get_type() && a == Mixed(b).get_bool();
493 }
494
495 inline bool operator!=(bool a, Wrap<Mixed> b) noexcept
496 {
497     return type_Bool != Mixed(b).get_type() || a != Mixed(b).get_bool();
498 }
499
500
501 // Compare mixed with float
502
503 inline bool operator==(Wrap<Mixed> a, float b)
504 {
505     return Mixed(a).get_type() == type_Float && Mixed(a).get_float() == b;
506 }
507
508 inline bool operator!=(Wrap<Mixed> a, float b)
509 {
510     return Mixed(a).get_type() != type_Float || Mixed(a).get_float() != b;
511 }
512
513 inline bool operator==(float a, Wrap<Mixed> b)
514 {
515     return type_Float == Mixed(b).get_type() && a == Mixed(b).get_float();
516 }
517
518 inline bool operator!=(float a, Wrap<Mixed> b)
519 {
520     return type_Float != Mixed(b).get_type() || a != Mixed(b).get_float();
521 }
522
523
524 // Compare mixed with double
525
526 inline bool operator==(Wrap<Mixed> a, double b)
527 {
528     return Mixed(a).get_type() == type_Double && Mixed(a).get_double() == b;
529 }
530
531 inline bool operator!=(Wrap<Mixed> a, double b)
532 {
533     return Mixed(a).get_type() != type_Double || Mixed(a).get_double() != b;
534 }
535
536 inline bool operator==(double a, Wrap<Mixed> b)
537 {
538     return type_Double == Mixed(b).get_type() && a == Mixed(b).get_double();
539 }
540
541 inline bool operator!=(double a, Wrap<Mixed> b)
542 {
543     return type_Double != Mixed(b).get_type() || a != Mixed(b).get_double();
544 }
545
546
547 // Compare mixed with string
548
549 inline bool operator==(Wrap<Mixed> a, StringData b) noexcept
550 {
551     return Mixed(a).get_type() == type_String && Mixed(a).get_string() == b;
552 }
553
554 inline bool operator!=(Wrap<Mixed> a, StringData b) noexcept
555 {
556     return Mixed(a).get_type() != type_String || Mixed(a).get_string() != b;
557 }
558
559 inline bool operator==(StringData a, Wrap<Mixed> b) noexcept
560 {
561     return type_String == Mixed(b).get_type() && a == Mixed(b).get_string();
562 }
563
564 inline bool operator!=(StringData a, Wrap<Mixed> b) noexcept
565 {
566     return type_String != Mixed(b).get_type() || a != Mixed(b).get_string();
567 }
568
569 inline bool operator==(Wrap<Mixed> a, const char* b) noexcept
570 {
571     return a == StringData(b);
572 }
573
574 inline bool operator!=(Wrap<Mixed> a, const char* b) noexcept
575 {
576     return a != StringData(b);
577 }
578
579 inline bool operator==(const char* a, Wrap<Mixed> b) noexcept
580 {
581     return StringData(a) == b;
582 }
583
584 inline bool operator!=(const char* a, Wrap<Mixed> b) noexcept
585 {
586     return StringData(a) != b;
587 }
588
589 inline bool operator==(Wrap<Mixed> a, char* b) noexcept
590 {
591     return a == StringData(b);
592 }
593
594 inline bool operator!=(Wrap<Mixed> a, char* b) noexcept
595 {
596     return a != StringData(b);
597 }
598
599 inline bool operator==(char* a, Wrap<Mixed> b) noexcept
600 {
601     return StringData(a) == b;
602 }
603
604 inline bool operator!=(char* a, Wrap<Mixed> b) noexcept
605 {
606     return StringData(a) != b;
607 }
608
609
610 // Compare mixed with binary data
611
612 inline bool operator==(Wrap<Mixed> a, BinaryData b) noexcept
613 {
614     return Mixed(a).get_type() == type_Binary && Mixed(a).get_binary() == b;
615 }
616
617 inline bool operator!=(Wrap<Mixed> a, BinaryData b) noexcept
618 {
619     return Mixed(a).get_type() != type_Binary || Mixed(a).get_binary() != b;
620 }
621
622 inline bool operator==(BinaryData a, Wrap<Mixed> b) noexcept
623 {
624     return type_Binary == Mixed(b).get_type() && a == Mixed(b).get_binary();
625 }
626
627 inline bool operator!=(BinaryData a, Wrap<Mixed> b) noexcept
628 {
629     return type_Binary != Mixed(b).get_type() || a != Mixed(b).get_binary();
630 }
631
632
633 // Compare mixed with date
634
635 inline bool operator==(Wrap<Mixed> a, OldDateTime b) noexcept
636 {
637     return Mixed(a).get_type() == type_OldDateTime && OldDateTime(Mixed(a).get_olddatetime()) == b;
638 }
639
640 inline bool operator!=(Wrap<Mixed> a, OldDateTime b) noexcept
641 {
642     return Mixed(a).get_type() != type_OldDateTime || OldDateTime(Mixed(a).get_olddatetime()) != b;
643 }
644
645 inline bool operator==(OldDateTime a, Wrap<Mixed> b) noexcept
646 {
647     return type_OldDateTime == Mixed(b).get_type() && a == OldDateTime(Mixed(b).get_olddatetime());
648 }
649
650 inline bool operator!=(OldDateTime a, Wrap<Mixed> b) noexcept
651 {
652     return type_OldDateTime != Mixed(b).get_type() || a != OldDateTime(Mixed(b).get_olddatetime());
653 }
654
655 // Compare mixed with Timestamp
656
657 inline bool operator==(Wrap<Mixed> a, Timestamp b) noexcept
658 {
659     return Mixed(a).get_type() == type_Timestamp && Timestamp(Mixed(a).get_timestamp()) == b;
660 }
661
662 inline bool operator!=(Wrap<Mixed> a, Timestamp b) noexcept
663 {
664     return Mixed(a).get_type() != type_Timestamp || Timestamp(Mixed(a).get_timestamp()) != b;
665 }
666
667 inline bool operator==(Timestamp a, Wrap<Mixed> b) noexcept
668 {
669     return type_Timestamp == Mixed(b).get_type() && a == Timestamp(Mixed(b).get_timestamp());
670 }
671
672 inline bool operator!=(Timestamp a, Wrap<Mixed> b) noexcept
673 {
674     return type_Timestamp != Mixed(b).get_type() || a != Timestamp(Mixed(b).get_timestamp());
675 }
676
677
678 } // namespace realm
679
680 #endif // REALM_MIXED_HPP