PDI 1.1.0

Data exchange made easy

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  Datatype* cloned_type = m_type->clone_type().release(); // 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  delete (cloned_type);
192  freefunc(data);
193  }, readable, writable};
194  m_buffer->m_owners++;
195  }
196 
197  Referenced_data() = delete;
198 
199  Referenced_data(const Referenced_data&) = delete;
200 
201  Referenced_data(Referenced_data&&) = delete;
202 
204  {
205  m_buffer->m_owners--;
206  if (m_buffer->m_owners == 0) {
207  delete m_buffer;
208  }
209  }
210  };
211 
212 
216 
217 
220  static Referenced_data PDI_NO_EXPORT* get_content(const Reference_base& other) noexcept
221  {
222  if ( !other.m_content ) return NULL;
223  if ( !other.m_content->m_data ) return NULL;
224  return other.m_content;
225  }
226 
227  // Symbol should not be exported, but it required to force
228  // generation of all 4 variants of `Ref_any::copy`
229  static Ref do_copy(Ref_r ref);
230 
233  Reference_base() noexcept:
234  m_content(nullptr)
235  {}
236 
237  Reference_base(const Reference_base&) = delete;
238 
239  Reference_base(Reference_base&&) = delete;
240 
241  Reference_base& operator = (const Reference_base&) = delete;
242 
243  Reference_base& operator = (Reference_base&&) = delete;
244 
245 public:
248  const Datatype& type() const noexcept;
249 
250  size_t hash() const noexcept
251  {
252  return std::hash<Referenced_data*>()(get_content(*this));
253  }
254 
255 }; // class Data_ref_base
256 
257 
271 template<bool R, bool W>
272 class PDI_EXPORT Ref_any:
273  public Reference_base
274 {
275 public:
278  Ref_any() = default;
279 
286  Ref_any(const Ref_any& other) noexcept:
288  {
289  link(get_content(other));
290  }
291 
298  template<bool OR, bool OW>
299  Ref_any(const Ref_any<OR, OW>& other) noexcept:
301  {
302  link(get_content(other));
303  }
304 
308  Ref_any(Ref_any&& other) noexcept:
310  {
311  if (!other.m_content) return;
312  // the other ref notification disappears
313  other.m_content->m_buffer->m_notifications.erase(&other);
314  // since we get the same privileges as those we release we can just steal the content
315  m_content = other.m_content;
316  other.m_content = nullptr;
317  }
318 
327  Ref_any(void* data, std::function<void(void*)> freefunc, Datatype_uptr type, bool readable, bool writable):
329  {
330  if (type->datasize() && !data && (readable||writable)) {
331  throw Type_error{"Referencing null data with non-null size"};
332  }
333  if (data) {
334  link(new Referenced_data(data, freefunc, std::move(type), readable, writable));
335  }
336  }
337 
343  Ref_any(Ref other, const Datatype::Accessor_base& accessor):
345  {
346  if (other) {
347  std::pair<void*, Datatype_uptr> subref_info = other.type().subaccess(get_content(other)->m_data, accessor);
348  link(new Referenced_data(
349  get_content(other)->m_buffer,
350  subref_info.first,
351  std::move(subref_info.second)));
352  }
353  }
354 
360  Ref_any(Ref other, const std::vector<std::unique_ptr<Datatype::Accessor_base>>& accessors):
362  {
363  if (other) {
364  std::pair<void*, Datatype_uptr> subref_info = other.type().subaccess(get_content(other)->m_data, accessors);
365  link(new Referenced_data(
366  get_content(other)->m_buffer,
367  subref_info.first,
368  std::move(subref_info.second)));
369  }
370  }
371 
375  {
376  reset();
377  }
378 
379  Ref_any& operator= (Ref_any&& other) noexcept
380  {
381  // self-copy: nothing to do
382  if (&other == this) return *this;
383  // we'll be copied into, start nullifying ourselves first
384  reset();
385  // if the other is null also, we're done
386  if (other.is_null()) return *this;
387  // the other ref notification disappears
388  other.m_content->m_buffer->m_notifications.erase(&other);
389  // since we get the same privileges as those we release we can just steal the content
390  m_content = other.m_content;
391  other.m_content = nullptr;
392  return *this;
393  }
394 
395  Ref_any& operator= (const Ref_any& other) const noexcept
396  {
397  // self-copy: nothing to do
398  if (&other == this) return *this;
399  // we'll be copied into, start nullifying ourselves first
400  reset();
401  // and copy the content from the other
402  link(get_content(other));
403  return *this;
404  }
405 
406  bool operator== (const Reference_base& o) const noexcept
407  {
408  is_null();
409  return m_content == get_content(o);
410  }
411 
412  bool operator!= (const Reference_base& o) const noexcept
413  {
414  is_null();
415  return m_content != get_content(o);
416  }
417 
418  bool operator< (const Reference_base& o) const noexcept
419  {
420  is_null();
421  return m_content < get_content(o);
422  }
423 
424  bool operator> (const Reference_base& o) const noexcept
425  {
426  is_null();
427  return m_content > get_content(o);
428  }
429 
430  bool operator<= (const Reference_base& o) const noexcept
431  {
432  is_null();
433  return m_content <= get_content(o);
434  }
435 
436  bool operator>= (const Reference_base& o) const noexcept
437  {
438  is_null();
439  return m_content >= get_content(o);
440  }
441 
447  Ref operator[] (const std::string& member_name) const
448  {
449  if (is_null()) {
450  throw Type_error{"Cannot access member from empty Ref: `{}'", member_name};
451  }
452  return Ref{*this, Record_datatype::Member_accessor{member_name}};
453  }
454 
460  Ref operator[] (const char* member_name) const
461  {
462  return this->operator[](std::string(member_name));
463  }
464 
470  Ref operator[] (std::size_t index) const
471  {
472  if (is_null()) {
473  throw Type_error{"Cannot access array index from empty Ref: `{}'", index};
474  }
475  return Ref{*this, Array_datatype::Index_accessor{index}};
476  }
477 
483  Ref operator[] (std::pair<std::size_t, std::size_t> slice) const
484  {
485  if (is_null()) {
486  throw Type_error{"Cannot access array slice from empty Ref: `{}'", index};
487  }
488  return Ref{*this, Array_datatype::Slice_accessor{slice.first, slice.second}};
489  }
490 
495  operator typename Ref_access<R, W>::type() const
496  {
497  return get();
498  }
499 
504  typename Ref_access<R, W>::type get() const
505  {
506  if (is_null()) throw Right_error{"Trying to dereference a null reference"};
507  return m_content->m_data;
508  }
509 
514  typename Ref_access<R, W>::type get(std::nothrow_t) const noexcept
515  {
516  if (is_null()) return nullptr;
517  return m_content->m_data;
518  }
519 
523  template<class T>
525  {
526  static_assert(R, "Cannot get scalar_value from Ref without read access");
527  if (const Scalar_datatype* scalar_type = dynamic_cast<const Scalar_datatype*>(&type())) {
528  if (scalar_type->kind() == PDI::Scalar_kind::UNSIGNED) {
529  switch (scalar_type->buffersize()) {
530  case 1L:
531  return *static_cast<const uint8_t*>(m_content->m_data);
532  case 2L:
533  return *static_cast<const uint16_t*>(m_content->m_data);
534  case 4L:
535  return *static_cast<const uint32_t*>(m_content->m_data);
536  case 8L:
537  return *static_cast<const uint64_t*>(m_content->m_data);
538  default:
539  throw Type_error{"Unknown size of unsigned integer datatype"};
540  }
541  } else if (scalar_type->kind() == PDI::Scalar_kind::SIGNED) {
542  switch (scalar_type->buffersize()) {
543  case 1L:
544  return *static_cast<const int8_t*>(m_content->m_data);
545  case 2L:
546  return *static_cast<const int16_t*>(m_content->m_data);
547  case 4L:
548  return *static_cast<const int32_t*>(m_content->m_data);
549  case 8L:
550  return *static_cast<const int64_t*>(m_content->m_data);
551  default:
552  throw Type_error{"Unknown size of integer datatype"};
553  }
554  } else if (scalar_type->kind() == PDI::Scalar_kind::FLOAT) {
555  switch (type().buffersize()) {
556  case 4L: {
557  return *static_cast<const float*>(m_content->m_data);
558  }
559  case 8L: {
560  return *static_cast<const double*>(m_content->m_data);
561  }
562  default:
563  throw Type_error{"Unknown size of float datatype"};
564  }
565  } else {
566  throw Type_error{"Unknown datatype to get value"};
567  }
568  }
569  throw Type_error{"Expected scalar, found invalid type instead: {}", type().debug_string()};
570  }
571 
576  operator bool () const noexcept
577  {
578  return !is_null();
579  }
580 
583  void reset() noexcept
584  {
585  if (m_content) unlink();
586  }
587 
593  Ref copy() const
594  {
595  return do_copy(*this);
596  }
597 
604  void* release() noexcept
605  {
606  if (is_null()) return nullptr;
607 
608  // notify everybody of the nullification
609  while (!m_content->m_buffer->m_notifications.empty()) {
610  // get the key of a notification
611  const Reference_base* key = m_content->m_buffer->m_notifications.begin()->first;
612  // call this notification, this might invalidate any iterator
613  m_content->m_buffer->m_notifications.begin()->second(*this);
614  // remove the notification we just called
615  m_content->m_buffer->m_notifications.erase(key);
616  }
617 
618  void* result = m_content->m_data;
619  m_content->m_data = nullptr;
620  m_content->m_buffer->m_delete = []() {}; // Referenced_metadata won't delete data
621 
622  unlink();
623 
624  return result;
625  }
626 
631  void on_nullify(std::function<void(Ref)> notifier) const noexcept
632  {
633  if (!is_null()) m_content->m_buffer->m_notifications[this] = notifier;
634  }
635 
636 private:
644  bool PDI_NO_EXPORT is_null() const noexcept
645  {
646  if (!m_content) return true;
647  if (!m_content->m_data) {
648  unlink();
649  return true;
650  }
651  return false;
652  }
653 
658  void PDI_NO_EXPORT unlink() const noexcept
659  {
660  assert(m_content);
661  m_content->m_buffer->m_notifications.erase(this);
662  if (R || W) --m_content->m_buffer->m_write_locks;
663  if (W) --m_content->m_buffer->m_read_locks;
664  --m_content->m_owners;
665  if (!m_content->m_owners) delete m_content;
666  m_content = nullptr;
667  }
668 
676  void PDI_NO_EXPORT link(Referenced_data* content) noexcept
677  {
678  assert(!m_content);
679  if (!content || !content->m_data) return; // null ref
680  if (R && content->m_buffer->m_read_locks) return;
681  if (W && content->m_buffer->m_write_locks) return;
682  m_content = content;
683  ++m_content->m_owners;
684  if (R || W) ++m_content->m_buffer->m_write_locks;
685  if (W) ++m_content->m_buffer->m_read_locks;
686  }
687 
688 };
689 
690 } // namespace PDI
691 
692 namespace std {
693 
694 template<bool R, bool W>
695 struct hash<PDI::Ref_any<R,W>> {
696  size_t operator() (const PDI::Ref_any<R,W>& r) const noexcept
697  {
698  return r.hash();
699  }
700 };
701 
702 } // namespace std
703 
704 #endif // PDI_REF_ANY_H_
virtual Datatype_uptr clone_type() const =0
Creates a new datatype as an exact copy of this one.
Definition: error.h:180
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:524
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
virtual void destroy_data(void *ptr) const =0
Function used to delete the data behind the datatype.
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:286
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:93
~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:220
Ref_any(Ref other, const Datatype::Accessor_base &accessor)
Creates a subreference from reference.
Definition: ref_any.h:343
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:250
int m_owners
Number of references to this buffer.
Definition: ref_any.h:94
std::unique_ptr< Datatype > Datatype_uptr
Definition: pdi_fwd.h:78
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:233
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:166
void * release() noexcept
Releases ownership of the referenced raw data by nullifying all existing references.
Definition: ref_any.h:604
A descriptor for a buffer in which references can point.
Definition: ref_any.h:88
~Ref_any()
Destructor.
Definition: ref_any.h:374
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:583
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:327
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:631
Ref_any(const Ref_any< OR, OW > &other) noexcept
Copies an existing reference with different privileges.
Definition: ref_any.h:299
Ref_any(Ref other, const std::vector< std::unique_ptr< Datatype::Accessor_base >> &accessors)
Creates a subreference from reference.
Definition: ref_any.h:360
Ref_any(Ref_any &&other) noexcept
Moves an existing reference.
Definition: ref_any.h:308
Referenced_data * m_content
Pointer on the data content, can be null if the ref is null.
Definition: ref_any.h:215
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:593
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:203