PDI 1.6.0

the PDI data interface

ref_any.h
1/*******************************************************************************
2 * Copyright (C) 2015-2021 Commissariat a l'energie atomique et aux energies alternatives (CEA)
3 * Copyright (C) 2020-2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
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 copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of CEA nor the names of its contributors may be used to
14 * endorse or promote products derived from this software without specific
15 * prior written permission.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 ******************************************************************************/
25
26#ifndef PDI_REF_ANY_H_
27#define PDI_REF_ANY_H_
28
29#include <algorithm>
30#include <cassert>
31#include <functional>
32#include <memory>
33#include <new>
34#include <unordered_map>
35
36#include <pdi/array_datatype.h>
37#include <pdi/pdi_fwd.h>
38#include <pdi/datatype.h>
39#include <pdi/error.h>
40#include <pdi/record_datatype.h>
41#include <pdi/scalar_datatype.h>
42
43
44namespace PDI {
45
46namespace {
47
50template<bool R, bool W>
51struct Ref_access {
53 typedef void type;
54};
55
58template<bool R>
59struct Ref_access<R, true> {
61 typedef void* type;
62};
63
66template<>
67struct Ref_access<true, false> {
69 typedef const void* type;
70};
71
72} //namespace <anonymous>
73
74
80class PDI_EXPORT Reference_base
81{
82protected:
83
88 struct PDI_NO_EXPORT Referenced_buffer {
89
91 std::function<void()> m_delete;
92
95
98
104
106 std::unordered_map<const Reference_base*, std::function<void(Ref)> > m_notifications;
107
114 Referenced_buffer(std::function<void()> deleter, bool readable, bool writable) noexcept:
115 m_delete{deleter},
116 m_owners{0},
117 m_read_locks{readable ? 0 : 1},
118 m_write_locks{writable ? 0 : 1}
119 {}
120
121
123
125
127
129 {
130 m_delete();
131 assert(!m_owners);
132 assert(m_read_locks == 0 || m_read_locks == 1);
133 assert(m_write_locks == 0 || m_write_locks == 1);
134 assert(m_notifications.empty());
135 }
136
137 };
138
143 struct PDI_NO_EXPORT Referenced_data {
144
147
149 void* m_data;
150
153
156
164 m_buffer{buffer},
165 m_data{data},
166 m_type{std::move(type)},
167 m_owners{0}
168 {
169 assert(buffer);
170 assert(data);
171 m_buffer->m_owners++;
172 }
173
182 Referenced_data(void* data, std::function<void(void*)> freefunc, Datatype_sptr type, bool readable, bool writable):
183 m_data{data},
184 m_type{type},
185 m_owners{0}
186 {
187 assert(data);
188 m_buffer = new Referenced_buffer{[data, freefunc, type]()
189 {
190 type->destroy_data(data);
191 freefunc(data);
192 }, readable, writable};
193 m_buffer->m_owners++;
194 }
195
196 Referenced_data() = delete;
197
199
201
203 {
204 m_buffer->m_owners--;
205 if (m_buffer->m_owners == 0) {
206 delete m_buffer;
207 }
208 }
209 };
210
211
215
216
219 static Referenced_data PDI_NO_EXPORT* get_content(const Reference_base& other) noexcept
220 {
221 if ( !other.m_content ) return NULL;
222 if ( !other.m_content->m_data ) return NULL;
223 return other.m_content;
224 }
225
226 // Symbol should not be exported, but it required to force
227 // generation of all 4 variants of `Ref_any::copy`
228 static Ref do_copy(Ref_r ref);
229
232 Reference_base() noexcept:
233 m_content(nullptr)
234 {}
235
237
239
240 Reference_base& operator = (const Reference_base&) = delete;
241
242 Reference_base& operator = (Reference_base&&) = delete;
243
244public:
247 Datatype_sptr type() const noexcept;
248
249 size_t hash() const noexcept
250 {
251 return std::hash<Referenced_data*>()(get_content(*this));
252 }
253
254}; // class Data_ref_base
255
256
270template<bool R, bool W>
271class PDI_EXPORT Ref_any:
272 public Reference_base
273{
274public:
275 template<bool OR, bool OW>
276 friend class Ref_any;
277
280 Ref_any() = default;
281
288 Ref_any(const Ref_any& other) noexcept:
290 {
291 link(get_content(other));
292 }
293
300 template<bool OR, bool OW>
301 Ref_any(const Ref_any<OR, OW>& other) noexcept:
303 {
304 link(get_content(other));
305 }
306
310 Ref_any(Ref_any&& other) noexcept:
312 {
313 if (!other.m_content) return;
314 // the other ref notification disappears
315 other.m_content->m_buffer->m_notifications.erase(&other);
316 // since we get the same privileges as those we release we can just steal the content
317 m_content = other.m_content;
318 other.m_content = nullptr;
319 }
320
329 Ref_any(void* data, std::function<void(void*)> freefunc, Datatype_sptr type, bool readable, bool writable):
331 {
332 if (type->datasize() && !data && (readable||writable)) {
333 throw Type_error{"Referencing null data with non-null size"};
334 }
335 if (data) {
336 link(new Referenced_data(data, freefunc, std::move(type), readable, writable));
337 }
338 }
339
343 {
344 reset();
345 }
346
347 Ref_any& operator= (Ref_any&& other) noexcept
348 {
349 // self-copy: nothing to do
350 if (&other == this) return *this;
351 // we'll be copied into, start nullifying ourselves first
352 reset();
353 // if the other is null also, we're done
354 if (other.is_null()) return *this;
355 // the other ref notification disappears
356 other.m_content->m_buffer->m_notifications.erase(&other);
357 // since we get the same privileges as those we release we can just steal the content
358 m_content = other.m_content;
359 other.m_content = nullptr;
360 return *this;
361 }
362
363 Ref_any& operator= (const Ref_any& other) const noexcept
364 {
365 // self-copy: nothing to do
366 if (&other == this) return *this;
367 // we'll be copied into, start nullifying ourselves first
368 reset();
369 // and copy the content from the other
370 link(get_content(other));
371 return *this;
372 }
373
374 bool operator== (const Reference_base& o) const noexcept
375 {
376 is_null();
377 return m_content == get_content(o);
378 }
379
380 bool operator!= (const Reference_base& o) const noexcept
381 {
382 is_null();
383 return m_content != get_content(o);
384 }
385
386 bool operator< (const Reference_base& o) const noexcept
387 {
388 is_null();
389 return m_content < get_content(o);
390 }
391
392 bool operator> (const Reference_base& o) const noexcept
393 {
394 is_null();
395 return m_content > get_content(o);
396 }
397
398 bool operator<= (const Reference_base& o) const noexcept
399 {
400 is_null();
401 return m_content <= get_content(o);
402 }
403
404 bool operator>= (const Reference_base& o) const noexcept
405 {
406 is_null();
407 return m_content >= get_content(o);
408 }
409
415 Ref operator[] (const std::string& member_name) const
416 {
417 return this->operator[](member_name.c_str());
418 }
419
425 Ref operator[] (const char* member_name) const
426 {
427 if (is_null()) {
428 throw Type_error{"Cannot access member from empty Ref: `{}'", member_name};
429 }
430 std::pair<void*, Datatype_sptr> subref_info = type()->member(member_name, m_content->m_data);
431 Ref result;
432 result.link(new Referenced_data(
433 m_content->m_buffer,
434 subref_info.first,
435 std::move(subref_info.second)));
436 return result;
437 }
438
444 template<class T>
445 std::enable_if_t<std::is_integral<T>::value, Ref> operator[] (T index) const
446 {
447 if (is_null()) {
448 throw Type_error{"Cannot access array index from empty Ref: `{}'", index};
449 }
450 std::pair<void*, Datatype_sptr> subref_info = type()->index(index, m_content->m_data);
451 Ref result;
452 result.link(new Referenced_data(
453 m_content->m_buffer,
454 subref_info.first,
455 std::move(subref_info.second)));
456 return result;
457 }
458
464 Ref operator[] (std::pair<std::size_t, std::size_t> slice) const
465 {
466 if (is_null()) {
467 throw Type_error("Cannot access array slice from empty Ref: `{}:{}'", slice.first, slice.second);
468 }
469 std::pair<void*, Datatype_sptr> subref_info = type()->slice(slice.first, slice.second, m_content->m_data);
470 Ref result;
471 result.link(new Referenced_data(
472 m_content->m_buffer,
473 subref_info.first,
474 std::move(subref_info.second)));
475 return result;
476 }
477
482 operator typename Ref_access<R, W>::type() const
483 {
484 return get();
485 }
486
491 typename Ref_access<R, W>::type get() const
492 {
493 if (is_null()) throw Right_error{"Trying to dereference a null reference"};
494 return m_content->m_data;
495 }
496
501 typename Ref_access<R, W>::type get(std::nothrow_t) const noexcept
502 {
503 if (is_null()) return nullptr;
504 return m_content->m_data;
505 }
506
510 template<class T>
512 {
513 static_assert(R, "Cannot get scalar_value from Ref without read access");
514 if (auto&& scalar_type = std::dynamic_pointer_cast<const Scalar_datatype>(type())) {
515 if (scalar_type->kind() == PDI::Scalar_kind::UNSIGNED) {
516 switch (scalar_type->buffersize()) {
517 case 1L:
518 return *static_cast<const uint8_t*>(m_content->m_data);
519 case 2L:
520 return *static_cast<const uint16_t*>(m_content->m_data);
521 case 4L:
522 return *static_cast<const uint32_t*>(m_content->m_data);
523 case 8L:
524 return *static_cast<const uint64_t*>(m_content->m_data);
525 default:
526 throw Type_error{"Unknown size of unsigned integer datatype"};
527 }
528 } else if (scalar_type->kind() == PDI::Scalar_kind::SIGNED) {
529 switch (scalar_type->buffersize()) {
530 case 1L:
531 return *static_cast<const int8_t*>(m_content->m_data);
532 case 2L:
533 return *static_cast<const int16_t*>(m_content->m_data);
534 case 4L:
535 return *static_cast<const int32_t*>(m_content->m_data);
536 case 8L:
537 return *static_cast<const int64_t*>(m_content->m_data);
538 default:
539 throw Type_error{"Unknown size of integer datatype"};
540 }
541 } else if (scalar_type->kind() == PDI::Scalar_kind::FLOAT) {
542 switch (type()->buffersize()) {
543 case 4L: {
544 return *static_cast<const float*>(m_content->m_data);
545 }
546 case 8L: {
547 return *static_cast<const double*>(m_content->m_data);
548 }
549 default:
550 throw Type_error{"Unknown size of float datatype"};
551 }
552 } else {
553 throw Type_error{"Unknown datatype to get value"};
554 }
555 }
556 throw Type_error{"Expected scalar, found invalid type instead: {}", type()->debug_string()};
557 }
558
563 operator bool () const noexcept
564 {
565 return !is_null();
566 }
567
570 void reset() noexcept
571 {
572 if (m_content) unlink();
573 }
574
580 Ref copy() const
581 {
582 return do_copy(*this);
583 }
584
591 void* release() noexcept
592 {
593 if (is_null()) return nullptr;
594
595 // notify everybody of the nullification
596 while (!m_content->m_buffer->m_notifications.empty()) {
597 // get the key of a notification
598 const Reference_base* key = m_content->m_buffer->m_notifications.begin()->first;
599 // call this notification, this might invalidate any iterator
600 m_content->m_buffer->m_notifications.begin()->second(*this);
601 // remove the notification we just called
602 m_content->m_buffer->m_notifications.erase(key);
603 }
604
605 void* result = m_content->m_data;
606 m_content->m_data = nullptr;
607 m_content->m_buffer->m_delete = []() {}; // Referenced_metadata won't delete data
608
609 unlink();
610
611 return result;
612 }
613
618 void on_nullify(std::function<void(Ref)> notifier) const noexcept
619 {
620 if (!is_null()) m_content->m_buffer->m_notifications[this] = notifier;
621 }
622
623private:
631 bool PDI_NO_EXPORT is_null() const noexcept
632 {
633 if (!m_content) return true;
634 if (!m_content->m_data) {
635 unlink();
636 return true;
637 }
638 return false;
639 }
640
645 void PDI_NO_EXPORT unlink() const noexcept
646 {
647 assert(m_content);
648 m_content->m_buffer->m_notifications.erase(this);
649 if (R || W) --m_content->m_buffer->m_write_locks;
650 if (W) --m_content->m_buffer->m_read_locks;
651 --m_content->m_owners;
652 if (!m_content->m_owners) delete m_content;
653 m_content = nullptr;
654 }
655
664 void PDI_NO_EXPORT link(Referenced_data* content) noexcept
665 {
666 assert(!m_content);
667 if (!content || !content->m_data) return; // null ref
668 if (R && content->m_buffer->m_read_locks || W && content->m_buffer->m_write_locks) {
669 if (content->m_owners == 0) delete content;
670 return;
671 }
672 m_content = content;
673 ++m_content->m_owners;
674 if (R || W) ++m_content->m_buffer->m_write_locks;
675 if (W) ++m_content->m_buffer->m_read_locks;
676 }
677
678};
679
680} // namespace PDI
681
682namespace std {
683
684template<bool R, bool W>
685struct hash<PDI::Ref_any<R,W>> {
686 size_t operator() (const PDI::Ref_any<R,W>& r) const noexcept
687 {
688 return r.hash();
689 }
690};
691
692} // namespace std
693
694#endif // PDI_REF_ANY_H_
A dynamically typed reference to data with automatic memory management and read/write locking semanti...
Definition: ref_any.h:273
Ref_any(void *data, std::function< void(void *)> freefunc, Datatype_sptr type, bool readable, bool writable)
Creates a reference to currently unreferenced data.
Definition: ref_any.h:329
Ref_any()=default
Constructs a null reference.
Ref_any(const Ref_any< OR, OW > &other) noexcept
Copies an existing reference with different privileges.
Definition: ref_any.h:301
Ref_access< R, W >::type get(std::nothrow_t) const noexcept
Offers access to the referenced raw data, returns null for null references.
Definition: ref_any.h:501
Ref_any(Ref_any &&other) noexcept
Moves an existing reference.
Definition: ref_any.h:310
~Ref_any()
Destructor.
Definition: ref_any.h:342
Ref_access< R, W >::type get() const
Offers access to the referenced raw data, throws on null references.
Definition: ref_any.h:491
void * release() noexcept
Releases ownership of the referenced raw data by nullifying all existing references.
Definition: ref_any.h:591
Ref copy() const
Makes a copy of the raw content behind this reference and returns a new reference.
Definition: ref_any.h:580
Ref_any(const Ref_any &other) noexcept
Copies an existing reference.
Definition: ref_any.h:288
void on_nullify(std::function< void(Ref)> notifier) const noexcept
Registers a nullification callback.
Definition: ref_any.h:618
T scalar_value()
Returns a scalar value of type T taken from the data buffer.
Definition: ref_any.h:511
void reset() noexcept
Nullify the reference.
Definition: ref_any.h:570
A common base for all references, whatever their access privileges.
Definition: ref_any.h:81
Reference_base(Reference_base &&)=delete
static Referenced_data * get_content(const Reference_base &other) noexcept
Function to access the content from a reference with different access right.
Definition: ref_any.h:219
Reference_base() noexcept
Constructs a null reference.
Definition: ref_any.h:232
Reference_base(const Reference_base &)=delete
Referenced_data * m_content
Pointer on the data content, can be null if the ref is null.
Definition: ref_any.h:214
static Ref do_copy(Ref_r ref)
Datatype_sptr type() const noexcept
accesses the type of the referenced raw data
Definition: error.h:197
Definition: error.h:211
Definition: array_datatype.h:38
std::shared_ptr< const Datatype > Datatype_sptr
Definition: pdi_fwd.h:78
STL namespace.
A descriptor for a buffer in which references can point.
Definition: ref_any.h:88
std::unordered_map< const Reference_base *, std::function< void(Ref)> > m_notifications
Nullification notifications registered on this instance.
Definition: ref_any.h:106
Referenced_buffer(const Referenced_buffer &)=delete
int m_write_locks
Number of locks preventing write access.
Definition: ref_any.h:103
Referenced_buffer(Referenced_buffer &&)=delete
int m_read_locks
Number of locks preventing read access.
Definition: ref_any.h:97
std::function< void()> m_delete
The function to call to deallocate the buffer memory.
Definition: ref_any.h:91
int m_owners
Number of references to this buffer.
Definition: ref_any.h:94
Referenced_buffer(std::function< void()> deleter, bool readable, bool writable) noexcept
Constructs a new buffer descriptor.
Definition: ref_any.h:114
~Referenced_buffer()
Definition: ref_any.h:128
A descriptor for data on which references can point.
Definition: ref_any.h:143
Referenced_data(Referenced_buffer *buffer, void *data, Datatype_sptr type)
Constructs a new data descriptor from an already referenced buffer.
Definition: ref_any.h:163
Referenced_buffer * m_buffer
The buffer in which the data lives.
Definition: ref_any.h:146
Datatype_sptr m_type
Type of the data.
Definition: ref_any.h:152
~Referenced_data()
Definition: ref_any.h:202
Referenced_data(void *data, std::function< void(void *)> freefunc, Datatype_sptr type, bool readable, bool writable)
Constructs a new data descriptor.
Definition: ref_any.h:182
Referenced_data(const Referenced_data &)=delete
void * m_data
In-memory location of the data.
Definition: ref_any.h:149
int m_owners
Number of references to this data.
Definition: ref_any.h:155
Referenced_data(Referenced_data &&)=delete