null.hpp
5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*************************************************************************
*
* REALM CONFIDENTIAL
* __________________
*
* [2011] - [2015] Realm Inc
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Realm Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Realm Incorporated
* and its suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Realm Incorporated.
*
**************************************************************************/
#ifndef REALM_NULL_HPP
#define REALM_NULL_HPP
#include <cmath>
#include <realm/util/features.h>
#include <realm/util/optional.hpp>
#include <realm/utilities.hpp>
#include <realm/exceptions.hpp>
namespace realm {
/*
Represents null in Query, find(), get(), set(), etc.
Float/Double: Realm can both store user-given NaNs and null. Any user-given signaling NaN is converted to
0x7fa00000 (if float) or 0x7ff4000000000000 (if double). Any user-given quiet NaN is converted to
0x7fc00000 (if float) or 0x7ff8000000000000 (if double). So Realm does not preserve the optional bits in
user-given NaNs.
However, since both clang and gcc on x64 and ARM, and also Java on x64, return these bit patterns when
requesting NaNs, these will actually seem to roundtrip bit-exact for the end-user in most cases.
If set_null() is called, a null is stored in form of the bit pattern 0xffffffff (if float) or
0xffffffffffffffff (if double). These are quiet NaNs.
Executing a query that involves a float/double column that contains NaNs gives an undefined result. If
it contains signaling NaNs, it may throw an exception.
Notes on IEEE:
A NaN float is any bit pattern `s 11111111 S xxxxxxxxxxxxxxxxxxxxxx` where `s` and `x` are arbitrary, but at
least 1 `x` must be 1. If `S` is 1, it's a quiet NaN, else it's a signaling NaN.
A NaN doubule is the same as above, but for `s eeeeeeeeeee S xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
The `S` bit is at position 22 (float) or 51 (double).
*/
struct null {
null(int) {}
null() {}
operator int64_t() { throw(LogicError::type_mismatch); }
template<class T>
operator util::Optional<T>() { return util::none; }
template<class T>
bool operator == (const T&) const { REALM_ASSERT(false); return false; }
template<class T>
bool operator != (const T&) const { REALM_ASSERT(false); return false; }
template<class T>
bool operator > (const T&) const { REALM_ASSERT(false); return false; }
template<class T>
bool operator >= (const T&) const { REALM_ASSERT(false); return false; }
template<class T>
bool operator <= (const T&) const { REALM_ASSERT(false); return false; }
template<class T>
bool operator < (const T&) const { REALM_ASSERT(false); return false; }
/// Returns whether `v` bitwise equals the null bit-pattern
template<class T>
static bool is_null_float(T v) {
T i = null::get_null_float<T>();
return std::memcmp(&i, &v, sizeof(T)) == 0;
}
/// Returns the quiet NaNs that represent null for floats/doubles in Realm in stored payload.
template<class T>
static T get_null_float() {
typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type i;
int64_t double_nan = 0x7ff80000000000aa;
i = std::is_same<T, float>::value ? 0x7fc000aa : static_cast<decltype(i)>(double_nan);
T d = type_punning<T, decltype(i)>(i);
REALM_ASSERT_DEBUG(std::isnan(static_cast<double>(d)));
REALM_ASSERT_DEBUG(!is_signaling(d));
return d;
}
/// Takes a NaN as argument and returns whether or not it's signaling
template<class T>
static bool is_signaling(T v) {
REALM_ASSERT(std::isnan(static_cast<double>(v)));
typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type i;
size_t signal_bit = std::is_same<T, float>::value ? 22 : 51; // If this bit is set, it's quiet
i = type_punning<decltype(i), T>(v);
return !(i & (1ull << signal_bit));
}
/// Converts any signaling or quiet NaN to their their respective bit patterns that are used on x64 gcc+clang,
/// ARM clang and x64 Java.
template<class T>
static T to_realm(T v) {
if (std::isnan(static_cast<double>(v))) {
typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type i;
if (std::is_same<T, float>::value) {
i = is_signaling(v) ? 0x7fa00000 : 0x7fc00000;
}
else {
i = static_cast<decltype(i)>(is_signaling(v) ? 0x7ff4000000000000 : 0x7ff8000000000000);
}
return type_punning<T, decltype(i)>(i);
}
else {
return v;
}
}
};
} // namespace realm
#endif // REALM_NULL_HPP