collection.hpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. #ifndef REALM_COLLECTION_HPP
  2. #define REALM_COLLECTION_HPP
  3. #include <realm/obj.hpp>
  4. #include <realm/bplustree.hpp>
  5. #include <realm/obj_list.hpp>
  6. #include <realm/table.hpp>
  7. #include <iosfwd> // std::ostream
  8. #include <type_traits> // std::void_t
  9. namespace realm {
  10. template <class L>
  11. struct CollectionIterator;
  12. /// Base class for all collection accessors.
  13. ///
  14. /// Collections are bound to particular properties of an object. In a
  15. /// collection's public interface, the implementation must take care to keep the
  16. /// object consistent with the persisted state, mindful of the fact that the
  17. /// state may have changed as a consequence of modifications from other instances
  18. /// referencing the same persisted state.
  19. class CollectionBase {
  20. public:
  21. virtual ~CollectionBase() {}
  22. /// The size of the collection.
  23. virtual size_t size() const = 0;
  24. /// True if the element at @a ndx is NULL.
  25. virtual bool is_null(size_t ndx) const = 0;
  26. /// Get element at @a ndx as a `Mixed`.
  27. virtual Mixed get_any(size_t ndx) const = 0;
  28. /// Clear the collection.
  29. virtual void clear() = 0;
  30. /// Get the min element, according to whatever comparison function is
  31. /// meaningful for the collection, or none if min is not supported for this type.
  32. virtual util::Optional<Mixed> min(size_t* return_ndx = nullptr) const = 0;
  33. /// Get the max element, according to whatever comparison function is
  34. /// meaningful for the collection, or none if max is not supported for this type.
  35. virtual util::Optional<Mixed> max(size_t* return_ndx = nullptr) const = 0;
  36. /// For collections of arithmetic types, return the sum of all elements.
  37. /// For non arithmetic types, returns none.
  38. virtual util::Optional<Mixed> sum(size_t* return_cnt = nullptr) const = 0;
  39. /// For collections of arithmetic types, return the average of all elements.
  40. /// For non arithmetic types, returns none.
  41. virtual util::Optional<Mixed> avg(size_t* return_cnt = nullptr) const = 0;
  42. /// Produce a clone of the collection accessor referring to the same
  43. /// underlying memory.
  44. virtual std::unique_ptr<CollectionBase> clone_collection() const = 0;
  45. /// Modifies a vector of indices so that they refer to values sorted
  46. /// according to the specified sort order.
  47. virtual void sort(std::vector<size_t>& indices, bool ascending = true) const = 0;
  48. /// Modifies a vector of indices so that they refer to distinct values. If
  49. /// @a sort_order is supplied, the indices will refer to values in sort
  50. /// order, otherwise the indices will be in the same order as they appear in
  51. /// the collection.
  52. virtual void distinct(std::vector<size_t>& indices, util::Optional<bool> sort_order = util::none) const = 0;
  53. // Return index of the first occurrence of 'value'
  54. virtual size_t find_any(Mixed value) const = 0;
  55. /// True if `size()` returns 0.
  56. virtual bool is_empty() const final
  57. {
  58. return size() == 0;
  59. }
  60. /// Get the object that owns this collection.
  61. virtual const Obj& get_obj() const noexcept = 0;
  62. /// Get the column key for this collection.
  63. virtual ColKey get_col_key() const noexcept = 0;
  64. /// Return true if the collection has changed since the last call to
  65. /// `has_changed()`. Note that this function is not idempotent and updates
  66. /// the internal state of the accessor if it has changed.
  67. virtual bool has_changed() const = 0;
  68. /// Returns true if the accessor is in the attached state. By default, this
  69. /// checks if the owning object is still valid.
  70. virtual bool is_attached() const
  71. {
  72. return get_obj().is_valid();
  73. }
  74. // Note: virtual..final prevents static override.
  75. /// Get the key of the object that owns this collection.
  76. virtual ObjKey get_owner_key() const noexcept final
  77. {
  78. return get_obj().get_key();
  79. }
  80. /// Get the table of the object that owns this collection.
  81. virtual ConstTableRef get_table() const noexcept final
  82. {
  83. return get_obj().get_table();
  84. }
  85. /// If this is a collection of links, get the target table.
  86. virtual TableRef get_target_table() const final
  87. {
  88. return get_obj().get_target_table(get_col_key());
  89. }
  90. virtual size_t translate_index(size_t ndx) const noexcept
  91. {
  92. return ndx;
  93. }
  94. StringData get_property_name() const
  95. {
  96. return get_table()->get_column_name(get_col_key());
  97. }
  98. bool operator==(const CollectionBase& other) const noexcept
  99. {
  100. return get_table() == other.get_table() && get_owner_key() == other.get_owner_key() &&
  101. get_col_key() == other.get_col_key();
  102. }
  103. bool operator!=(const CollectionBase& other) const noexcept
  104. {
  105. return !(*this == other);
  106. }
  107. // These are shadowed by typed versions in subclasses
  108. using value_type = Mixed;
  109. CollectionIterator<CollectionBase> begin() const;
  110. CollectionIterator<CollectionBase> end() const;
  111. protected:
  112. friend class Transaction;
  113. CollectionBase() noexcept = default;
  114. CollectionBase(const CollectionBase&) noexcept = default;
  115. CollectionBase(CollectionBase&&) noexcept = default;
  116. CollectionBase& operator=(const CollectionBase&) noexcept = default;
  117. CollectionBase& operator=(CollectionBase&&) noexcept = default;
  118. void validate_index(const char* msg, size_t index, size_t size) const;
  119. };
  120. inline std::string_view collection_type_name(ColKey col, bool uppercase = false)
  121. {
  122. if (col.is_list())
  123. return uppercase ? "List" : "list";
  124. if (col.is_set())
  125. return uppercase ? "Set" : "set";
  126. if (col.is_dictionary())
  127. return uppercase ? "Dictionary" : "dictionary";
  128. return "";
  129. }
  130. inline void CollectionBase::validate_index(const char* msg, size_t index, size_t size) const
  131. {
  132. if (index >= size) {
  133. throw OutOfBounds(util::format("%1 on %2 '%3.%4'", msg, collection_type_name(get_col_key()),
  134. get_table()->get_class_name(), get_property_name()),
  135. index, size);
  136. }
  137. }
  138. template <class T>
  139. inline void check_column_type(ColKey col)
  140. {
  141. if (col && col.get_type() != ColumnTypeTraits<T>::column_id) {
  142. throw InvalidColumnKey();
  143. }
  144. }
  145. template <>
  146. inline void check_column_type<Int>(ColKey col)
  147. {
  148. if (col && (col.get_type() != col_type_Int || col.get_attrs().test(col_attr_Nullable))) {
  149. throw InvalidColumnKey();
  150. }
  151. }
  152. template <>
  153. inline void check_column_type<util::Optional<Int>>(ColKey col)
  154. {
  155. if (col && (col.get_type() != col_type_Int || !col.get_attrs().test(col_attr_Nullable))) {
  156. throw InvalidColumnKey();
  157. }
  158. }
  159. template <>
  160. inline void check_column_type<ObjKey>(ColKey col)
  161. {
  162. if (col) {
  163. bool is_link_list = (col.get_type() == col_type_LinkList);
  164. bool is_link_set = (col.is_set() && col.get_type() == col_type_Link);
  165. if (!(is_link_list || is_link_set))
  166. throw InvalidArgument(ErrorCodes::TypeMismatch, "Property not a list or set");
  167. }
  168. }
  169. template <class T, class = void>
  170. struct MinHelper {
  171. template <class U>
  172. static util::Optional<Mixed> eval(U&, size_t*) noexcept
  173. {
  174. return util::none;
  175. }
  176. static util::Optional<Mixed> not_found(size_t*) noexcept
  177. {
  178. return util::none;
  179. }
  180. };
  181. template <class T>
  182. struct MinHelper<T, std::void_t<ColumnMinMaxType<T>>> {
  183. template <class U>
  184. static util::Optional<Mixed> eval(U& tree, size_t* return_ndx)
  185. {
  186. auto optional_min = bptree_minimum<T>(tree, return_ndx);
  187. if (optional_min) {
  188. return Mixed{*optional_min};
  189. }
  190. return Mixed{};
  191. }
  192. static util::Optional<Mixed> not_found(size_t* return_ndx) noexcept
  193. {
  194. if (return_ndx)
  195. *return_ndx = realm::not_found;
  196. return Mixed{};
  197. }
  198. };
  199. template <class T, class Enable = void>
  200. struct MaxHelper {
  201. template <class U>
  202. static util::Optional<Mixed> eval(U&, size_t*) noexcept
  203. {
  204. return util::none;
  205. }
  206. static util::Optional<Mixed> not_found(size_t*) noexcept
  207. {
  208. return util::none;
  209. }
  210. };
  211. template <class T>
  212. struct MaxHelper<T, std::void_t<ColumnMinMaxType<T>>> {
  213. template <class U>
  214. static util::Optional<Mixed> eval(U& tree, size_t* return_ndx)
  215. {
  216. auto optional_max = bptree_maximum<T>(tree, return_ndx);
  217. if (optional_max) {
  218. return Mixed{*optional_max};
  219. }
  220. return Mixed{};
  221. }
  222. static util::Optional<Mixed> not_found(size_t* return_ndx) noexcept
  223. {
  224. if (return_ndx)
  225. *return_ndx = realm::not_found;
  226. return Mixed{};
  227. }
  228. };
  229. template <class T, class Enable = void>
  230. class SumHelper {
  231. public:
  232. template <class U>
  233. static util::Optional<Mixed> eval(U&, size_t* return_cnt) noexcept
  234. {
  235. if (return_cnt)
  236. *return_cnt = 0;
  237. return util::none;
  238. }
  239. static util::Optional<Mixed> not_found(size_t*) noexcept
  240. {
  241. return util::none;
  242. }
  243. };
  244. template <class T>
  245. class SumHelper<T, std::void_t<ColumnSumType<T>>> {
  246. public:
  247. template <class U>
  248. static util::Optional<Mixed> eval(U& tree, size_t* return_cnt)
  249. {
  250. return Mixed{bptree_sum<T>(tree, return_cnt)};
  251. }
  252. static util::Optional<Mixed> not_found(size_t* return_cnt) noexcept
  253. {
  254. if (return_cnt)
  255. *return_cnt = 0;
  256. using ResultType = typename aggregate_operations::Sum<typename util::RemoveOptional<T>::type>::ResultType;
  257. return Mixed{ResultType{}};
  258. }
  259. };
  260. template <class T, class = void>
  261. struct AverageHelper {
  262. template <class U>
  263. static util::Optional<Mixed> eval(U&, size_t* return_cnt) noexcept
  264. {
  265. if (return_cnt)
  266. *return_cnt = 0;
  267. return util::none;
  268. }
  269. static util::Optional<Mixed> not_found(size_t*) noexcept
  270. {
  271. return util::none;
  272. }
  273. };
  274. template <class T>
  275. struct AverageHelper<T, std::void_t<ColumnSumType<T>>> {
  276. template <class U>
  277. static util::Optional<Mixed> eval(U& tree, size_t* return_cnt)
  278. {
  279. size_t count = 0;
  280. auto result = Mixed{bptree_average<T>(tree, &count)};
  281. if (return_cnt) {
  282. *return_cnt = count;
  283. }
  284. return count == 0 ? util::none : result;
  285. }
  286. static util::Optional<Mixed> not_found(size_t* return_cnt) noexcept
  287. {
  288. if (return_cnt)
  289. *return_cnt = 0;
  290. return Mixed{};
  291. }
  292. };
  293. /// Convenience base class for collections, which implements most of the
  294. /// relevant interfaces for a collection that is bound to an object accessor and
  295. /// representable as a BPlusTree<T>.
  296. template <class Interface>
  297. class CollectionBaseImpl : public Interface, protected ArrayParent {
  298. public:
  299. static_assert(std::is_base_of_v<CollectionBase, Interface>);
  300. // Overriding members of CollectionBase:
  301. ColKey get_col_key() const noexcept final
  302. {
  303. return m_col_key;
  304. }
  305. const Obj& get_obj() const noexcept final
  306. {
  307. return m_obj;
  308. }
  309. /// Returns true if the accessor has changed since the last time
  310. /// `has_changed()` was called.
  311. ///
  312. /// Note: This method is not idempotent.
  313. ///
  314. /// Note: This involves a call to `update_if_needed()`.
  315. ///
  316. /// Note: This function does not return true for an accessor that became
  317. /// detached since the last call, even though it may look to the caller as
  318. /// if the size of the collection suddenly became zero.
  319. bool has_changed() const final
  320. {
  321. // `has_changed()` sneakily modifies internal state.
  322. update_if_needed();
  323. if (m_last_content_version != m_content_version) {
  324. m_last_content_version = m_content_version;
  325. return true;
  326. }
  327. return false;
  328. }
  329. using Interface::get_owner_key;
  330. using Interface::get_table;
  331. using Interface::get_target_table;
  332. protected:
  333. Obj m_obj;
  334. ColKey m_col_key;
  335. bool m_nullable = false;
  336. mutable uint_fast64_t m_content_version = 0;
  337. // Content version used by `has_changed()`.
  338. mutable uint_fast64_t m_last_content_version = 0;
  339. CollectionBaseImpl() = default;
  340. CollectionBaseImpl(const CollectionBaseImpl& other) = default;
  341. CollectionBaseImpl(CollectionBaseImpl&& other) = default;
  342. CollectionBaseImpl(const Obj& obj, ColKey col_key) noexcept
  343. : m_obj(obj)
  344. , m_col_key(col_key)
  345. , m_nullable(col_key.is_nullable())
  346. {
  347. }
  348. CollectionBaseImpl& operator=(const CollectionBaseImpl& other) = default;
  349. CollectionBaseImpl& operator=(CollectionBaseImpl&& other) = default;
  350. /// Refresh the associated `Obj` (if needed), and update the internal
  351. /// content version number. This is meant to be called from a derived class
  352. /// before accessing its data.
  353. ///
  354. /// If the `Obj` changed since the last call, or the content version was
  355. /// bumped, this returns `UpdateStatus::Updated`. In response, the caller
  356. /// must invoke `init_from_parent()` or similar on its internal state
  357. /// accessors to refresh its view of the data.
  358. ///
  359. /// If the owning object (or parent container) was deleted, this returns
  360. /// `UpdateStatus::Detached`, and the caller is allowed to enter a
  361. /// degenerate state.
  362. ///
  363. /// If no change has happened to the data, this function returns
  364. /// `UpdateStatus::NoChange`, and the caller is allowed to not do anything.
  365. virtual UpdateStatus update_if_needed() const
  366. {
  367. UpdateStatus status = m_obj.update_if_needed_with_status();
  368. if (status != UpdateStatus::Detached) {
  369. auto content_version = m_obj.get_alloc().get_content_version();
  370. if (content_version != m_content_version) {
  371. m_content_version = content_version;
  372. status = UpdateStatus::Updated;
  373. }
  374. }
  375. return status;
  376. }
  377. /// Refresh the associated `Obj` (if needed) and ensure that the
  378. /// collection is created. Must be used in places where you
  379. /// modify a potentially detached collection.
  380. ///
  381. /// The caller must react to the `UpdateStatus` in the same way as with
  382. /// `update_if_needed()`, i.e., eventually end up calling
  383. /// `init_from_parent()` or similar.
  384. ///
  385. /// Throws if the owning object no longer exists. Note: This means that this
  386. /// method will never return `UpdateStatus::Detached`.
  387. virtual UpdateStatus ensure_created()
  388. {
  389. bool changed = m_obj.update_if_needed(); // Throws if the object does not exist.
  390. auto content_version = m_obj.get_alloc().get_content_version();
  391. if (changed || content_version != m_content_version) {
  392. m_content_version = content_version;
  393. return UpdateStatus::Updated;
  394. }
  395. return UpdateStatus::NoChange;
  396. }
  397. void bump_content_version()
  398. {
  399. m_content_version = m_obj.bump_content_version();
  400. }
  401. /// Reset the accessor's tracking of the content version. Derived classes
  402. /// may choose to call this to force the accessor to become out of date,
  403. /// such that `update_if_needed()` returns `UpdateStatus::Updated` the next
  404. /// time it is called (or `UpdateStatus::Detached` if the data vanished in
  405. /// the meantime).
  406. void reset_content_version()
  407. {
  408. m_content_version = 0;
  409. }
  410. // Overriding ArrayParent interface:
  411. ref_type get_child_ref(size_t child_ndx) const noexcept final
  412. {
  413. static_cast<void>(child_ndx);
  414. try {
  415. return to_ref(m_obj._get<int64_t>(m_col_key.get_index()));
  416. }
  417. catch (const KeyNotFound&) {
  418. return ref_type(0);
  419. }
  420. }
  421. void update_child_ref(size_t child_ndx, ref_type new_ref) final
  422. {
  423. static_cast<void>(child_ndx);
  424. m_obj.set_int(m_col_key, from_ref(new_ref));
  425. }
  426. };
  427. namespace _impl {
  428. /// Translate from condensed index to uncondensed index in collections that hide
  429. /// tombstones.
  430. size_t virtual2real(const std::vector<size_t>& vec, size_t ndx) noexcept;
  431. size_t virtual2real(const BPlusTree<ObjKey>* tree, size_t ndx) noexcept;
  432. /// Translate from uncondensed index to condensed into in collections that hide
  433. /// tombstones.
  434. size_t real2virtual(const std::vector<size_t>& vec, size_t ndx) noexcept;
  435. /// Rebuild the list of unresolved keys for tombstone handling.
  436. void update_unresolved(std::vector<size_t>& vec, const BPlusTree<ObjKey>* tree);
  437. /// Clear the context flag on the tree if there are no more unresolved links.
  438. void check_for_last_unresolved(BPlusTree<ObjKey>* tree);
  439. /// Proxy class needed because the ObjList interface clobbers method names from
  440. /// CollectionBase.
  441. struct ObjListProxy : ObjList {
  442. virtual TableRef proxy_get_target_table() const = 0;
  443. TableRef get_target_table() const final
  444. {
  445. return proxy_get_target_table();
  446. }
  447. };
  448. } // namespace _impl
  449. /// Base class for collections of objects, where unresolved links (tombstones)
  450. /// can occur.
  451. template <class Interface>
  452. class ObjCollectionBase : public Interface, public _impl::ObjListProxy {
  453. public:
  454. static_assert(std::is_base_of_v<CollectionBase, Interface>);
  455. using Interface::get_col_key;
  456. using Interface::get_obj;
  457. using Interface::get_table;
  458. using Interface::is_attached;
  459. using Interface::size;
  460. // Overriding methods in ObjList:
  461. void get_dependencies(TableVersions& versions) const final
  462. {
  463. if (is_attached()) {
  464. auto table = this->get_table();
  465. versions.emplace_back(table->get_key(), table->get_content_version());
  466. }
  467. }
  468. void sync_if_needed() const final
  469. {
  470. update_if_needed();
  471. }
  472. bool is_in_sync() const noexcept final
  473. {
  474. return true;
  475. }
  476. bool has_unresolved() const noexcept
  477. {
  478. update_if_needed();
  479. return m_unresolved.size() != 0;
  480. }
  481. using Interface::get_target_table;
  482. protected:
  483. ObjCollectionBase() = default;
  484. ObjCollectionBase(const ObjCollectionBase&) = default;
  485. ObjCollectionBase(ObjCollectionBase&&) = default;
  486. ObjCollectionBase& operator=(const ObjCollectionBase&) = default;
  487. ObjCollectionBase& operator=(ObjCollectionBase&&) = default;
  488. /// Implementations should call `update_if_needed()` on their inner accessor
  489. /// (without `update_unresolved()`).
  490. virtual UpdateStatus do_update_if_needed() const = 0;
  491. /// Implementations should return a non-const reference to their internal
  492. /// `BPlusTree<T>`.
  493. virtual BPlusTree<ObjKey>* get_mutable_tree() const = 0;
  494. /// Implements update_if_needed() in a way that ensures the consistency of
  495. /// the unresolved list. Derived classes should call this instead of calling
  496. /// `update_if_needed()` on their inner accessor.
  497. UpdateStatus update_if_needed() const
  498. {
  499. auto status = do_update_if_needed();
  500. update_unresolved(status);
  501. return status;
  502. }
  503. /// Translate from condensed index to uncondensed.
  504. size_t virtual2real(size_t ndx) const noexcept
  505. {
  506. return _impl::virtual2real(m_unresolved, ndx);
  507. }
  508. /// Translate from uncondensed index to condensed.
  509. size_t real2virtual(size_t ndx) const noexcept
  510. {
  511. return _impl::real2virtual(m_unresolved, ndx);
  512. }
  513. bool real_is_unresolved(size_t ndx) const noexcept
  514. {
  515. return std::find(m_unresolved.begin(), m_unresolved.end(), ndx) != m_unresolved.end();
  516. }
  517. /// Rebuild the list of tombstones if there is a possibility that it has
  518. /// changed.
  519. ///
  520. /// If the accessor became detached, this clears the unresolved list.
  521. void update_unresolved(UpdateStatus status) const
  522. {
  523. switch (status) {
  524. case UpdateStatus::Detached: {
  525. clear_unresolved();
  526. break;
  527. }
  528. case UpdateStatus::Updated: {
  529. _impl::update_unresolved(m_unresolved, get_mutable_tree());
  530. break;
  531. }
  532. case UpdateStatus::NoChange:
  533. break;
  534. }
  535. }
  536. /// When a tombstone is removed from a list, call this to update internal
  537. /// flags that indicate the presence of tombstones.
  538. void check_for_last_unresolved()
  539. {
  540. _impl::check_for_last_unresolved(get_mutable_tree());
  541. }
  542. /// Clear the list of tombstones. It will be rebuilt the next time
  543. /// `update_if_needed()` is called.
  544. void clear_unresolved() const noexcept
  545. {
  546. m_unresolved.clear();
  547. }
  548. /// Return the number of tombstones.
  549. size_t num_unresolved() const noexcept
  550. {
  551. return m_unresolved.size();
  552. }
  553. private:
  554. // Sorted set of indices containing unresolved links.
  555. mutable std::vector<size_t> m_unresolved;
  556. TableRef proxy_get_target_table() const final
  557. {
  558. return Interface::get_target_table();
  559. }
  560. bool matches(const ObjList& other) const final
  561. {
  562. return get_owning_obj().get_key() == other.get_owning_obj().get_key() &&
  563. get_owning_col_key() == other.get_owning_col_key();
  564. }
  565. Obj get_owning_obj() const final
  566. {
  567. return get_obj();
  568. }
  569. ColKey get_owning_col_key() const final
  570. {
  571. return get_col_key();
  572. }
  573. };
  574. /// Random-access iterator over elements of a collection.
  575. ///
  576. /// Values are cached into a member variable in order to support `operator->`
  577. /// and `operator*` returning a pointer and a reference, respectively.
  578. template <class L>
  579. struct CollectionIterator {
  580. using iterator_category = std::random_access_iterator_tag;
  581. using value_type = typename L::value_type;
  582. using difference_type = ptrdiff_t;
  583. using pointer = const value_type*;
  584. using reference = const value_type&;
  585. CollectionIterator(const L* l, size_t ndx) noexcept
  586. : m_list(l)
  587. , m_ndx(ndx)
  588. {
  589. }
  590. pointer operator->() const
  591. {
  592. if constexpr (std::is_same_v<L, CollectionBase>) {
  593. m_val = m_list->get_any(m_ndx);
  594. }
  595. else {
  596. m_val = m_list->get(m_ndx);
  597. }
  598. return &m_val;
  599. }
  600. reference operator*() const
  601. {
  602. return *operator->();
  603. }
  604. CollectionIterator& operator++() noexcept
  605. {
  606. ++m_ndx;
  607. return *this;
  608. }
  609. CollectionIterator operator++(int) noexcept
  610. {
  611. auto tmp = *this;
  612. operator++();
  613. return tmp;
  614. }
  615. CollectionIterator& operator--() noexcept
  616. {
  617. --m_ndx;
  618. return *this;
  619. }
  620. CollectionIterator operator--(int) noexcept
  621. {
  622. auto tmp = *this;
  623. operator--();
  624. return tmp;
  625. }
  626. CollectionIterator& operator+=(ptrdiff_t n) noexcept
  627. {
  628. m_ndx += n;
  629. return *this;
  630. }
  631. CollectionIterator& operator-=(ptrdiff_t n) noexcept
  632. {
  633. m_ndx -= n;
  634. return *this;
  635. }
  636. friend ptrdiff_t operator-(const CollectionIterator& lhs, const CollectionIterator& rhs) noexcept
  637. {
  638. return ptrdiff_t(lhs.m_ndx) - ptrdiff_t(rhs.m_ndx);
  639. }
  640. friend CollectionIterator operator+(CollectionIterator lhs, ptrdiff_t rhs) noexcept
  641. {
  642. lhs.m_ndx += rhs;
  643. return lhs;
  644. }
  645. friend CollectionIterator operator+(ptrdiff_t lhs, CollectionIterator rhs) noexcept
  646. {
  647. return rhs + lhs;
  648. }
  649. bool operator!=(const CollectionIterator& rhs) const noexcept
  650. {
  651. REALM_ASSERT_DEBUG(m_list == rhs.m_list);
  652. return m_ndx != rhs.m_ndx;
  653. }
  654. bool operator==(const CollectionIterator& rhs) const noexcept
  655. {
  656. REALM_ASSERT_DEBUG(m_list == rhs.m_list);
  657. return m_ndx == rhs.m_ndx;
  658. }
  659. size_t index() const noexcept
  660. {
  661. return m_ndx;
  662. }
  663. private:
  664. mutable value_type m_val;
  665. const L* m_list;
  666. size_t m_ndx = size_t(-1);
  667. };
  668. inline CollectionIterator<CollectionBase> CollectionBase::begin() const
  669. {
  670. return CollectionIterator<CollectionBase>(this, 0);
  671. }
  672. inline CollectionIterator<CollectionBase> CollectionBase::end() const
  673. {
  674. return CollectionIterator<CollectionBase>(this, size());
  675. }
  676. namespace _impl {
  677. size_t get_collection_size_from_ref(ref_type, Allocator& alloc);
  678. }
  679. } // namespace realm
  680. #endif // REALM_COLLECTION_HPP