instructions.hpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176
  1. #ifndef REALM_IMPL_INSTRUCTIONS_HPP
  2. #define REALM_IMPL_INSTRUCTIONS_HPP
  3. #include <iosfwd> // string conversion, debug prints
  4. #include <memory> // shared_ptr
  5. #include <type_traits>
  6. #include <unordered_map>
  7. #include <vector>
  8. #include <external/mpark/variant.hpp>
  9. #include <realm/binary_data.hpp>
  10. #include <realm/data_type.hpp>
  11. #include <realm/string_data.hpp>
  12. #include <realm/sync/object_id.hpp>
  13. #include <realm/table_ref.hpp>
  14. #include <realm/timestamp.hpp>
  15. #include <realm/util/input_stream.hpp>
  16. #include <realm/util/overload.hpp>
  17. namespace realm {
  18. namespace sync {
  19. #define REALM_FOR_EACH_INSTRUCTION_TYPE(X) \
  20. X(AddTable) \
  21. X(EraseTable) \
  22. X(AddColumn) \
  23. X(EraseColumn) \
  24. X(CreateObject) \
  25. X(EraseObject) \
  26. X(Update) \
  27. X(AddInteger) \
  28. X(ArrayInsert) \
  29. X(ArrayMove) \
  30. X(ArrayErase) \
  31. X(Clear) \
  32. X(SetInsert) \
  33. X(SetErase)
  34. struct StringBufferRange {
  35. uint32_t offset, size;
  36. friend bool operator==(const StringBufferRange& lhs, const StringBufferRange& rhs) noexcept
  37. {
  38. return lhs.offset == rhs.offset && lhs.size == rhs.size;
  39. }
  40. };
  41. struct InternString {
  42. static const InternString npos;
  43. explicit constexpr InternString(uint32_t v = uint32_t(-1)) noexcept
  44. : value(v)
  45. {
  46. }
  47. uint32_t value;
  48. constexpr bool operator==(const InternString& other) const noexcept
  49. {
  50. return value == other.value;
  51. }
  52. constexpr bool operator!=(const InternString& other) const noexcept
  53. {
  54. return value != other.value;
  55. }
  56. constexpr bool operator<(const InternString& other) const noexcept
  57. {
  58. return value < other.value;
  59. }
  60. explicit operator bool() const noexcept
  61. {
  62. return (value != npos.value);
  63. }
  64. };
  65. struct Instruction;
  66. namespace instr {
  67. using PrimaryKey = mpark::variant<mpark::monostate, int64_t, GlobalKey, InternString, ObjectId, UUID>;
  68. struct Path {
  69. using Element = mpark::variant<InternString, uint32_t>;
  70. // FIXME: Use a "small_vector" type for this -- most paths are very short.
  71. // Alternatively, we could use some kind of interning with copy-on-write,
  72. // but that seems complicated.
  73. std::vector<Element> m_path;
  74. size_t size() const noexcept
  75. {
  76. return m_path.size();
  77. }
  78. // If this path is referring to an element of an array (the last path
  79. // element is an integer index), return true.
  80. bool is_array_index() const noexcept
  81. {
  82. return !m_path.empty() && mpark::holds_alternative<uint32_t>(m_path.back());
  83. }
  84. uint32_t& index() noexcept
  85. {
  86. REALM_ASSERT(is_array_index());
  87. return mpark::get<uint32_t>(m_path.back());
  88. }
  89. uint32_t index() const noexcept
  90. {
  91. REALM_ASSERT(is_array_index());
  92. return mpark::get<uint32_t>(m_path.back());
  93. }
  94. Element& back() noexcept
  95. {
  96. REALM_ASSERT(!m_path.empty());
  97. return m_path.back();
  98. }
  99. const Element& back() const noexcept
  100. {
  101. REALM_ASSERT(!m_path.empty());
  102. return m_path.back();
  103. }
  104. Element& operator[](size_t idx) noexcept
  105. {
  106. REALM_ASSERT(idx < m_path.size());
  107. return m_path[idx];
  108. }
  109. const Element& operator[](size_t idx) const noexcept
  110. {
  111. REALM_ASSERT(idx < m_path.size());
  112. return m_path[idx];
  113. }
  114. void push_back(Element element)
  115. {
  116. m_path.push_back(element);
  117. }
  118. friend bool operator==(const Path& lhs, const Path& rhs) noexcept
  119. {
  120. return lhs.m_path == rhs.m_path;
  121. }
  122. using const_iterator = typename std::vector<Element>::const_iterator;
  123. const_iterator begin() const noexcept
  124. {
  125. return m_path.begin();
  126. }
  127. const_iterator end() const noexcept
  128. {
  129. return m_path.end();
  130. }
  131. };
  132. struct Payload {
  133. /// Create a new object in-place (embedded object).
  134. struct ObjectValue {
  135. };
  136. /// Create an empty dictionary in-place (does not clear an existing dictionary).
  137. struct Dictionary {
  138. };
  139. /// Sentinel value for an erased dictionary element.
  140. struct Erased {
  141. };
  142. /// Payload data types, corresponding loosely to the `DataType` enum in
  143. /// Core, but with some special values:
  144. ///
  145. /// - Null (0) indicates a NULL value of any type.
  146. /// - GlobalKey (-1) indicates an internally generated object ID.
  147. /// - ObjectValue (-2) indicates the creation of an embedded object.
  148. /// - Dictionary (-3) indicates the creation of a dictionary.
  149. /// - Erased (-4) indicates that a dictionary element should be erased.
  150. /// - Undefined (-5) indicates the
  151. ///
  152. /// Furthermore, link values for both Link and LinkList columns are
  153. /// represented by a single Link type.
  154. ///
  155. /// Note: For Mixed columns (including typed links), no separate value is required, because the
  156. /// instruction set encodes the type of each value in the instruction.
  157. enum class Type : int8_t {
  158. // Special value indicating that a dictionary element should be erased.
  159. Erased = -4,
  160. // Special value indicating that a dictionary should be created at the position.
  161. Dictionary = -3,
  162. // Special value indicating that an embedded object should be created at
  163. // the position.
  164. ObjectValue = -2,
  165. GlobalKey = -1,
  166. Null = 0,
  167. Int = 1,
  168. Bool = 2,
  169. String = 3,
  170. Binary = 4,
  171. Timestamp = 5,
  172. Float = 6,
  173. Double = 7,
  174. Decimal = 8,
  175. Link = 9,
  176. ObjectId = 10,
  177. UUID = 11,
  178. };
  179. struct Link {
  180. InternString target_table;
  181. PrimaryKey target;
  182. friend bool operator==(const Link& lhs, const Link& rhs) noexcept
  183. {
  184. return lhs.target_table == rhs.target_table && lhs.target == rhs.target;
  185. }
  186. };
  187. union Data {
  188. GlobalKey key;
  189. int64_t integer;
  190. bool boolean;
  191. StringBufferRange str;
  192. StringBufferRange binary;
  193. Timestamp timestamp;
  194. float fnum;
  195. double dnum;
  196. Decimal128 decimal;
  197. ObjectId object_id;
  198. UUID uuid;
  199. Link link;
  200. ObjLink typed_link;
  201. Data() {}
  202. };
  203. Data data;
  204. Type type;
  205. Payload()
  206. : Payload(realm::util::none)
  207. {
  208. }
  209. explicit Payload(bool value) noexcept
  210. : type(Type::Bool)
  211. {
  212. data.boolean = value;
  213. }
  214. explicit Payload(int64_t value) noexcept
  215. : type(Type::Int)
  216. {
  217. data.integer = value;
  218. }
  219. explicit Payload(float value) noexcept
  220. : type(Type::Float)
  221. {
  222. data.fnum = value;
  223. }
  224. explicit Payload(double value) noexcept
  225. : type(Type::Double)
  226. {
  227. data.dnum = value;
  228. }
  229. explicit Payload(Link value) noexcept
  230. : type(Type::Link)
  231. {
  232. data.link = value;
  233. }
  234. explicit Payload(StringBufferRange value, bool is_binary = false) noexcept
  235. : type(is_binary ? Type::Binary : Type::String)
  236. {
  237. if (is_binary) {
  238. data.binary = value;
  239. }
  240. else {
  241. data.str = value;
  242. }
  243. }
  244. explicit Payload(realm::util::None) noexcept
  245. : type(Type::Null)
  246. {
  247. }
  248. // Note: Intentionally implicit.
  249. Payload(const ObjectValue&) noexcept
  250. : type(Type::ObjectValue)
  251. {
  252. }
  253. // Note: Intentionally implicit.
  254. Payload(const Erased&) noexcept
  255. : type(Type::Erased)
  256. {
  257. }
  258. explicit Payload(Timestamp value) noexcept
  259. : type(value.is_null() ? Type::Null : Type::Timestamp)
  260. {
  261. if (value.is_null()) {
  262. type = Type::Null;
  263. }
  264. else {
  265. type = Type::Timestamp;
  266. data.timestamp = value;
  267. }
  268. }
  269. explicit Payload(ObjectId value) noexcept
  270. : type(Type::ObjectId)
  271. {
  272. data.object_id = value;
  273. }
  274. explicit Payload(Decimal128 value) noexcept
  275. {
  276. if (value.is_null()) {
  277. type = Type::Null;
  278. }
  279. else {
  280. type = Type::Decimal;
  281. data.decimal = value;
  282. }
  283. }
  284. explicit Payload(UUID value) noexcept
  285. : type(Type::UUID)
  286. {
  287. data.uuid = value;
  288. }
  289. Payload(const Payload&) noexcept = default;
  290. Payload& operator=(const Payload&) noexcept = default;
  291. bool is_null() const noexcept
  292. {
  293. return type == Type::Null;
  294. }
  295. friend bool operator==(const Payload& lhs, const Payload& rhs) noexcept
  296. {
  297. if (lhs.type == rhs.type) {
  298. switch (lhs.type) {
  299. case Type::Erased:
  300. return true;
  301. case Type::Dictionary:
  302. return true;
  303. case Type::ObjectValue:
  304. return true;
  305. case Type::GlobalKey:
  306. return lhs.data.key == rhs.data.key;
  307. case Type::Null:
  308. return true;
  309. case Type::Int:
  310. return lhs.data.integer == rhs.data.integer;
  311. case Type::Bool:
  312. return lhs.data.boolean == rhs.data.boolean;
  313. case Type::String:
  314. return lhs.data.str == rhs.data.str;
  315. case Type::Binary:
  316. return lhs.data.binary == rhs.data.binary;
  317. case Type::Timestamp:
  318. return lhs.data.timestamp == rhs.data.timestamp;
  319. case Type::Float:
  320. return lhs.data.fnum == rhs.data.fnum;
  321. case Type::Double:
  322. return lhs.data.dnum == rhs.data.dnum;
  323. case Type::Decimal:
  324. return lhs.data.decimal == rhs.data.decimal;
  325. case Type::Link:
  326. return lhs.data.link == rhs.data.link;
  327. case Type::ObjectId:
  328. return lhs.data.object_id == rhs.data.object_id;
  329. case Type::UUID:
  330. return lhs.data.uuid == rhs.data.uuid;
  331. }
  332. }
  333. return false;
  334. }
  335. friend bool operator!=(const Payload& lhs, const Payload& rhs) noexcept
  336. {
  337. return !(lhs == rhs);
  338. }
  339. };
  340. /// All instructions are TableInstructions.
  341. struct TableInstruction {
  342. InternString table;
  343. protected:
  344. bool operator==(const TableInstruction& rhs) const noexcept
  345. {
  346. return table == rhs.table;
  347. }
  348. };
  349. /// All instructions except schema instructions are ObjectInstructions.
  350. struct ObjectInstruction : TableInstruction {
  351. PrimaryKey object;
  352. protected:
  353. bool operator==(const ObjectInstruction& rhs) const noexcept
  354. {
  355. return TableInstruction::operator==(rhs) && object == rhs.object;
  356. }
  357. };
  358. /// All instructions except schema instructions and CreateObject/EraseObject are PathInstructions.
  359. struct PathInstruction : ObjectInstruction {
  360. InternString field;
  361. Path path;
  362. uint32_t& index() noexcept
  363. {
  364. return path.index();
  365. }
  366. uint32_t index() const noexcept
  367. {
  368. return path.index();
  369. }
  370. protected:
  371. bool operator==(const PathInstruction& rhs) const noexcept
  372. {
  373. return ObjectInstruction::operator==(rhs) && field == rhs.field && path == rhs.path;
  374. }
  375. };
  376. struct AddTable : TableInstruction {
  377. // Note: Tables "without" a primary key have a secret primary key of type
  378. // ObjKey. The field name of such primary keys is assumed to be "_id".
  379. struct TopLevelTable {
  380. InternString pk_field;
  381. Payload::Type pk_type;
  382. bool pk_nullable;
  383. bool is_asymmetric;
  384. bool operator==(const TopLevelTable& rhs) const noexcept
  385. {
  386. return pk_field == rhs.pk_field && pk_type == rhs.pk_type && pk_nullable == rhs.pk_nullable &&
  387. is_asymmetric == rhs.is_asymmetric;
  388. }
  389. };
  390. struct EmbeddedTable {
  391. bool operator==(const EmbeddedTable&) const noexcept
  392. {
  393. return true;
  394. }
  395. };
  396. mpark::variant<TopLevelTable, EmbeddedTable> type;
  397. bool operator==(const AddTable& rhs) const noexcept
  398. {
  399. return TableInstruction::operator==(rhs) && type == rhs.type;
  400. }
  401. };
  402. struct EraseTable : TableInstruction {
  403. using TableInstruction::TableInstruction;
  404. bool operator==(const EraseTable& rhs) const noexcept
  405. {
  406. return TableInstruction::operator==(rhs);
  407. }
  408. };
  409. struct AddColumn : TableInstruction {
  410. using TableInstruction::TableInstruction;
  411. // This is backwards compatible with previous boolean type where 0
  412. // indicated simple type and 1 indicated list.
  413. enum class CollectionType : uint8_t { Single, List, Dictionary, Set };
  414. InternString field;
  415. // `Type::Null` for Mixed columns. Mixed columns are always nullable.
  416. Payload::Type type;
  417. // `Type::Null` for other than dictionary columns
  418. Payload::Type key_type;
  419. bool nullable;
  420. // For Mixed columns, this is `none`. Mixed columns are always nullable.
  421. //
  422. // For dictionaries, this must always be `Type::String`.
  423. CollectionType collection_type;
  424. InternString link_target_table;
  425. bool operator==(const AddColumn& rhs) const noexcept
  426. {
  427. return TableInstruction::operator==(rhs) && field == rhs.field && type == rhs.type &&
  428. key_type == rhs.key_type && nullable == rhs.nullable && collection_type == rhs.collection_type &&
  429. link_target_table == rhs.link_target_table;
  430. }
  431. };
  432. struct EraseColumn : TableInstruction {
  433. using TableInstruction::TableInstruction;
  434. InternString field;
  435. bool operator==(const EraseColumn& rhs) const noexcept
  436. {
  437. return TableInstruction::operator==(rhs) && field == rhs.field;
  438. }
  439. };
  440. struct CreateObject : ObjectInstruction {
  441. using ObjectInstruction::ObjectInstruction;
  442. bool operator==(const CreateObject& rhs) const noexcept
  443. {
  444. return ObjectInstruction::operator==(rhs);
  445. }
  446. };
  447. struct EraseObject : ObjectInstruction {
  448. using ObjectInstruction::ObjectInstruction;
  449. bool operator==(const EraseObject& rhs) const noexcept
  450. {
  451. return ObjectInstruction::operator==(rhs);
  452. }
  453. };
  454. struct Update : PathInstruction {
  455. using PathInstruction::PathInstruction;
  456. // Note: For "ArrayUpdate", the path ends with an integer.
  457. Payload value;
  458. union {
  459. bool is_default; // For fields
  460. uint32_t prior_size; // For "ArrayUpdate"
  461. };
  462. Update()
  463. : prior_size(0)
  464. {
  465. }
  466. bool is_array_update() const noexcept
  467. {
  468. return path.is_array_index();
  469. }
  470. bool operator==(const Update& rhs) const noexcept
  471. {
  472. return PathInstruction::operator==(rhs) && value == rhs.value &&
  473. (is_array_update() ? prior_size == rhs.prior_size : is_default == rhs.is_default);
  474. }
  475. };
  476. struct AddInteger : PathInstruction {
  477. using PathInstruction::PathInstruction;
  478. int64_t value;
  479. bool operator==(const AddInteger& rhs) const noexcept
  480. {
  481. return PathInstruction::operator==(rhs) && value == rhs.value;
  482. }
  483. };
  484. struct ArrayInsert : PathInstruction {
  485. // Note: The insertion index is the last path component.
  486. using PathInstruction::PathInstruction;
  487. Payload value;
  488. uint32_t prior_size;
  489. bool operator==(const ArrayInsert& rhs) const noexcept
  490. {
  491. return PathInstruction::operator==(rhs) && value == rhs.value && prior_size == rhs.prior_size;
  492. }
  493. };
  494. struct ArrayMove : PathInstruction {
  495. // Note: The move-from index is the last path component.
  496. using PathInstruction::PathInstruction;
  497. uint32_t ndx_2;
  498. uint32_t prior_size;
  499. bool operator==(const ArrayMove& rhs) const noexcept
  500. {
  501. return PathInstruction::operator==(rhs) && ndx_2 == rhs.ndx_2 && prior_size == rhs.prior_size;
  502. }
  503. };
  504. struct ArrayErase : PathInstruction {
  505. // Note: The erased index is the last path component.
  506. using PathInstruction::PathInstruction;
  507. uint32_t prior_size;
  508. bool operator==(const ArrayErase& rhs) const noexcept
  509. {
  510. return PathInstruction::operator==(rhs) && prior_size == rhs.prior_size;
  511. }
  512. };
  513. struct Clear : PathInstruction {
  514. using PathInstruction::PathInstruction;
  515. bool operator==(const Clear& rhs) const noexcept
  516. {
  517. return PathInstruction::operator==(rhs);
  518. }
  519. };
  520. struct SetInsert : PathInstruction {
  521. using PathInstruction::PathInstruction;
  522. Payload value;
  523. bool operator==(const SetInsert& rhs) const noexcept
  524. {
  525. return PathInstruction::operator==(rhs) && value == rhs.value;
  526. }
  527. };
  528. struct SetErase : PathInstruction {
  529. using PathInstruction::PathInstruction;
  530. Payload value;
  531. bool operator==(const SetErase& rhs) const noexcept
  532. {
  533. return PathInstruction::operator==(rhs) && value == rhs.value;
  534. }
  535. };
  536. } // namespace instr
  537. struct Instruction {
  538. #define REALM_DECLARE_INSTRUCTION_STRUCT(X) using X = instr::X;
  539. REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DECLARE_INSTRUCTION_STRUCT)
  540. #undef REALM_DECLARE_INSTRUCTION_STRUCT
  541. using TableInstruction = instr::TableInstruction;
  542. using ObjectInstruction = instr::ObjectInstruction;
  543. using PathInstruction = instr::PathInstruction;
  544. using PrimaryKey = instr::PrimaryKey;
  545. using Payload = instr::Payload;
  546. using Path = instr::Path;
  547. using Vector = std::vector<Instruction>;
  548. // CAUTION: Any change to the enum values for the instruction types is a protocol-breaking
  549. // change!
  550. enum class Type : uint8_t {
  551. AddTable = 0,
  552. EraseTable = 1,
  553. CreateObject = 2,
  554. EraseObject = 3,
  555. Update = 4, // Note: Also covers ArrayUpdate
  556. AddInteger = 5,
  557. AddColumn = 6,
  558. EraseColumn = 7,
  559. ArrayInsert = 8,
  560. ArrayMove = 9,
  561. ArrayErase = 10,
  562. Clear = 11,
  563. SetInsert = 12,
  564. SetErase = 13,
  565. };
  566. template <Type t>
  567. struct GetType;
  568. template <class T>
  569. struct GetInstructionType;
  570. template <class T>
  571. Instruction(T instr);
  572. mpark::variant<Vector
  573. #define REALM_INSTRUCTION_VARIANT_ALTERNATIVE(X) , X
  574. REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_INSTRUCTION_VARIANT_ALTERNATIVE)
  575. #undef REALM_INSTRUCTION_VARIANT_ALTERNATIVE
  576. >
  577. m_instr;
  578. Type type() const noexcept;
  579. template <class F>
  580. decltype(auto) visit(F&& lambda);
  581. template <class F>
  582. decltype(auto) visit(F&& lambda) const;
  583. template <class T>
  584. T* get_if() noexcept;
  585. template <class T>
  586. const T* get_if() const noexcept
  587. {
  588. return const_cast<Instruction&>(*this).get_if<T>();
  589. }
  590. template <class T>
  591. T& get_as()
  592. {
  593. auto ptr = get_if<T>();
  594. REALM_ASSERT(ptr);
  595. return *ptr;
  596. }
  597. template <class T>
  598. const T& get_as() const
  599. {
  600. auto ptr = get_if<T>();
  601. REALM_ASSERT(ptr);
  602. return *ptr;
  603. }
  604. bool operator==(const Instruction& other) const noexcept;
  605. bool operator!=(const Instruction& other) const noexcept
  606. {
  607. return !(*this == other);
  608. }
  609. bool is_vector() const noexcept
  610. {
  611. return mpark::holds_alternative<Vector>(m_instr);
  612. }
  613. size_t path_length() const noexcept;
  614. Vector& convert_to_vector();
  615. void insert(size_t pos, Instruction instr);
  616. void erase(size_t pos);
  617. size_t size() const noexcept;
  618. bool is_empty() const noexcept;
  619. Instruction& at(size_t) noexcept;
  620. const Instruction& at(size_t) const noexcept;
  621. private:
  622. template <class>
  623. struct Visitor;
  624. };
  625. inline const char* get_type_name(Instruction::Type type)
  626. {
  627. switch (type) {
  628. #define REALM_INSTRUCTION_TYPE_TO_STRING(X) \
  629. case Instruction::Type::X: \
  630. return #X;
  631. REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_INSTRUCTION_TYPE_TO_STRING)
  632. #undef REALM_INSTRUCTION_TYPE_TO_STRING
  633. }
  634. return "(invalid)";
  635. }
  636. inline std::ostream& operator<<(std::ostream& os, Instruction::Type type)
  637. {
  638. return os << get_type_name(type);
  639. }
  640. inline const char* get_type_name(Instruction::Payload::Type type)
  641. {
  642. using Type = Instruction::Payload::Type;
  643. switch (type) {
  644. case Type::Erased:
  645. return "Erased";
  646. case Type::Dictionary:
  647. return "Dictionary";
  648. case Type::ObjectValue:
  649. return "ObjectValue";
  650. case Type::GlobalKey:
  651. return "GlobalKey";
  652. case Type::Null:
  653. return "Null";
  654. case Type::Int:
  655. return "Int";
  656. case Type::Bool:
  657. return "Bool";
  658. case Type::String:
  659. return "String";
  660. case Type::Binary:
  661. return "Binary";
  662. case Type::Timestamp:
  663. return "Timestamp";
  664. case Type::Float:
  665. return "Float";
  666. case Type::Double:
  667. return "Double";
  668. case Type::Decimal:
  669. return "Decimal";
  670. case Type::Link:
  671. return "Link";
  672. case Type::ObjectId:
  673. return "ObjectId";
  674. case Type::UUID:
  675. return "UUID";
  676. }
  677. return "(unknown)";
  678. }
  679. inline const char* get_collection_type(Instruction::AddColumn::CollectionType type)
  680. {
  681. using Type = Instruction::AddColumn::CollectionType;
  682. switch (type) {
  683. case Type::Single:
  684. return "Single";
  685. case Type::List:
  686. return "List";
  687. case Type::Dictionary:
  688. return "Dictionary";
  689. case Type::Set:
  690. return "Set";
  691. }
  692. return "(unknown)";
  693. }
  694. inline const char* get_type_name(util::Optional<Instruction::Payload::Type> type)
  695. {
  696. if (type) {
  697. return get_type_name(*type);
  698. }
  699. else {
  700. return "Mixed";
  701. }
  702. }
  703. inline std::ostream& operator<<(std::ostream& os, Instruction::Payload::Type type)
  704. {
  705. return os << get_type_name(type);
  706. }
  707. inline bool is_valid_key_type(Instruction::Payload::Type type) noexcept
  708. {
  709. using Type = Instruction::Payload::Type;
  710. switch (type) {
  711. case Type::Int:
  712. [[fallthrough]];
  713. case Type::String:
  714. [[fallthrough]];
  715. case Type::ObjectId:
  716. [[fallthrough]];
  717. case Type::UUID:
  718. [[fallthrough]];
  719. case Type::GlobalKey:
  720. return true;
  721. case Type::Null: // Mixed is not a valid primary key
  722. [[fallthrough]];
  723. default:
  724. return false;
  725. }
  726. }
  727. inline DataType get_data_type(Instruction::Payload::Type type) noexcept
  728. {
  729. using Type = Instruction::Payload::Type;
  730. switch (type) {
  731. case Type::Int:
  732. return type_Int;
  733. case Type::Bool:
  734. return type_Bool;
  735. case Type::String:
  736. return type_String;
  737. case Type::Binary:
  738. return type_Binary;
  739. case Type::Timestamp:
  740. return type_Timestamp;
  741. case Type::Float:
  742. return type_Float;
  743. case Type::Double:
  744. return type_Double;
  745. case Type::Decimal:
  746. return type_Decimal;
  747. case Type::Link:
  748. return type_Link;
  749. case Type::ObjectId:
  750. return type_ObjectId;
  751. case Type::UUID:
  752. return type_UUID;
  753. case Type::Null: // Mixed is encoded as null
  754. return type_Mixed;
  755. case Type::Erased:
  756. [[fallthrough]];
  757. case Type::Dictionary:
  758. [[fallthrough]];
  759. case Type::ObjectValue:
  760. [[fallthrough]];
  761. case Type::GlobalKey:
  762. REALM_TERMINATE(util::format("Invalid data type: %1", int8_t(type)).c_str());
  763. }
  764. return type_Int; // Make compiler happy
  765. }
  766. // 0x3f is the largest value that fits in a single byte in the variable-length
  767. // encoded integer instruction format.
  768. static constexpr uint8_t InstrTypeInternString = 0x3f;
  769. // This instruction code is only ever used internally by the Changeset class
  770. // to allow insertion/removal while keeping iterators stable. Should never
  771. // make it onto the wire.
  772. static constexpr uint8_t InstrTypeMultiInstruction = 0xff;
  773. struct InstructionHandler {
  774. /// Notify the handler that an InternString meta-instruction was found.
  775. virtual void set_intern_string(uint32_t index, StringBufferRange) = 0;
  776. /// Notify the handler of the string value. The handler guarantees that the
  777. /// returned string range is valid at least until the next invocation of
  778. /// add_string_range().
  779. ///
  780. /// Instances of `StringBufferRange` passed to operator() after invoking
  781. /// this function are assumed to refer to ranges in this buffer.
  782. virtual StringBufferRange add_string_range(StringData) = 0;
  783. /// Handle an instruction.
  784. virtual void operator()(const Instruction&) = 0;
  785. };
  786. /// Implementation:
  787. #define REALM_DEFINE_INSTRUCTION_GET_TYPE(X) \
  788. template <> \
  789. struct Instruction::GetType<Instruction::Type::X> { \
  790. using Type = Instruction::X; \
  791. }; \
  792. template <> \
  793. struct Instruction::GetInstructionType<Instruction::X> { \
  794. static const Instruction::Type value = Instruction::Type::X; \
  795. };
  796. REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_GET_TYPE)
  797. #undef REALM_DEFINE_INSTRUCTION_GET_TYPE
  798. template <class T>
  799. Instruction::Instruction(T instr)
  800. : m_instr(std::move(instr))
  801. {
  802. static_assert(!std::is_same_v<T, Vector>);
  803. }
  804. template <class F>
  805. struct Instruction::Visitor {
  806. F lambda; // reference type
  807. Visitor(F lambda)
  808. : lambda(lambda)
  809. {
  810. }
  811. template <class T>
  812. decltype(auto) operator()(T& instr)
  813. {
  814. return lambda(instr);
  815. }
  816. template <class T>
  817. decltype(auto) operator()(const T& instr)
  818. {
  819. return lambda(instr);
  820. }
  821. auto operator()(const Instruction::Vector&) -> decltype(lambda(std::declval<const Instruction::Update&>()))
  822. {
  823. REALM_TERMINATE("visiting instruction vector");
  824. }
  825. auto operator()(Instruction::Vector&) -> decltype(lambda(std::declval<Instruction::Update&>()))
  826. {
  827. REALM_TERMINATE("visiting instruction vector");
  828. }
  829. };
  830. template <class F>
  831. inline decltype(auto) Instruction::visit(F&& lambda)
  832. {
  833. // Cannot use std::visit, because it does not pass lvalue references to the visitor.
  834. if (mpark::holds_alternative<Vector>(m_instr)) {
  835. REALM_TERMINATE("visiting instruction vector");
  836. }
  837. #define REALM_VISIT_VARIANT(X) \
  838. else if (mpark::holds_alternative<Instruction::X>(m_instr)) \
  839. { \
  840. return lambda(mpark::get<Instruction::X>(m_instr)); \
  841. }
  842. REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_VISIT_VARIANT)
  843. #undef REALM_VISIT_VARIANT
  844. else
  845. {
  846. REALM_TERMINATE("Unhandled instruction variant entry");
  847. }
  848. }
  849. template <class F>
  850. inline decltype(auto) Instruction::visit(F&& lambda) const
  851. {
  852. // Cannot use std::visit, because it does not pass lvalue references to the visitor.
  853. if (mpark::holds_alternative<Vector>(m_instr)) {
  854. REALM_TERMINATE("visiting instruction vector");
  855. }
  856. #define REALM_VISIT_VARIANT(X) \
  857. else if (mpark::holds_alternative<Instruction::X>(m_instr)) \
  858. { \
  859. return lambda(mpark::get<Instruction::X>(m_instr)); \
  860. }
  861. REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_VISIT_VARIANT)
  862. #undef REALM_VISIT_VARIANT
  863. else
  864. {
  865. REALM_TERMINATE("Unhandled instruction variant entry");
  866. }
  867. }
  868. inline Instruction::Type Instruction::type() const noexcept
  869. {
  870. return visit([](auto&& instr) {
  871. using T = std::remove_cv_t<std::remove_reference_t<decltype(instr)>>;
  872. return GetInstructionType<T>::value;
  873. });
  874. }
  875. inline bool Instruction::operator==(const Instruction& other) const noexcept
  876. {
  877. return m_instr == other.m_instr;
  878. }
  879. template <class T>
  880. REALM_NOINLINE T* Instruction::get_if() noexcept
  881. {
  882. // FIXME: Is there a way to express this without giant switch statements? Note: Putting the
  883. // base class into a union does not seem to be allowed by the standard.
  884. if constexpr (std::is_same_v<TableInstruction, T>) {
  885. // This should compile to nothing but a comparison of the type.
  886. return visit([](auto& instr) -> TableInstruction* {
  887. return &instr;
  888. });
  889. }
  890. else if constexpr (std::is_same_v<ObjectInstruction, T>) {
  891. // This should compile to nothing but a comparison of the type.
  892. return visit(util::overload{
  893. [](AddTable&) -> ObjectInstruction* {
  894. return nullptr;
  895. },
  896. [](EraseTable&) -> ObjectInstruction* {
  897. return nullptr;
  898. },
  899. [](AddColumn&) -> ObjectInstruction* {
  900. return nullptr;
  901. },
  902. [](EraseColumn&) -> ObjectInstruction* {
  903. return nullptr;
  904. },
  905. [](auto& instr) -> ObjectInstruction* {
  906. return &instr;
  907. },
  908. });
  909. }
  910. else if constexpr (std::is_same_v<PathInstruction, T>) {
  911. // This should compile to nothing but a comparison of the type.
  912. return visit(util::overload{
  913. [](AddTable&) -> PathInstruction* {
  914. return nullptr;
  915. },
  916. [](EraseTable&) -> PathInstruction* {
  917. return nullptr;
  918. },
  919. [](AddColumn&) -> PathInstruction* {
  920. return nullptr;
  921. },
  922. [](EraseColumn&) -> PathInstruction* {
  923. return nullptr;
  924. },
  925. [](CreateObject&) -> PathInstruction* {
  926. return nullptr;
  927. },
  928. [](EraseObject&) -> PathInstruction* {
  929. return nullptr;
  930. },
  931. [](auto& instr) -> PathInstruction* {
  932. return &instr;
  933. },
  934. });
  935. }
  936. else {
  937. return mpark::get_if<T>(&m_instr);
  938. }
  939. }
  940. inline size_t Instruction::size() const noexcept
  941. {
  942. if (auto vec = mpark::get_if<Vector>(&m_instr)) {
  943. return vec->size();
  944. }
  945. return 1;
  946. }
  947. inline bool Instruction::is_empty() const noexcept
  948. {
  949. return size() == 0;
  950. }
  951. inline Instruction& Instruction::at(size_t idx) noexcept
  952. {
  953. if (auto vec = mpark::get_if<Vector>(&m_instr)) {
  954. REALM_ASSERT(idx < vec->size());
  955. return (*vec)[idx];
  956. }
  957. REALM_ASSERT(idx == 0);
  958. return *this;
  959. }
  960. inline const Instruction& Instruction::at(size_t idx) const noexcept
  961. {
  962. if (auto vec = mpark::get_if<Vector>(&m_instr)) {
  963. REALM_ASSERT(idx < vec->size());
  964. return (*vec)[idx];
  965. }
  966. REALM_ASSERT(idx == 0);
  967. return *this;
  968. }
  969. inline size_t Instruction::path_length() const noexcept
  970. {
  971. // Find the path length of the instruction. This affects how OT decides
  972. // which instructions are potentially nesting.
  973. //
  974. // AddTable/EraseTable: Length 1
  975. // AddColumn/EraseColumn: Length 2 (table, field)
  976. // Object instructions: Length 2 (table, object)
  977. // Path instructions: Length 3 + m_path.size (table, object, field, path...)
  978. if (auto path_instr = get_if<Instruction::PathInstruction>()) {
  979. return 3 + path_instr->path.size();
  980. }
  981. if (get_if<Instruction::ObjectInstruction>()) {
  982. return 2;
  983. }
  984. switch (type()) {
  985. case Instruction::Type::AddColumn:
  986. [[fallthrough]];
  987. case Instruction::Type::EraseColumn: {
  988. return 2;
  989. }
  990. case Instruction::Type::AddTable:
  991. [[fallthrough]];
  992. case Instruction::Type::EraseTable: {
  993. return 1;
  994. }
  995. default:
  996. REALM_TERMINATE("Unhandled instruction type in Instruction::path_len()");
  997. }
  998. }
  999. inline Instruction::Vector& Instruction::convert_to_vector()
  1000. {
  1001. if (auto v = mpark::get_if<Vector>(&m_instr)) {
  1002. return *v;
  1003. }
  1004. else {
  1005. Vector vec;
  1006. vec.emplace_back(std::move(*this));
  1007. m_instr = std::move(vec);
  1008. return mpark::get<Vector>(m_instr);
  1009. }
  1010. }
  1011. inline void Instruction::insert(size_t idx, Instruction instr)
  1012. {
  1013. auto& vec = convert_to_vector();
  1014. REALM_ASSERT(idx <= vec.size());
  1015. vec.emplace(vec.begin() + idx, std::move(instr));
  1016. }
  1017. inline void Instruction::erase(size_t idx)
  1018. {
  1019. auto& vec = convert_to_vector();
  1020. REALM_ASSERT(idx < vec.size());
  1021. vec.erase(vec.begin() + idx);
  1022. }
  1023. } // namespace sync
  1024. } // namespace realm
  1025. #endif // REALM_IMPL_INSTRUCTIONS_HPP