clove.h
Go to the documentation of this file.
1 #ifndef GARLIC_CLOVE_H
2 #define GARLIC_CLOVE_H
3 
14 #include "garlic.h"
15 #include "allocators.h"
16 #include "layer.h"
17 
18 namespace garlic {
19 
20  template<typename SizeType = unsigned>
21  struct StringData {
22  SizeType length;
23  char* data;
24  };
25 
26  template<typename Type, typename SizeType = unsigned>
27  struct Array {
28  using Container = Type*;
29 
30  SizeType capacity;
31  SizeType length;
32  Container data;
33  };
34 
35  template<GARLIC_ALLOCATOR Allocator, typename SizeType = unsigned>
36  struct GenericData {
37  using List = Array<GenericData, SizeType>;
38  using Object = Array<MemberPair<GenericData>, SizeType>;
39  using AllocatorType = Allocator;
40 
41  TypeFlag type = TypeFlag::Null;
42  union {
43  double dvalue;
44  int integer;
45  bool boolean;
46  StringData<SizeType> string;
47  List list;
48  Object object;
49  };
50  };
51 
52  template<typename Layer, typename Iterator>
53  struct ConstMemberIteratorWrapper {
54  using output_type = MemberPair<Layer>;
55  using iterator_type = Iterator;
56 
57  iterator_type iterator;
58 
59  inline output_type wrap() const {
60  return output_type {
61  Layer { iterator->key },
62  Layer { iterator->value }
63  };
64  }
65  };
66 
67  template<typename Layer, typename Iterator, typename Allocator>
68  struct MemberIteratorWrapper {
69  using output_type = MemberPair<Layer>;
70  using iterator_type = Iterator;
71  using allocator_type = Allocator;
72 
73  iterator_type iterator;
74  allocator_type* allocator;
75 
76  inline output_type wrap() const {
77  return output_type {
78  Layer { iterator->key, *allocator },
79  Layer { iterator->value, *allocator }
80  };
81  }
82  };
83 
84  template<typename Layer, typename Iterator, typename Allocator>
85  struct ValueIteratorWrapper {
86  using output_type = Layer;
87  using iterator_type = Iterator;
88  using allocator_type = Allocator;
89 
90  iterator_type iterator;
91  allocator_type* allocator;
92 
93  inline output_type wrap() const {
94  return output_type { *iterator, *allocator };
95  }
96  };
97 
98  template<GARLIC_ALLOCATOR Allocator, typename SizeType = unsigned>
99  class GenericCloveView {
100  public:
101  using DataType = GenericData<Allocator, SizeType>;
102  using ProviderValueIterator = typename DataType::List::Container;
103  using ProviderMemberIterator = typename DataType::Object::Container;
104  using ConstValueIterator = BasicRandomAccessIterator<GenericCloveView, ProviderValueIterator>;
105  using ConstMemberIterator = RandomAccessIterator<
106  ConstMemberIteratorWrapper<GenericCloveView, ProviderMemberIterator>>;
107 
108  GenericCloveView (const DataType& data) : data_(data) {}
109 
110  bool is_null() const noexcept { return data_.type & TypeFlag::Null; }
111  bool is_int() const noexcept { return data_.type & TypeFlag::Integer; }
112  bool is_string() const noexcept { return data_.type & TypeFlag::String; }
113  bool is_double() const noexcept { return data_.type & TypeFlag::Double; }
114  bool is_object() const noexcept { return data_.type & TypeFlag::Object; }
115  bool is_list() const noexcept { return data_.type & TypeFlag::List; }
116  bool is_bool() const noexcept { return data_.type & TypeFlag::Boolean; }
117 
118  int get_int() const { return data_.integer; }
119  double get_double() const { return data_.dvalue; }
120  bool get_bool() const { return data_.boolean; }
121  const char* get_cstr() const { return data_.string.data; }
122  std::string get_string() const {
123  return std::string{data_.string.data, data_.string.length};
124  }
125  std::string_view get_string_view() const {
126  return std::string_view{data_.string.data, data_.string.length};
127  }
128 
129  ConstValueIterator begin_list() const { return ConstValueIterator({data_.list.data}); }
130  ConstValueIterator end_list() const {
131  return ConstValueIterator({data_.list.data + data_.list.length});
132  }
133  auto get_list() const { return ConstListRange<GenericCloveView>{*this}; }
134 
135  ConstMemberIterator begin_member() const { return ConstMemberIterator({data_.object.data}); }
136  ConstMemberIterator end_member() const {
137  return ConstMemberIterator({data_.object.data + data_.object.length});
138  }
139  ConstMemberIterator find_member(text key) const {
140  return std::find_if(this->begin_member(), this->end_member(), [&key](auto item) {
141  return strncmp(item.key.get_cstr(), key.data(), key.size()) == 0;
142  });
143  }
144  ConstMemberIterator find_member(const GenericCloveView& value) const {
145  return this->find_member(value.get_cstr());
146  }
147  auto get_object() const { return ConstMemberRange<GenericCloveView>{*this}; }
148 
149  GenericCloveView get_view() const { return GenericCloveView{data_}; }
150  const DataType& get_inner_value() const { return data_; }
151 
152  private:
153  const DataType& data_;
154  };
155 
156 
157  template<GARLIC_ALLOCATOR Allocator, typename SizeType = unsigned>
158  class GenericCloveRef : public GenericCloveView<Allocator> {
159  public:
160  using ViewType = GenericCloveView<Allocator, SizeType>;
161  using DataType = typename ViewType::DataType;
162  using AllocatorType = Allocator;
163  using ProviderValueIterator = typename ViewType::ProviderValueIterator;
164  using ProviderMemberIterator = typename ViewType::ProviderMemberIterator;
165  using ValueIterator = RandomAccessIterator<
166  ValueIteratorWrapper<GenericCloveRef, ProviderValueIterator, AllocatorType>>;
167  using MemberIterator = RandomAccessIterator<
168  MemberIteratorWrapper<GenericCloveRef, ProviderMemberIterator, AllocatorType>>;
169  using ViewType::begin_list;
170  using ViewType::end_list;
171  using ViewType::get_list;
172  using ViewType::begin_member;
173  using ViewType::end_member;
174  using ViewType::get_object;
175  using ViewType::find_member;
176 
177  GenericCloveRef (
178  DataType& data, AllocatorType& allocator
179  ) : data_(data), allocator_(allocator), ViewType(data) {}
180 
181 
182  void set_string(const char* str) {
183  this->prepare_string(strlen(str));
184  strcpy(this->data_.string.data, str);
185  }
186 
187  void set_string(text value) {
188  this->prepare_string(value.size());
189  strncpy(this->data_.string.data, value.data(), value.size());
190  }
191 
192  void set_double(double value) {
193  this->clean();
194  this->data_.type = TypeFlag::Double;
195  this->data_.dvalue = value;
196  }
197  void set_int(int value) {
198  this->clean();
199  this->data_.type = TypeFlag::Integer;
200  this->data_.integer = value;
201  }
202  void set_bool(bool value) {
203  this->clean();
204  this->data_.type = TypeFlag::Boolean;
205  this->data_.boolean = value;
206  }
207  void set_null() {
208  this->clean();
209  this->data_.type = TypeFlag::Null;
210  }
211  void set_list() {
212  if (this->is_list()) return;
213  this->clean();
214  this->data_.type = TypeFlag::List;
215  this->data_.list.data = reinterpret_cast<typename DataType::List::Container>(
216  allocator_.allocate(256 * sizeof(DataType))
217  );
218  this->data_.list.length = 0;
219  this->data_.list.capacity = 16;
220  }
221  void set_object() {
222  if (this->is_object()) return;
223  this->clean();
224  this->data_.type = TypeFlag::Object;
225  this->data_.object.data = reinterpret_cast<typename DataType::Object::Container>(
226  allocator_.allocate(16 * sizeof(MemberPair<DataType>))
227  );
228  this->data_.object.length = 0;
229  this->data_.object.capacity = 16;
230  }
231 
232  GenericCloveRef& operator = (double value) { this->set_double(value); return *this; }
233  GenericCloveRef& operator = (int value) { this->set_int(value); return *this; }
234  GenericCloveRef& operator = (bool value) { this->set_bool(value); return *this; }
235  GenericCloveRef& operator = (text value) { this->set_string(value); return *this; }
236 
237  ValueIterator begin_list() {
238  return ValueIterator({data_.list.data, &allocator_});
239  }
240  ValueIterator end_list() {
241  return ValueIterator({data_.list.data + data_.list.length, &allocator_});
242  }
243  ListRange<GenericCloveRef> get_list() { return ListRange<GenericCloveRef>{*this}; }
244 
245  MemberIterator begin_member() { return MemberIterator({data_.object.data, &allocator_}); }
246  MemberIterator end_member() {
247  return MemberIterator({
248  data_.object.data + data_.object.length,
249  &allocator_
250  });
251  }
252  MemberRange<GenericCloveRef> get_object() { return MemberRange<GenericCloveRef>{*this}; }
253 
254  // list functions
255  void clear() {
256  // destruct all the elements but keep the pointers as is.
257  std::for_each(this->begin_list(), this->end_list(), [](auto item){ item.clean(); });
258  this->data_.list.length = 0;
259  }
260 
261  template<typename Callable>
262  void push_back_builder(Callable&& cb) {
263  this->check_list();
264  DataType value;
265  cb(GenericCloveRef(value, allocator_));
266  this->push_back(std::move(value));
267  }
268  void push_back(DataType&& value) {
269  this->check_list();
270  this->data_.list.data[this->data_.list.length++] = std::move(value);
271  }
272  void push_back() {
273  this->push_back(DataType{});
274  }
275  void push_back(const char* value) {
276  this->push_back(text (value));
277  }
278  void push_back(text value) {
279  DataType data;
280  GenericCloveRef(data, allocator_).set_string(value);
281  this->push_back(std::move(data));
282  }
283  void push_back(double value) {
284  DataType data;
285  GenericCloveRef(data, allocator_).set_double(value);
286  this->push_back(std::move(data));
287  }
288  void push_back(int value) {
289  DataType data;
290  GenericCloveRef(data, allocator_).set_int(value);
291  this->push_back(std::move(data));
292  }
293  void push_back(bool value) {
294  DataType data;
295  GenericCloveRef(data, allocator_).set_bool(value);
296  this->push_back(std::move(data));
297  }
298  void pop_back() {
299  (*ValueIterator({this->data_.list.data + this->data_.list.length - 1, &allocator_})).clean();
300  this->data_.list.length--;
301  }
302  void erase(const ValueIterator& first, const ValueIterator& last) {
303  std::for_each(first, last, [](auto item) { item.clean(); });
304  auto count = last.get_inner_iterator() - first.get_inner_iterator();
305  std::memmove(
306  static_cast<void*>(first.get_inner_iterator()),
307  static_cast<void*>(last.get_inner_iterator()),
308  static_cast<SizeType>(this->end_list().get_inner_iterator() - last.get_inner_iterator()) * sizeof(DataType)
309  );
310  data_.list.length -= count;
311  }
312  void erase(const ValueIterator& position) { this->erase(position, std::next(position)); }
313 
314  // member functions
315  MemberIterator find_member(text key) {
316  return std::find_if(this->begin_member(), this->end_member(), [&key](auto item) {
317  return key.compare(item.key.get_cstr()) == 0;
318  });
319  }
320 
321  template<typename Callable>
322  void add_member_builder(text key, Callable&& cb) {
323  DataType value;
324  cb(GenericCloveRef(value, allocator_));
325  this->add_member(key, std::move(value));
326  }
327 
328  void add_member(DataType&& key, DataType&& value) {
329  this->check_members();
330  this->data_.object.data[this->data_.object.length] = MemberPair<DataType>{std::move(key), std::move(value)};
331  this->data_.object.length++;
332  }
333  void add_member(text key, DataType&& value) {
334  DataType data; GenericCloveRef(data, allocator_).set_string(key);
335  this->add_member(std::move(data), std::move(value));
336  }
337  void add_member(text key) {
338  this->add_member(key, DataType{});
339  }
340  void add_member(text key, const char* value) {
341  this->add_member(key, text(value));
342  }
343  void add_member(text key, text value) {
344  DataType data; GenericCloveRef(data, allocator_).set_string(value);
345  this->add_member(key, std::move(data));
346  }
347  void add_member(text key, bool value) {
348  DataType data; GenericCloveRef(data, allocator_).set_bool(value);
349  this->add_member(key, std::move(data));
350  }
351  void add_member(text key, double value) {
352  DataType data; GenericCloveRef(data, allocator_).set_double(value);
353  this->add_member(key, std::move(data));
354  }
355  void add_member(text key, int value) {
356  DataType data; GenericCloveRef(data, allocator_).set_int(value);
357  this->add_member(key, std::move(data));
358  }
359  void remove_member(text key) {
360  auto it = this->find_member(key);
361  if (it != this->end_member()) this->erase_member(it);
362  }
363  void erase_member(const MemberIterator& position) {
364  (*position).key.clean();
365  (*position).value.clean();
366  memmove(
367  static_cast<void*>(position.get_inner_iterator()),
368  static_cast<void*>(position.get_inner_iterator() + 1),
369  static_cast<SizeType>(this->end_member().get_inner_iterator() - position.get_inner_iterator() - 1) * sizeof(MemberPair<DataType>)
370  );
371  }
372 
373  GenericCloveRef get_reference() { return GenericCloveRef(data_, allocator_); }
374  DataType& get_inner_value() { return data_; }
375 
376  private:
377  DataType& data_;
378  AllocatorType& allocator_;
379 
380  void check_list() {
381  // make sure we have enough space for another item.
382  if (this->data_.list.length >= this->data_.list.capacity) {
383  auto new_capacity = this->data_.list.capacity + (this->data_.list.capacity + 1) / 2;
384  this->data_.list.data = reinterpret_cast<typename DataType::List::Container>(
385  allocator_.reallocate(
386  this->data_.list.data,
387  this->data_.list.capacity * sizeof(DataType),
388  new_capacity * sizeof(DataType))
389  );
390  this->data_.list.capacity = new_capacity;
391  }
392  }
393 
394  void check_members() {
395  // make sure we have enough space for another member.
396  if (this->data_.object.length >= this->data_.object.capacity) {
397  auto new_capacity = this->data_.object.capacity + (this->data_.object.capacity + 1) / 2;
398  this->data_.object.data = reinterpret_cast<typename DataType::Object::Container>(
399  allocator_.reallocate(
400  this->data_.object.data,
401  this->data_.object.capacity * sizeof(DataType),
402  new_capacity * sizeof(DataType))
403  );
404  this->data_.object.capacity = new_capacity;
405  }
406  }
407 
408  inline void prepare_string(SizeType length) {
409  this->clean();
410  this->data_.type = TypeFlag::String;
411  this->data_.string.length = length;
412  this->data_.string.data = reinterpret_cast<char*>(
413  allocator_.allocate(sizeof(char) * (length + 1)));
414  this->data_.string.data[length] = '\0'; // make it null terminated.
415  }
416 
417  void clean() {
418  if (!AllocatorType::needs_free) return;
419  switch (data_.type) {
420  case TypeFlag::String:
421  {
422  allocator_.free(const_cast<char*>(data_.string.data));
423  }
424  break;
425  case TypeFlag::Object:
426  {
427  std::for_each(this->begin_member(), this->end_member(), [](auto item) {
428  item.key.clean();
429  item.value.clean();
430  });
431  allocator_.free(data_.object.data);
432  }
433  break;
434  case TypeFlag::List:
435  {
436  std::for_each(this->begin_list(), this->end_list(), [](auto item) { item.clean(); });
437  allocator_.free(data_.list.data);
438  }
439  break;
440  default:
441  break;
442  }
443  }
444  };
445 
446 
447  template<GARLIC_ALLOCATOR Allocator, typename SizeType = unsigned>
448  class GenericCloveDocument : public GenericCloveRef<Allocator, SizeType> {
449  public:
450  using DataType = GenericData<Allocator, SizeType>;
451  using ViewType = GenericCloveView<Allocator, SizeType>;
452  using ReferenceType = GenericCloveRef<Allocator, SizeType>;
453  using DocumentType = GenericCloveDocument<Allocator, SizeType>;
454 
455  explicit GenericCloveDocument(
456  std::shared_ptr<Allocator> allocator
457  ) : allocator_(allocator), ReferenceType(data_, *allocator) {}
458  GenericCloveDocument(
459  ) : allocator_(std::make_shared<Allocator>()), ReferenceType(data_, *allocator_) {}
460  ~GenericCloveDocument() { this->get_reference().set_null(); }
461 
462  Allocator& get_allocator() { return *allocator_; }
463 
464  private:
465  DataType data_;
466  std::shared_ptr<Allocator> allocator_;
467  };
468 
469 
470  template<GARLIC_ALLOCATOR Allocator, typename SizeType = unsigned>
471  class GenericCloveValue : public GenericCloveRef<Allocator, SizeType> {
472  public:
473  using DataType = GenericData<Allocator>;
474  using ViewType = GenericCloveView<Allocator>;
475  using ReferenceType = GenericCloveRef<Allocator>;
476  using DocumentType = GenericCloveDocument<Allocator>;
477 
478  explicit GenericCloveValue(
479  DocumentType& root) : ReferenceType(data_, root.get_allocator()) {}
480 
481  explicit GenericCloveValue(
482  Allocator& allocator) : ReferenceType(data_, allocator) {}
483  ~GenericCloveValue() { this->get_reference().set_null(); }
484 
485  DataType&& move_data() { return std::move(data_); }
486 
487  private:
488  DataType data_;
489  };
490 
491 
493  using CloveView = GenericCloveView<CAllocator>;
494 
496  using CloveRef = GenericCloveRef<CAllocator>;
497 
499  using CloveValue = GenericCloveValue<CAllocator>;
500 
502  using CloveDocument = GenericCloveDocument<CAllocator>;
503 
504 }
505 
506 #endif /* end of include guard: GARLIC_CLOVE_H */
layer.h
This file contains concepts for C++ 20 along with some helper types to make it easier to produce iter...
garlic::CloveDocument
GenericCloveDocument< CAllocator > CloveDocument
A clove document (value) conforming to garlic::RefLayer.
Definition: clove.h:502
garlic::CloveValue
GenericCloveValue< CAllocator > CloveValue
A clove value conforming to garlic::RefLayer.
Definition: clove.h:499
garlic::CloveRef
GenericCloveRef< CAllocator > CloveRef
A reference to the a clove value/document conforming to garlic::RefLayer.
Definition: clove.h:496
garlic::CloveView
GenericCloveView< CAllocator > CloveView
A view to the a clove value/document conforming to garlic::ViewLayer.
Definition: clove.h:493