PDI 1.4.3

the PDI data interface

ref_any.h
1 /*******************************************************************************
2  * Copyright (C) 2015-2019 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 
44 namespace PDI {
45 
46 namespace {
47 
50 template<bool R, bool W>
51 struct Ref_access {
53  typedef void type;
54 };
55 
58 template<bool R>
59 struct Ref_access<R, true> {
61  typedef void* type;
62 };
63 
66 template<>
67 struct Ref_access<true, false> {
69  typedef const void* type;
70 };
71 
72 } //namespace <anonymous>
73 
74 
80 class PDI_EXPORT Reference_base
81 {
82 protected:
83 
88  struct PDI_NO_EXPORT Referenced_buffer {
89 
91  std::function<void()> m_delete;
92 
94  int m_owners;
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 
122  Referenced_buffer() = delete;
123 
124  Referenced_buffer(const Referenced_buffer&) = delete;
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 
155  int m_owners;
156 
163  Referenced_data(Referenced_buffer* buffer, void* data, Datatype_uptr type):
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_uptr type, bool readable, bool writable):
183  m_data{data},
184  m_type{std::move(type)},
185  m_owners{0}
186  {
187  assert(data);
188  std::shared_ptr<Datatype> cloned_type = m_type->clone_type(); // cannot use uptr, because std::function must be CopyConstructible
189  m_buffer = new Referenced_buffer{[data, freefunc, cloned_type]() mutable {
190  cloned_type->destroy_data(data);
191  freefunc(data);
192  }, readable, writable};
193  m_buffer->m_owners++;
194  }
195 
196  Referenced_data() = delete;
197 
198  Referenced_data(const Referenced_data&) = delete;
199 
200  Referenced_data(Referenced_data&&) = delete;
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 
236  Reference_base(const Reference_base&) = delete;
237 
238  Reference_base(Reference_base&&) = delete;
239 
240  Reference_base& operator = (const Reference_base&) = delete;
241 
242  Reference_base& operator = (Reference_base&&) = delete;
243 
244 public:
247  const Datatype& 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 
270 template<bool R, bool W>
271 class PDI_EXPORT Ref_any:
272  public Reference_base
273 {
274 public:
277  Ref_any() = default;
278 
285  Ref_any(const Ref_any& other) noexcept:
287  {
288  link(get_content(other));
289  }
290 
297  template<bool OR, bool OW>
298  Ref_any(const Ref_any<OR, OW>& other) noexcept:
300  {
301  link(get_content(other));
302  }
303 
307  Ref_any(Ref_any&& other) noexcept:
309  {
310  if (!other.m_content) return;
311  // the other ref notification disappears
312  other.m_content->m_buffer->m_notifications.erase(&other);
313  // since we get the same privileges as those we release we can just steal the content
314  m_content = other.m_content;
315  other.m_content = nullptr;
316  }
317 
326  Ref_any(void* data, std::function<void(void*)> freefunc, Datatype_uptr type, bool readable, bool writable):
328  {
329  if (type->datasize() && !data && (readable||writable)) {
330  throw Type_error{"Referencing null data with non-null size"};
331  }
332  if (data) {
333  link(new Referenced_data(data, freefunc, std::move(type), readable, writable));
334  }
335  }
336 
342  Ref_any(Ref other, const Datatype::Accessor_base& accessor):
344  {
345  if (other) {
346  std::pair<void*, Datatype_uptr> subref_info = other.type().subaccess(get_content(other)->m_data, accessor);
347  link(new Referenced_data(
348  get_content(other)->m_buffer,
349  subref_info.first,
350  std::move(subref_info.second)));
351  }
352  }
353 
359  Ref_any(Ref other, const std::vector<std::unique_ptr<Datatype::Accessor_base>>& accessors):
361  {
362  if (other) {
363  std::pair<void*, Datatype_uptr> subref_info = other.type().subaccess(get_content(other)->m_data, accessors);
364  link(new Referenced_data(
365  get_content(other)->m_buffer,
366  subref_info.first,
367  std::move(subref_info.second)));
368  }
369  }
370 
374  {
375  reset();
376  }
377 
378  Ref_any& operator= (Ref_any&& other) noexcept
379  {
380  // self-copy: nothing to do
381  if (&other == this) return *this;
382  // we'll be copied into, start nullifying ourselves first
383  reset();
384  // if the other is null also, we're done
385  if (other.is_null()) return *this;
386  // the other ref notification disappears
387  other.m_content->m_buffer->m_notifications.erase(&other);
388  // since we get the same privileges as those we release we can just steal the content
389  m_content = other.m_content;
390  other.m_content = nullptr;
391  return *this;
392  }
393 
394  Ref_any& operator= (const Ref_any& other) const noexcept
395  {
396  // self-copy: nothing to do
397  if (&other == this) return *this;
398  // we'll be copied into, start nullifying ourselves first
399  reset();
400  // and copy the content from the other
401  link(get_content(other));
402  return *this;
403  }
404 
405  bool operator== (const Reference_base& o) const noexcept
406  {
407  is_null();
408  return m_content == get_content(o);
409  }
410 
411  bool operator!= (const Reference_base& o) const noexcept
412  {
413  is_null();
414  return m_content != get_content(o);
415  }
416 
417  bool operator< (const Reference_base& o) const noexcept
418  {
419  is_null();
420  return m_content < get_content(o);
421  }
422 
423  bool operator> (const Reference_base& o) const noexcept
424  {
425  is_null();
426  return m_content > get_content(o);
427  }
428 
429  bool operator<= (const Reference_base& o) const noexcept
430  {
431  is_null();
432  return m_content <= get_content(o);
433  }
434 
435  bool operator>= (const Reference_base& o) const noexcept
436  {
437  is_null();
438  return m_content >= get_content(o);
439  }
440 
446  Ref operator[] (const std::string& member_name) const
447  {
448  if (is_null()) {
449  throw Type_error{"Cannot access member from empty Ref: `{}'", member_name};
450  }
451  return Ref{*this, Record_datatype::Member_accessor{member_name}};
452  }
453 
459  Ref operator[] (const char* member_name) const
460  {
461  return this->operator[](std::string(member_name));
462  }
463 
469  Ref operator[] (std::size_t index) const
470  {
471  if (is_null()) {
472  throw Type_error{"Cannot access array index from empty Ref: `{}'", index};
473  }
474  return Ref{*this, Array_datatype::Index_accessor{index}};
475  }
476 
482  Ref operator[] (std::pair<std::size_t, std::size_t> slice) const
483  {
484  if (is_null()) {
485  throw Type_error{"Cannot access array slice from empty Ref: `{}'", index};
486  }
487  return Ref{*this, Array_datatype::Slice_accessor{slice.first, slice.second}};
488  }
489 
494  operator typename Ref_access<R, W>::type() const
495  {
496  return get();
497  }
498 
503  typename Ref_access<R, W>::type get() const
504  {
505  if (is_null()) throw Right_error{"Trying to dereference a null reference"};
506  return m_content->m_data;
507  }
508 
513  typename Ref_access<R, W>::type get(std::nothrow_t) const noexcept
514  {
515  if (is_null()) return nullptr;
516  return m_content->m_data;
517  }
518 
522  template<class T>
524  {
525  static_assert(R, "Cannot get scalar_value from Ref without read access");
526  if (const Scalar_datatype* scalar_type = dynamic_cast<const Scalar_datatype*>(&type())) {
527  if (scalar_type->kind() == PDI::Scalar_kind::UNSIGNED) {
528  switch (scalar_type->buffersize()) {
529  case 1L:
530  return *static_cast<const uint8_t*>(m_content->m_data);
531  case 2L:
532  return *static_cast<const uint16_t*>(m_content->m_data);
533  case 4L:
534  return *static_cast<const uint32_t*>(m_content->m_data);
535  case 8L:
536  return *static_cast<const uint64_t*>(m_content->m_data);
537  default:
538  throw Type_error{"Unknown size of unsigned integer datatype"};
539  }
540  } else if (scalar_type->kind() == PDI::Scalar_kind::SIGNED) {
541  switch (scalar_type->buffersize()) {
542  case 1L:
543  return *static_cast<const int8_t*>(m_content->m_data);
544  case 2L:
545  return *static_cast<const int16_t*>(m_content->m_data);
546  case 4L:
547  return *static_cast<const int32_t*>(m_content->m_data);
548  case 8L:
549  return *static_cast<const int64_t*>(m_content->m_data);
550  default:
551  throw Type_error{"Unknown size of integer datatype"};
552  }
553  } else if (scalar_type->kind() == PDI::Scalar_kind::FLOAT) {
554  switch (type().buffersize()) {
555  case 4L: {
556  return *static_cast<const float*>(m_content->m_data);
557  }
558  case 8L: {
559  return *static_cast<const double*>(m_content->m_data);
560  }
561  default:
562  throw Type_error{"Unknown size of float datatype"};
563  }
564  } else {
565  throw Type_error{"Unknown datatype to get value"};
566  }
567  }
568  throw Type_error{"Expected scalar, found invalid type instead: {}", type().debug_string()};
569  }
570 
575  operator bool () const noexcept
576  {
577  return !is_null();
578  }
579 
582  void reset() noexcept
583  {
584  if (m_content) unlink();
585  }
586 
592  Ref copy() const
593  {
594  return do_copy(*this);
595  }
596 
603  void* release() noexcept
604  {
605  if (is_null()) return nullptr;
606 
607  // notify everybody of the nullification
608  while (!m_content->m_buffer->m_notifications.empty()) {
609  // get the key of a notification
610  const Reference_base* key = m_content->m_buffer->m_notifications.begin()->first;
611  // call this notification, this might invalidate any iterator
612  m_content->m_buffer->m_notifications.begin()->second(*this);
613  // remove the notification we just called
614  m_content->m_buffer->m_notifications.erase(key);
615  }
616 
617  void* result = m_content->m_data;
618  m_content->m_data = nullptr;
619  m_content->m_buffer->m_delete = []() {}; // Referenced_metadata won't delete data
620 
621  unlink();
622 
623  return result;
624  }
625 
630  void on_nullify(std::function<void(Ref)> notifier) const noexcept
631  {
632  if (!is_null()) m_content->m_buffer->m_notifications[this] = notifier;
633  }
634 
635 private:
643  bool PDI_NO_EXPORT is_null() const noexcept
644  {
645  if (!m_content) return true;
646  if (!m_content->m_data) {
647  unlink();
648  return true;
649  }
650  return false;
651  }
652 
657  void PDI_NO_EXPORT unlink() const noexcept
658  {
659  assert(m_content);
660  m_content->m_buffer->m_notifications.erase(this);
661  if (R || W) --m_content->m_buffer->m_write_locks;
662  if (W) --m_content->m_buffer->m_read_locks;
663  --m_content->m_owners;
664  if (!m_content->m_owners) delete m_content;
665  m_content = nullptr;
666  }
667 
675  void PDI_NO_EXPORT link(Referenced_data* content) noexcept
676  {
677  assert(!m_content);
678  if (!content || !content->m_data) return; // null ref
679  if (R && content->m_buffer->m_read_locks) return;
680  if (W && content->m_buffer->m_write_locks) return;
681  m_content = content;
682  ++m_content->m_owners;
683  if (R || W) ++m_content->m_buffer->m_write_locks;
684  if (W) ++m_content->m_buffer->m_read_locks;
685  }
686 
687 };
688 
689 } // namespace PDI
690 
691 namespace std {
692 
693 template<bool R, bool W>
694 struct hash<PDI::Ref_any<R,W>> {
695  size_t operator() (const PDI::Ref_any<R,W>& r) const noexcept
696  {
697  return r.hash();
698  }
699 };
700 
701 } // namespace std
702 
703 #endif // PDI_REF_ANY_H_
Definition: error.h:209
A descriptor for data on which references can point.
Definition: ref_any.h:143
Accessor to get single element from array.
Definition: array_datatype.h:61
T scalar_value()
Returns a scalar value of type T taken from the data buffer.
Definition: ref_any.h:523
Definition: scalar_datatype.h:38
STL namespace.
std::unordered_map< const Reference_base *, std::function< void(Ref)> > m_notifications
Nullification notifications registered on this instance.
Definition: ref_any.h:106
A Datatype is a Datatype_template that accepts no argument.
Definition: datatype.h:46
Base class for datatype accesssors, that allow to get pointer to subtype.
Definition: datatype.h:52
Ref_any(const Ref_any &other) noexcept
Copies an existing reference.
Definition: ref_any.h:285
void * m_data
In-memory location of the data.
Definition: ref_any.h:149
A dynamically typed reference to data with automatic memory management and read/write locking semanti...
Definition: pdi_fwd.h:85
~Referenced_buffer()
Definition: ref_any.h:128
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
Ref_any(Ref other, const Datatype::Accessor_base &accessor)
Creates a subreference from reference.
Definition: ref_any.h:342
Accessor to get a slice of an array, returns array of the same subtype.
Definition: array_datatype.h:84
size_t hash() const noexcept
Definition: ref_any.h:249
int m_owners
Number of references to this buffer.
Definition: ref_any.h:94
std::unique_ptr< Datatype > Datatype_uptr
Definition: pdi_fwd.h:74
Datatype_uptr m_type
Type of the data.
Definition: ref_any.h:152
Referenced_data(Referenced_buffer *buffer, void *data, Datatype_uptr type)
Constructs a new data descriptor from an already referenced buffer.
Definition: ref_any.h:163
Reference_base() noexcept
Constructs a null reference.
Definition: ref_any.h:232
std::function< void()> m_delete
The function to call to deallocate the buffer memory.
Definition: ref_any.h:91
int m_read_locks
Number of locks preventing read access.
Definition: ref_any.h:97
int m_owners
Number of references to this data.
Definition: ref_any.h:155
Definition: error.h:195
void * release() noexcept
Releases ownership of the referenced raw data by nullifying all existing references.
Definition: ref_any.h:603
A descriptor for a buffer in which references can point.
Definition: ref_any.h:88
~Ref_any()
Destructor.
Definition: ref_any.h:373
Referenced_data(void *data, std::function< void(void *)> freefunc, Datatype_uptr type, bool readable, bool writable)
Constructs a new data descriptor.
Definition: ref_any.h:182
Referenced_buffer * m_buffer
The buffer in which the data lives.
Definition: ref_any.h:146
void reset() noexcept
Nullify the reference.
Definition: ref_any.h:582
Ref_any(void *data, std::function< void(void *)> freefunc, Datatype_uptr type, bool readable, bool writable)
Creates a reference to currently unreferenced data.
Definition: ref_any.h:326
int m_write_locks
Number of locks preventing write access.
Definition: ref_any.h:103
Definition: array_datatype.h:37
void on_nullify(std::function< void(Ref)> notifier) const noexcept
Registers a nullification callback.
Definition: ref_any.h:630
Ref_any(const Ref_any< OR, OW > &other) noexcept
Copies an existing reference with different privileges.
Definition: ref_any.h:298
Ref_any(Ref other, const std::vector< std::unique_ptr< Datatype::Accessor_base >> &accessors)
Creates a subreference from reference.
Definition: ref_any.h:359
Ref_any(Ref_any &&other) noexcept
Moves an existing reference.
Definition: ref_any.h:307
Referenced_data * m_content
Pointer on the data content, can be null if the ref is null.
Definition: ref_any.h:214
std::pair< void *, Datatype_uptr > subaccess(void *from, const Accessor_base &accessor) const
Creates datatype of subtype and returns it with a moved pointer.
Member accessor for record datatype.
Definition: record_datatype.h:48
A common base for all references, whatever their access privileges.
Definition: ref_any.h:80
const Datatype & type() const noexcept
accesses the type of the referenced raw data
Ref copy() const
Makes a copy of the raw content behind this reference and returns a new reference.
Definition: ref_any.h:592
Referenced_buffer(std::function< void()> deleter, bool readable, bool writable) noexcept
Constructs a new buffer descriptor.
Definition: ref_any.h:114
~Referenced_data()
Definition: ref_any.h:202