Metall  v0.28
A persistent memory allocator for data-centric analytics
value.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_JSON_VALUE_HPP
7 #define METALL_JSON_VALUE_HPP
8 
9 #include <memory>
10 #include <utility>
11 #include <string_view>
12 #include <variant>
13 #include <type_traits>
14 
15 #include <metall/json/json_fwd.hpp>
16 
17 namespace metall::json {
18 
19 namespace {
20 namespace mc = metall::container;
21 }
22 
23 namespace jsndtl {
24 
27 template <typename allocator_type, typename other_value_type>
29  const other_value_type &other_value) noexcept {
30  if (other_value.is_null()) {
31  return value.is_null();
32  } else if (other_value.is_bool()) {
33  return value.is_bool() && value.as_bool() == other_value.as_bool();
34  } else if (other_value.is_int64()) {
35  if (value.is_int64()) {
36  return value.as_int64() == other_value.as_int64();
37  }
38  if (value.is_uint64()) {
39  return (other_value.as_int64() < 0)
40  ? false
41  : value.as_uint64() ==
42  static_cast<std::uint64_t>(other_value.as_int64());
43  }
44  return false;
45  } else if (other_value.is_uint64()) {
46  if (value.is_uint64()) {
47  return value.as_uint64() == other_value.as_uint64();
48  }
49  if (value.is_int64()) {
50  return (value.as_int64() < 0)
51  ? false
52  : static_cast<std::uint64_t>(value.as_int64()) ==
53  other_value.as_uint64();
54  }
55  return false;
56  } else if (other_value.is_double()) {
57  return value.is_double() && value.as_double() == other_value.as_double();
58  } else if (other_value.is_object()) {
59  return value.is_object() && (value.as_object() == other_value.as_object());
60  } else if (other_value.is_array()) {
61  return value.is_array() && (value.as_array() == other_value.as_array());
62  } else if (other_value.is_string()) {
63  if (!value.is_string()) return false;
64  const auto &str = value.as_string();
65  const auto &other_srt = other_value.as_string();
66  return str.compare(other_srt.c_str()) == 0;
67  }
68 
69  assert(false);
70  return false;
71 }
72 } // namespace jsndtl
73 
77 #ifdef DOXYGEN_SKIP
78 template <typename Alloc = std::allocator<std::byte>>
79 #else
80 template <typename Alloc>
81 #endif
82 class value {
83  public:
84  using allocator_type = Alloc;
85  using string_type =
87  typename std::allocator_traits<
88  allocator_type>::template rebind_alloc<char>>;
91 
92  private:
93  using internal_data_type =
94  std::variant<null_type, bool, std::int64_t, std::uint64_t, double,
96 
97  public:
99  value() { priv_reset(); }
100 
103  explicit value(const allocator_type &alloc) : m_allocator(alloc) {
104  priv_reset();
105  }
106 
108  value(const value &other)
109  : m_allocator(
110  std::allocator_traits<allocator_type>::
111  select_on_container_copy_construction(other.get_allocator())),
112  m_data(other.m_data) {}
113 
115  value(const value &other, const allocator_type &alloc) : m_allocator(alloc) {
116  if (other.is_object()) {
117  m_data.template emplace<object_type>(other.as_object(), m_allocator);
118  } else if (other.is_array()) {
119  m_data.template emplace<array_type>(other.as_array(), m_allocator);
120  } else if (other.is_string()) {
121  m_data.template emplace<string_type>(other.as_string(), m_allocator);
122  } else {
123  m_data = other.m_data;
124  }
125  }
126 
128  value(value &&other) noexcept
129  : m_allocator(std::move(other.m_allocator)),
130  m_data(std::move(other.m_data)) {
131  other.priv_reset();
132  }
133 
135  value(value &&other, const allocator_type &alloc) noexcept
136  : m_allocator(alloc) {
137  if (other.is_object()) {
138  m_data.template emplace<object_type>(std::move(other.as_object()),
139  m_allocator);
140  } else if (other.is_array()) {
141  m_data.template emplace<array_type>(std::move(other.as_array()),
142  m_allocator);
143  } else if (other.is_string()) {
144  m_data.template emplace<string_type>(std::move(other.as_string()),
145  m_allocator);
146  } else {
147  m_data = std::move(other.m_data);
148  }
149  other.priv_reset();
150  }
151 
153  ~value() noexcept { priv_reset(); }
154 
156  value &operator=(const value &other) {
157  if (this == &other) return *this;
158 
159  if constexpr (std::is_same_v<
160  typename std::allocator_traits<allocator_type>::
161  propagate_on_container_copy_assignment,
162  std::true_type>) {
163  m_allocator = other.m_allocator;
164  }
165 
166  // Cannot do `m_data = other.m_data`
167  // because std::variant calls the allocator-extended copy constructor of the
168  // holding data, which ignores propagate_on_container_copy_assignment value.
169  if (other.is_object()) {
170  emplace_object() = other.as_object();
171  } else if (other.is_array()) {
172  emplace_array() = other.as_array();
173  } else if (other.is_string()) {
174  emplace_string() = other.as_string();
175  } else {
176  m_data = other.m_data;
177  }
178 
179  return *this;
180  }
181 
183  value &operator=(value &&other) noexcept {
184  if (this == &other) return *this;
185 
186  if constexpr (std::is_same_v<
187  typename std::allocator_traits<allocator_type>::
188  propagate_on_container_move_assignment,
189  std::true_type>) {
190  m_allocator = std::move(other.m_allocator);
191  }
192 
193  if (other.is_object()) {
194  emplace_object() = std::move(other.as_object());
195  } else if (other.is_array()) {
196  emplace_array() = std::move(other.as_array());
197  } else if (other.is_string()) {
198  emplace_string() = std::move(other.as_string());
199  } else {
200  m_data = std::move(other.m_data);
201  }
202 
203  other.priv_reset();
204 
205  return *this;
206  }
207 
209  void swap(value &other) noexcept {
210  using std::swap;
211  if constexpr (std::is_same_v<
212  typename std::allocator_traits<
213  allocator_type>::propagate_on_container_swap,
214  std::true_type>) {
215  swap(m_allocator, other.m_allocator);
216  } else {
217  // This is an undefined behavior in the C++ standard.
218  assert(get_allocator() == other.m_allocator);
219  }
220 
221  swap(m_data, other.m_data);
222  }
223 
226  value &operator=(const bool b) {
227  emplace_bool() = b;
228  return *this;
229  }
230 
233  value &operator=(const signed char i) {
234  return operator=(static_cast<long long>(i));
235  }
236 
239  value &operator=(const short i) {
240  return operator=(static_cast<long long>(i));
241  }
242 
245  value &operator=(const int i) { return operator=(static_cast<long long>(i)); }
246 
249  value &operator=(const long i) {
250  return operator=(static_cast<long long>(i));
251  }
252 
255  value &operator=(const long long i) {
256  emplace_int64() = i;
257  return *this;
258  }
259 
262  value &operator=(const unsigned char u) {
263  return operator=(static_cast<unsigned long long>(u));
264  }
265 
268  value &operator=(const unsigned short u) {
269  return operator=(static_cast<unsigned long long>(u));
270  }
271 
274  value &operator=(const unsigned int u) {
275  return operator=(static_cast<unsigned long long>(u));
276  }
277 
280  value &operator=(const unsigned long u) {
281  return operator=(static_cast<unsigned long long>(u));
282  }
283 
286  value &operator=(const unsigned long long u) {
287  emplace_uint64() = u;
288  return *this;
289  }
290 
293  value &operator=(std::nullptr_t) {
294  emplace_null();
295  return *this;
296  }
297 
300  value &operator=(const double d) {
301  emplace_double() = d;
302  return *this;
303  }
304 
307  value &operator=(std::string_view s) {
308  emplace_string() = s.data();
309  return *this;
310  }
311 
314  value &operator=(const char *const s) {
315  emplace_string() = s;
316  return *this;
317  }
318 
322  emplace_string() = s;
323  return *this;
324  }
325 
326  // value &operator=(std::initializer_list<value_ref> init);
327 
331  emplace_string() = std::move(s);
332  return *this;
333  }
334 
337  value &operator=(const array_type &arr) {
338  emplace_array() = arr;
339  return *this;
340  }
341 
345  emplace_array() = std::move(arr);
346  return *this;
347  }
348 
351  value &operator=(const object_type &obj) {
352  emplace_object() = obj;
353  return *this;
354  }
355 
359  emplace_object() = std::move(obj);
360  return *this;
361  }
362 
365  void emplace_null() { priv_reset(); }
366 
369  bool &emplace_bool() {
370  priv_reset();
371  return m_data.template emplace<bool>();
372  }
373 
376  std::int64_t &emplace_int64() {
377  priv_reset();
378  return m_data.template emplace<std::int64_t>();
379  }
380 
383  std::uint64_t &emplace_uint64() {
384  priv_reset();
385  return m_data.template emplace<std::uint64_t>();
386  }
387 
390  double &emplace_double() {
391  priv_reset();
392  return m_data.template emplace<double>();
393  }
394 
398  priv_reset();
399  return m_data.template emplace<string_type>(m_allocator);
400  }
401 
405  priv_reset();
406  return m_data.template emplace<array_type>(m_allocator);
407  }
408 
412  priv_reset();
413  return m_data.template emplace<object_type>(m_allocator);
414  }
415 
417  bool &as_bool() { return std::get<bool>(m_data); }
418 
421  const bool &as_bool() const { return std::get<bool>(m_data); }
422 
425  std::int64_t &as_int64() { return std::get<std::int64_t>(m_data); }
426 
429  const std::int64_t &as_int64() const {
430  return std::get<std::int64_t>(m_data);
431  }
432 
435  std::uint64_t &as_uint64() { return std::get<std::uint64_t>(m_data); }
436 
439  const std::uint64_t &as_uint64() const {
440  return std::get<std::uint64_t>(m_data);
441  }
442 
444  double &as_double() { return std::get<double>(m_data); }
445 
448  const double &as_double() const { return std::get<double>(m_data); }
449 
451  string_type &as_string() { return std::get<string_type>(m_data); }
452 
455  const string_type &as_string() const { return std::get<string_type>(m_data); }
456 
458  array_type &as_array() { return std::get<array_type>(m_data); }
459 
462  const array_type &as_array() const { return std::get<array_type>(m_data); }
463 
465  object_type &as_object() { return std::get<object_type>(m_data); }
466 
469  const object_type &as_object() const { return std::get<object_type>(m_data); }
470 
472  bool is_null() const noexcept {
473  return std::holds_alternative<null_type>(m_data);
474  }
475 
477  bool is_bool() const noexcept { return std::holds_alternative<bool>(m_data); }
478 
480  bool is_int64() const noexcept {
481  return std::holds_alternative<int64_t>(m_data);
482  }
483 
485  bool is_uint64() const noexcept {
486  return std::holds_alternative<uint64_t>(m_data);
487  }
488 
490  bool is_double() const noexcept {
491  return std::holds_alternative<double>(m_data);
492  }
493 
495  bool is_string() const noexcept {
496  return std::holds_alternative<string_type>(m_data);
497  }
498 
500  bool is_array() const noexcept {
501  return std::holds_alternative<array_type>(m_data);
502  }
503 
505  bool is_object() const noexcept {
506  return std::holds_alternative<object_type>(m_data);
507  }
508 
510  allocator_type get_allocator() const noexcept { return m_allocator; }
511 
513  friend bool operator==(const value &lhs, const value &rhs) noexcept {
514  return jsndtl::general_value_equal(lhs, rhs);
515  ;
516  }
517 
523  friend bool operator!=(const value &lhs, const value &rhs) noexcept {
524  return !(lhs == rhs);
525  }
526 
527  private:
528  bool priv_reset() {
529  m_data.template emplace<null_type>();
530  return true;
531  }
532 
533  allocator_type m_allocator{allocator_type{}};
534  internal_data_type m_data{null_type{}};
535 };
536 
538 template <typename allocator_type>
539 inline void swap(value<allocator_type> &lhd,
540  value<allocator_type> &rhd) noexcept {
541  lhd.swap(rhd);
542 }
543 
544 } // namespace metall::json
545 
546 #endif // METALL_JSON_VALUE_HPP
JSON array. An array is an ordered collection of values.
Definition: array.hpp:43
JSON object. An object is a table key and value pairs. The order of key-value pairs depends on the im...
Definition: object.hpp:22
value & operator=(const unsigned int u)
Assign an unsigned int value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:274
value & operator=(const double d)
Assign a double value. Allocates a memory storage or destroy the old content, if necessary.
Definition: value.hpp:300
value & operator=(std::nullptr_t)
Assign a null value. Allocates a memory storage or destroy the old content, if necessary.
Definition: value.hpp:293
const std::uint64_t & as_uint64() const
Return a const reference to the underlying std::uint64_t, or throw an exception.
Definition: value.hpp:439
value & operator=(array_type &&arr)
Assign an array_type value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:344
double & as_double()
Return a reference to the underlying double, or throw an exception.
Definition: value.hpp:444
value & operator=(const short i)
Assign a short value. Allocates a memory storage or destroy the old content, if necessary.
Definition: value.hpp:239
value & operator=(const int i)
Assign an int value. Allocates a memory storage or destroy the old content, if necessary.
Definition: value.hpp:245
value & operator=(const unsigned long u)
Assign an unsigned long value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:280
value & operator=(const signed char i)
Assign a char value. Allocates a memory storage or destroy the old content, if necessary.
Definition: value.hpp:233
bool is_null() const noexcept
Return true if this is a null.
Definition: value.hpp:472
const std::int64_t & as_int64() const
Return a const reference to the underlying std::int64_t, or throw an exception.
Definition: value.hpp:429
std::uint64_t & emplace_uint64()
Set a uint64 and return a reference. The old content is destroyed.
Definition: value.hpp:383
bool is_string() const noexcept
Return true if this is a string.
Definition: value.hpp:495
bool is_int64() const noexcept
Return true if this is a int64.
Definition: value.hpp:480
object_type & as_object()
Return a reference to the underlying object, or throw an exception.
Definition: value.hpp:465
object_type & emplace_object()
Set an empty object and return a reference. The old content is destroyed.
Definition: value.hpp:411
string_type & emplace_string()
Set an empty string and return a reference. The old content is destroyed.
Definition: value.hpp:397
value & operator=(string_type &&s)
Assign a string_type value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:330
friend bool operator==(const value &lhs, const value &rhs) noexcept
Equal operator.
Definition: value.hpp:513
value(value &&other) noexcept
Move constructor.
Definition: value.hpp:128
bool is_uint64() const noexcept
Return true if this is a uint64.
Definition: value.hpp:485
friend bool operator!=(const value &lhs, const value &rhs) noexcept
Return true if two values are not equal. Two values are equal when they are the same kind and their r...
Definition: value.hpp:523
void swap(value &other) noexcept
Swap contents.
Definition: value.hpp:209
const string_type & as_string() const
Return a const reference to the underlying string, or throw an exception.
Definition: value.hpp:455
value & operator=(const long i)
Assign a long value. Allocates a memory storage or destroy the old content, if necessary.
Definition: value.hpp:249
value & operator=(const object_type &obj)
Assign an object_type value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:351
bool is_bool() const noexcept
Return true if this is a bool.
Definition: value.hpp:477
const array_type & as_array() const
Return a const reference to the underlying array, or throw an exception.
Definition: value.hpp:462
const bool & as_bool() const
Return a const reference to the underlying bool, or throw an exception.
Definition: value.hpp:421
std::uint64_t & as_uint64()
Return a reference to the underlying std::uint64_t, or throw an exception.
Definition: value.hpp:435
string_type & as_string()
Return a reference to the underlying string, or throw an exception.
Definition: value.hpp:451
double & emplace_double()
Set a double and return a reference. The old content is destroyed.
Definition: value.hpp:390
value & operator=(const unsigned short u)
Assign an unsigned short value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:268
~value() noexcept
Destructor.
Definition: value.hpp:153
value(const value &other, const allocator_type &alloc)
Allocator-extended copy constructor.
Definition: value.hpp:115
object< Alloc > object_type
Definition: value.hpp:89
bool is_object() const noexcept
Return true if this is a object.
Definition: value.hpp:505
value & operator=(const unsigned char u)
Assign an unsigned char value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:262
value & operator=(const unsigned long long u)
Assign an unsigned long long value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:286
value & operator=(const string_type &s)
Assign a string_type value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:321
value & operator=(value &&other) noexcept
Move assignment operator.
Definition: value.hpp:183
value(const allocator_type &alloc)
Constructor.
Definition: value.hpp:103
std::int64_t & as_int64()
Return a reference to the underlying std::int64_t, or throw an exception.
Definition: value.hpp:425
allocator_type get_allocator() const noexcept
Return an allocator object.
Definition: value.hpp:510
array< Alloc > array_type
Definition: value.hpp:90
bool is_array() const noexcept
Return true if this is an array.
Definition: value.hpp:500
value & operator=(const char *const s)
Assign a const char* value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:314
value & operator=(std::string_view s)
Assign a std::string_view value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:307
value & operator=(object_type &&obj)
Assign an object_type value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:358
basic_string< char, std::char_traits< char >, typename std::allocator_traits< allocator_type >::template rebind_alloc< char > > string_type
Definition: value.hpp:88
const double & as_double() const
Return a const reference to the underlying double, or throw an exception.
Definition: value.hpp:448
bool & emplace_bool()
Set a bool and return a reference. The old content is destroyed.
Definition: value.hpp:369
value & operator=(const bool b)
Assign a bool value. Allocates a memory storage or destroy the old content, if necessary.
Definition: value.hpp:226
value & operator=(const value &other)
Copy assignment operator.
Definition: value.hpp:156
value(value &&other, const allocator_type &alloc) noexcept
Allocator-extended move constructor.
Definition: value.hpp:135
value & operator=(const long long i)
Assign a long long value. Allocates a memory storage or destroy the old content, if necessary.
Definition: value.hpp:255
array_type & as_array()
Return a reference to the underlying array, or throw an exception.
Definition: value.hpp:458
value & operator=(const array_type &arr)
Assign an array_type value. Allocates a memory storage or destroy the old content,...
Definition: value.hpp:337
bool & as_bool()
Return a reference to the underlying bool, or throw an exception.
Definition: value.hpp:417
bool is_double() const noexcept
Return true if this is a double.
Definition: value.hpp:490
Alloc allocator_type
Definition: value.hpp:84
void emplace_null()
Set a null. The old content is destroyed.
Definition: value.hpp:365
array_type & emplace_array()
Set an empty array and return a reference. The old content is destroyed.
Definition: value.hpp:404
value(const value &other)
Copy constructor.
Definition: value.hpp:108
value()
Constructor.
Definition: value.hpp:99
const object_type & as_object() const
Return a const reference to the underlying object, or throw an exception.
Definition: value.hpp:469
std::int64_t & emplace_int64()
Set a int64 and return a reference. The old content is destroyed.
Definition: value.hpp:376
Namespace for Metall container.
bool general_value_equal(const value< allocator_type > &value, const other_value_type &other_value) noexcept
Provides 'equal' calculation for other value types that have the same interface as the value class.
Definition: value.hpp:28
Namespace for Metall JSON container, which is in an experimental phase.
Definition: array.hpp:17
metall::container::basic_string< char_t, traits, allocator_type > basic_string
JSON basic string type.
Definition: json_fwd.hpp:44
std::monostate null_type
JSON null type.
Definition: json_fwd.hpp:39
void swap(array< allocator_type > &lhd, array< allocator_type > &rhd) noexcept
Swap value instances.
Definition: array.hpp:193
void swap(value< allocator_type > &lhd, value< allocator_type > &rhd) noexcept
Swap value instances.
Definition: value.hpp:539