instructions.hpp 34 KB

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