Metall  v0.28
A persistent memory allocator for data-centric analytics
jgraph.hpp
Go to the documentation of this file.
1 // Copyright 2021 Lawrence Livermore National Security, LLC and other Metall
2 // Project Developers. See the top-level COPYRIGHT file for details.
3 //
4 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
5 
6 #ifndef METALL_CONTAINER_EXPERIMENT_JGRAPH_JGRAPH_HPP
7 #define METALL_CONTAINER_EXPERIMENT_JGRAPH_JGRAPH_HPP
8 
9 #include <iostream>
10 #include <string_view>
11 #include <functional>
12 #include <utility>
13 #include <optional>
14 
18 #include <metall/utility/hash.hpp>
19 #include <metall/json/json.hpp>
20 
24 
29 
30 namespace {
31 namespace mc = metall::container;
32 namespace mj = metall::json;
33 } // namespace
34 
35 // --- Forward declarations --- //
36 template <typename allocator_type>
37 class jgraph;
38 
39 namespace jgdtl {
40 template <typename storage_iterator_type>
42 
43 template <typename adj_list_edge_list_iterator_type,
44  typename storage_pointer_type>
45 class edge_iterator_impl;
46 } // namespace jgdtl
47 
48 template <typename _allocator_type = std::allocator<std::byte>>
49 class jgraph {
50  public:
51  using allocator_type = _allocator_type;
52 
54  using id_type = std::string_view;
55 
58 
59  private:
60  template <typename T>
61  using other_allocator =
62  typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
63 
64  template <typename T>
65  using other_scoped_allocator =
67 
68  using string_type =
70  typename std::allocator_traits<
71  allocator_type>::template rebind_alloc<char>>;
72 
73  using internal_id_type = uint64_t;
74  static constexpr internal_id_type k_max_internal_id =
75  std::numeric_limits<internal_id_type>::max();
76 
77  class vertex_data_type {
78  public:
79  using allocator_type = _allocator_type;
80 
81  explicit vertex_data_type(
82  const id_type &id, const allocator_type &allocator = allocator_type())
83  : m_id(id, allocator), m_value(allocator) {}
84 
86  vertex_data_type(const vertex_data_type &) = default;
87 
89  vertex_data_type(const vertex_data_type &other, const allocator_type &alloc)
90  : m_id(other.m_id, alloc), m_value(other.m_value, alloc) {}
91 
93  vertex_data_type(vertex_data_type &&) noexcept = default;
94 
96 
97  vertex_data_type(vertex_data_type &&other,
98  const allocator_type &alloc) noexcept
99  : m_id(std::move(other.m_id), alloc),
100  m_value(std::move(other.m_value), alloc) {}
101 
103  vertex_data_type &operator=(const vertex_data_type &) = default;
104 
106  vertex_data_type &operator=(vertex_data_type &&) noexcept = default;
107 
108  const id_type id() const { return id_type(m_id); }
109 
110  value_type &value() { return m_value; }
111 
112  const value_type &value() const { return m_value; }
113 
114  private:
115  string_type m_id;
116  value_type m_value;
117  };
118 
119  using vertex_storage_type =
120  mc::unordered_map<internal_id_type, vertex_data_type,
121  metall::utility::hash<>, std::equal_to<>,
122  other_scoped_allocator<std::pair<const internal_id_type,
123  vertex_data_type>>>;
124 
125  class edge_data_type {
126  public:
127  using allocator_type = _allocator_type;
128 
129  explicit edge_data_type(const id_type &source_id,
130  const id_type &destination_id,
131  const internal_id_type &edge_id,
132  const allocator_type &allocator = allocator_type())
133  : m_source_id(source_id, allocator),
134  m_destination_id(destination_id, allocator),
135  m_edge_id(edge_id),
136  m_value(allocator) {}
137 
139  edge_data_type(const edge_data_type &) = default;
140 
142 
143  edge_data_type(const edge_data_type &other, const allocator_type &alloc)
144  : m_source_id(other.m_source_id, alloc),
145  m_destination_id(other.m_destination_id, alloc),
146  m_edge_id(other.m_edge_id),
147  m_value(other.m_value, alloc) {}
148 
150  edge_data_type(edge_data_type &&) noexcept = default;
151 
153 
154  edge_data_type(edge_data_type &&other, const allocator_type &alloc) noexcept
155  : m_source_id(other.m_source_id, alloc),
156  m_destination_id(other.m_destination_id, alloc),
157  m_edge_id(other.m_edge_id),
158  m_value(other.m_value, alloc) {}
159 
161  edge_data_type &operator=(const edge_data_type &) = default;
162 
164  edge_data_type &operator=(edge_data_type &&) noexcept = default;
165 
166  const id_type source_id() const { return id_type(m_source_id); }
167 
168  const id_type destination_id() const { return id_type(m_destination_id); }
169 
170  // const id_type edge_id() const {
171  // return id_type(m_edge_id);
172  // }
173 
174  value_type &value() { return m_value; }
175 
176  const value_type &value() const { return m_value; }
177 
178  private:
179  string_type m_source_id;
180  string_type m_destination_id;
181  internal_id_type m_edge_id;
182  value_type m_value;
183  };
184 
185  using edge_storage_type =
186  mc::unordered_map<internal_id_type, edge_data_type,
187  metall::utility::hash<>, std::equal_to<>,
188  other_scoped_allocator<
189  std::pair<const internal_id_type, edge_data_type>>>;
190 
191  // Adj-list
192  using adj_list_edge_list_type =
193  mc::unordered_multimap<internal_id_type, // hashed destination vid
194  internal_id_type, // edge id
195  std::hash<internal_id_type>, std::equal_to<>,
196  other_scoped_allocator<std::pair<
197  const internal_id_type, internal_id_type>>>;
198 
199  using adj_list_type = mc::unordered_map<
200  internal_id_type, // hashed source vid
201  adj_list_edge_list_type, std::hash<internal_id_type>, std::equal_to<>,
202  other_scoped_allocator<
203  std::pair<const internal_id_type, adj_list_edge_list_type>>>;
204 
205  // ID tables
206  using id_table_type = mc::unordered_map<
207  internal_id_type, string_type, std::hash<internal_id_type>,
208  std::equal_to<>,
209  other_scoped_allocator<std::pair<const internal_id_type, string_type>>>;
210 
211  public:
216 
220 
224  typename adj_list_edge_list_type::iterator,
225  typename std::pointer_traits<typename std::allocator_traits<
226  allocator_type>::pointer>::template rebind<edge_storage_type>>;
229  typename adj_list_edge_list_type::const_iterator,
230  typename std::pointer_traits<typename std::allocator_traits<
231  allocator_type>::pointer>::template rebind<const edge_storage_type>>;
232 
235  explicit jgraph(const allocator_type &alloc = allocator_type())
236  : m_vertex_storage(alloc),
237  m_edge_storage(alloc),
238  m_adj_list(alloc),
239  m_vertex_id_table(alloc) {}
240 
244  bool has_vertex(const id_type &vertex_id) const {
245  return priv_get_vertex_internal_id(vertex_id) != k_max_internal_id;
246  }
247 
248  std::size_t has_edges(const id_type &source_vertex_id,
249  const id_type &destination_vertex_id) const {
250  const auto src = priv_get_vertex_internal_id(source_vertex_id);
251  if (src == k_max_internal_id) return 0;
252 
253  const auto dst = priv_get_vertex_internal_id(destination_vertex_id);
254  if (dst == k_max_internal_id) return 0;
255 
256  const auto &edge_list = m_adj_list.at(src);
257  return edge_list.count(dst);
258  }
259 
261  auto internal_id = priv_get_vertex_internal_id(vertex_id);
262  if (internal_id != k_max_internal_id) {
263  return vertex_iterator(m_vertex_storage.find(internal_id));
264  }
265 
266  internal_id = priv_generate_vertex_internal_id(vertex_id);
267 
268  m_adj_list.emplace(internal_id,
269  adj_list_edge_list_type{m_adj_list.get_allocator()});
270 
271  auto ret = m_vertex_storage.emplace(
272  internal_id,
273  vertex_data_type{vertex_id, m_vertex_storage.get_allocator()});
274  return vertex_iterator(ret.first);
275  }
276 
277  edge_iterator register_edge(const id_type &source_vertex_id,
278  const id_type &destination_vertex_id,
279  const bool undirected = false) {
280  register_vertex(source_vertex_id);
281  register_vertex(destination_vertex_id);
282 
283  const auto src_internal_id = priv_get_vertex_internal_id(source_vertex_id);
284  assert(src_internal_id != k_max_internal_id);
285  const auto dst_internal_id =
286  priv_get_vertex_internal_id(destination_vertex_id);
287  assert(dst_internal_id != k_max_internal_id);
288  const auto edge_id = priv_generate_edge_id();
289 
290  auto itr = m_adj_list.at(src_internal_id).emplace(dst_internal_id, edge_id);
291  if (undirected) {
292  m_adj_list.at(dst_internal_id).emplace(src_internal_id, edge_id);
293  }
294 
295  m_edge_storage.emplace(
296  edge_id, edge_data_type{source_vertex_id, destination_vertex_id,
297  edge_id, m_edge_storage.get_allocator()});
298  return edge_iterator(itr, &m_edge_storage);
299  }
300 
301  vertex_iterator find_vertex(const id_type &vertex_id) {
302  return vertex_iterator(
303  m_vertex_storage.find(priv_get_vertex_internal_id(vertex_id)));
304  }
305 
306  const_vertex_iterator find_vertex(const id_type &vertex_id) const {
307  return const_vertex_iterator(
308  m_vertex_storage.find(priv_get_vertex_internal_id(vertex_id)));
309  }
310 
311  std::pair<edge_iterator, edge_iterator> find_edges(
312  const id_type &source_vertex_id, const id_type &destination_vertex_id) {
313  const auto src_internal_id = priv_get_vertex_internal_id(source_vertex_id);
314  const auto dst_internal_id =
315  priv_get_vertex_internal_id(destination_vertex_id);
316 
317  if (src_internal_id == k_max_internal_id ||
318  dst_internal_id == k_max_internal_id) {
319  return std::make_pair(edge_iterator{}, edge_iterator{});
320  }
321 
322  auto &edge_list = m_adj_list.at(src_internal_id);
323  auto range = edge_list.equal_range(dst_internal_id);
324  return std::make_pair(edge_iterator{range.first, &m_edge_storage},
325  edge_iterator{range.second, &m_edge_storage});
326  }
327 
330  std::size_t num_vertices() const { return m_vertex_storage.size(); }
331 
334  std::size_t num_edges() const { return m_edge_storage.size(); }
335 
340  std::size_t degree(const id_type &vertex_id) const {
341  const auto internal_id = priv_get_vertex_internal_id(vertex_id);
342  if (internal_id == k_max_internal_id) {
343  return 0;
344  }
345 
346  return m_adj_list.at(internal_id).size();
347  }
348 
350  return vertex_iterator(m_vertex_storage.begin());
351  }
352 
354  return const_vertex_iterator(m_vertex_storage.begin());
355  }
356 
358  return vertex_iterator(m_vertex_storage.end());
359  }
360 
362  return const_vertex_iterator(m_vertex_storage.end());
363  }
364 
366  const auto internal_id = priv_get_vertex_internal_id(vid);
367  if (internal_id == k_max_internal_id) {
368  return edge_iterator();
369  }
370  return edge_iterator{m_adj_list.at(internal_id).begin(), &m_edge_storage};
371  }
372 
374  const auto internal_id = priv_get_vertex_internal_id(vid);
375  if (internal_id == k_max_internal_id) {
376  return const_edge_iterator();
377  }
378  return const_edge_iterator{m_adj_list.at(internal_id).begin(),
379  &m_edge_storage};
380  }
381 
383  const auto internal_id = priv_get_vertex_internal_id(vid);
384  if (internal_id == k_max_internal_id) {
385  return edge_iterator();
386  }
387  return edge_iterator{m_adj_list.at(internal_id).end(), &m_edge_storage};
388  }
389 
391  const auto internal_id = priv_get_vertex_internal_id(vid);
392  if (internal_id == k_max_internal_id) {
393  return const_edge_iterator();
394  }
395  return const_edge_iterator{m_adj_list.at(internal_id).end(),
396  &m_edge_storage};
397  }
398 
400  return m_vertex_storage.get_allocator();
401  }
402 
403  private:
404  internal_id_type priv_get_vertex_internal_id(
405  const std::string_view &vid) const {
406  auto hash = priv_hash_id(vid.data());
407 
408  for (std::size_t d = 0; d <= m_max_vid_distance; ++d) {
409  const auto vitr = m_vertex_id_table.find(hash);
410  if (vitr == m_vertex_id_table.end()) {
411  break;
412  }
413 
414  if (vitr->second == vid) {
415  return hash;
416  }
417  hash = (hash + 1) % k_max_internal_id;
418  }
419 
420  return k_max_internal_id; // Couldn't find
421  }
422 
423  internal_id_type priv_generate_vertex_internal_id(
424  const std::string_view &vid) {
425  auto hash = priv_hash_id(vid.data());
426 
427  std::size_t distance = 0;
428  while (m_vertex_id_table.count(hash) > 0) {
429  hash = (hash + 1) % k_max_internal_id;
430  ++distance;
431  }
432  m_max_vid_distance = std::max(distance, m_max_vid_distance);
433 
434  m_vertex_id_table[hash] = vid.data();
435 
436  return hash;
437  }
438 
439  internal_id_type priv_generate_edge_id() { return ++m_max_edge_id; }
440 
441  static internal_id_type priv_hash_id(const std::string_view &id) {
442  return metall::mtlldetail::murmur_hash_64a(id.data(), id.length(), 1234);
443  }
444 
445  vertex_storage_type m_vertex_storage;
446  edge_storage_type m_edge_storage;
447  adj_list_type m_adj_list;
448  id_table_type m_vertex_id_table;
449  internal_id_type m_max_edge_id{0};
450  std::size_t m_max_vid_distance{0};
451 };
452 
453 namespace jgdtl {
454 
455 template <typename storage_iterator_type>
457  private:
458  static constexpr bool is_const_value = std::is_const_v<
459  typename std::iterator_traits<storage_iterator_type>::value_type>;
460 
461  // Memo: this type will be always non-const even element_type is const
462  using raw_value_type = typename std::tuple_element<
463  1,
464  typename std::iterator_traits<storage_iterator_type>::value_type>::type;
465 
466  public:
467  using value_type =
468  std::conditional_t<is_const_value, const raw_value_type, raw_value_type>;
469  using pointer = typename std::pointer_traits<typename std::iterator_traits<
470  storage_iterator_type>::pointer>::template rebind<value_type>;
473  typename std::iterator_traits<storage_iterator_type>::difference_type;
474 
475  explicit vertex_iterator_impl(storage_iterator_type begin_pos)
476  : m_current_pos(begin_pos) {}
477 
480 
481  vertex_iterator_impl &operator=(const vertex_iterator_impl &) = default;
482  vertex_iterator_impl &operator=(vertex_iterator_impl &&) noexcept = default;
483 
484  vertex_iterator_impl &operator++() {
485  ++m_current_pos;
486  return *this;
487  }
488 
490  vertex_iterator_impl tmp(*this);
491  operator++();
492  return tmp;
493  }
494 
495  bool equal(const vertex_iterator_impl &other) const {
496  return m_current_pos == other.m_current_pos;
497  }
498 
499  pointer operator->() { return const_cast<pointer>(&(m_current_pos->second)); }
500 
501  const pointer operator->() const { return &(m_current_pos->second); }
502 
503  reference operator*() { return m_current_pos->second; }
504 
505  const reference operator*() const { return m_current_pos->second; }
506 
507  private:
508  storage_iterator_type m_current_pos;
509 };
510 
511 template <typename storage_iterator_type>
514  return lhs.equal(rhs);
515 }
516 
517 template <typename storage_iterator_type>
520  return !(lhs == rhs);
521 }
522 
523 template <typename adj_list_edge_list_iterator_type,
524  typename storage_pointer_type>
526  private:
527  static constexpr bool is_const_value = std::is_const_v<
528  typename std::pointer_traits<storage_pointer_type>::element_type>;
529 
530  // Memo: this type will be always non-const even element_type is const
531  using raw_value_type = typename std::pointer_traits<
532  storage_pointer_type>::element_type::mapped_type;
533 
534  public:
535  using value_type =
536  std::conditional_t<is_const_value, const raw_value_type, raw_value_type>;
537  using pointer = typename std::pointer_traits<
538  typename std::pointer_traits<storage_pointer_type>::element_type::
539  iterator::pointer>::template rebind<value_type>;
541  using difference_type = typename std::iterator_traits<
542  adj_list_edge_list_iterator_type>::difference_type;
543 
544  edge_iterator_impl() : m_current_pos(), m_storage_pointer(nullptr) {}
545 
546  edge_iterator_impl(adj_list_edge_list_iterator_type begin_pos,
547  storage_pointer_type storage)
548  : m_current_pos(begin_pos), m_storage_pointer(storage) {}
549 
551  edge_iterator_impl(edge_iterator_impl &&) noexcept = default;
552 
553  edge_iterator_impl &operator=(const edge_iterator_impl &) = default;
554  edge_iterator_impl &operator=(edge_iterator_impl &&) noexcept = default;
555 
556  edge_iterator_impl &operator++() {
557  ++m_current_pos;
558  return *this;
559  }
560 
562  edge_iterator_impl tmp(*this);
563  operator++();
564  return tmp;
565  }
566 
567  bool equal(const edge_iterator_impl &other) const {
568  return m_current_pos == other.m_current_pos;
569  }
570 
572  const auto &edge_id = m_current_pos->second;
573  return &(m_storage_pointer->at(edge_id));
574  }
575 
576  const pointer operator->() const {
577  const auto &edge_id = m_current_pos->second;
578  return &(m_storage_pointer->at(edge_id));
579  }
580 
582  const auto &edge_id = m_current_pos->second;
583  return (m_storage_pointer->at(edge_id));
584  }
585 
586  const reference operator*() const {
587  const auto &edge_id = m_current_pos->second;
588  return (m_storage_pointer->at(edge_id));
589  }
590 
591  private:
592  adj_list_edge_list_iterator_type m_current_pos;
593  storage_pointer_type m_storage_pointer;
594 };
595 
596 template <typename adj_list_edge_list_iterator_type,
597  typename storage_pointer_type>
598 inline bool operator==(
599  const edge_iterator_impl<adj_list_edge_list_iterator_type,
600  storage_pointer_type> &lhs,
601  const edge_iterator_impl<adj_list_edge_list_iterator_type,
602  storage_pointer_type> &rhs) {
603  return lhs.equal(rhs);
604 }
605 
606 template <typename adj_list_edge_list_iterator_type,
607  typename storage_pointer_type>
608 inline bool operator!=(
609  const edge_iterator_impl<adj_list_edge_list_iterator_type,
610  storage_pointer_type> &lhs,
611  const edge_iterator_impl<adj_list_edge_list_iterator_type,
612  storage_pointer_type> &rhs) {
613  return !(lhs == rhs);
614 }
615 
616 } // namespace jgdtl
617 
618 } // namespace metall::container::experimental::jgraph
619 
622 
623 #endif // METALL_CONTAINER_EXPERIMENT_JGRAPH_JGRAPH_HPP
edge_iterator_impl(adj_list_edge_list_iterator_type begin_pos, storage_pointer_type storage)
Definition: jgraph.hpp:546
const pointer operator->() const
Definition: jgraph.hpp:576
bool equal(const edge_iterator_impl &other) const
Definition: jgraph.hpp:567
edge_iterator_impl operator++(int)
Definition: jgraph.hpp:561
edge_iterator_impl & operator++()
Definition: jgraph.hpp:556
typename std::pointer_traits< typename std::pointer_traits< storage_pointer_type >::element_type::iterator::pointer >::template rebind< value_type > pointer
Definition: jgraph.hpp:539
edge_iterator_impl(edge_iterator_impl &&) noexcept=default
typename std::iterator_traits< adj_list_edge_list_iterator_type >::difference_type difference_type
Definition: jgraph.hpp:542
std::conditional_t< is_const_value, const raw_value_type, raw_value_type > value_type
Definition: jgraph.hpp:536
const reference operator*() const
Definition: jgraph.hpp:586
vertex_iterator_impl(vertex_iterator_impl &&) noexcept=default
typename std::iterator_traits< storage_iterator_type >::difference_type difference_type
Definition: jgraph.hpp:473
bool equal(const vertex_iterator_impl &other) const
Definition: jgraph.hpp:495
vertex_iterator_impl operator++(int)
Definition: jgraph.hpp:489
const reference operator*() const
Definition: jgraph.hpp:505
std::conditional_t< is_const_value, const raw_value_type, raw_value_type > value_type
Definition: jgraph.hpp:468
vertex_iterator_impl(storage_iterator_type begin_pos)
Definition: jgraph.hpp:475
vertex_iterator_impl & operator++()
Definition: jgraph.hpp:484
typename std::pointer_traits< typename std::iterator_traits< storage_iterator_type >::pointer >::template rebind< value_type > pointer
Definition: jgraph.hpp:470
const pointer operator->() const
Definition: jgraph.hpp:501
const_edge_iterator edges_begin(const id_type &vid) const
Definition: jgraph.hpp:373
const_vertex_iterator vertices_begin() const
Definition: jgraph.hpp:353
jgdtl::vertex_iterator_impl< typename vertex_storage_type::const_iterator > const_vertex_iterator
Const vertex iterator.
Definition: jgraph.hpp:219
std::string_view id_type
The type of vertex ID and edge ID.
Definition: jgraph.hpp:54
std::size_t num_vertices() const
Returns the number of vertices.
Definition: jgraph.hpp:330
jgdtl::edge_iterator_impl< typename adj_list_edge_list_type::iterator, typename std::pointer_traits< typename std::allocator_traits< allocator_type >::pointer >::template rebind< edge_storage_type > > edge_iterator
Edge iterator over a container of edge data, which is metall::container::experimental::json::key_valu...
Definition: jgraph.hpp:226
jgdtl::vertex_iterator_impl< typename vertex_storage_type::iterator > vertex_iterator
Vertex iterator over a container of vertex data, which is metall::container::experimental::json::key_...
Definition: jgraph.hpp:215
_allocator_type allocator_type
Definition: jgraph.hpp:51
const_vertex_iterator find_vertex(const id_type &vertex_id) const
Definition: jgraph.hpp:306
edge_iterator edges_begin(const id_type &vid)
Definition: jgraph.hpp:365
std::pair< edge_iterator, edge_iterator > find_edges(const id_type &source_vertex_id, const id_type &destination_vertex_id)
Definition: jgraph.hpp:311
vertex_iterator register_vertex(const id_type &vertex_id)
Definition: jgraph.hpp:260
allocator_type get_allocator() const
Definition: jgraph.hpp:399
edge_iterator register_edge(const id_type &source_vertex_id, const id_type &destination_vertex_id, const bool undirected=false)
Definition: jgraph.hpp:277
jgraph(const allocator_type &alloc=allocator_type())
Constructor.
Definition: jgraph.hpp:235
jgdtl::edge_iterator_impl< typename adj_list_edge_list_type::const_iterator, typename std::pointer_traits< typename std::allocator_traits< allocator_type >::pointer >::template rebind< const edge_storage_type > > const_edge_iterator
Const edge iterator.
Definition: jgraph.hpp:231
bool has_vertex(const id_type &vertex_id) const
Checks if a vertex exists.
Definition: jgraph.hpp:244
const_vertex_iterator vertices_end() const
Definition: jgraph.hpp:361
vertex_iterator find_vertex(const id_type &vertex_id)
Definition: jgraph.hpp:301
const_edge_iterator edges_end(const id_type &vid) const
Definition: jgraph.hpp:390
vertex_iterator vertices_end()
Definition: jgraph.hpp:357
mj::value< allocator_type > value_type
JSON value type every vertex and edge has,.
Definition: jgraph.hpp:57
std::size_t num_edges() const
Returns the number of edges.
Definition: jgraph.hpp:334
edge_iterator edges_end(const id_type &vid)
Definition: jgraph.hpp:382
vertex_iterator vertices_begin()
Definition: jgraph.hpp:349
std::size_t has_edges(const id_type &source_vertex_id, const id_type &destination_vertex_id) const
Definition: jgraph.hpp:248
std::size_t degree(const id_type &vertex_id) const
Returns the degree of the vertex corresponds to 'vid'.
Definition: jgraph.hpp:340
bool operator!=(const vertex_iterator_impl< storage_iterator_type > &lhs, const vertex_iterator_impl< storage_iterator_type > &rhs)
Definition: jgraph.hpp:518
bool operator==(const vertex_iterator_impl< storage_iterator_type > &lhs, const vertex_iterator_impl< storage_iterator_type > &rhs)
Definition: jgraph.hpp:512
Namespace for Metall JSON graph container, which is in an experimental phase.
Namespace for Metall containers in an experimental phase.
Namespace for Metall container.
boost::container::basic_string< CharT, Traits, Allocator > basic_string
A string container that uses Metall as its default allocator.
Definition: string.hpp:19
boost::unordered_map< Key, T, Hash, KeyEqual, Allocator > unordered_map
An unordered_map container that uses Metall as its default allocator.
Definition: unordered_map.hpp:21
boost::unordered_multimap< Key, T, Hash, KeyEqual, Allocator > unordered_multimap
An unordered_multimap container that uses Metall as its default allocator.
Definition: unordered_map.hpp:29
boost::container::scoped_allocator_adaptor< OuterAlloc, InnerAlloc... > scoped_allocator_adaptor
An allocator which can be used with multilevel containers.
Definition: scoped_allocator.hpp:16
Namespace for Metall JSON container, which is in an experimental phase.
Definition: array.hpp:17
metall::mtlldetail::hash< seed > hash
Hash a value of type T.
Definition: hash.hpp:18