PDI 1.11.0

the PDI data interface

ref_any.h
1/*******************************************************************************
2 * Copyright (C) 2015-2026 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/pdi_fwd.h>
37#include <pdi/array_datatype.h>
38#include <pdi/datatype.h>
39#include <pdi/error.h>
40#include <pdi/record_datatype.h>
41#include <pdi/scalar_datatype.h>
42
43namespace PDI {
44
45namespace {
46
49template <bool R, bool W>
50struct Ref_access {
52 using type = void;
53};
54
57template <bool R>
58struct Ref_access<R, true> {
60 using type = void*;
61};
62
65template <>
66struct Ref_access<true, false> {
68 using type = void const *;
69};
70
71template <bool R, bool W>
72using ref_access_t = typename Ref_access<R, W>::type;
73
74} // namespace
75
81class PDI_EXPORT Reference_base
82{
83protected:
88 struct PDI_NO_EXPORT Referenced_buffer {
90 std::function<void()> m_deallocator;
91
94
100
102 std::unordered_map<const Reference_base*, std::function<void(Ref)> > m_notifications;
103
110 Referenced_buffer(std::function<void()> deleter, bool readable, bool writable) noexcept
111 : m_deallocator{deleter}
112 , m_read_locks{readable ? 0 : 1}
113 , m_write_locks{writable ? 0 : 1}
114 {}
115
117
119
121
123 {
125 assert(m_read_locks == 0 || m_read_locks == 1);
126 assert(m_write_locks == 0 || m_write_locks == 1);
127 assert(m_notifications.empty());
128 }
129 };
130
135 struct PDI_NO_EXPORT Referenced_data {
137 mutable std::shared_ptr<Referenced_buffer> m_buffer;
138
140 void* m_data;
141
144
151 Referenced_data(std::shared_ptr<Referenced_buffer> buffer, void* data, Datatype_sptr type)
152 : m_buffer{std::move(buffer)}
153 , m_data{data}
154 , m_type{std::move(type)}
155 {
156 assert(m_buffer);
157 assert(data);
158 }
159
168 Referenced_data(void* data, std::function<void(void*)> freefunc, Datatype_sptr type, bool readable, bool writable)
169 : m_buffer(std::make_shared<Referenced_buffer>(
170 [data, freefunc, type]() {
171 type->destroy_data(data);
172 freefunc(data);
173 },
174 readable,
175 writable
176 ))
177 , m_data{data}
178 , m_type{type}
179 {
180 assert(data);
181 }
182
183 Referenced_data() = delete;
184
186
188 };
189
192 mutable std::shared_ptr<Referenced_data> m_content;
193
196 static std::shared_ptr<Referenced_data> PDI_NO_EXPORT get_content(const Reference_base& other) noexcept
197 {
198 if (!other.m_content) return nullptr;
199 if (!other.m_content->m_data) return nullptr;
200 return other.m_content;
201 }
202
203 // Symbol should not be exported, but it required to force
204 // generation of all 4 variants of `Ref_any::copy`
205 static Ref do_copy(Ref_r ref);
206
209 Reference_base() noexcept
210 : m_content(nullptr)
211 {}
212
214
216
217 Reference_base& operator= (const Reference_base&) = delete;
218
219 Reference_base& operator= (Reference_base&&) = delete;
220
221public:
224 Datatype_sptr type() const noexcept;
225
226 size_t hash() const noexcept { return std::hash<Referenced_data*>()(get_content(*this).get()); }
227
228}; // class Data_ref_base
229
240template <bool R, bool W>
241class PDI_EXPORT Ref_any: public Reference_base
242{
243public:
244 template <bool OR, bool OW>
245 friend class Ref_any;
246
249 Ref_any() = default;
250
257 Ref_any(const Ref_any& other) noexcept
259 {
260 link(get_content(other));
261 }
262
269 template <bool OR, bool OW>
270 Ref_any(const Ref_any<OR, OW>& other) noexcept
272 {
273 link(get_content(other));
274 }
275
279 Ref_any(Ref_any&& other) noexcept
281 {
282 if (!other.m_content) return;
283 // the other ref notification disappears
284 other.m_content->m_buffer->m_notifications.erase(&other);
285 // since we get the same privileges as those we release we can just steal the content
286 m_content = other.m_content;
287 other.m_content = nullptr;
288 }
289
298 Ref_any(void* data, std::function<void(void*)> freefunc, Datatype_sptr type, bool readable, bool writable)
300 {
301 if (type->datasize() && !data && (readable || writable)) {
302 throw Type_error{"Referencing null data with non-null size"};
303 }
304 if (data) {
305 link(std::make_shared<Referenced_data>(data, freefunc, std::move(type), readable, writable));
306 }
307 }
308
312
313 Ref_any& operator= (Ref_any&& other) noexcept
314 {
315 // self-copy: nothing to do
316 if (&other == this) return *this;
317 // we'll be copied into, start nullifying ourselves first
318 reset();
319 // if the other is null also, we're done
320 if (other.is_null()) return *this;
321 // the other ref notification disappears
322 other.m_content->m_buffer->m_notifications.erase(&other);
323 // since we get the same privileges as those we release we can just steal the content
324 m_content = other.m_content;
325 other.m_content = nullptr;
326 return *this;
327 }
328
329 Ref_any& operator= (const Ref_any& other) const noexcept
330 {
331 // self-copy: nothing to do
332 if (&other == this) return *this;
333 // we'll be copied into, start nullifying ourselves first
334 reset();
335 // and copy the content from the other
336 link(get_content(other));
337 return *this;
338 }
339
340 bool operator== (const Reference_base& o) const noexcept
341 {
342 is_null();
343 return m_content == get_content(o);
344 }
345
346 bool operator== (const Ref_any& o) const noexcept
347 {
348 is_null();
349 return m_content == get_content(o);
350 }
351
352 template <bool OR, bool OW>
353 bool operator== (const Ref_any<OR, OW>& o) const noexcept
354 {
355 is_null();
356 return m_content == get_content(o);
357 }
358
359 bool operator!= (const Ref_any& o) const noexcept
360 {
361 is_null();
362 return m_content != get_content(o);
363 }
364
365 template <bool OR, bool OW>
366 bool operator!= (const Ref_any<OR, OW>& o) const noexcept
367 {
368 is_null();
369 return m_content != get_content(o);
370 }
371
372 bool operator!= (const Reference_base& o) const noexcept
373 {
374 is_null();
375 return m_content != get_content(o);
376 }
377
378 bool operator< (const Reference_base& o) const noexcept
379 {
380 is_null();
381 return m_content < get_content(o);
382 }
383
384 bool operator> (const Reference_base& o) const noexcept
385 {
386 is_null();
387 return m_content > get_content(o);
388 }
389
390 bool operator<= (const Reference_base& o) const noexcept
391 {
392 is_null();
393 return m_content <= get_content(o);
394 }
395
396 bool operator>= (const Reference_base& o) const noexcept
397 {
398 is_null();
399 return m_content >= get_content(o);
400 }
401
407 Ref operator[] (const std::string& member_name) const { return this->operator[] (member_name.c_str()); }
408
414 Ref operator[] (const char* member_name) const
415 {
416 if (is_null()) {
417 throw Type_error{"Cannot access member from empty Ref: `{}'", member_name};
418 }
419 std::pair<void*, Datatype_sptr> subref_info = type()->member(member_name, m_content->m_data);
420 Ref result;
421 result.link(std::make_shared<Referenced_data>(m_content->m_buffer, subref_info.first, std::move(subref_info.second)));
422 return result;
423 }
424
430 template <class T>
431 std::enable_if_t<std::is_integral<T>::value, Ref> operator[] (T index) const
432 {
433 if (is_null()) {
434 throw Type_error{"Cannot access array index from empty Ref: `{}'", index};
435 }
436 std::pair<void*, Datatype_sptr> subref_info = type()->index(index, m_content->m_data);
437 Ref result;
438 result.link(std::make_shared<Referenced_data>(m_content->m_buffer, subref_info.first, std::move(subref_info.second)));
439 return result;
440 }
441
447 Ref operator[] (std::pair<std::size_t, std::size_t> slice) const
448 {
449 if (is_null()) {
450 throw Type_error("Cannot access array slice from empty Ref: `{}:{}'", slice.first, slice.second);
451 }
452 std::pair<void*, Datatype_sptr> subref_info = type()->slice(slice.first, slice.second, m_content->m_data);
453 Ref result;
454 result.link(std::make_shared<Referenced_data>(m_content->m_buffer, subref_info.first, std::move(subref_info.second)));
455 return result;
456 }
457
463 {
464 if (is_null()) {
465 throw Type_error{"Cannot dereference an empty Ref"};
466 }
467
468 if (auto&& pointer_type = std::dynamic_pointer_cast<const PDI::Pointer_datatype>(type())) {
469 if constexpr (R) {
470 std::pair<void*, Datatype_sptr> subref_info = type()->dereference(m_content->m_data);
471 Ref result;
472 result.link(std::make_shared<Referenced_data>(m_content->m_buffer, subref_info.first, std::move(subref_info.second)));
473 return result;
474 } else {
475 return Ref_r(*this).dereference();
476 }
477 } else {
478 throw Type_error{"Cannot dereference a non pointer_type"};
479 }
480 }
481
486 operator ref_access_t<R, W> () const { return get(); }
487
492 ref_access_t<R, W> get() const
493 {
494 if (is_null()) throw Permission_error{"Trying to dereference a null reference"};
495 return m_content->m_data;
496 }
497
502 ref_access_t<R, W> get(std::nothrow_t) const noexcept
503 {
504 if (is_null()) return nullptr;
505 return m_content->m_data;
506 }
507
511 template <class T>
512 T scalar_value() const
513 {
514 static_assert(R, "Cannot get scalar_value from Ref without read access");
515 if (auto&& scalar_type = std::dynamic_pointer_cast<const Scalar_datatype>(type())) {
516 if (scalar_type->kind() == PDI::Scalar_kind::UNSIGNED) {
517 switch (scalar_type->buffersize()) {
518 case 1L:
519 return *static_cast<const uint8_t*>(m_content->m_data);
520 case 2L:
521 return *static_cast<const uint16_t*>(m_content->m_data);
522 case 4L:
523 return *static_cast<const uint32_t*>(m_content->m_data);
524 case 8L:
525 return *static_cast<const uint64_t*>(m_content->m_data);
526 default:
527 throw Type_error{"Unknown size of unsigned integer datatype"};
528 }
529 } else if (scalar_type->kind() == PDI::Scalar_kind::SIGNED) {
530 switch (scalar_type->buffersize()) {
531 case 1L:
532 return *static_cast<const int8_t*>(m_content->m_data);
533 case 2L:
534 return *static_cast<const int16_t*>(m_content->m_data);
535 case 4L:
536 return *static_cast<const int32_t*>(m_content->m_data);
537 case 8L:
538 return *static_cast<const int64_t*>(m_content->m_data);
539 default:
540 throw Type_error{"Unknown size of integer datatype"};
541 }
542 } else if (scalar_type->kind() == PDI::Scalar_kind::FLOAT) {
543 switch (type()->buffersize()) {
544 case 4L: {
545 return *static_cast<const float*>(m_content->m_data);
546 }
547 case 8L: {
548 return *static_cast<const double*>(m_content->m_data);
549 }
550 default:
551 throw Type_error{"Unknown size of float datatype"};
552 }
553 } else {
554 throw Type_error{"Unknown datatype to get value"};
555 }
556 }
557 throw Type_error{"Expected scalar, found invalid type instead: {}", type()->debug_string()};
558 }
559
563 template <class T>
564 void scalar_assign(T value)
565 {
566 static_assert(std::is_scalar<T>::value, "T is not a scalar type");
567 static_assert(W, "Cannot assign a scalar value to Ref without write access");
568 if (auto&& scalar_type = std::dynamic_pointer_cast<const Scalar_datatype>(type())) {
569 if (scalar_type->kind() == PDI::Scalar_kind::UNSIGNED) {
570 switch (scalar_type->buffersize()) {
571 case 1L:
572 *static_cast<uint8_t*>(this->get()) = value;
573 return;
574 case 2L:
575 *static_cast<uint16_t*>(this->get()) = value;
576 return;
577 case 4L:
578 *static_cast<uint32_t*>(this->get()) = value;
579 return;
580 case 8L:
581 *static_cast<uint64_t*>(this->get()) = value;
582 return;
583 default:
584 throw Type_error{"Unknown size of unsigned integer datatype"};
585 }
586 } else if (scalar_type->kind() == PDI::Scalar_kind::SIGNED) {
587 switch (scalar_type->buffersize()) {
588 case 1L:
589 *static_cast<int8_t*>(this->get()) = value;
590 return;
591 case 2L:
592 *static_cast<int16_t*>(this->get()) = value;
593 return;
594 case 4L:
595 *static_cast<int32_t*>(this->get()) = value;
596 return;
597 case 8L:
598 *static_cast<int64_t*>(this->get()) = value;
599 return;
600 default:
601 throw Type_error{"Unknown size of integer datatype"};
602 }
603 } else if (scalar_type->kind() == PDI::Scalar_kind::FLOAT) {
604 switch (type()->buffersize()) {
605 case 4L: {
606 *static_cast<float*>(this->get()) = value;
607 return;
608 }
609 case 8L: {
610 *static_cast<double*>(this->get()) = value;
611 return;
612 }
613 default:
614 throw Type_error{"Unknown size of float datatype"};
615 }
616 } else {
617 throw Type_error{"Unknown datatype to get value"};
618 }
619 } else {
620 throw Type_error{"Expected scalar, found invalid type instead: {}", type()->debug_string()};
621 }
622 }
623
628 operator bool () const noexcept { return !is_null(); }
629
632 void reset() noexcept
633 {
634 if (m_content) unlink();
635 }
636
642 Ref copy() const { return do_copy(*this); }
643
650 void* release() noexcept
651 {
652 if (is_null()) return nullptr;
653
654 // notify everybody of the nullification
655 while (!m_content->m_buffer->m_notifications.empty()) {
656 // get the key of a notification
657 const Reference_base* key = m_content->m_buffer->m_notifications.begin()->first;
658 // call this notification, this might invalidate any iterator
659 m_content->m_buffer->m_notifications.begin()->second(*this);
660 // remove the notification we just called
661 m_content->m_buffer->m_notifications.erase(key);
662 }
663
664 void* result = m_content->m_data;
665 m_content->m_data = nullptr;
666 m_content->m_buffer->m_deallocator = []() {
667 }; // Referenced_metadata won't delete data
668
669 unlink();
670
671 return result;
672 }
673
678 void on_nullify(std::function<void(Ref)> notifier) const noexcept
679 {
680 if (!is_null()) m_content->m_buffer->m_notifications[this] = notifier;
681 }
682
683private:
691 bool PDI_NO_EXPORT is_null() const noexcept
692 {
693 if (!m_content) return true;
694 if (!m_content->m_data) {
695 unlink();
696 return true;
697 }
698 return false;
699 }
700
705 void PDI_NO_EXPORT unlink() const noexcept
706 {
707 assert(m_content);
708 m_content->m_buffer->m_notifications.erase(this);
709 if (R || W) --m_content->m_buffer->m_write_locks;
710 if (W) --m_content->m_buffer->m_read_locks;
711 m_content.reset();
712 }
713
722 void PDI_NO_EXPORT link(std::shared_ptr<Referenced_data> content) noexcept
723 {
724 assert(!m_content);
725 if (!content || !content->m_data) return; // null ref
726 if ((R && content->m_buffer->m_read_locks) || (W && content->m_buffer->m_write_locks)) {
727 return;
728 }
729 m_content = std::move(content);
730 if (R || W) ++m_content->m_buffer->m_write_locks;
731 if (W) ++m_content->m_buffer->m_read_locks;
732 }
733};
734
735} // namespace PDI
736
737namespace std {
738
739template <bool R, bool W>
740struct hash<PDI::Ref_any<R, W>> {
741 size_t operator() (const PDI::Ref_any<R, W>& r) const noexcept { return r.hash(); }
742};
743
744} // namespace std
745
746#endif // PDI_REF_ANY_H_
Definition error.h:174
A dynamically typed reference to data with automatic memory management and read/write locking semanti...
Definition ref_any.h:242
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:298
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:270
friend class Ref_any
Definition ref_any.h:245
void scalar_assign(T value)
Assign a scalar value to the data buffer according to its type.
Definition ref_any.h:564
Ref_any(Ref_any &&other) noexcept
Moves an existing reference.
Definition ref_any.h:279
~Ref_any()
Destructor.
Definition ref_any.h:311
void * release() noexcept
Releases ownership of the referenced raw data by nullifying all existing references.
Definition ref_any.h:650
ref_access_t< R, W > get() const
Definition ref_any.h:492
Ref copy() const
Makes a copy of the raw content behind this reference and returns a new reference.
Definition ref_any.h:642
Ref_any(const Ref_any &other) noexcept
Copies an existing reference.
Definition ref_any.h:257
ref_access_t< R, W > get(std::nothrow_t) const noexcept
Offers access to the referenced raw data, returns null for null references.
Definition ref_any.h:502
T scalar_value() const
Returns a scalar value of type T taken from the data buffer.
Definition ref_any.h:512
Ref dereference() const
Create a reference to the pointed content in case the ref type is a reference.
Definition ref_any.h:462
void on_nullify(std::function< void(Ref)> notifier) const noexcept
Registers a nullification callback.
Definition ref_any.h:678
void reset() noexcept
Definition ref_any.h:632
std::shared_ptr< Referenced_data > m_content
Pointer on the data content, can be null if the ref is null.
Definition ref_any.h:192
Reference_base(Reference_base &&)=delete
Reference_base() noexcept
Constructs a null reference.
Definition ref_any.h:209
Reference_base(const Reference_base &)=delete
size_t hash() const noexcept
Definition ref_any.h:226
static Ref do_copy(Ref_r ref)
Datatype_sptr type() const noexcept
accesses the type of the referenced raw data
static std::shared_ptr< 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:196
Definition error.h:187
Definition array_datatype.h:38
Ref_any< true, false > Ref_r
Definition pdi_fwd.h:96
std::shared_ptr< const Datatype > Datatype_sptr
Definition pdi_fwd.h:80
@ SIGNED
Definition pdi_fwd.h:106
@ UNSIGNED
Definition pdi_fwd.h:107
@ FLOAT
Definition pdi_fwd.h:108
Ref_any< false, false > Ref
Definition pdi_fwd.h:94
Definition ref_any.h:737
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:102
Referenced_buffer(const Referenced_buffer &)=delete
std::function< void()> m_deallocator
The function to call to deallocate the buffer memory.
Definition ref_any.h:90
int m_write_locks
Number of locks preventing write access.
Definition ref_any.h:99
Referenced_buffer(Referenced_buffer &&)=delete
int m_read_locks
Number of locks preventing read access.
Definition ref_any.h:93
Referenced_buffer(std::function< void()> deleter, bool readable, bool writable) noexcept
Constructs a new buffer descriptor.
Definition ref_any.h:110
~Referenced_buffer()
Definition ref_any.h:122
std::shared_ptr< Referenced_buffer > m_buffer
The buffer in which the data lives.
Definition ref_any.h:137
Referenced_data(std::shared_ptr< Referenced_buffer > buffer, void *data, Datatype_sptr type)
Constructs a new data descriptor from an already referenced buffer.
Definition ref_any.h:151
Datatype_sptr m_type
Type of the data.
Definition ref_any.h:143
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:168
Referenced_data(const Referenced_data &)=delete
void * m_data
In-memory location of the data.
Definition ref_any.h:140
Referenced_data(Referenced_data &&)=delete