RDKit
Open-source cheminformatics and machine learning.
Loading...
Searching...
No Matches
RDValue-doublemagic.h
Go to the documentation of this file.
1// Copyright (c) 2015, Novartis Institutes for BioMedical Research Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following
12// disclaimer in the documentation and/or other materials provided
13// with the distribution.
14// * Neither the name of Novartis Institutes for BioMedical Research Inc.
15// nor the names of its contributors may be used to endorse or promote
16// products derived from this software without specific prior written
17// permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30//
31#include <RDGeneral/export.h>
32#ifndef RDKIT_RDVALUE_PTRMAGIC_H
33#define RDKIT_RDVALUE_PTRMAGIC_H
34
35#include <cstdint>
36#include <type_traits>
37#include <cassert>
38#include <any>
39#include "Invariant.h"
40#include <iomanip>
41#include <sstream>
42#include <vector>
43#include <string>
45#include <boost/utility.hpp>
46#include <boost/lexical_cast.hpp>
48#include <cmath>
49#include "LocaleSwitcher.h"
50
51#define RDVALUE_HASBOOL
52
53namespace RDKit {
54
55// Inspired by
56// https://nikic.github.io/2012/02/02/Pointer-magic-for-efficient-dynamic-value-representations.html
57// 16 bit storage for value types using Quiet NaN spaces in
58// doubles
59// Won't work on Solaris and some other os's as mmaping maps from
60// top memory down
61// Example check:
62// std::string *pointer = new std::string(v);
63// assert((reinterpret_cast<std::uint64_t>(pointer) & StringTag) == 0);
64
65// implementations, need a typedef at compile time to figure this out.
66// current implementation is probably little endian, need to check.
67
68/*
69 Encoding for storing other things as a double. Use
70 Quiet NaN
71 Quiet NaN: // used to encode types
72 F F F 1XXX < - X = type bits (first bit is set to one)
73
74 seeeeeee|eeeemmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm
75 s1111111|11111ppp|pppppppp|pppppppp|pppppppp|pppppppp|pppppppp|pppppppp
76 ^- first mantissa bit 1 everything else is "payload" -^
77 ^- exponent bits all 1 and mustn't be all-zero (as it
78 ^- any sign bit would be INF then)
79
80 Available
81 8 = 1000 MaxDouble // Not really a tag, is a sentinel
82 9 = 1001 Float
83 b = 1010 Int32
84 a = 1011 Uint32
85 C = 1100 <none>
86 D = 1101 <none>
87 E = 1110 <none>
88 F = 1111 PtrTag (look at lower 3 bits for type)
89*/
90
91namespace RDTypeTag {
92static const std::uint64_t NaN = 0xfff7FFFFFFFFFFFF; // signalling NaN
93static const std::uint64_t MaxDouble = 0xfff8000000000000; //
94static const std::uint64_t DoubleTag = 0xfff8000000000000; //
95static const std::uint64_t FloatTag = 0xfff9000000000000; //
96static const std::uint64_t IntTag = 0xfffa000000000000; //
97static const std::uint64_t UnsignedIntTag = 0xfffb000000000000; //
98static const std::uint64_t BoolTag = 0xfffc000000000000; //
99
100// PTR Tags use the last 3 bits for typing info
101static const std::uint64_t PtrTag = 0xffff000000000000;
102static const std::uint64_t StringTag = 0xffff000000000001; // 001
103static const std::uint64_t VecDoubleTag = 0xffff000000000002; // 010
104static const std::uint64_t VecFloatTag = 0xffff000000000003; // 011
105static const std::uint64_t VecIntTag = 0xffff000000000004; // 100
106static const std::uint64_t VecUnsignedIntTag = 0xffff000000000005; // 101
107static const std::uint64_t VecStringTag = 0xffff000000000006; // 110
108static const std::uint64_t AnyTag = 0xffff000000000007; // 111
109
110// Retrieves the tag (and PtrMask) from the type
111template <class T>
112inline std::uint64_t GetTag() {
113 return AnyTag;
114}
115template <>
116inline std::uint64_t GetTag<double>() {
117 return MaxDouble;
118}
119template <>
120inline std::uint64_t GetTag<float>() {
121 return FloatTag;
122}
123template <>
124inline std::uint64_t GetTag<int>() {
125 return IntTag;
126}
127template <>
128inline std::uint64_t GetTag<unsigned int>() {
129 return UnsignedIntTag;
130}
131template <>
132inline std::uint64_t GetTag<bool>() {
133 return BoolTag;
134}
135template <>
136inline std::uint64_t GetTag<std::string>() {
137 return StringTag;
138}
139template <>
140inline std::uint64_t GetTag<std::vector<double>>() {
141 return VecDoubleTag;
142}
143template <>
144inline std::uint64_t GetTag<std::vector<float>>() {
145 return VecFloatTag;
146}
147template <>
148inline std::uint64_t GetTag<std::vector<int>>() {
149 return VecIntTag;
150}
151template <>
152inline std::uint64_t GetTag<std::vector<unsigned int>>() {
153 return VecUnsignedIntTag;
154}
155template <>
156inline std::uint64_t GetTag<std::vector<std::string>>() {
157 return VecStringTag;
158}
159template <>
160inline std::uint64_t GetTag<std::any>() {
161 return AnyTag;
162}
163} // namespace RDTypeTag
164
165struct RDValue {
166 // Bit Twidling for conversion from the Tag to a Pointer
167 static const std::uint64_t TagMask = 0xFFFF000000000000;
168 static const std::uint64_t PointerTagMask = 0xFFFF000000000007;
169 static const std::uint64_t ApplyMask = 0x0000FFFFFFFFFFFF;
170 static const std::uint64_t ApplyPtrMask = 0x0000FFFFFFFFFFF8;
171
172 union {
174 std::uint64_t otherBits;
175 };
176
177 inline RDValue() : doubleBits(0.0) {}
178
179 inline RDValue(double number) {
180 if (std::isnan(number)) {
181 // Store a signalling NaN for NaN's.
182 // quiet NaNs are used for other types.
184 assert(std::isnan(doubleBits));
185 } else {
186 doubleBits = number;
187 }
188 }
189
190 inline RDValue(float number) {
192 memcpy(((char *)&otherBits), &number, sizeof(float));
193 }
194
195 inline RDValue(int32_t number) {
196 otherBits = (((std::uint64_t)number) & ApplyMask) | RDTypeTag::IntTag;
197 }
198
199 inline RDValue(unsigned int number) {
200 otherBits =
201 (((std::uint64_t)number) & ApplyMask) | RDTypeTag::UnsignedIntTag;
202 }
203
204 inline RDValue(bool number) {
205 otherBits =
206 (static_cast<std::uint64_t>(number) & ApplyMask) | RDTypeTag::BoolTag;
207 }
208
209 inline RDValue(std::any *pointer) {
210 // ensure that the pointer really is only 48 bit
211 assert((reinterpret_cast<std::uint64_t>(pointer) & RDTypeTag::AnyTag) ==
212 0);
213 otherBits = reinterpret_cast<std::uint64_t>(pointer) | RDTypeTag::AnyTag;
214 }
215
216 inline RDValue(const std::any &any) {
217 // ensure that the pointer really is only 48 bit
218 std::any *pointer = new std::any(any);
219 assert((reinterpret_cast<std::uint64_t>(pointer) & RDTypeTag::AnyTag) ==
220 0);
221 otherBits = reinterpret_cast<std::uint64_t>(pointer) | RDTypeTag::AnyTag;
222 }
223
224 // Unknown types are stored as std::any
225 template <class T>
226 inline RDValue(const T &v) {
227 std::any *pointer = new std::any(v);
228 assert((reinterpret_cast<std::uint64_t>(pointer) & RDTypeTag::AnyTag) ==
229 0);
230 otherBits = reinterpret_cast<std::uint64_t>(pointer) | RDTypeTag::AnyTag;
231 }
232
233 inline RDValue(const std::string &v) {
234 std::string *pointer = new std::string(v);
235 assert((reinterpret_cast<std::uint64_t>(pointer) &
237 otherBits =
238 reinterpret_cast<std::uint64_t>(pointer) | RDTypeTag::StringTag;
239 }
240
241 inline RDValue(const std::vector<double> &v) {
242 std::vector<double> *pointer = new std::vector<double>(v);
243 assert((reinterpret_cast<std::uint64_t>(pointer) &
245 otherBits =
246 reinterpret_cast<std::uint64_t>(pointer) | RDTypeTag::VecDoubleTag;
247 }
248
249 inline RDValue(const std::vector<float> &v) {
250 std::vector<float> *pointer = new std::vector<float>(v);
251 assert((reinterpret_cast<std::uint64_t>(pointer) &
253 otherBits =
254 reinterpret_cast<std::uint64_t>(pointer) | RDTypeTag::VecFloatTag;
255 }
256
257 inline RDValue(const std::vector<int> &v) {
258 std::vector<int> *pointer = new std::vector<int>(v);
259 assert((reinterpret_cast<std::uint64_t>(pointer) &
261 otherBits =
262 reinterpret_cast<std::uint64_t>(pointer) | RDTypeTag::VecIntTag;
263 }
264
265 inline RDValue(const std::vector<unsigned int> &v) {
266 std::vector<unsigned int> *pointer = new std::vector<unsigned int>(v);
267 assert((reinterpret_cast<std::uint64_t>(pointer) &
269 otherBits = reinterpret_cast<std::uint64_t>(pointer) |
271 }
272
273 inline RDValue(const std::vector<std::string> &v) {
274 std::vector<std::string> *pointer = new std::vector<std::string>(v);
275 assert((reinterpret_cast<std::uint64_t>(pointer) &
277 otherBits =
278 reinterpret_cast<std::uint64_t>(pointer) | RDTypeTag::VecStringTag;
279 }
280
281 std::uint64_t getTag() const {
285 }
286
287 std::uint64_t tag = otherBits & TagMask;
288 if (tag == RDTypeTag::PtrTag) return otherBits & PointerTagMask;
289 return tag;
290 }
291
292 // ptrCast - unsafe, use rdvalue_cast instead.
293 template <class T>
294 inline T *ptrCast() const {
295 return reinterpret_cast<T *>(otherBits & ~RDTypeTag::GetTag<T>());
296 }
297
298 // RDValue doesn't have an explicit destructor, it must
299 // be wrapped in a container.
300 // The idea is that POD types don't need to be destroyed
301 // and this allows the container optimization possibilities.
302 inline void destroy() {
303 switch (getTag()) {
305 delete ptrCast<std::string>();
306 break;
309 break;
312 break;
314 delete ptrCast<std::vector<int>>();
315 break;
318 break;
321 break;
323 delete ptrCast<std::any>();
324 break;
325 default:
326 break;
327 }
328 }
329
330 static inline void cleanup_rdvalue(RDValue v) { v.destroy(); }
331};
332
333/////////////////////////////////////////////////////////////////////////////////////
334// Given two RDValue::Values - copy the appropriate structure
335// RDValue doesn't have a copy constructor, the default
336// copy act's like a move for better value semantics.
337// Containers may need to copy though.
338inline void copy_rdvalue(RDValue &dest, const RDValue &src) {
339 dest.destroy();
340 switch (src.getTag()) {
342 dest = RDValue(*src.ptrCast<std::string>());
343 break;
345 dest = RDValue(*src.ptrCast<std::vector<double>>());
346 break;
348 dest = RDValue(*src.ptrCast<std::vector<float>>());
349 break;
351 dest = RDValue(*src.ptrCast<std::vector<int>>());
352 break;
354 dest = RDValue(*src.ptrCast<std::vector<unsigned int>>());
355 break;
357 dest = RDValue(*src.ptrCast<std::vector<std::string>>());
358 break;
360 dest = RDValue(*src.ptrCast<std::any>());
361 break;
362 default:
363 dest = src;
364 }
365}
366
367/////////////////////////////////////////////////////////////////////////////////////
368// rdvalue_is<T>
369
370template <class T>
371inline bool rdvalue_is(const RDValue_cast_t) {
372 const short tag =
374 if (v.getTag() == tag) return true;
375
376 // If we are an Any tag, check the any type info
377 if (v.getTag() == RDTypeTag::AnyTag) {
378 return v.value.a->type() == typeid(T);
379 }
380
381 return false;
382}
383
384template <>
386 return v.otherBits < RDTypeTag::MaxDouble ||
387 (v.otherBits & RDTypeTag::NaN) == RDTypeTag::NaN;
388}
389
390template <>
392 return rdvalue_is<double>(v);
393}
394
395/*
396template<>
397inline bool rdvalue_is<bool>(const RDValue_cast_t) {
398 return (v.getTag() == RDTypeTag::IntTag &&
399 (static_cast<int32_t>(v.otherBits & ~RDTypeTag::IntTag) == 1 ||
400 static_cast<int32_t>(v.otherBits & ~RDTypeTag::IntTag) == 0 ));
401}
402
403template<>
404inline bool rdvalue_is<const bool&>(const RDValue_cast_t) {
405 return rdvalue_is<bool>(v);
406}
407*/
408
409/////////////////////////////////////////////////////////////////////////////////////
410// rdvalue_cast<T>
411//
412// POD types do not support reference semantics. Other types do.
413// rdvalue_cast<const std::vector<double> &>(RDValue); // ok
414// rdvalue_cast<const float &>(RDValue); // bad_any_cast
415
417// Get stuff stored in boost any
418template <class T>
420 // Disable reference and pointer casts to POD data.
421 static_assert(
422 !((std::is_pointer<T>::value &&
423 (std::is_integral<typename std::remove_pointer<T>::type>::value ||
424 std::is_floating_point<typename std::remove_pointer<T>::type>::value)) ||
425 (std::is_reference<T>::value &&
426 (std::is_integral<typename std::remove_reference<T>::type>::value ||
427 std::is_floating_point<
428 typename std::remove_reference<T>::type>::value))),
429 "rdvalue_cast cannot be used for pointer or reference casts to POD data");
430
431 if (rdvalue_is<std::any>(v)) {
432 return std::any_cast<T>(*v.ptrCast<std::any>());
433 }
434 throw std::bad_any_cast();
435}
436
437// POD casts
438template <>
440 if (rdvalue_is<double>(v)) return v.doubleBits;
441 throw std::bad_any_cast();
442}
443
444template <>
446 if (rdvalue_is<float>(v)) {
447 float f;
448 memcpy(&f, ((char *)&v.otherBits), sizeof(float));
449 return f;
450 }
451 throw std::bad_any_cast();
452}
453
454// n.b. with const expressions, could use ~RDTagTypes::GetTag<T>()
455// and enable_if
456template <>
458 if (rdvalue_is<int>(v)) {
459 return static_cast<int32_t>(v.otherBits & ~RDTypeTag::IntTag);
460 }
461 throw std::bad_any_cast();
462}
463template <>
466 return static_cast<uint32_t>(v.otherBits & ~RDTypeTag::UnsignedIntTag);
467 }
468 throw std::bad_any_cast();
469}
470
471template <>
473 if (rdvalue_is<bool>(v)) {
474 return static_cast<bool>(v.otherBits & ~RDTypeTag::BoolTag);
475 }
476 throw std::bad_any_cast();
477}
478
479} // namespace RDKit
480#endif
static const std::uint64_t PtrTag
static const std::uint64_t UnsignedIntTag
static const std::uint64_t StringTag
std::uint64_t GetTag< std::any >()
std::uint64_t GetTag< bool >()
static const std::uint64_t VecStringTag
static const std::uint64_t NaN
static const std::uint64_t VecIntTag
static const std::uint64_t FloatTag
std::uint64_t GetTag< std::string >()
static const std::uint64_t VecUnsignedIntTag
static const std::uint64_t DoubleTag
std::uint64_t GetTag()
static const std::uint64_t MaxDouble
static const std::uint64_t IntTag
std::uint64_t GetTag< int >()
std::uint64_t GetTag< float >()
std::uint64_t GetTag< unsigned int >()
static const std::uint64_t AnyTag
std::uint64_t GetTag< double >()
static const std::uint64_t VecFloatTag
static const std::uint64_t VecDoubleTag
static const std::uint64_t BoolTag
Std stuff.
int rdvalue_cast< int >(RDValue_cast_t v)
unsigned int rdvalue_cast< unsigned int >(RDValue_cast_t v)
bool rdvalue_is(const RDValue_cast_t)
double rdvalue_cast< double >(RDValue_cast_t v)
bool rdvalue_is< const double & >(const RDValue_cast_t)
void copy_rdvalue(RDValue &dest, const RDValue &src)
T rdvalue_cast(RDValue_cast_t v)
bool rdvalue_is< double >(const RDValue_cast_t)
RDValue RDValue_cast_t
bool rdvalue_cast< bool >(RDValue_cast_t v)
float rdvalue_cast< float >(RDValue_cast_t v)
static const std::uint64_t ApplyPtrMask
RDValue(const std::vector< int > &v)
RDValue(const std::vector< std::string > &v)
RDValue(const std::vector< double > &v)
RDValue(const std::vector< unsigned int > &v)
RDValue(double number)
std::uint64_t getTag() const
RDValue(int32_t number)
RDValue(const std::any &any)
static const std::uint64_t ApplyMask
RDValue(float number)
RDValue(const std::string &v)
RDValue(unsigned int number)
RDValue(const std::vector< float > &v)
static const std::uint64_t TagMask
static void cleanup_rdvalue(RDValue v)
static const std::uint64_t PointerTagMask
std::uint64_t otherBits
RDValue(std::any *pointer)