dictionary.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*************************************************************************
  2. *
  3. * Copyright 2019 Realm Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. **************************************************************************/
  18. #ifndef REALM_DICTIONARY_HPP
  19. #define REALM_DICTIONARY_HPP
  20. #include <realm/collection.hpp>
  21. #include <realm/obj.hpp>
  22. #include <realm/mixed.hpp>
  23. #include <realm/array_mixed.hpp>
  24. namespace realm {
  25. class Dictionary final : public CollectionBaseImpl<CollectionBase, Dictionary> {
  26. public:
  27. using Base = CollectionBaseImpl<CollectionBase, Dictionary>;
  28. class Iterator;
  29. Dictionary() {}
  30. ~Dictionary();
  31. Dictionary(const Obj& obj, ColKey col_key);
  32. Dictionary(const Dictionary& other)
  33. : Base(static_cast<const Base&>(other))
  34. , m_key_type(other.m_key_type)
  35. {
  36. *this = other;
  37. }
  38. Dictionary& operator=(const Dictionary& other);
  39. using Base::operator==;
  40. DataType get_key_data_type() const;
  41. DataType get_value_data_type() const;
  42. std::pair<Mixed, Mixed> get_pair(size_t ndx) const;
  43. Mixed get_key(size_t ndx) const;
  44. // Overriding members of CollectionBase:
  45. std::unique_ptr<CollectionBase> clone_collection() const final;
  46. size_t size() const final;
  47. bool is_null(size_t ndx) const final;
  48. Mixed get_any(size_t ndx) const final;
  49. size_t find_any(Mixed value) const final;
  50. size_t find_any_key(Mixed value) const noexcept;
  51. util::Optional<Mixed> min(size_t* return_ndx = nullptr) const final;
  52. util::Optional<Mixed> max(size_t* return_ndx = nullptr) const final;
  53. util::Optional<Mixed> sum(size_t* return_cnt = nullptr) const final;
  54. util::Optional<Mixed> avg(size_t* return_cnt = nullptr) const final;
  55. void sort(std::vector<size_t>& indices, bool ascending = true) const final;
  56. void distinct(std::vector<size_t>& indices, util::Optional<bool> sort_order = util::none) const final;
  57. void sort_keys(std::vector<size_t>& indices, bool ascending = true) const;
  58. void distinct_keys(std::vector<size_t>& indices, util::Optional<bool> sort_order = util::none) const;
  59. // first points to inserted/updated element.
  60. // second is true if the element was inserted
  61. std::pair<Iterator, bool> insert(Mixed key, Mixed value);
  62. std::pair<Iterator, bool> insert(Mixed key, const Obj& obj);
  63. Obj create_and_insert_linked_object(Mixed key);
  64. // throws std::out_of_range if key is not found
  65. Mixed get(Mixed key) const;
  66. // Noexcept version
  67. util::Optional<Mixed> try_get(Mixed key) const noexcept;
  68. // adds entry if key is not found
  69. const Mixed operator[](Mixed key);
  70. Obj get_object(StringData key);
  71. bool contains(Mixed key) const noexcept;
  72. Iterator find(Mixed key) const noexcept;
  73. void erase(Mixed key);
  74. Iterator erase(Iterator it);
  75. bool try_erase(Mixed key);
  76. void nullify(Mixed);
  77. void remove_backlinks(CascadeState& state) const;
  78. void clear() final;
  79. template <class T>
  80. void for_all_values(T&& f)
  81. {
  82. if (update()) {
  83. BPlusTree<Mixed> values(m_obj.get_alloc());
  84. values.init_from_ref(m_dictionary_top->get_as_ref(1));
  85. auto func = [&f](BPlusTreeNode* node, size_t) {
  86. auto leaf = static_cast<BPlusTree<Mixed>::LeafNode*>(node);
  87. size_t sz = leaf->size();
  88. for (size_t i = 0; i < sz; i++) {
  89. f(leaf->get(i));
  90. }
  91. return IteratorControl::AdvanceToNext;
  92. };
  93. values.traverse(func);
  94. }
  95. }
  96. template <class T, class Func>
  97. void for_all_keys(Func&& f)
  98. {
  99. if (update()) {
  100. BPlusTree<T> keys(m_obj.get_alloc());
  101. keys.init_from_ref(m_dictionary_top->get_as_ref(0));
  102. auto func = [&f](BPlusTreeNode* node, size_t) {
  103. auto leaf = static_cast<typename BPlusTree<T>::LeafNode*>(node);
  104. size_t sz = leaf->size();
  105. for (size_t i = 0; i < sz; i++) {
  106. f(leaf->get(i));
  107. }
  108. return IteratorControl::AdvanceToNext;
  109. };
  110. keys.traverse(func);
  111. }
  112. }
  113. Iterator begin() const;
  114. Iterator end() const;
  115. void migrate();
  116. private:
  117. template <typename T, typename Op>
  118. friend class CollectionColumnAggregate;
  119. friend class DictionaryLinkValues;
  120. friend class Cluster;
  121. friend void Obj::assign_pk_and_backlinks(const Obj& other);
  122. mutable std::unique_ptr<Array> m_dictionary_top;
  123. mutable std::unique_ptr<BPlusTreeBase> m_keys;
  124. mutable std::unique_ptr<BPlusTree<Mixed>> m_values;
  125. DataType m_key_type = type_String;
  126. Dictionary(Allocator& alloc, ColKey col_key, ref_type ref);
  127. bool init_from_parent(bool allow_create) const;
  128. Mixed do_get(size_t ndx) const;
  129. void do_erase(size_t ndx, Mixed key);
  130. Mixed do_get_key(size_t ndx) const;
  131. size_t do_find_key(Mixed key) const noexcept;
  132. std::pair<size_t, Mixed> find_impl(Mixed key) const noexcept;
  133. std::pair<Mixed, Mixed> do_get_pair(size_t ndx) const;
  134. bool clear_backlink(Mixed value, CascadeState& state) const;
  135. void align_indices(std::vector<size_t>& indices) const;
  136. void swap_content(Array& fields1, Array& fields2, size_t index1, size_t index2);
  137. util::Optional<Mixed> do_min(size_t* return_ndx = nullptr) const;
  138. util::Optional<Mixed> do_max(size_t* return_ndx = nullptr) const;
  139. util::Optional<Mixed> do_sum(size_t* return_cnt = nullptr) const;
  140. util::Optional<Mixed> do_avg(size_t* return_cnt = nullptr) const;
  141. Mixed find_value(Mixed) const noexcept;
  142. template <typename AggregateType>
  143. void do_accumulate(size_t* return_ndx, AggregateType& agg) const;
  144. UpdateStatus update_if_needed() const final;
  145. UpdateStatus ensure_created() final;
  146. inline bool update() const
  147. {
  148. return update_if_needed() != UpdateStatus::Detached;
  149. }
  150. void verify() const;
  151. };
  152. class Dictionary::Iterator {
  153. public:
  154. using iterator_category = std::random_access_iterator_tag;
  155. using value_type = std::pair<Mixed, Mixed>;
  156. using difference_type = ptrdiff_t;
  157. using pointer = const value_type*;
  158. using reference = const value_type&;
  159. pointer operator->() const
  160. {
  161. m_val = m_list->get_pair(m_ndx);
  162. return &m_val;
  163. }
  164. reference operator*() const
  165. {
  166. return *operator->();
  167. }
  168. Iterator& operator++() noexcept
  169. {
  170. ++m_ndx;
  171. return *this;
  172. }
  173. Iterator operator++(int) noexcept
  174. {
  175. auto tmp = *this;
  176. operator++();
  177. return tmp;
  178. }
  179. Iterator& operator--() noexcept
  180. {
  181. --m_ndx;
  182. return *this;
  183. }
  184. Iterator operator--(int) noexcept
  185. {
  186. auto tmp = *this;
  187. operator--();
  188. return tmp;
  189. }
  190. Iterator& operator+=(ptrdiff_t n) noexcept
  191. {
  192. m_ndx += n;
  193. return *this;
  194. }
  195. Iterator& operator-=(ptrdiff_t n) noexcept
  196. {
  197. m_ndx -= n;
  198. return *this;
  199. }
  200. friend ptrdiff_t operator-(const Iterator& lhs, const Iterator& rhs) noexcept
  201. {
  202. return ptrdiff_t(lhs.m_ndx) - ptrdiff_t(rhs.m_ndx);
  203. }
  204. friend Iterator operator+(Iterator lhs, ptrdiff_t rhs) noexcept
  205. {
  206. lhs.m_ndx += rhs;
  207. return lhs;
  208. }
  209. friend Iterator operator+(ptrdiff_t lhs, Iterator rhs) noexcept
  210. {
  211. return rhs + lhs;
  212. }
  213. bool operator!=(const Iterator& rhs) const noexcept
  214. {
  215. REALM_ASSERT_DEBUG(m_list == rhs.m_list);
  216. return m_ndx != rhs.m_ndx;
  217. }
  218. bool operator==(const Iterator& rhs) const noexcept
  219. {
  220. REALM_ASSERT_DEBUG(m_list == rhs.m_list);
  221. return m_ndx == rhs.m_ndx;
  222. }
  223. size_t index() const noexcept
  224. {
  225. return m_ndx;
  226. }
  227. private:
  228. Iterator(const Dictionary* l, size_t ndx) noexcept
  229. : m_list(l)
  230. , m_ndx(ndx)
  231. {
  232. }
  233. friend class Dictionary;
  234. mutable value_type m_val;
  235. const Dictionary* m_list;
  236. size_t m_ndx = size_t(-1);
  237. };
  238. // An interface used when the value type of the dictionary consists of
  239. // links to a single table. Implementation of the ObjList interface on
  240. // top of a Dictionary of objects. This is the dictionary equivilent of
  241. // LnkLst and LnkSet.
  242. class DictionaryLinkValues final : public ObjCollectionBase<CollectionBase> {
  243. public:
  244. DictionaryLinkValues() = default;
  245. DictionaryLinkValues(const Obj& obj, ColKey col_key);
  246. DictionaryLinkValues(const Dictionary& source);
  247. // Overrides of ObjList:
  248. ObjKey get_key(size_t ndx) const final;
  249. Obj get_object(size_t row_ndx) const final;
  250. // Overrides of CollectionBase, these simply forward to the underlying dictionary.
  251. size_t size() const final
  252. {
  253. return m_source.size();
  254. }
  255. bool is_null(size_t ndx) const final
  256. {
  257. return m_source.is_null(ndx);
  258. }
  259. Mixed get_any(size_t ndx) const final
  260. {
  261. return m_source.get_any(ndx);
  262. }
  263. void clear() final
  264. {
  265. m_source.clear();
  266. }
  267. util::Optional<Mixed> min(size_t* return_ndx = nullptr) const final
  268. {
  269. return m_source.min(return_ndx);
  270. }
  271. util::Optional<Mixed> max(size_t* return_ndx = nullptr) const final
  272. {
  273. return m_source.max(return_ndx);
  274. }
  275. util::Optional<Mixed> sum(size_t* return_cnt = nullptr) const final
  276. {
  277. return m_source.sum(return_cnt);
  278. }
  279. util::Optional<Mixed> avg(size_t* return_cnt = nullptr) const final
  280. {
  281. return m_source.avg(return_cnt);
  282. }
  283. std::unique_ptr<CollectionBase> clone_collection() const final
  284. {
  285. return std::make_unique<DictionaryLinkValues>(m_source);
  286. }
  287. LinkCollectionPtr clone_obj_list() const final
  288. {
  289. return std::make_unique<DictionaryLinkValues>(m_source);
  290. }
  291. void sort(std::vector<size_t>& indices, bool ascending = true) const final
  292. {
  293. m_source.sort(indices, ascending);
  294. }
  295. void distinct(std::vector<size_t>& indices, util::Optional<bool> sort_order = util::none) const final
  296. {
  297. m_source.distinct(indices, sort_order);
  298. }
  299. size_t find_any(Mixed value) const final
  300. {
  301. return m_source.find_any(value);
  302. }
  303. const Obj& get_obj() const noexcept final
  304. {
  305. return m_source.get_obj();
  306. }
  307. ColKey get_col_key() const noexcept final
  308. {
  309. return m_source.get_col_key();
  310. }
  311. bool has_changed() const final
  312. {
  313. return m_source.has_changed();
  314. }
  315. // Overrides of ObjCollectionBase:
  316. UpdateStatus do_update_if_needed() const final
  317. {
  318. return m_source.update_if_needed();
  319. }
  320. BPlusTree<ObjKey>* get_mutable_tree() const
  321. {
  322. // We are faking being an ObjList because the underlying storage is not
  323. // actually a BPlusTree<ObjKey> for dictionaries it is all mixed values.
  324. // But this is ok, because we don't need to deal with unresolved link
  325. // maintenance because they are not hidden from view in dictionaries in
  326. // the same way as for LnkSet and LnkLst. This means that the functions
  327. // that call get_mutable_tree do not need to do anything for dictionaries.
  328. return nullptr;
  329. }
  330. private:
  331. Dictionary m_source;
  332. };
  333. inline std::pair<Dictionary::Iterator, bool> Dictionary::insert(Mixed key, const Obj& obj)
  334. {
  335. return insert(key, Mixed(obj.get_link()));
  336. }
  337. inline std::unique_ptr<CollectionBase> Dictionary::clone_collection() const
  338. {
  339. return m_obj.get_dictionary_ptr(m_col_key);
  340. }
  341. } // namespace realm
  342. #endif /* SRC_REALM_DICTIONARY_HPP_ */