changeset.hpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. #ifndef REALM_SYNC_CHANGESET_HPP
  2. #define REALM_SYNC_CHANGESET_HPP
  3. #include <realm/sync/instructions.hpp>
  4. #include <realm/util/optional.hpp>
  5. #include <realm/util/allocation_metrics.hpp>
  6. #include <realm/util/metered/vector.hpp>
  7. #include <type_traits>
  8. namespace realm {
  9. namespace sync {
  10. using InternStrings = util::metered::vector<StringBufferRange>;
  11. struct BadChangesetError : ExceptionWithBacktrace<std::runtime_error> {
  12. using ExceptionWithBacktrace<std::runtime_error>::ExceptionWithBacktrace;
  13. };
  14. struct Changeset {
  15. struct Range;
  16. using timestamp_type = uint_fast64_t;
  17. using file_ident_type = uint_fast64_t;
  18. using version_type = uint_fast64_t; // FIXME: Get from `History`.
  19. using StringBuffer = util::BasicStringBuffer<util::MeteredAllocator>;
  20. Changeset();
  21. struct share_buffers_tag {
  22. };
  23. Changeset(const Changeset&, share_buffers_tag);
  24. Changeset(Changeset&&) = default;
  25. Changeset& operator=(Changeset&&) = default;
  26. Changeset(const Changeset&) = delete;
  27. Changeset& operator=(const Changeset&) = delete;
  28. InternString intern_string(StringData); // Slow!
  29. InternString find_string(StringData) const noexcept; // Slow!
  30. StringData string_data() const noexcept;
  31. StringBuffer& string_buffer() noexcept;
  32. const StringBuffer& string_buffer() const noexcept;
  33. const InternStrings& interned_strings() const noexcept;
  34. InternStrings& interned_strings() noexcept;
  35. StringBufferRange get_intern_string(InternString) const noexcept;
  36. util::Optional<StringBufferRange> try_get_intern_string(InternString) const noexcept;
  37. util::Optional<StringData> try_get_string(StringBufferRange) const noexcept;
  38. util::Optional<StringData> try_get_string(InternString) const noexcept;
  39. StringData get_string(StringBufferRange) const noexcept;
  40. StringData get_string(InternString) const noexcept;
  41. StringBufferRange append_string(StringData);
  42. PrimaryKey get_key(const Instruction::PrimaryKey& value) const noexcept;
  43. std::ostream& print_value(std::ostream& os, const Instruction::Payload& value) const noexcept;
  44. std::ostream& print_path(std::ostream& os, const Instruction::Path& value) const noexcept;
  45. std::ostream& print_path(std::ostream& os, InternString table, const Instruction::PrimaryKey& pk,
  46. util::Optional<InternString> field = util::none,
  47. const Instruction::Path* path = nullptr) const;
  48. /// Mark the changeset as "dirty" (i.e. modified by the merge algorithm).
  49. void set_dirty(bool dirty = true) noexcept;
  50. /// Whether or not the changeset is "dirty" (i.e. has been modified by the
  51. /// merge algorithm).
  52. bool is_dirty() const noexcept;
  53. // Interface to imitate std::vector:
  54. template <bool is_const>
  55. struct IteratorImpl;
  56. using iterator = IteratorImpl<false>;
  57. using const_iterator = IteratorImpl<true>;
  58. using value_type = Instruction;
  59. iterator begin() noexcept;
  60. iterator end() noexcept;
  61. const_iterator begin() const noexcept;
  62. const_iterator end() const noexcept;
  63. const_iterator cbegin() const noexcept;
  64. const_iterator cend() const noexcept;
  65. bool empty() const noexcept;
  66. /// Size of the Changeset, not counting tombstones.
  67. ///
  68. /// FIXME: This is an O(n) operation.
  69. size_t size() const noexcept;
  70. void clear() noexcept;
  71. //@{
  72. /// Insert instructions, invalidating all iterators.
  73. iterator insert(const_iterator pos, Instruction);
  74. template <class InputIt>
  75. iterator insert(const_iterator pos, InputIt begin, InputIt end);
  76. //@}
  77. /// Erase an instruction, invalidating all iterators.
  78. iterator erase(const_iterator);
  79. /// Insert an instruction at the end, invalidating all iterators.
  80. void push_back(const Instruction&);
  81. //@{
  82. /// Insert instructions at \a position without invalidating other
  83. /// iterators.
  84. ///
  85. /// Only iterators created before any call to `insert_stable()` may be
  86. /// considered stable across calls to `insert_stable()`. In addition,
  87. /// "iterator stability" has a very specific meaning here: Other copies of
  88. /// \a position in the program will point to the newly inserted elements
  89. /// after calling `insert_stable()`, rather than point to the value at the
  90. /// position prior to insertion. This is different from, say, a tree
  91. /// structure, where iterator stability signifies the property that
  92. /// iterators keep pointing to the same element after insertion before or
  93. /// after that position.
  94. ///
  95. /// For the purpose of supporting `ChangesetIndex`, and the OT merge
  96. /// algorithm, these semantics are acceptable, since prepended instructions
  97. /// can never create new object or table references.
  98. iterator insert_stable(const_iterator position, Instruction);
  99. template <class InputIt>
  100. iterator insert_stable(const_iterator position, InputIt begin, InputIt end);
  101. //@}
  102. /// Erase instruction at \a position without invalidating other iterators.
  103. /// If erasing the object would invalidate other iterators, it is turned
  104. /// into a tombstone instead, and subsequent derefencing of the iterator
  105. /// will return `nullptr`. An iterator pointing to a tombstone remains valid
  106. /// and can be incremented.
  107. ///
  108. /// Only iterators created before any call to `insert_stable()` may be
  109. /// considered stable across calls to `erase_stable()`. If other copies of
  110. /// \a position exist in the program, they will either point to the
  111. /// subsequent element if that element was previously inserted with
  112. /// `insert_stable()`, or otherwise it will be turned into a tombstone.
  113. iterator erase_stable(const_iterator position);
  114. #if REALM_DEBUG
  115. struct Reflector;
  116. struct Printer;
  117. void verify() const;
  118. void print(std::ostream&) const;
  119. void print() const; // prints to std::err
  120. #endif
  121. /// The version that this changeset produced. Note: This may not be the
  122. /// version produced by this changeset on the client on which this changeset
  123. /// originated, but may for instance be the version produced on the server
  124. /// after receiving and re-sending this changeset to another client.
  125. ///
  126. /// FIXME: The explanation above is confusing. The truth is that if this
  127. /// changeset was received by a client from the server, then \a version is
  128. /// the version that was produced on the server by this changeset.
  129. ///
  130. /// FIXME: This property, as well as \a last_integrated_remote_version, \a
  131. /// origin_timestamp, and \a origin_file_ident should probably be removed
  132. /// from this class, as they are not a logical part of a changeset, and also
  133. /// are difficult to document without knowing more about what context the
  134. /// changeset object occurs. Also, functions such as
  135. /// InstructionApplier::apply() that a changeset as argument, but do not
  136. /// care about those properties.
  137. version_type version = 0;
  138. /// On clients, the last integrated server version. On the server, this is
  139. /// the last integrated client version.
  140. ///
  141. /// FIXME: The explanation above is confusing. The truth is that if this
  142. /// changeset was received by a client from the server, then \a
  143. /// last_integrated_remote_version is the last client version that was
  144. /// integrated by the server at the server version referencened by \a
  145. /// version.
  146. version_type last_integrated_remote_version = 0;
  147. /// Timestamp at origin when the original untransformed changeset was
  148. /// produced.
  149. timestamp_type origin_timestamp = 0;
  150. /// The identifier of the file in the context of which the original
  151. /// untransformed changeset was produced.
  152. file_ident_type origin_file_ident = 0;
  153. /// Compare for exact equality, including that interned strings have the
  154. /// same integer values, and there is the same number of interned strings,
  155. /// same topology of tombstones, etc.
  156. bool operator==(const Changeset& that) const noexcept;
  157. bool operator!=(const Changeset& that) const noexcept;
  158. private:
  159. util::metered::vector<Instruction> m_instructions;
  160. std::shared_ptr<StringBuffer> m_string_buffer;
  161. std::shared_ptr<InternStrings> m_strings;
  162. bool m_is_dirty = false;
  163. iterator const_iterator_to_iterator(const_iterator);
  164. };
  165. std::ostream& operator<<(std::ostream&, const Changeset& changeset);
  166. /// An iterator type that hides the implementation details of the support for
  167. /// iterator stability.
  168. ///
  169. /// A `Changeset::iterator` is composed of an
  170. /// `std::vector<InstructionContainer>::iterator` and a `size_t` representing
  171. /// the index into the current `InstructionContainer`. If that container is
  172. /// empty, and the position is zero, the iterator is pointing to a tombstone.
  173. template <bool is_const>
  174. struct Changeset::IteratorImpl {
  175. using list_type = util::metered::vector<Instruction>;
  176. using inner_iterator_type = std::conditional_t<is_const, list_type::const_iterator, list_type::iterator>;
  177. // reference_type is a pointer because we have no way to create a reference
  178. // to a tombstone instruction. Alternatively, it could have been
  179. // `util::Optional<Instruction&>`, but that runs into other issues.
  180. using reference_type = std::conditional_t<is_const, const Instruction*, Instruction*>;
  181. using pointer_type = std::conditional_t<is_const, const Instruction*, Instruction*>;
  182. using difference_type = std::ptrdiff_t;
  183. IteratorImpl()
  184. : m_pos(0)
  185. {
  186. }
  187. template <bool is_const_ = is_const>
  188. IteratorImpl(const IteratorImpl<false>& other, std::enable_if_t<is_const_>* = nullptr)
  189. : m_inner(other.m_inner)
  190. , m_pos(other.m_pos)
  191. {
  192. }
  193. IteratorImpl(inner_iterator_type inner, size_t pos = 0)
  194. : m_inner(inner)
  195. , m_pos(pos)
  196. {
  197. }
  198. inline IteratorImpl& operator++()
  199. {
  200. ++m_pos;
  201. if (m_pos >= m_inner->size()) {
  202. ++m_inner;
  203. m_pos = 0;
  204. }
  205. return *this;
  206. }
  207. IteratorImpl operator++(int)
  208. {
  209. auto copy = *this;
  210. ++(*this);
  211. return copy;
  212. }
  213. IteratorImpl& operator--()
  214. {
  215. if (m_pos == 0) {
  216. --m_inner;
  217. m_pos = m_inner->size();
  218. if (m_pos != 0)
  219. --m_pos;
  220. }
  221. else {
  222. --m_pos;
  223. }
  224. return *this;
  225. }
  226. IteratorImpl operator--(int)
  227. {
  228. auto copy = *this;
  229. --(*this);
  230. return copy;
  231. }
  232. reference_type operator*() const
  233. {
  234. if (m_inner->size()) {
  235. return &m_inner->at(m_pos);
  236. }
  237. // It was a tombstone.
  238. return nullptr;
  239. }
  240. pointer_type operator->() const
  241. {
  242. if (m_inner->size()) {
  243. return &m_inner->at(m_pos);
  244. }
  245. // It was a tombstone.
  246. return nullptr;
  247. }
  248. bool operator==(const IteratorImpl& other) const
  249. {
  250. return m_inner == other.m_inner && m_pos == other.m_pos;
  251. }
  252. bool operator!=(const IteratorImpl& other) const
  253. {
  254. return !(*this == other);
  255. }
  256. bool operator<(const IteratorImpl& other) const
  257. {
  258. if (m_inner == other.m_inner)
  259. return m_pos < other.m_pos;
  260. return m_inner < other.m_inner;
  261. }
  262. bool operator<=(const IteratorImpl& other) const
  263. {
  264. if (m_inner == other.m_inner)
  265. return m_pos <= other.m_pos;
  266. return m_inner < other.m_inner;
  267. }
  268. bool operator>(const IteratorImpl& other) const
  269. {
  270. if (m_inner == other.m_inner)
  271. return m_pos > other.m_pos;
  272. return m_inner > other.m_inner;
  273. }
  274. bool operator>=(const IteratorImpl& other) const
  275. {
  276. if (m_inner == other.m_inner)
  277. return m_pos >= other.m_pos;
  278. return m_inner > other.m_inner;
  279. }
  280. inner_iterator_type m_inner;
  281. size_t m_pos;
  282. };
  283. struct Changeset::Range {
  284. iterator begin;
  285. iterator end;
  286. };
  287. #if REALM_DEBUG
  288. struct Changeset::Reflector {
  289. struct Tracer {
  290. virtual void name(StringData) = 0;
  291. virtual void path(StringData, InternString table, const Instruction::PrimaryKey& object_key,
  292. util::Optional<InternString> field, const Instruction::Path* path) = 0;
  293. virtual void field(StringData, InternString) = 0;
  294. virtual void field(StringData, Instruction::Payload::Type) = 0;
  295. virtual void field(StringData, Instruction::AddColumn::CollectionType) = 0;
  296. virtual void field(StringData, const Instruction::PrimaryKey&) = 0;
  297. virtual void field(StringData, const Instruction::Payload&) = 0;
  298. virtual void field(StringData, const Instruction::Path&) = 0;
  299. virtual void field(StringData, uint32_t) = 0;
  300. virtual void set_changeset(const Changeset*) = 0;
  301. virtual void after_each() {}
  302. virtual void before_each() {}
  303. };
  304. Reflector(Tracer& tracer, const Changeset& changeset)
  305. : m_tracer(tracer)
  306. , m_changeset(changeset)
  307. {
  308. tracer.set_changeset(&changeset);
  309. }
  310. void visit_all() const;
  311. private:
  312. Tracer& m_tracer;
  313. const Changeset& m_changeset;
  314. void table_instr(const Instruction::TableInstruction&) const;
  315. void object_instr(const Instruction::ObjectInstruction&) const;
  316. void path_instr(const Instruction::PathInstruction&) const;
  317. friend struct Instruction;
  318. #define REALM_DEFINE_REFLECTOR_VISITOR(X) void operator()(const Instruction::X&) const;
  319. REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_REFLECTOR_VISITOR)
  320. #undef REALM_DEFINE_REFLECTOR_VISITOR
  321. };
  322. struct Changeset::Printer : Changeset::Reflector::Tracer {
  323. explicit Printer(std::ostream& os)
  324. : m_out(os)
  325. {
  326. }
  327. // ChangesetReflector::Tracer interface:
  328. void name(StringData) final;
  329. void path(StringData, InternString table, const Instruction::PrimaryKey&, util::Optional<InternString> field,
  330. const Instruction::Path* path) final;
  331. void field(StringData, InternString) final;
  332. void field(StringData, Instruction::Payload::Type) final;
  333. void field(StringData, Instruction::AddColumn::CollectionType) final;
  334. void field(StringData, const Instruction::PrimaryKey&) final;
  335. void field(StringData, const Instruction::Payload&) final;
  336. void field(StringData, const Instruction::Path&) final;
  337. void field(StringData, uint32_t) final;
  338. void set_changeset(const Changeset* changeset) final
  339. {
  340. m_changeset = changeset;
  341. }
  342. void after_each() final;
  343. private:
  344. std::ostream& m_out;
  345. bool m_first = true;
  346. const Changeset* m_changeset = nullptr;
  347. void pad_or_ellipsis(StringData, int width) const;
  348. void print_field(StringData name, std::string value);
  349. std::string primary_key_to_string(const Instruction::PrimaryKey&);
  350. };
  351. #endif // REALM_DEBUG
  352. /// Implementation:
  353. inline Changeset::iterator Changeset::begin() noexcept
  354. {
  355. return m_instructions.begin();
  356. }
  357. inline Changeset::iterator Changeset::end() noexcept
  358. {
  359. return m_instructions.end();
  360. }
  361. inline Changeset::const_iterator Changeset::begin() const noexcept
  362. {
  363. return m_instructions.begin();
  364. }
  365. inline Changeset::const_iterator Changeset::end() const noexcept
  366. {
  367. return m_instructions.end();
  368. }
  369. inline Changeset::const_iterator Changeset::cbegin() const noexcept
  370. {
  371. return m_instructions.cbegin();
  372. }
  373. inline Changeset::const_iterator Changeset::cend() const noexcept
  374. {
  375. return m_instructions.end();
  376. }
  377. inline bool Changeset::empty() const noexcept
  378. {
  379. return size() == 0;
  380. }
  381. inline size_t Changeset::size() const noexcept
  382. {
  383. size_t sum = 0;
  384. for (auto& x : m_instructions)
  385. sum += x.size();
  386. return sum;
  387. }
  388. inline void Changeset::clear() noexcept
  389. {
  390. m_instructions.clear();
  391. }
  392. inline util::Optional<StringBufferRange> Changeset::try_get_intern_string(InternString string) const noexcept
  393. {
  394. if (string.value >= m_strings->size())
  395. return util::none;
  396. return (*m_strings)[string.value];
  397. }
  398. inline StringBufferRange Changeset::get_intern_string(InternString string) const noexcept
  399. {
  400. auto str = try_get_intern_string(string);
  401. REALM_ASSERT(str);
  402. return *str;
  403. }
  404. inline InternStrings& Changeset::interned_strings() noexcept
  405. {
  406. return *m_strings;
  407. }
  408. inline const InternStrings& Changeset::interned_strings() const noexcept
  409. {
  410. return *m_strings;
  411. }
  412. inline auto Changeset::string_buffer() noexcept -> StringBuffer&
  413. {
  414. return *m_string_buffer;
  415. }
  416. inline auto Changeset::string_buffer() const noexcept -> const StringBuffer&
  417. {
  418. return *m_string_buffer;
  419. }
  420. inline util::Optional<StringData> Changeset::try_get_string(StringBufferRange range) const noexcept
  421. {
  422. if (range.offset > m_string_buffer->size())
  423. return util::none;
  424. if (range.offset + range.size > m_string_buffer->size())
  425. return util::none;
  426. return StringData{m_string_buffer->data() + range.offset, range.size};
  427. }
  428. inline util::Optional<StringData> Changeset::try_get_string(InternString str) const noexcept
  429. {
  430. if (auto range = try_get_intern_string(str)) {
  431. return try_get_string(*range);
  432. }
  433. return util::none;
  434. }
  435. inline StringData Changeset::get_string(StringBufferRange range) const noexcept
  436. {
  437. auto string = try_get_string(range);
  438. REALM_ASSERT(string);
  439. return *string;
  440. }
  441. inline StringData Changeset::get_string(InternString string) const noexcept
  442. {
  443. return get_string(get_intern_string(string));
  444. }
  445. inline StringData Changeset::string_data() const noexcept
  446. {
  447. return StringData{m_string_buffer->data(), m_string_buffer->size()};
  448. }
  449. inline StringBufferRange Changeset::append_string(StringData string)
  450. {
  451. m_string_buffer->reserve(1024); // we expect more strings
  452. size_t offset = m_string_buffer->size();
  453. m_string_buffer->append(string.data(), string.size());
  454. return StringBufferRange{uint32_t(offset), uint32_t(string.size())};
  455. }
  456. inline bool Changeset::is_dirty() const noexcept
  457. {
  458. return m_is_dirty;
  459. }
  460. inline void Changeset::set_dirty(bool dirty) noexcept
  461. {
  462. m_is_dirty = dirty;
  463. }
  464. inline Changeset::iterator Changeset::insert(const_iterator pos, Instruction instr)
  465. {
  466. Instruction* p = &instr;
  467. return insert(pos, p, p + 1);
  468. }
  469. template <class InputIt>
  470. inline Changeset::iterator Changeset::insert(const_iterator pos, InputIt begin, InputIt end)
  471. {
  472. if (pos.m_pos == 0)
  473. return m_instructions.insert(pos.m_inner, begin, end);
  474. return insert_stable(pos, begin, end);
  475. }
  476. inline Changeset::iterator Changeset::erase(const_iterator pos)
  477. {
  478. if (pos.m_inner->size() <= 1)
  479. return m_instructions.erase(pos.m_inner);
  480. return erase_stable(pos);
  481. }
  482. inline Changeset::iterator Changeset::insert_stable(const_iterator pos, Instruction instr)
  483. {
  484. Instruction* p = &instr;
  485. return insert_stable(pos, p, p + 1);
  486. }
  487. template <class InputIt>
  488. inline Changeset::iterator Changeset::insert_stable(const_iterator cpos, InputIt begin, InputIt end)
  489. {
  490. iterator pos = const_iterator_to_iterator(cpos);
  491. size_t i = 0;
  492. for (auto it = begin; it != end; ++it, ++i) {
  493. pos.m_inner->insert(pos.m_pos + i, *it);
  494. }
  495. return pos;
  496. }
  497. inline Changeset::iterator Changeset::erase_stable(const_iterator cpos)
  498. {
  499. auto pos = const_iterator_to_iterator(cpos);
  500. auto begin = m_instructions.begin();
  501. auto end = m_instructions.end();
  502. REALM_ASSERT(pos.m_inner >= begin);
  503. REALM_ASSERT(pos.m_inner < end);
  504. pos.m_inner->erase(pos.m_pos);
  505. if (pos.m_pos >= pos.m_inner->size()) {
  506. do {
  507. ++pos.m_inner;
  508. } while (pos.m_inner != end && pos.m_inner->is_empty());
  509. pos.m_pos = 0;
  510. }
  511. return pos;
  512. }
  513. inline void Changeset::push_back(const Instruction& instr)
  514. {
  515. m_instructions.emplace_back(instr);
  516. }
  517. inline auto Changeset::const_iterator_to_iterator(const_iterator cpos) -> iterator
  518. {
  519. size_t offset = cpos.m_inner - m_instructions.cbegin();
  520. return iterator{m_instructions.begin() + offset, cpos.m_pos};
  521. }
  522. inline bool Changeset::operator!=(const Changeset& that) const noexcept
  523. {
  524. return !(*this == that);
  525. }
  526. } // namespace sync
  527. } // namespace realm
  528. namespace std {
  529. template <bool is_const>
  530. struct iterator_traits<realm::sync::Changeset::IteratorImpl<is_const>> {
  531. using difference_type = std::ptrdiff_t;
  532. using iterator_category = std::bidirectional_iterator_tag;
  533. };
  534. } // namespace std
  535. #endif // REALM_SYNC_CHANGESET_HPP