Metall  v0.28
A persistent memory allocator for data-centric analytics
key_value_pair.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_KEY_VALUE_PAIR_HPP
7 #define METALL_JSON_KEY_VALUE_PAIR_HPP
8 
9 #include <string_view>
10 #include <memory>
11 #include <string>
12 #include <algorithm>
13 #include <cstring>
14 
15 #include <metall/offset_ptr.hpp>
16 #include <metall/detail/utilities.hpp>
17 #include <metall/json/json_fwd.hpp>
18 
19 namespace metall::json {
20 
21 namespace jsndtl {
24 template <typename char_type, typename char_traits, typename allocator_type,
25  typename other_key_value_pair_type>
28  const other_key_value_pair_type &other_key_value) noexcept {
29  if (key_value.key().length() != other_key_value.key().length())
30  return false;
31  if (std::strcmp(key_value.key_c_str(), other_key_value.key_c_str()) != 0)
32  return false;
33  return key_value.value() == other_key_value.value();
34 }
35 } // namespace jsndtl
36 
40 #ifdef DOXYGEN_SKIP
41 template <typename char_type = char,
42  typename char_traits = std::char_traits<char_type>,
43  typename Alloc = std::allocator<char_type>>
44 #else
45 template <typename _char_type, typename _char_traits, typename Alloc>
46 #endif
48  private:
49  using char_allocator_type =
50  typename std::allocator_traits<Alloc>::template rebind_alloc<_char_type>;
51  using char_pointer =
52  typename std::allocator_traits<char_allocator_type>::pointer;
53 
54  public:
55  using char_type = _char_type;
56  using char_traits = _char_traits;
57  using allocator_type = Alloc;
58  using key_type = std::basic_string_view<char_type, char_traits>;
60  using size_type = std::size_t;
61 
62  public:
69  const allocator_type &alloc = allocator_type())
70  : m_value(value, alloc) {
71  priv_allocate_key(key.data(), key.length(), m_value.get_allocator());
72  }
73 
80  const allocator_type &alloc = allocator_type())
81  : m_value(std::move(value), alloc) {
82  priv_allocate_key(key.data(), key.length(), m_value.get_allocator());
83  }
84 
86  key_value_pair(const key_value_pair &other) : m_value(other.m_value) {
87  priv_allocate_key(other.key_c_str(), other.m_key_length,
88  m_value.get_allocator());
89  }
90 
92  key_value_pair(const key_value_pair &other, const allocator_type &alloc)
93  : m_value(other.m_value, alloc) {
94  priv_allocate_key(other.key_c_str(), other.m_key_length,
95  m_value.get_allocator());
96  }
97 
99  key_value_pair(key_value_pair &&other) noexcept
100  : m_value(std::move(other.m_value)) {
101  if (other.priv_short_key()) {
102  m_short_key_buf = other.m_short_key_buf;
103  } else {
104  m_long_key = std::move(other.m_long_key);
105  other.m_long_key = nullptr;
106  }
107  m_key_length = other.m_key_length;
108  other.m_key_length = 0;
109  }
110 
112  key_value_pair(key_value_pair &&other, const allocator_type &alloc) noexcept
113  : m_value(alloc) {
114  if (alloc == other.get_allocator()) {
115  if (other.priv_short_key()) {
116  m_short_key_buf = other.m_short_key_buf;
117  } else {
118  m_long_key = std::move(other.m_long_key);
119  other.m_long_key = nullptr;
120  }
121  m_key_length = other.m_key_length;
122  other.m_key_length = 0;
123  } else {
124  priv_allocate_key(other.key_c_str(), other.m_key_length, get_allocator());
125  other.priv_deallocate_key(other.get_allocator());
126  }
127  m_value = std::move(other.m_value);
128  }
129 
132  if (this == &other) return *this;
133 
134  priv_deallocate_key(get_allocator()); // deallocate the key using the
135  // current allocator first
136  m_value = other.m_value;
137  // This line has to come after copying m_value because m_value decides if
138  // allocator is needed to be propagated.
139  priv_allocate_key(other.key_c_str(), other.m_key_length, get_allocator());
140 
141  return *this;
142  }
143 
146  if (this == &other) return *this;
147 
148  priv_deallocate_key(get_allocator()); // deallocate the key using the
149  // current allocator first
150 
151  auto other_allocator = other.m_value.get_allocator();
152  m_value = std::move(other.m_value);
153 
154  if (get_allocator() == other.get_allocator()) {
155  if (other.priv_short_key()) {
156  m_short_key_buf = other.m_short_key_buf;
157  } else {
158  m_long_key = std::move(other.m_long_key);
159  other.m_long_key = nullptr;
160  }
161  m_key_length = other.m_key_length;
162  other.m_key_length = 0;
163  } else {
164  priv_allocate_key(other.key_c_str(), other.m_key_length, get_allocator());
165  other.priv_deallocate_key(other_allocator);
166  }
167 
168  return *this;
169  }
170 
172  void swap(key_value_pair &other) noexcept {
173  if constexpr (!std::is_same_v<
174  typename std::allocator_traits<
175  allocator_type>::propagate_on_container_swap,
176  std::true_type>) {
177  // This is an undefined behavior in the C++ standard.
178  assert(get_allocator() == other.get_allocator());
179  }
180 
181  using std::swap;
182 
183  if (priv_short_key()) {
184  swap(m_short_key, other.m_short_key);
185  } else {
186  swap(m_long_key, other.m_long_key);
187  }
188  swap(m_key_length, other.m_key_length);
189 
190  swap(m_value, other.m_value);
191  }
192 
194  ~key_value_pair() noexcept { priv_deallocate_key(get_allocator()); }
195 
198  const key_type key() const noexcept {
199  return key_type(key_c_str(), m_key_length);
200  }
201 
204  const char_type *key_c_str() const noexcept { return priv_key_c_str(); }
205 
208  value_type &value() noexcept { return m_value; }
209 
212  const value_type &value() const noexcept { return m_value; }
213 
218  friend bool operator==(const key_value_pair &lhs,
219  const key_value_pair &rhs) noexcept {
220  return jsndtl::general_key_value_pair_equal(lhs, rhs);
221  }
222 
227  friend bool operator!=(const key_value_pair &lhs,
228  const key_value_pair &rhs) noexcept {
229  return !(lhs == rhs);
230  }
231 
233  allocator_type get_allocator() const noexcept {
234  return m_value.get_allocator();
235  }
236 
237  private:
238  static constexpr uint32_t k_short_key_max_length =
239  sizeof(char_pointer) - 1; // -1 for '0'
240 
241  const char_type *priv_key_c_str() const noexcept {
242  if (priv_short_key()) {
243  return m_short_key;
244  }
245  return metall::to_raw_pointer(m_long_key);
246  }
247 
248  bool priv_short_key() const noexcept {
249  return (m_key_length <= k_short_key_max_length);
250  }
251 
252  bool priv_long_key() const noexcept { return !priv_short_key(); }
253 
254  bool priv_allocate_key(const char_type *const key, const size_type length,
255  char_allocator_type alloc) {
256  assert(m_key_length == 0);
257  m_key_length = length;
258 
259  if (priv_short_key()) {
260  std::char_traits<char_type>::copy(m_short_key, key, m_key_length);
261  std::char_traits<char_type>::assign(m_short_key[m_key_length], '\0');
262  } else {
263  m_long_key = std::allocator_traits<char_allocator_type>::allocate(
264  alloc, m_key_length + 1);
265  if (!m_long_key) {
266  m_key_length = 0;
267  std::abort(); // TODO: change
268  return false;
269  }
270 
271  std::char_traits<char_type>::copy(metall::to_raw_pointer(m_long_key), key,
272  m_key_length);
273  std::char_traits<char_type>::assign(m_long_key[m_key_length], '\0');
274  }
275 
276  return true;
277  }
278 
279  bool priv_deallocate_key(char_allocator_type alloc) {
280  if (m_key_length > k_short_key_max_length) {
281  std::allocator_traits<char_allocator_type>::deallocate(alloc, m_long_key,
282  m_key_length + 1);
283  m_long_key = nullptr;
284  }
285  m_key_length = 0;
286  return true;
287  }
288 
289  union {
290  char_type m_short_key[k_short_key_max_length + 1]; // + 1 for '\0'
291  static_assert(sizeof(char_type) * (k_short_key_max_length + 1) <=
292  sizeof(uint64_t),
293  "sizeof(m_short_key) is bigger than sizeof(uint64_t)");
294  uint64_t m_short_key_buf;
295 
296  char_pointer m_long_key{nullptr};
297  };
298  size_type m_key_length{0};
299 
300  value_type m_value;
301 };
302 
304 template <typename char_type, typename char_traits, typename allocator_type>
305 inline void swap(
308  lhd.swap(rhd);
309 }
310 
311 } // namespace metall::json
312 
313 #endif // METALL_JSON_KEY_VALUE_PAIR_HPP
A class for holding a pair of JSON string (as its key) and JSON value (as its value).
Definition: key_value_pair.hpp:47
char_type m_short_key[k_short_key_max_length+1]
Definition: key_value_pair.hpp:290
Alloc allocator_type
Definition: key_value_pair.hpp:57
key_value_pair(key_type key, value_type &&value, const allocator_type &alloc=allocator_type())
Constructor.
Definition: key_value_pair.hpp:79
friend bool operator==(const key_value_pair &lhs, const key_value_pair &rhs) noexcept
Return true if two key-value pairs are equal.
Definition: key_value_pair.hpp:218
void swap(key_value_pair &other) noexcept
Swap contents.
Definition: key_value_pair.hpp:172
std::basic_string_view< char_type, char_traits > key_type
Definition: key_value_pair.hpp:58
key_value_pair & operator=(key_value_pair &&other) noexcept
Move assignment operator.
Definition: key_value_pair.hpp:145
const char_type * key_c_str() const noexcept
Returns the stored key as const char*.
Definition: key_value_pair.hpp:204
allocator_type get_allocator() const noexcept
Return an allocator object.
Definition: key_value_pair.hpp:233
_char_type char_type
Definition: key_value_pair.hpp:55
value_type & value() noexcept
References the stored JSON value.
Definition: key_value_pair.hpp:208
~key_value_pair() noexcept
Destructor.
Definition: key_value_pair.hpp:194
key_value_pair(key_value_pair &&other, const allocator_type &alloc) noexcept
Allocator-extended move constructor.
Definition: key_value_pair.hpp:112
key_value_pair(key_type key, const value_type &value, const allocator_type &alloc=allocator_type())
Constructor.
Definition: key_value_pair.hpp:68
key_value_pair(const key_value_pair &other)
Copy constructor.
Definition: key_value_pair.hpp:86
key_value_pair(const key_value_pair &other, const allocator_type &alloc)
Allocator-extended copy constructor.
Definition: key_value_pair.hpp:92
key_value_pair(key_value_pair &&other) noexcept
Move constructor.
Definition: key_value_pair.hpp:99
_char_traits char_traits
Definition: key_value_pair.hpp:56
friend bool operator!=(const key_value_pair &lhs, const key_value_pair &rhs) noexcept
Return true if two key-value pairs are not equal.
Definition: key_value_pair.hpp:227
metall::json::value< allocator_type > value_type
Definition: key_value_pair.hpp:59
const key_type key() const noexcept
Returns the stored key.
Definition: key_value_pair.hpp:198
key_value_pair & operator=(const key_value_pair &other)
Copy assignment operator.
Definition: key_value_pair.hpp:131
const value_type & value() const noexcept
References the stored JSON value.
Definition: key_value_pair.hpp:212
uint64_t m_short_key_buf
Definition: key_value_pair.hpp:293
std::size_t size_type
Definition: key_value_pair.hpp:60
char_pointer m_long_key
Definition: key_value_pair.hpp:296
allocator_type get_allocator() const noexcept
Return an allocator object.
Definition: value.hpp:510
bool general_key_value_pair_equal(const key_value_pair< char_type, char_traits, allocator_type > &key_value, const other_key_value_pair_type &other_key_value) noexcept
Provides 'equal' calculation for other key-value types that have the same interface as the object cla...
Definition: key_value_pair.hpp:26
Namespace for Metall JSON container, which is in an experimental phase.
Definition: array.hpp:17
void swap(array< allocator_type > &lhd, array< allocator_type > &rhd) noexcept
Swap value instances.
Definition: array.hpp:193
void swap(key_value_pair< char_type, char_traits, allocator_type > &rhd, key_value_pair< char_type, char_traits, allocator_type > &lhd) noexcept
Swap value instances.
Definition: key_value_pair.hpp:305