added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / impl / transact_log.hpp
1 /*************************************************************************
2  *
3  * Copyright 2016 Realm Inc.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  **************************************************************************/
18
19 #ifndef REALM_IMPL_TRANSACT_LOG_HPP
20 #define REALM_IMPL_TRANSACT_LOG_HPP
21
22 #include <stdexcept>
23
24 #include <realm/string_data.hpp>
25 #include <realm/data_type.hpp>
26 #include <realm/binary_data.hpp>
27 #include <realm/olddatetime.hpp>
28 #include <realm/mixed.hpp>
29 #include <realm/util/buffer.hpp>
30 #include <realm/util/string_buffer.hpp>
31 #include <realm/impl/input_stream.hpp>
32
33 #include <realm/group.hpp>
34 #include <realm/descriptor.hpp>
35 #include <realm/link_view.hpp>
36
37 namespace realm {
38 namespace _impl {
39
40 /// Transaction log instruction encoding
41 /// NOTE: Any change to this enum is a file-format breaking change.
42 enum Instruction {
43     instr_InsertGroupLevelTable = 1,
44     instr_EraseGroupLevelTable = 2, // Remove columnless table from group
45     instr_RenameGroupLevelTable = 3,
46     instr_MoveGroupLevelTable = 4, // UNSUPPORTED/UNUSED. FIXME: remove in next breaking change
47     instr_SelectTable = 5,
48     instr_Set = 6,
49     instr_SetUnique = 7,
50     instr_SetDefault = 8,
51     instr_AddInteger = 9,   // Add value to integer field
52     instr_NullifyLink = 10, // Set link to null due to target being erased
53     instr_InsertSubstring = 11,
54     instr_EraseFromString = 12,
55     instr_InsertEmptyRows = 13,
56     instr_EraseRows = 14, // Remove (multiple) rows
57     instr_SwapRows = 15,
58     instr_MoveRow = 16,
59     instr_MergeRows = 17,  // Replace links pointing to row A with links to row B
60     instr_ClearTable = 18, // Remove all rows in selected table
61     instr_OptimizeTable = 19,
62     instr_SelectDescriptor = 20, // Select descriptor from currently selected root table
63     instr_InsertColumn =
64         21, // Insert new non-nullable column into to selected descriptor (nullable is instr_InsertNullableColumn)
65     instr_InsertLinkColumn = 22,     // do, but for a link-type column
66     instr_InsertNullableColumn = 23, // Insert nullable column
67     instr_EraseColumn = 24,          // Remove column from selected descriptor
68     instr_EraseLinkColumn = 25,      // Remove link-type column from selected descriptor
69     instr_RenameColumn = 26,         // Rename column in selected descriptor
70     instr_MoveColumn = 27,           // Move column in selected descriptor (UNSUPPORTED/UNUSED) FIXME: remove
71     instr_AddSearchIndex = 28,       // Add a search index to a column
72     instr_RemoveSearchIndex = 29,    // Remove a search index from a column
73     instr_SetLinkType = 30,          // Strong/weak
74     instr_SelectLinkList = 31,
75     instr_LinkListSet = 32,     // Assign to link list entry
76     instr_LinkListInsert = 33,  // Insert entry into link list
77     instr_LinkListMove = 34,    // Move an entry within a link list
78     instr_LinkListSwap = 35,    // Swap two entries within a link list
79     instr_LinkListErase = 36,   // Remove an entry from a link list
80     instr_LinkListNullify = 37, // Remove an entry from a link list due to linked row being erased
81     instr_LinkListClear = 38,   // Ramove all entries from a link list
82     instr_LinkListSetAll = 39,  // Assign to link list entry
83     instr_AddRowWithKey = 40,   // Insert a row with a given key
84 };
85
86 class TransactLogStream {
87 public:
88     /// Ensure contiguous free space in the transaction log
89     /// buffer. This method must update `out_free_begin`
90     /// and `out_free_end` such that they refer to a chunk
91     /// of free space whose size is at least \a n.
92     ///
93     /// \param n The required amount of contiguous free space. Must be
94     /// small (probably not greater than 1024)
95     /// \param n Must be small (probably not greater than 1024)
96     virtual void transact_log_reserve(size_t size, char** out_free_begin, char** out_free_end) = 0;
97
98     /// Copy the specified data into the transaction log buffer. This
99     /// function should be called only when the specified data does
100     /// not fit inside the chunk of free space currently referred to
101     /// by `out_free_begin` and `out_free_end`.
102     ///
103     /// This method must update `out_begin` and
104     /// `out_end` such that, upon return, they still
105     /// refer to a (possibly empty) chunk of free space.
106     virtual void transact_log_append(const char* data, size_t size, char** out_free_begin, char** out_free_end) = 0;
107 };
108
109 class TransactLogBufferStream : public TransactLogStream {
110 public:
111     void transact_log_reserve(size_t size, char** out_free_begin, char** out_free_end) override;
112     void transact_log_append(const char* data, size_t size, char** out_free_begin, char** out_free_end) override;
113
114     const char* transact_log_data() const;
115
116     util::Buffer<char> m_buffer;
117 };
118
119
120 // LCOV_EXCL_START (because the NullInstructionObserver is trivial)
121 class NullInstructionObserver {
122 public:
123     /// The following methods are also those that TransactLogParser expects
124     /// to find on the `InstructionHandler`.
125
126     // No selection needed:
127     bool select_table(size_t, size_t, const size_t*)
128     {
129         return true;
130     }
131     bool select_descriptor(size_t, const size_t*)
132     {
133         return true;
134     }
135     bool select_link_list(size_t, size_t, size_t)
136     {
137         return true;
138     }
139     bool insert_group_level_table(size_t, size_t, StringData)
140     {
141         return true;
142     }
143     bool erase_group_level_table(size_t, size_t)
144     {
145         return true;
146     }
147     bool rename_group_level_table(size_t, StringData)
148     {
149         return true;
150     }
151
152     // Must have table selected:
153     bool insert_empty_rows(size_t, size_t, size_t, bool)
154     {
155         return true;
156     }
157     bool add_row_with_key(size_t, size_t, size_t, int64_t)
158     {
159         return true;
160     }
161     bool erase_rows(size_t, size_t, size_t, bool)
162     {
163         return true;
164     }
165     bool swap_rows(size_t, size_t)
166     {
167         return true;
168     }
169     bool move_row(size_t, size_t)
170     {
171         return true;
172     }
173     bool merge_rows(size_t, size_t)
174     {
175         return true;
176     }
177     bool clear_table(size_t)
178     {
179         return true;
180     }
181     bool set_int(size_t, size_t, int_fast64_t, Instruction, size_t)
182     {
183         return true;
184     }
185     bool add_int(size_t, size_t, int_fast64_t)
186     {
187         return true;
188     }
189     bool set_bool(size_t, size_t, bool, Instruction)
190     {
191         return true;
192     }
193     bool set_float(size_t, size_t, float, Instruction)
194     {
195         return true;
196     }
197     bool set_double(size_t, size_t, double, Instruction)
198     {
199         return true;
200     }
201     bool set_string(size_t, size_t, StringData, Instruction, size_t)
202     {
203         return true;
204     }
205     bool set_binary(size_t, size_t, BinaryData, Instruction)
206     {
207         return true;
208     }
209     bool set_olddatetime(size_t, size_t, OldDateTime, Instruction)
210     {
211         return true;
212     }
213     bool set_timestamp(size_t, size_t, Timestamp, Instruction)
214     {
215         return true;
216     }
217     bool set_table(size_t, size_t, Instruction)
218     {
219         return true;
220     }
221     bool set_mixed(size_t, size_t, const Mixed&, Instruction)
222     {
223         return true;
224     }
225     bool set_link(size_t, size_t, size_t, size_t, Instruction)
226     {
227         return true;
228     }
229     bool set_null(size_t, size_t, Instruction, size_t)
230     {
231         return true;
232     }
233     bool nullify_link(size_t, size_t, size_t)
234     {
235         return true;
236     }
237     bool insert_substring(size_t, size_t, size_t, StringData)
238     {
239         return true;
240     }
241     bool erase_substring(size_t, size_t, size_t, size_t)
242     {
243         return true;
244     }
245     bool optimize_table()
246     {
247         return true;
248     }
249
250     // Must have descriptor selected:
251     bool insert_link_column(size_t, DataType, StringData, size_t, size_t)
252     {
253         return true;
254     }
255     bool insert_column(size_t, DataType, StringData, bool)
256     {
257         return true;
258     }
259     bool erase_link_column(size_t, size_t, size_t)
260     {
261         return true;
262     }
263     bool erase_column(size_t)
264     {
265         return true;
266     }
267     bool rename_column(size_t, StringData)
268     {
269         return true;
270     }
271     bool add_search_index(size_t)
272     {
273         return true;
274     }
275     bool remove_search_index(size_t)
276     {
277         return true;
278     }
279     bool set_link_type(size_t, LinkType)
280     {
281         return true;
282     }
283
284     // Must have linklist selected:
285     bool link_list_set(size_t, size_t, size_t)
286     {
287         return true;
288     }
289     bool link_list_insert(size_t, size_t, size_t)
290     {
291         return true;
292     }
293     bool link_list_move(size_t, size_t)
294     {
295         return true;
296     }
297     bool link_list_swap(size_t, size_t)
298     {
299         return true;
300     }
301     bool link_list_erase(size_t, size_t)
302     {
303         return true;
304     }
305     bool link_list_nullify(size_t, size_t)
306     {
307         return true;
308     }
309     bool link_list_clear(size_t)
310     {
311         return true;
312     }
313
314     void parse_complete()
315     {
316     }
317 };
318 // LCOV_EXCL_STOP (NullInstructionObserver)
319
320
321 /// See TransactLogConvenientEncoder for information about the meaning of the
322 /// arguments of each of the functions in this class.
323 class TransactLogEncoder {
324 public:
325     /// The following methods are also those that TransactLogParser expects
326     /// to find on the `InstructionHandler`.
327
328     // No selection needed:
329     bool select_table(size_t group_level_ndx, size_t levels, const size_t* path);
330     bool select_descriptor(size_t levels, const size_t* path);
331     bool select_link_list(size_t col_ndx, size_t row_ndx, size_t link_target_group_level_ndx);
332     bool insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name);
333     bool erase_group_level_table(size_t table_ndx, size_t num_tables);
334     bool rename_group_level_table(size_t table_ndx, StringData new_name);
335
336     /// Must have table selected.
337     bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool unordered);
338     bool add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx, int64_t key);
339     bool erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool unordered);
340     bool swap_rows(size_t row_ndx_1, size_t row_ndx_2);
341     bool move_row(size_t from_ndx, size_t to_ndx);
342     bool merge_rows(size_t row_ndx, size_t new_row_ndx);
343     bool clear_table(size_t old_table_size);
344
345     bool set_int(size_t col_ndx, size_t row_ndx, int_fast64_t, Instruction = instr_Set, size_t = 0);
346     bool add_int(size_t col_ndx, size_t row_ndx, int_fast64_t);
347     bool set_bool(size_t col_ndx, size_t row_ndx, bool, Instruction = instr_Set);
348     bool set_float(size_t col_ndx, size_t row_ndx, float, Instruction = instr_Set);
349     bool set_double(size_t col_ndx, size_t row_ndx, double, Instruction = instr_Set);
350     bool set_string(size_t col_ndx, size_t row_ndx, StringData, Instruction = instr_Set, size_t = 0);
351     bool set_binary(size_t col_ndx, size_t row_ndx, BinaryData, Instruction = instr_Set);
352     bool set_olddatetime(size_t col_ndx, size_t row_ndx, OldDateTime, Instruction = instr_Set);
353     bool set_timestamp(size_t col_ndx, size_t row_ndx, Timestamp, Instruction = instr_Set);
354     bool set_table(size_t col_ndx, size_t row_ndx, Instruction = instr_Set);
355     bool set_mixed(size_t col_ndx, size_t row_ndx, const Mixed&, Instruction = instr_Set);
356     bool set_link(size_t col_ndx, size_t row_ndx, size_t, size_t target_group_level_ndx, Instruction = instr_Set);
357     bool set_null(size_t col_ndx, size_t row_ndx, Instruction = instr_Set, size_t = 0);
358     bool nullify_link(size_t col_ndx, size_t row_ndx, size_t target_group_level_ndx);
359     bool insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData);
360     bool erase_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t size);
361     bool optimize_table();
362
363     // Must have descriptor selected:
364     bool insert_link_column(size_t col_ndx, DataType, StringData name, size_t link_target_table_ndx,
365                             size_t backlink_col_ndx);
366     bool insert_column(size_t col_ndx, DataType, StringData name, bool nullable = false);
367     bool erase_link_column(size_t col_ndx, size_t link_target_table_ndx, size_t backlink_col_ndx);
368     bool erase_column(size_t col_ndx);
369     bool rename_column(size_t col_ndx, StringData new_name);
370     bool add_search_index(size_t col_ndx);
371     bool remove_search_index(size_t col_ndx);
372     bool set_link_type(size_t col_ndx, LinkType);
373
374     // Must have linklist selected:
375     bool link_list_set(size_t link_ndx, size_t value, size_t prior_size);
376     bool link_list_set_all(const IntegerColumn& values);
377     bool link_list_insert(size_t link_ndx, size_t value, size_t prior_size);
378     bool link_list_move(size_t from_link_ndx, size_t to_link_ndx);
379     bool link_list_swap(size_t link1_ndx, size_t link2_ndx);
380     bool link_list_erase(size_t link_ndx, size_t prior_size);
381     bool link_list_nullify(size_t link_ndx, size_t prior_size);
382     bool link_list_clear(size_t old_list_size);
383
384     /// End of methods expected by parser.
385
386
387     TransactLogEncoder(TransactLogStream& out_stream);
388     void set_buffer(char* new_free_begin, char* new_free_end);
389     char* write_position() const
390     {
391         return m_transact_log_free_begin;
392     }
393
394 private:
395     using IntegerList = std::tuple<IntegerColumnIterator, IntegerColumnIterator>;
396     using UnsignedList = std::tuple<const size_t*, const size_t*>;
397
398     // Make sure this is in agreement with the actual integer encoding
399     // scheme (see encode_int()).
400     static constexpr int max_enc_bytes_per_int = 10;
401     static constexpr int max_enc_bytes_per_double = sizeof(double);
402     static constexpr int max_enc_bytes_per_num =
403         max_enc_bytes_per_int < max_enc_bytes_per_double ? max_enc_bytes_per_double : max_enc_bytes_per_int;
404 // Space is reserved in chunks to avoid excessive over allocation.
405 #ifdef REALM_DEBUG
406     static constexpr int max_numbers_per_chunk = 2; // Increase the chance of chunking in debug mode
407 #else
408     static constexpr int max_numbers_per_chunk = 8;
409 #endif
410
411     // This value is used in Set* instructions in place of the 'type' field in
412     // the stream to indicate that the value of the Set* instruction is NULL,
413     // which doesn't have a type.
414     static constexpr int set_null_sentinel()
415     {
416         return -1;
417     }
418
419     TransactLogStream& m_stream;
420
421     // These two delimit a contiguous region of free space in a
422     // transaction log buffer following the last written data. It may
423     // be empty.
424     char* m_transact_log_free_begin = nullptr;
425     char* m_transact_log_free_end = nullptr;
426
427     char* reserve(size_t size);
428     /// \param ptr Must be in the range [m_transact_log_free_begin, m_transact_log_free_end]
429     void advance(char* ptr) noexcept;
430
431     template <class T>
432     size_t max_size(T);
433
434     size_t max_size_list()
435     {
436         return 0;
437     }
438
439     template <class T, class... Args>
440     size_t max_size_list(T val, Args... args)
441     {
442         return max_size(val) + max_size_list(args...);
443     }
444
445     template <class T>
446     char* encode(char* ptr, T value);
447
448     char* encode_list(char* ptr)
449     {
450         advance(ptr);
451         return ptr;
452     }
453
454     template <class T, class... Args>
455     char* encode_list(char* ptr, T value, Args... args)
456     {
457         return encode_list(encode(ptr, value), args...);
458     }
459
460     template <class... L>
461     void append_simple_instr(L... numbers);
462
463     template <class... L>
464     void append_mixed_instr(Instruction instr, const Mixed& value, L... numbers);
465
466     template <class T>
467     static char* encode_int(char*, T value);
468     friend class TransactLogParser;
469 };
470
471 class TransactLogConvenientEncoder {
472 public:
473     virtual void insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name);
474     virtual void erase_group_level_table(size_t table_ndx, size_t num_tables);
475     virtual void rename_group_level_table(size_t table_ndx, StringData new_name);
476     virtual void insert_column(const Descriptor&, size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link,
477                                bool nullable = false);
478     virtual void erase_column(const Descriptor&, size_t col_ndx);
479     virtual void rename_column(const Descriptor&, size_t col_ndx, StringData name);
480
481     virtual void set_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant = instr_Set);
482     virtual void add_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value);
483     virtual void set_bool(const Table*, size_t col_ndx, size_t ndx, bool value, Instruction variant = instr_Set);
484     virtual void set_float(const Table*, size_t col_ndx, size_t ndx, float value, Instruction variant = instr_Set);
485     virtual void set_double(const Table*, size_t col_ndx, size_t ndx, double value, Instruction variant = instr_Set);
486     virtual void set_string(const Table*, size_t col_ndx, size_t ndx, StringData value, Instruction variant = instr_Set);
487     virtual void set_binary(const Table*, size_t col_ndx, size_t ndx, BinaryData value, Instruction variant = instr_Set);
488     virtual void set_olddatetime(const Table*, size_t col_ndx, size_t ndx, OldDateTime value,
489                                  Instruction variant = instr_Set);
490     virtual void set_timestamp(const Table*, size_t col_ndx, size_t ndx, Timestamp value, Instruction variant = instr_Set);
491     virtual void set_table(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set);
492     virtual void set_mixed(const Table*, size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant = instr_Set);
493     virtual void set_link(const Table*, size_t col_ndx, size_t ndx, size_t value, Instruction variant = instr_Set);
494     virtual void set_null(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set);
495     virtual void set_link_list(const LinkView&, const IntegerColumn& values);
496     virtual void insert_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, StringData);
497     virtual void erase_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, size_t size);
498
499     /// \param prior_num_rows The number of rows in the table prior to the
500     /// modification.
501     virtual void insert_empty_rows(const Table*, size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows);
502     virtual void add_row_with_key(const Table* t, size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx,
503                                   int64_t key);
504
505     /// \param prior_num_rows The number of rows in the table prior to the
506     /// modification.
507     virtual void erase_rows(const Table*, size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows,
508                             bool is_move_last_over);
509
510     virtual void swap_rows(const Table*, size_t row_ndx_1, size_t row_ndx_2);
511     virtual void move_row(const Table*, size_t from_ndx, size_t to_ndx);
512     virtual void merge_rows(const Table*, size_t row_ndx, size_t new_row_ndx);
513     virtual void add_search_index(const Descriptor&, size_t col_ndx);
514     virtual void remove_search_index(const Descriptor&, size_t col_ndx);
515     virtual void set_link_type(const Table*, size_t col_ndx, LinkType);
516     virtual void clear_table(const Table*, size_t prior_num_rows);
517     virtual void optimize_table(const Table*);
518
519     virtual void link_list_set(const LinkView&, size_t link_ndx, size_t value);
520     virtual void link_list_insert(const LinkView&, size_t link_ndx, size_t value);
521     virtual void link_list_move(const LinkView&, size_t from_link_ndx, size_t to_link_ndx);
522     virtual void link_list_swap(const LinkView&, size_t link_ndx_1, size_t link_ndx_2);
523     virtual void link_list_erase(const LinkView&, size_t link_ndx);
524     virtual void link_list_clear(const LinkView&);
525
526     //@{
527
528     /// Implicit nullifications due to removal of target row. This is redundant
529     /// information from the point of view of replication, as the removal of the
530     /// target row will reproduce the implicit nullifications in the target
531     /// Realm anyway. The purpose of this instruction is to allow observers
532     /// (reactor pattern) to be explicitly notified about the implicit
533     /// nullifications.
534
535     virtual void nullify_link(const Table*, size_t col_ndx, size_t ndx);
536     virtual void link_list_nullify(const LinkView&, size_t link_ndx);
537
538     //@}
539
540     void on_table_destroyed(const Table*) noexcept;
541     void on_spec_destroyed(const Spec*) noexcept;
542     void on_link_list_destroyed(const LinkView&) noexcept;
543
544 protected:
545     TransactLogConvenientEncoder(TransactLogStream& encoder);
546
547     void reset_selection_caches() noexcept;
548     void set_buffer(char* new_free_begin, char* new_free_end)
549     {
550         m_encoder.set_buffer(new_free_begin, new_free_end);
551     }
552     char* write_position() const
553     {
554         return m_encoder.write_position();
555     }
556
557 private:
558     TransactLogEncoder m_encoder;
559     // These are mutable because they are caches.
560     mutable util::Buffer<size_t> m_subtab_path_buf;
561     mutable const Table* m_selected_table;
562     mutable const Spec* m_selected_spec;
563     // Has to be atomic to support concurrent reset when a linklist
564     // is unselected. This can happen on a different thread. In case
565     // of races, setting of a new value must win.
566     mutable std::atomic<const LinkView*> m_selected_link_list;
567
568     void unselect_all() noexcept;
569     void select_table(const Table*);        // unselects descriptor and link list
570     void select_desc(const Descriptor&);    // unselects link list
571     void select_link_list(const LinkView&); // unselects descriptor
572
573     void record_subtable_path(const Table&, size_t*& out_begin, size_t*& out_end);
574     void do_select_table(const Table*);
575     void do_select_desc(const Descriptor&);
576     void do_select_link_list(const LinkView&);
577
578     friend class TransactReverser;
579 };
580
581
582 class TransactLogParser {
583 public:
584     class BadTransactLog; // Exception
585
586     TransactLogParser();
587     ~TransactLogParser() noexcept;
588
589     /// See `TransactLogEncoder` for a list of methods that the `InstructionHandler` must define.
590     /// parse() promises that the path passed by reference to
591     /// InstructionHandler::select_descriptor() will remain valid
592     /// during subsequent calls to all descriptor modifying functions.
593     template <class InstructionHandler>
594     void parse(InputStream&, InstructionHandler&);
595
596     template <class InstructionHandler>
597     void parse(NoCopyInputStream&, InstructionHandler&);
598
599 private:
600     util::Buffer<char> m_input_buffer;
601
602     // The input stream is assumed to consist of chunks of memory organised such that
603     // every instruction resides in a single chunk only.
604     NoCopyInputStream* m_input;
605     // pointer into transaction log, each instruction is parsed from m_input_begin and onwards.
606     // Each instruction are assumed to be contiguous in memory.
607     const char* m_input_begin;
608     // pointer to one past current instruction log chunk. If m_input_begin reaches m_input_end,
609     // a call to next_input_buffer will move m_input_begin and m_input_end to a new chunk of
610     // memory. Setting m_input_end to 0 disables this check, and is used if it is already known
611     // that all of the instructions are in memory.
612     const char* m_input_end;
613     util::StringBuffer m_string_buffer;
614     static const int m_max_levels = 1024;
615     util::Buffer<size_t> m_path;
616
617     REALM_NORETURN void parser_error() const;
618
619     template <class InstructionHandler>
620     void parse_one(InstructionHandler&);
621     bool has_next() noexcept;
622
623     template <class T>
624     T read_int();
625
626     void read_bytes(char* data, size_t size);
627     BinaryData read_buffer(util::StringBuffer&, size_t size);
628
629     bool read_bool();
630     float read_float();
631     double read_double();
632
633     StringData read_string(util::StringBuffer&);
634     BinaryData read_binary(util::StringBuffer&);
635     Timestamp read_timestamp();
636     void read_mixed(Mixed*);
637
638     // Advance m_input_begin and m_input_end to reflect the next block of instructions
639     // Returns false if no more input was available
640     bool next_input_buffer();
641
642     // return true if input was available
643     bool read_char(char&); // throws
644
645     bool is_valid_data_type(int type);
646     bool is_valid_link_type(int type);
647 };
648
649
650 class TransactLogParser::BadTransactLog : public std::exception {
651 public:
652     const char* what() const noexcept override
653     {
654         return "Bad transaction log";
655     }
656 };
657
658
659 /// Implementation:
660
661 inline void TransactLogBufferStream::transact_log_reserve(size_t n, char** inout_new_begin, char** out_new_end)
662 {
663     char* data = m_buffer.data();
664     REALM_ASSERT(*inout_new_begin >= data);
665     REALM_ASSERT(*inout_new_begin <= (data + m_buffer.size()));
666     size_t size = *inout_new_begin - data;
667     m_buffer.reserve_extra(size, n);
668     data = m_buffer.data(); // May have changed
669     *inout_new_begin = data + size;
670     *out_new_end = data + m_buffer.size();
671 }
672
673 inline void TransactLogBufferStream::transact_log_append(const char* data, size_t size, char** out_new_begin,
674                                                          char** out_new_end)
675 {
676     transact_log_reserve(size, out_new_begin, out_new_end);
677     *out_new_begin = realm::safe_copy_n(data, size, *out_new_begin);
678 }
679
680 inline const char* TransactLogBufferStream::transact_log_data() const
681 {
682     return m_buffer.data();
683 }
684
685 inline TransactLogEncoder::TransactLogEncoder(TransactLogStream& stream)
686     : m_stream(stream)
687 {
688 }
689
690 inline void TransactLogEncoder::set_buffer(char* free_begin, char* free_end)
691 {
692     REALM_ASSERT(free_begin <= free_end);
693     m_transact_log_free_begin = free_begin;
694     m_transact_log_free_end = free_end;
695 }
696
697 inline void TransactLogConvenientEncoder::reset_selection_caches() noexcept
698 {
699     unselect_all();
700 }
701
702 inline char* TransactLogEncoder::reserve(size_t n)
703 {
704     if (size_t(m_transact_log_free_end - m_transact_log_free_begin) < n) {
705         m_stream.transact_log_reserve(n, &m_transact_log_free_begin, &m_transact_log_free_end);
706     }
707     return m_transact_log_free_begin;
708 }
709
710 inline void TransactLogEncoder::advance(char* ptr) noexcept
711 {
712     REALM_ASSERT_DEBUG(m_transact_log_free_begin <= ptr);
713     REALM_ASSERT_DEBUG(ptr <= m_transact_log_free_end);
714     m_transact_log_free_begin = ptr;
715 }
716
717
718 // The integer encoding is platform independent. Also, it does not
719 // depend on the type of the specified integer. Integers of any type
720 // can be encoded as long as the specified buffer is large enough (see
721 // below). The decoding does not have to use the same type. Decoding
722 // will fail if, and only if the encoded value falls outside the range
723 // of the requested destination type.
724 //
725 // The encoding uses one or more bytes. It never uses more than 8 bits
726 // per byte. The last byte in the sequence is the first one that has
727 // its 8th bit set to zero.
728 //
729 // Consider a particular non-negative value V. Let W be the number of
730 // bits needed to encode V using the trivial binary encoding of
731 // integers. The total number of bytes produced is then
732 // ceil((W+1)/7). The first byte holds the 7 least significant bits of
733 // V. The last byte holds at most 6 bits of V including the most
734 // significant one. The value of the first bit of the last byte is
735 // always 2**((N-1)*7) where N is the total number of bytes.
736 //
737 // A negative value W is encoded by setting the sign bit to one and
738 // then encoding the positive result of -(W+1) as described above. The
739 // advantage of this representation is that it converts small negative
740 // values to small positive values which require a small number of
741 // bytes. This would not have been true for 2's complements
742 // representation, for example. The sign bit is always stored as the
743 // 7th bit of the last byte.
744 //
745 //               value bits    value + sign    max bytes
746 //     --------------------------------------------------
747 //     int8_t         7              8              2
748 //     uint8_t        8              9              2
749 //     int16_t       15             16              3
750 //     uint16_t      16             17              3
751 //     int32_t       31             32              5
752 //     uint32_t      32             33              5
753 //     int64_t       63             64             10
754 //     uint64_t      64             65             10
755 //
756 template <class T>
757 char* TransactLogEncoder::encode_int(char* ptr, T value)
758 {
759     static_assert(std::numeric_limits<T>::is_integer, "Integer required");
760     bool negative = util::is_negative(value);
761     if (negative) {
762         // The following conversion is guaranteed by C++11 to never
763         // overflow (contrast this with "-value" which indeed could
764         // overflow). See C99+TC3 section 6.2.6.2 paragraph 2.
765         REALM_DIAG_PUSH();
766         REALM_DIAG_IGNORE_UNSIGNED_MINUS();
767         value = -(value + 1);
768         REALM_DIAG_POP();
769     }
770     // At this point 'value' is always a positive number. Also, small
771     // negative numbers have been converted to small positive numbers.
772     REALM_ASSERT(!util::is_negative(value));
773     // One sign bit plus number of value bits
774     const int num_bits = 1 + std::numeric_limits<T>::digits;
775     // Only the first 7 bits are available per byte. Had it not been
776     // for the fact that maximum guaranteed bit width of a char is 8,
777     // this value could have been increased to 15 (one less than the
778     // number of value bits in 'unsigned').
779     const int bits_per_byte = 7;
780     const int max_bytes = (num_bits + (bits_per_byte - 1)) / bits_per_byte;
781     static_assert(max_bytes <= max_enc_bytes_per_int, "Bad max_enc_bytes_per_int");
782     // An explicit constant maximum number of iterations is specified
783     // in the hope that it will help the optimizer (to do loop
784     // unrolling, for example).
785     typedef unsigned char uchar;
786     for (int i = 0; i < max_bytes; ++i) {
787         if (value >> (bits_per_byte - 1) == 0)
788             break;
789         *reinterpret_cast<uchar*>(ptr) = uchar((1U << bits_per_byte) | unsigned(value & ((1U << bits_per_byte) - 1)));
790         ++ptr;
791         value >>= bits_per_byte;
792     }
793     *reinterpret_cast<uchar*>(ptr) = uchar(negative ? (1U << (bits_per_byte - 1)) | unsigned(value) : value);
794     return ++ptr;
795 }
796
797 template <class T>
798 char* TransactLogEncoder::encode(char* ptr, T value)
799 {
800     auto value_2 = value + 0; // Perform integral promotion
801     return encode_int(ptr, value_2);
802 }
803
804 template <>
805 inline char* TransactLogEncoder::encode<char>(char* ptr, char value)
806 {
807     // Write the char as-is without encoding.
808     *ptr++ = value;
809     return ptr;
810 }
811
812 template <>
813 inline char* TransactLogEncoder::encode<Instruction>(char* ptr, Instruction inst)
814 {
815     return encode<char>(ptr, inst);
816 }
817
818 template <>
819 inline char* TransactLogEncoder::encode<bool>(char* ptr, bool value)
820 {
821     return encode<char>(ptr, value);
822 }
823
824 template <>
825 inline char* TransactLogEncoder::encode<float>(char* ptr, float value)
826 {
827     static_assert(std::numeric_limits<float>::is_iec559 &&
828                       sizeof(float) * std::numeric_limits<unsigned char>::digits == 32,
829                   "Unsupported 'float' representation");
830     const char* val_ptr = reinterpret_cast<char*>(&value);
831     return realm::safe_copy_n(val_ptr, sizeof value, ptr);
832 }
833
834 template <>
835 inline char* TransactLogEncoder::encode<double>(char* ptr, double value)
836 {
837     static_assert(std::numeric_limits<double>::is_iec559 &&
838                       sizeof(double) * std::numeric_limits<unsigned char>::digits == 64,
839                   "Unsupported 'double' representation");
840     const char* val_ptr = reinterpret_cast<char*>(&value);
841     return realm::safe_copy_n(val_ptr, sizeof value, ptr);
842 }
843
844 template <>
845 inline char* TransactLogEncoder::encode<DataType>(char* ptr, DataType type)
846 {
847     return encode<char>(ptr, type);
848 }
849
850 template <>
851 inline char* TransactLogEncoder::encode<StringData>(char* ptr, StringData s)
852 {
853     ptr = encode_int(ptr, s.size());
854     return std::copy_n(s.data(), s.size(), ptr);
855 }
856
857 template <>
858 inline char* TransactLogEncoder::encode<TransactLogEncoder::IntegerList>(char* ptr,
859                                                                          TransactLogEncoder::IntegerList list)
860 {
861     IntegerColumnIterator i = std::get<0>(list);
862     IntegerColumnIterator end = std::get<1>(list);
863
864     while (end - i > max_numbers_per_chunk) {
865         for (int j = 0; j < max_numbers_per_chunk; ++j)
866             ptr = encode_int(ptr, *i++);
867         advance(ptr);
868         size_t max_required_bytes_2 = max_enc_bytes_per_num * max_numbers_per_chunk;
869         ptr = reserve(max_required_bytes_2); // Throws
870     }
871
872     while (i != end)
873         ptr = encode_int(ptr, *i++);
874
875     return ptr;
876 }
877
878 template <>
879 inline char* TransactLogEncoder::encode<TransactLogEncoder::UnsignedList>(char* ptr,
880                                                                           TransactLogEncoder::UnsignedList list)
881 {
882     const size_t* i = std::get<0>(list);
883     const size_t* end = std::get<1>(list);
884
885     while (i != end)
886         ptr = encode_int(ptr, *i++);
887
888     return ptr;
889 }
890
891 template <class T>
892 size_t TransactLogEncoder::max_size(T)
893 {
894     return max_enc_bytes_per_num;
895 }
896
897 template <>
898 inline size_t TransactLogEncoder::max_size(char)
899 {
900     return 1;
901 }
902
903 template <>
904 inline size_t TransactLogEncoder::max_size(bool)
905 {
906     return 1;
907 }
908
909 template <>
910 inline size_t TransactLogEncoder::max_size(Instruction)
911 {
912     return 1;
913 }
914
915 template <>
916 inline size_t TransactLogEncoder::max_size(DataType)
917 {
918     return 1;
919 }
920
921 template <>
922 inline size_t TransactLogEncoder::max_size(StringData s)
923 {
924     return max_enc_bytes_per_num + s.size();
925 }
926
927 template <>
928 inline size_t TransactLogEncoder::max_size<TransactLogEncoder::IntegerList>(IntegerList)
929 {
930     // We only allocate space for 'max_numbers_per_chunk' at a time
931     return max_enc_bytes_per_num * max_numbers_per_chunk;
932 }
933
934 template <>
935 inline size_t TransactLogEncoder::max_size<TransactLogEncoder::UnsignedList>(UnsignedList list)
936 {
937     const size_t* begin = std::get<0>(list);
938     const size_t* end = std::get<1>(list);
939     // list contains (end - begin) elements
940     return max_enc_bytes_per_num * (end - begin);
941 }
942
943 template <class... L>
944 void TransactLogEncoder::append_simple_instr(L... numbers)
945 {
946     size_t max_required_bytes = max_size_list(numbers...);
947     char* ptr = reserve(max_required_bytes); // Throws
948     encode_list(ptr, numbers...);
949 }
950
951 template <class... L>
952 void TransactLogEncoder::append_mixed_instr(Instruction instr, const Mixed& value, L... numbers)
953 {
954     DataType type = value.get_type();
955     switch (type) {
956         case type_Int:
957             append_simple_instr(instr, numbers..., type, value.get_int()); // Throws
958             return;
959         case type_Bool:
960             append_simple_instr(instr, numbers..., type, value.get_bool()); // Throws
961             return;
962         case type_Float:
963             append_simple_instr(instr, numbers..., type, value.get_float()); // Throws
964             return;
965         case type_Double:
966             append_simple_instr(instr, numbers..., type, value.get_double()); // Throws
967             return;
968         case type_OldDateTime: {
969             auto value_2 = value.get_olddatetime().get_olddatetime();
970             append_simple_instr(instr, numbers..., type, value_2); // Throws
971             return;
972         }
973         case type_String: {
974             append_simple_instr(instr, numbers..., type, value.get_string()); // Throws
975             return;
976         }
977         case type_Binary: {
978             BinaryData value_2 = value.get_binary();
979             StringData value_3(value_2.data(), value_2.size());
980             append_simple_instr(instr, numbers..., type, value_3); // Throws
981             return;
982         }
983         case type_Timestamp: {
984             Timestamp ts = value.get_timestamp();
985             int64_t seconds = ts.get_seconds();
986             int32_t nano_seconds = ts.get_nanoseconds();
987             append_simple_instr(instr, numbers..., type, seconds, nano_seconds); // Throws
988             return;
989         }
990         case type_Table:
991             append_simple_instr(instr, numbers..., type); // Throws
992             return;
993         case type_Mixed:
994             // Mixed in mixed is not possible
995             REALM_TERMINATE("Mixed in Mixed not possible");
996         case type_Link:
997         case type_LinkList:
998             // FIXME: Need to handle new link types here.
999             REALM_TERMINATE("Link types in Mixed not supported.");
1000     }
1001     REALM_TERMINATE("Invalid Mixed.");
1002 }
1003
1004 inline void TransactLogConvenientEncoder::unselect_all() noexcept
1005 {
1006     m_selected_table = nullptr;
1007     m_selected_spec = nullptr;
1008     // no race with on_link_list_destroyed since both are setting to nullptr
1009     m_selected_link_list = nullptr;
1010 }
1011
1012 inline void TransactLogConvenientEncoder::select_table(const Table* table)
1013 {
1014     if (table != m_selected_table)
1015         do_select_table(table); // Throws
1016     m_selected_spec = nullptr;
1017     // no race with on_link_list_destroyed since both are setting to nullptr
1018     m_selected_link_list = nullptr;
1019 }
1020
1021 inline void TransactLogConvenientEncoder::select_desc(const Descriptor& desc)
1022 {
1023     typedef _impl::DescriptorFriend df;
1024     if (&df::get_spec(desc) != m_selected_spec)
1025         do_select_desc(desc); // Throws
1026     // no race with on_link_list_destroyed since both are setting to nullptr
1027     m_selected_link_list = nullptr;
1028 }
1029
1030 inline void TransactLogConvenientEncoder::select_link_list(const LinkView& list)
1031 {
1032     // A race between this and a call to on_link_list_destroyed() must
1033     // end up with m_selected_link_list pointing to the list argument given
1034     // here. We assume that the list given to on_link_list_destroyed() can
1035     // *never* be the same as the list argument given here. We resolve the
1036     // race by a) always updating m_selected_link_list in do_select_link_list()
1037     // and b) only atomically and conditionally updating it in
1038     // on_link_list_destroyed().
1039     if (&list != m_selected_link_list) {
1040         do_select_link_list(list); // Throws
1041     }
1042     m_selected_spec = nullptr;
1043 }
1044
1045
1046 inline bool TransactLogEncoder::insert_group_level_table(size_t table_ndx, size_t prior_num_tables, StringData name)
1047 {
1048     append_simple_instr(instr_InsertGroupLevelTable, table_ndx, prior_num_tables, name); // Throws
1049     return true;
1050 }
1051
1052 inline void TransactLogConvenientEncoder::insert_group_level_table(size_t table_ndx, size_t prior_num_tables,
1053                                                                    StringData name)
1054 {
1055     unselect_all();
1056     m_encoder.insert_group_level_table(table_ndx, prior_num_tables, name); // Throws
1057 }
1058
1059 inline bool TransactLogEncoder::erase_group_level_table(size_t table_ndx, size_t prior_num_tables)
1060 {
1061     append_simple_instr(instr_EraseGroupLevelTable, table_ndx, prior_num_tables); // Throws
1062     return true;
1063 }
1064
1065 inline void TransactLogConvenientEncoder::erase_group_level_table(size_t table_ndx, size_t prior_num_tables)
1066 {
1067     unselect_all();
1068     m_encoder.erase_group_level_table(table_ndx, prior_num_tables); // Throws
1069 }
1070
1071 inline bool TransactLogEncoder::rename_group_level_table(size_t table_ndx, StringData new_name)
1072 {
1073     append_simple_instr(instr_RenameGroupLevelTable, table_ndx, new_name); // Throws
1074     return true;
1075 }
1076
1077 inline void TransactLogConvenientEncoder::rename_group_level_table(size_t table_ndx, StringData new_name)
1078 {
1079     unselect_all();
1080     m_encoder.rename_group_level_table(table_ndx, new_name); // Throws
1081 }
1082
1083 inline bool TransactLogEncoder::insert_column(size_t col_ndx, DataType type, StringData name, bool nullable)
1084 {
1085     Instruction instr = (nullable ? instr_InsertNullableColumn : instr_InsertColumn);
1086     append_simple_instr(instr, col_ndx, type, name); // Throws
1087     return true;
1088 }
1089
1090 inline bool TransactLogEncoder::insert_link_column(size_t col_ndx, DataType type, StringData name,
1091                                                    size_t link_target_table_ndx, size_t backlink_col_ndx)
1092 {
1093     REALM_ASSERT(_impl::TableFriend::is_link_type(ColumnType(type)));
1094     append_simple_instr(instr_InsertLinkColumn, col_ndx, type, link_target_table_ndx, backlink_col_ndx,
1095                         name); // Throws
1096     return true;
1097 }
1098
1099
1100 inline void TransactLogConvenientEncoder::insert_column(const Descriptor& desc, size_t col_ndx, DataType type,
1101                                                         StringData name, LinkTargetInfo& link, bool nullable)
1102 {
1103     select_desc(desc); // Throws
1104     if (link.is_valid()) {
1105         typedef _impl::TableFriend tf;
1106         typedef _impl::DescriptorFriend df;
1107         size_t target_table_ndx = link.m_target_table->get_index_in_group();
1108         const Table& origin_table = df::get_root_table(desc);
1109         REALM_ASSERT(origin_table.is_group_level());
1110         const Spec& target_spec = tf::get_spec(*(link.m_target_table));
1111         size_t origin_table_ndx = origin_table.get_index_in_group();
1112         size_t backlink_col_ndx = target_spec.find_backlink_column(origin_table_ndx, col_ndx);
1113         REALM_ASSERT_3(backlink_col_ndx, ==, link.m_backlink_col_ndx);
1114         m_encoder.insert_link_column(col_ndx, type, name, target_table_ndx, backlink_col_ndx); // Throws
1115     }
1116     else {
1117         m_encoder.insert_column(col_ndx, type, name, nullable); // Throws
1118     }
1119 }
1120
1121 inline bool TransactLogEncoder::erase_column(size_t col_ndx)
1122 {
1123     append_simple_instr(instr_EraseColumn, col_ndx); // Throws
1124     return true;
1125 }
1126
1127 inline bool TransactLogEncoder::erase_link_column(size_t col_ndx, size_t link_target_table_ndx,
1128                                                   size_t backlink_col_ndx)
1129 {
1130     append_simple_instr(instr_EraseLinkColumn, col_ndx, link_target_table_ndx, backlink_col_ndx); // Throws
1131     return true;
1132 }
1133
1134 inline void TransactLogConvenientEncoder::erase_column(const Descriptor& desc, size_t col_ndx)
1135 {
1136     select_desc(desc); // Throws
1137
1138     DataType type = desc.get_column_type(col_ndx);
1139     typedef _impl::TableFriend tf;
1140     if (!tf::is_link_type(ColumnType(type))) {
1141         m_encoder.erase_column(col_ndx); // Throws
1142     }
1143     else { // it's a link column:
1144         REALM_ASSERT(desc.is_root());
1145         typedef _impl::DescriptorFriend df;
1146         const Table& origin_table = df::get_root_table(desc);
1147         REALM_ASSERT(origin_table.is_group_level());
1148         const Table& target_table = *tf::get_link_target_table_accessor(origin_table, col_ndx);
1149         size_t target_table_ndx = target_table.get_index_in_group();
1150         const Spec& target_spec = tf::get_spec(target_table);
1151         size_t origin_table_ndx = origin_table.get_index_in_group();
1152         size_t backlink_col_ndx = target_spec.find_backlink_column(origin_table_ndx, col_ndx);
1153         m_encoder.erase_link_column(col_ndx, target_table_ndx, backlink_col_ndx); // Throws
1154     }
1155 }
1156
1157 inline bool TransactLogEncoder::rename_column(size_t col_ndx, StringData new_name)
1158 {
1159     append_simple_instr(instr_RenameColumn, col_ndx, new_name); // Throws
1160     return true;
1161 }
1162
1163 inline void TransactLogConvenientEncoder::rename_column(const Descriptor& desc, size_t col_ndx, StringData name)
1164 {
1165     select_desc(desc);                      // Throws
1166     m_encoder.rename_column(col_ndx, name); // Throws
1167 }
1168
1169
1170 inline bool TransactLogEncoder::set_int(size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant,
1171                                         size_t prior_num_rows)
1172 {
1173     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant);
1174     if (REALM_UNLIKELY(variant == instr_SetUnique))
1175         append_simple_instr(variant, type_Int, col_ndx, ndx, prior_num_rows, value); // Throws
1176     else
1177         append_simple_instr(variant, type_Int, col_ndx, ndx, value); // Throws
1178     return true;
1179 }
1180
1181 inline void TransactLogConvenientEncoder::set_int(const Table* t, size_t col_ndx, size_t ndx, int_fast64_t value,
1182                                                   Instruction variant)
1183 {
1184     select_table(t); // Throws
1185     size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0);
1186     m_encoder.set_int(col_ndx, ndx, value, variant, prior_num_rows); // Throws
1187 }
1188
1189
1190 inline bool TransactLogEncoder::add_int(size_t col_ndx, size_t ndx, int_fast64_t value)
1191 {
1192     append_simple_instr(instr_AddInteger, col_ndx, ndx, value); // Throws
1193     return true;
1194 }
1195
1196 inline void TransactLogConvenientEncoder::add_int(const Table* t, size_t col_ndx, size_t ndx, int_fast64_t value)
1197 {
1198     select_table(t); // Throws
1199     m_encoder.add_int(col_ndx, ndx, value);
1200 }
1201
1202 inline bool TransactLogEncoder::set_bool(size_t col_ndx, size_t ndx, bool value, Instruction variant)
1203 {
1204     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1205     append_simple_instr(variant, type_Bool, col_ndx, ndx, value); // Throws
1206     return true;
1207 }
1208
1209 inline void TransactLogConvenientEncoder::set_bool(const Table* t, size_t col_ndx, size_t ndx, bool value,
1210                                                    Instruction variant)
1211 {
1212     select_table(t);                                  // Throws
1213     m_encoder.set_bool(col_ndx, ndx, value, variant); // Throws
1214 }
1215
1216 inline bool TransactLogEncoder::set_float(size_t col_ndx, size_t ndx, float value, Instruction variant)
1217 {
1218     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1219     append_simple_instr(variant, type_Float, col_ndx, ndx, value); // Throws
1220     return true;
1221 }
1222
1223 inline void TransactLogConvenientEncoder::set_float(const Table* t, size_t col_ndx, size_t ndx, float value,
1224                                                     Instruction variant)
1225 {
1226     select_table(t);                                   // Throws
1227     m_encoder.set_float(col_ndx, ndx, value, variant); // Throws
1228 }
1229
1230 inline bool TransactLogEncoder::set_double(size_t col_ndx, size_t ndx, double value, Instruction variant)
1231 {
1232     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1233     append_simple_instr(instr_Set, type_Double, col_ndx, ndx, value); // Throws
1234     return true;
1235 }
1236
1237 inline void TransactLogConvenientEncoder::set_double(const Table* t, size_t col_ndx, size_t ndx, double value,
1238                                                      Instruction variant)
1239 {
1240     select_table(t);                                    // Throws
1241     m_encoder.set_double(col_ndx, ndx, value, variant); // Throws
1242 }
1243
1244 inline bool TransactLogEncoder::set_string(size_t col_ndx, size_t ndx, StringData value, Instruction variant,
1245                                            size_t prior_num_rows)
1246 {
1247     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant);
1248     if (value.is_null()) {
1249         set_null(col_ndx, ndx, variant, prior_num_rows); // Throws
1250     }
1251     else {
1252         if (REALM_UNLIKELY(variant == instr_SetUnique))
1253             append_simple_instr(variant, type_String, col_ndx, ndx, prior_num_rows, value); // Throws
1254         else
1255             append_simple_instr(variant, type_String, col_ndx, ndx, value); // Throws
1256     }
1257     return true;
1258 }
1259
1260 inline void TransactLogConvenientEncoder::set_string(const Table* t, size_t col_ndx, size_t ndx, StringData value,
1261                                                      Instruction variant)
1262 {
1263     select_table(t); // Throws
1264     size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0);
1265     m_encoder.set_string(col_ndx, ndx, value, variant, prior_num_rows); // Throws
1266 }
1267
1268 inline bool TransactLogEncoder::set_binary(size_t col_ndx, size_t row_ndx, BinaryData value, Instruction variant)
1269 {
1270     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1271     if (value.is_null()) {
1272         set_null(col_ndx, row_ndx, variant); // Throws
1273     }
1274     else {
1275         StringData value_2(value.data(), value.size());
1276         append_simple_instr(variant, type_Binary, col_ndx, row_ndx, value_2); // Throws
1277     }
1278     return true;
1279 }
1280
1281 inline void TransactLogConvenientEncoder::set_binary(const Table* t, size_t col_ndx, size_t ndx, BinaryData value,
1282                                                      Instruction variant)
1283 {
1284     select_table(t);                                    // Throws
1285     m_encoder.set_binary(col_ndx, ndx, value, variant); // Throws
1286 }
1287
1288 inline bool TransactLogEncoder::set_olddatetime(size_t col_ndx, size_t ndx, OldDateTime value, Instruction variant)
1289 {
1290     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1291     append_simple_instr(variant, type_OldDateTime, col_ndx, ndx, value.get_olddatetime()); // Throws
1292     return true;
1293 }
1294
1295 inline void TransactLogConvenientEncoder::set_olddatetime(const Table* t, size_t col_ndx, size_t ndx,
1296                                                           OldDateTime value, Instruction variant)
1297 {
1298     select_table(t);                                         // Throws
1299     m_encoder.set_olddatetime(col_ndx, ndx, value, variant); // Throws
1300 }
1301
1302 inline bool TransactLogEncoder::set_timestamp(size_t col_ndx, size_t ndx, Timestamp value, Instruction variant)
1303 {
1304     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1305     append_simple_instr(variant, type_Timestamp, col_ndx, ndx, value.get_seconds(),
1306                         value.get_nanoseconds()); // Throws
1307     return true;
1308 }
1309
1310 inline void TransactLogConvenientEncoder::set_timestamp(const Table* t, size_t col_ndx, size_t ndx, Timestamp value,
1311                                                         Instruction variant)
1312 {
1313     select_table(t);                                       // Throws
1314     m_encoder.set_timestamp(col_ndx, ndx, value, variant); // Throws
1315 }
1316
1317 inline bool TransactLogEncoder::set_table(size_t col_ndx, size_t ndx, Instruction variant)
1318 {
1319     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1320     append_simple_instr(variant, type_Table, col_ndx, ndx); // Throws
1321     return true;
1322 }
1323
1324 inline void TransactLogConvenientEncoder::set_table(const Table* t, size_t col_ndx, size_t ndx, Instruction variant)
1325 {
1326     select_table(t);                            // Throws
1327     m_encoder.set_table(col_ndx, ndx, variant); // Throws
1328 }
1329
1330 inline bool TransactLogEncoder::set_mixed(size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant)
1331 {
1332     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1333     append_mixed_instr(variant, value, type_Mixed, col_ndx, ndx); // Throws
1334     return true;
1335 }
1336
1337 inline void TransactLogConvenientEncoder::set_mixed(const Table* t, size_t col_ndx, size_t ndx, const Mixed& value,
1338                                                     Instruction variant)
1339 {
1340     select_table(t);                                   // Throws
1341     m_encoder.set_mixed(col_ndx, ndx, value, variant); // Throws
1342 }
1343
1344 inline bool TransactLogEncoder::set_link(size_t col_ndx, size_t ndx, size_t value, size_t target_group_level_ndx,
1345                                          Instruction variant)
1346 {
1347     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1348     // Map `realm::npos` to zero, and `n` to `n+1`, where `n` is a target row
1349     // index.
1350     size_t value_2 = size_t(1) + value;
1351     append_simple_instr(variant, type_Link, col_ndx, ndx, value_2, target_group_level_ndx); // Throws
1352     return true;
1353 }
1354
1355 inline void TransactLogConvenientEncoder::set_link(const Table* t, size_t col_ndx, size_t ndx, size_t value,
1356                                                    Instruction variant)
1357 {
1358     select_table(t); // Throws
1359     size_t target_group_level_ndx = t->get_descriptor()->get_column_link_target(col_ndx);
1360     m_encoder.set_link(col_ndx, ndx, value, target_group_level_ndx, variant); // Throws
1361 }
1362
1363 inline bool TransactLogEncoder::set_null(size_t col_ndx, size_t ndx, Instruction variant, size_t prior_num_rows)
1364 {
1365     REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant);
1366     if (REALM_UNLIKELY(variant == instr_SetUnique)) {
1367         append_simple_instr(variant, set_null_sentinel(), col_ndx, ndx, prior_num_rows); // Throws
1368     }
1369     else {
1370         append_simple_instr(variant, set_null_sentinel(), col_ndx, ndx); // Throws
1371     }
1372     return true;
1373 }
1374
1375 inline void TransactLogConvenientEncoder::set_null(const Table* t, size_t col_ndx, size_t row_ndx,
1376                                                    Instruction variant)
1377 {
1378     select_table(t); // Throws
1379     size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0);
1380     m_encoder.set_null(col_ndx, row_ndx, variant, prior_num_rows); // Throws
1381 }
1382
1383 inline bool TransactLogEncoder::nullify_link(size_t col_ndx, size_t ndx, size_t target_group_level_ndx)
1384 {
1385     append_simple_instr(instr_NullifyLink, col_ndx, ndx, target_group_level_ndx); // Throws
1386     return true;
1387 }
1388
1389 inline void TransactLogConvenientEncoder::nullify_link(const Table* t, size_t col_ndx, size_t ndx)
1390 {
1391     select_table(t); // Throws
1392     size_t target_group_level_ndx = t->get_descriptor()->get_column_link_target(col_ndx);
1393     m_encoder.nullify_link(col_ndx, ndx, target_group_level_ndx); // Throws
1394 }
1395
1396 inline bool TransactLogEncoder::insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData value)
1397 {
1398     append_simple_instr(instr_InsertSubstring, col_ndx, row_ndx, pos, value); // Throws
1399     return true;
1400 }
1401
1402 inline void TransactLogConvenientEncoder::insert_substring(const Table* t, size_t col_ndx, size_t row_ndx, size_t pos,
1403                                                            StringData value)
1404 {
1405     if (value.size() > 0) {
1406         select_table(t);                                          // Throws
1407         m_encoder.insert_substring(col_ndx, row_ndx, pos, value); // Throws
1408     }
1409 }
1410
1411 inline bool TransactLogEncoder::erase_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t size)
1412 {
1413     append_simple_instr(instr_EraseFromString, col_ndx, row_ndx, pos, size); // Throws
1414     return true;
1415 }
1416
1417 inline void TransactLogConvenientEncoder::erase_substring(const Table* t, size_t col_ndx, size_t row_ndx, size_t pos,
1418                                                           size_t size)
1419 {
1420     if (size > 0) {
1421         select_table(t);                                        // Throws
1422         m_encoder.erase_substring(col_ndx, row_ndx, pos, size); // Throws
1423     }
1424 }
1425
1426 inline bool TransactLogEncoder::insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows,
1427                                                   bool unordered)
1428 {
1429     append_simple_instr(instr_InsertEmptyRows, row_ndx, num_rows_to_insert, prior_num_rows, unordered); // Throws
1430     return true;
1431 }
1432
1433 inline void TransactLogConvenientEncoder::insert_empty_rows(const Table* t, size_t row_ndx, size_t num_rows_to_insert,
1434                                                             size_t prior_num_rows)
1435 {
1436     select_table(t); // Throws
1437     bool unordered = false;
1438     m_encoder.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows, unordered); // Throws
1439 }
1440
1441 inline bool TransactLogEncoder::add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx,
1442                                                  int64_t key)
1443 {
1444     append_simple_instr(instr_AddRowWithKey, row_ndx, prior_num_rows, key_col_ndx, key); // Throws
1445     return true;
1446 }
1447
1448 inline void TransactLogConvenientEncoder::add_row_with_key(const Table* t, size_t row_ndx, size_t prior_num_rows,
1449                                                            size_t key_col_ndx, int64_t key)
1450 {
1451     select_table(t);                                          // Throws
1452     m_encoder.add_row_with_key(row_ndx, prior_num_rows, key_col_ndx, key); // Throws
1453 }
1454
1455 inline bool TransactLogEncoder::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows,
1456                                            bool unordered)
1457 {
1458     append_simple_instr(instr_EraseRows, row_ndx, num_rows_to_erase, prior_num_rows, unordered); // Throws
1459     return true;
1460 }
1461
1462
1463 inline void TransactLogConvenientEncoder::erase_rows(const Table* t, size_t row_ndx, size_t num_rows_to_erase,
1464                                                      size_t prior_num_rows, bool is_move_last_over)
1465 {
1466     select_table(t); // Throws
1467     bool unordered = is_move_last_over;
1468     m_encoder.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, unordered); // Throws
1469 }
1470
1471 inline bool TransactLogEncoder::swap_rows(size_t row_ndx_1, size_t row_ndx_2)
1472 {
1473     append_simple_instr(instr_SwapRows, row_ndx_1, row_ndx_2); // Throws
1474     return true;
1475 }
1476
1477 inline void TransactLogConvenientEncoder::swap_rows(const Table* t, size_t row_ndx_1, size_t row_ndx_2)
1478 {
1479     REALM_ASSERT(row_ndx_1 < row_ndx_2);
1480     select_table(t); // Throws
1481     m_encoder.swap_rows(row_ndx_1, row_ndx_2);
1482 }
1483
1484 inline bool TransactLogEncoder::move_row(size_t from_ndx, size_t to_ndx)
1485 {
1486     append_simple_instr(instr_MoveRow, from_ndx, to_ndx); // Throws
1487     return true;
1488 }
1489
1490 inline void TransactLogConvenientEncoder::move_row(const Table* t, size_t from_ndx, size_t to_ndx)
1491 {
1492     REALM_ASSERT(from_ndx != to_ndx);
1493     select_table(t); // Throws
1494     m_encoder.move_row(from_ndx, to_ndx);
1495 }
1496
1497 inline bool TransactLogEncoder::merge_rows(size_t row_ndx, size_t new_row_ndx)
1498 {
1499     append_simple_instr(instr_MergeRows, row_ndx, new_row_ndx); // Throws
1500     return true;
1501 }
1502
1503 inline void TransactLogConvenientEncoder::merge_rows(const Table* t, size_t row_ndx, size_t new_row_ndx)
1504 {
1505     select_table(t); // Throws
1506     m_encoder.merge_rows(row_ndx, new_row_ndx);
1507 }
1508
1509 inline bool TransactLogEncoder::add_search_index(size_t col_ndx)
1510 {
1511     append_simple_instr(instr_AddSearchIndex, col_ndx); // Throws
1512     return true;
1513 }
1514
1515 inline void TransactLogConvenientEncoder::add_search_index(const Descriptor& desc, size_t col_ndx)
1516 {
1517     select_desc(desc);                   // Throws
1518     m_encoder.add_search_index(col_ndx); // Throws
1519 }
1520
1521
1522 inline bool TransactLogEncoder::remove_search_index(size_t col_ndx)
1523 {
1524     append_simple_instr(instr_RemoveSearchIndex, col_ndx); // Throws
1525     return true;
1526 }
1527
1528 inline void TransactLogConvenientEncoder::remove_search_index(const Descriptor& desc, size_t col_ndx)
1529 {
1530     select_desc(desc);                      // Throws
1531     m_encoder.remove_search_index(col_ndx); // Throws
1532 }
1533
1534 inline bool TransactLogEncoder::set_link_type(size_t col_ndx, LinkType link_type)
1535 {
1536     append_simple_instr(instr_SetLinkType, col_ndx, int(link_type)); // Throws
1537     return true;
1538 }
1539
1540 inline void TransactLogConvenientEncoder::set_link_type(const Table* t, size_t col_ndx, LinkType link_type)
1541 {
1542     select_table(t);                             // Throws
1543     m_encoder.set_link_type(col_ndx, link_type); // Throws
1544 }
1545
1546
1547 inline bool TransactLogEncoder::clear_table(size_t old_size)
1548 {
1549     append_simple_instr(instr_ClearTable, old_size); // Throws
1550     return true;
1551 }
1552
1553 inline void TransactLogConvenientEncoder::clear_table(const Table* t, size_t prior_num_rows)
1554 {
1555     select_table(t);         // Throws
1556     m_encoder.clear_table(prior_num_rows); // Throws
1557 }
1558
1559 inline bool TransactLogEncoder::optimize_table()
1560 {
1561     append_simple_instr(instr_OptimizeTable); // Throws
1562     return true;
1563 }
1564
1565 inline void TransactLogConvenientEncoder::optimize_table(const Table* t)
1566 {
1567     select_table(t);            // Throws
1568     m_encoder.optimize_table(); // Throws
1569 }
1570
1571 inline bool TransactLogEncoder::link_list_set(size_t link_ndx, size_t value, size_t prior_size)
1572 {
1573     append_simple_instr(instr_LinkListSet, link_ndx, value, prior_size); // Throws
1574     return true;
1575 }
1576
1577 inline void TransactLogConvenientEncoder::link_list_set(const LinkView& list, size_t link_ndx, size_t value)
1578 {
1579     select_link_list(list);                                // Throws
1580     m_encoder.link_list_set(link_ndx, value, list.size()); // Throws
1581 }
1582
1583 inline bool TransactLogEncoder::link_list_nullify(size_t link_ndx, size_t prior_size)
1584 {
1585     append_simple_instr(instr_LinkListNullify, link_ndx, prior_size); // Throws
1586     return true;
1587 }
1588
1589 inline void TransactLogConvenientEncoder::link_list_nullify(const LinkView& list, size_t link_ndx)
1590 {
1591     select_link_list(list);                            // Throws
1592     size_t prior_size = list.size();                   // Instruction is emitted before the fact.
1593     m_encoder.link_list_nullify(link_ndx, prior_size); // Throws
1594 }
1595
1596 inline bool TransactLogEncoder::link_list_set_all(const IntegerColumn& values)
1597 {
1598     size_t num_values = values.size();
1599     append_simple_instr(
1600         instr_LinkListSetAll, num_values,
1601         std::make_tuple(IntegerColumnIterator(&values, 0), IntegerColumnIterator(&values, num_values))); // Throws
1602     return true;
1603 }
1604
1605 inline void TransactLogConvenientEncoder::set_link_list(const LinkView& list, const IntegerColumn& values)
1606 {
1607     select_link_list(list);              // Throws
1608     m_encoder.link_list_set_all(values); // Throws
1609 }
1610
1611 inline bool TransactLogEncoder::link_list_insert(size_t link_ndx, size_t value, size_t prior_size)
1612 {
1613     append_simple_instr(instr_LinkListInsert, link_ndx, value, prior_size); // Throws
1614     return true;
1615 }
1616
1617 inline void TransactLogConvenientEncoder::link_list_insert(const LinkView& list, size_t link_ndx, size_t value)
1618 {
1619     select_link_list(list);                                  // Throws
1620     size_t prior_size = list.size() - 1;                     // The instruction is emitted after the fact.
1621     m_encoder.link_list_insert(link_ndx, value, prior_size); // Throws
1622 }
1623
1624 inline bool TransactLogEncoder::link_list_move(size_t from_link_ndx, size_t to_link_ndx)
1625 {
1626     REALM_ASSERT(from_link_ndx != to_link_ndx);
1627     append_simple_instr(instr_LinkListMove, from_link_ndx, to_link_ndx); // Throws
1628     return true;
1629 }
1630
1631 inline void TransactLogConvenientEncoder::link_list_move(const LinkView& list, size_t from_link_ndx,
1632                                                          size_t to_link_ndx)
1633 {
1634     select_link_list(list);                               // Throws
1635     m_encoder.link_list_move(from_link_ndx, to_link_ndx); // Throws
1636 }
1637
1638 inline bool TransactLogEncoder::link_list_swap(size_t link1_ndx, size_t link2_ndx)
1639 {
1640     append_simple_instr(instr_LinkListSwap, link1_ndx, link2_ndx); // Throws
1641     return true;
1642 }
1643
1644 inline void TransactLogConvenientEncoder::link_list_swap(const LinkView& list, size_t link1_ndx, size_t link2_ndx)
1645 {
1646     select_link_list(list);                         // Throws
1647     m_encoder.link_list_swap(link1_ndx, link2_ndx); // Throws
1648 }
1649
1650 inline bool TransactLogEncoder::link_list_erase(size_t link_ndx, size_t prior_size)
1651 {
1652     append_simple_instr(instr_LinkListErase, link_ndx, prior_size); // Throws
1653     return true;
1654 }
1655
1656 inline void TransactLogConvenientEncoder::link_list_erase(const LinkView& list, size_t link_ndx)
1657 {
1658     select_link_list(list);                          // Throws
1659     size_t prior_size = list.size();                 // The instruction is emitted before the fact.
1660     m_encoder.link_list_erase(link_ndx, prior_size); // Throws
1661 }
1662
1663 inline bool TransactLogEncoder::link_list_clear(size_t old_list_size)
1664 {
1665     append_simple_instr(instr_LinkListClear, old_list_size); // Throws
1666     return true;
1667 }
1668
1669 inline void TransactLogConvenientEncoder::on_table_destroyed(const Table* t) noexcept
1670 {
1671     if (m_selected_table == t)
1672         m_selected_table = nullptr;
1673 }
1674
1675 inline void TransactLogConvenientEncoder::on_spec_destroyed(const Spec* s) noexcept
1676 {
1677     if (m_selected_spec == s)
1678         m_selected_spec = nullptr;
1679 }
1680
1681
1682 inline void TransactLogConvenientEncoder::on_link_list_destroyed(const LinkView& list) noexcept
1683 {
1684     const LinkView* lw_ptr = &list;
1685     // atomically clear m_selected_link_list iff it already points to 'list':
1686     // (lw_ptr will be modified if the swap fails, but we ignore that)
1687     m_selected_link_list.compare_exchange_strong(lw_ptr, nullptr, std::memory_order_relaxed,
1688                                                  std::memory_order_relaxed);
1689 }
1690
1691
1692 inline TransactLogParser::TransactLogParser()
1693     : m_input_buffer(1024) // Throws
1694 {
1695 }
1696
1697
1698 inline TransactLogParser::~TransactLogParser() noexcept
1699 {
1700 }
1701
1702
1703 template <class InstructionHandler>
1704 void TransactLogParser::parse(NoCopyInputStream& in, InstructionHandler& handler)
1705 {
1706     m_input = &in;
1707     m_input_begin = m_input_end = nullptr;
1708
1709     while (has_next())
1710         parse_one(handler); // Throws
1711 }
1712
1713 template <class InstructionHandler>
1714 void TransactLogParser::parse(InputStream& in, InstructionHandler& handler)
1715 {
1716     NoCopyInputStreamAdaptor in_2(in, m_input_buffer.data(), m_input_buffer.size());
1717     parse(in_2, handler); // Throws
1718 }
1719
1720 inline bool TransactLogParser::has_next() noexcept
1721 {
1722     return m_input_begin != m_input_end || next_input_buffer();
1723 }
1724
1725 template <class InstructionHandler>
1726 void TransactLogParser::parse_one(InstructionHandler& handler)
1727 {
1728     char instr_ch;
1729     if (!read_char(instr_ch))
1730         parser_error(); // Throws
1731     //    std::cerr << "parsing " << util::promote(instr) << " @ " << std::hex << long(m_input_begin) << std::dec <<
1732     //    "\n";
1733     Instruction instr = Instruction(instr_ch);
1734     switch (instr) {
1735         case instr_SetDefault:
1736         case instr_SetUnique:
1737         case instr_Set: {
1738             int type = read_int<int>();          // Throws
1739             size_t col_ndx = read_int<size_t>(); // Throws
1740             size_t row_ndx = read_int<size_t>(); // Throws
1741             size_t prior_num_rows = 0;
1742             if (REALM_UNLIKELY(instr == instr_SetUnique))
1743                 prior_num_rows = read_int<size_t>(); // Throws
1744
1745             if (type == TransactLogEncoder::set_null_sentinel()) {
1746                 // Special case for set_null
1747                 if (!handler.set_null(col_ndx, row_ndx, instr, prior_num_rows)) // Throws
1748                     parser_error();
1749                 return;
1750             }
1751
1752             switch (DataType(type)) {
1753                 case type_Int: {
1754                     int_fast64_t value = read_int<int64_t>();                             // Throws
1755                     if (!handler.set_int(col_ndx, row_ndx, value, instr, prior_num_rows)) // Throws
1756                         parser_error();
1757                     return;
1758                 }
1759                 case type_Bool: {
1760                     bool value = read_bool();                              // Throws
1761                     if (!handler.set_bool(col_ndx, row_ndx, value, instr)) // Throws
1762                         parser_error();
1763                     return;
1764                 }
1765                 case type_Float: {
1766                     float value = read_float();                             // Throws
1767                     if (!handler.set_float(col_ndx, row_ndx, value, instr)) // Throws
1768                         parser_error();
1769                     return;
1770                 }
1771                 case type_Double: {
1772                     double value = read_double();                            // Throws
1773                     if (!handler.set_double(col_ndx, row_ndx, value, instr)) // Throws
1774                         parser_error();
1775                     return;
1776                 }
1777                 case type_String: {
1778                     StringData value = read_string(m_string_buffer);                         // Throws
1779                     if (!handler.set_string(col_ndx, row_ndx, value, instr, prior_num_rows)) // Throws
1780                         parser_error();
1781                     return;
1782                 }
1783                 case type_Binary: {
1784                     BinaryData value = read_binary(m_string_buffer);         // Throws
1785                     if (!handler.set_binary(col_ndx, row_ndx, value, instr)) // Throws
1786                         parser_error();
1787                     return;
1788                 }
1789                 case type_OldDateTime: {
1790                     int_fast64_t value = read_int<int_fast64_t>();                // Throws
1791                     if (!handler.set_olddatetime(col_ndx, row_ndx, value, instr)) // Throws
1792                         parser_error();
1793                     return;
1794                 }
1795                 case type_Timestamp: {
1796                     int64_t seconds = read_int<int64_t>();     // Throws
1797                     int32_t nanoseconds = read_int<int32_t>(); // Throws
1798                     Timestamp value = Timestamp(seconds, nanoseconds);
1799                     if (!handler.set_timestamp(col_ndx, row_ndx, value, instr)) // Throws
1800                         parser_error();
1801                     return;
1802                 }
1803                 case type_Table: {
1804                     if (!handler.set_table(col_ndx, row_ndx, instr)) // Throws
1805                         parser_error();
1806                     return;
1807                 }
1808                 case type_Mixed: {
1809                     Mixed value;
1810                     read_mixed(&value);                                     // Throws
1811                     if (!handler.set_mixed(col_ndx, row_ndx, value, instr)) // Throws
1812                         parser_error();
1813                     return;
1814                 }
1815                 case type_Link: {
1816                     size_t value = read_int<size_t>(); // Throws
1817                     // Map zero to realm::npos, and `n+1` to `n`, where `n` is a target row index.
1818                     size_t target_row_ndx = size_t(value - 1);
1819                     size_t target_group_level_ndx = read_int<size_t>();                                     // Throws
1820                     if (!handler.set_link(col_ndx, row_ndx, target_row_ndx, target_group_level_ndx, instr)) // Throws
1821                         parser_error();
1822                     return;
1823                 }
1824                 case type_LinkList: {
1825                     // Unsupported column type for Set.
1826                     parser_error();
1827                     return;
1828                 }
1829             }
1830             parser_error();
1831             return;
1832         }
1833         case instr_AddInteger: {
1834             size_t col_ndx = read_int<size_t>();           // Throws
1835             size_t row_ndx = read_int<size_t>();           // Throws
1836             int_fast64_t value = read_int<int64_t>();      // Throws
1837             if (!handler.add_int(col_ndx, row_ndx, value)) // Throws
1838                 parser_error();
1839             return;
1840         }
1841         case instr_NullifyLink: {
1842             size_t col_ndx = read_int<size_t>();                                 // Throws
1843             size_t row_ndx = read_int<size_t>();                                 // Throws
1844             size_t target_group_level_ndx = read_int<size_t>();                  // Throws
1845             if (!handler.nullify_link(col_ndx, row_ndx, target_group_level_ndx)) // Throws
1846                 parser_error();
1847             return;
1848         }
1849         case instr_InsertSubstring: {
1850             size_t col_ndx = read_int<size_t>();                         // Throws
1851             size_t row_ndx = read_int<size_t>();                         // Throws
1852             size_t pos = read_int<size_t>();                             // Throws
1853             StringData value = read_string(m_string_buffer);             // Throws
1854             if (!handler.insert_substring(col_ndx, row_ndx, pos, value)) // Throws
1855                 parser_error();
1856             return;
1857         }
1858         case instr_EraseFromString: {
1859             size_t col_ndx = read_int<size_t>();                       // Throws
1860             size_t row_ndx = read_int<size_t>();                       // Throws
1861             size_t pos = read_int<size_t>();                           // Throws
1862             size_t size = read_int<size_t>();                          // Throws
1863             if (!handler.erase_substring(col_ndx, row_ndx, pos, size)) // Throws
1864                 parser_error();
1865             return;
1866         }
1867         case instr_InsertEmptyRows: {
1868             size_t row_ndx = read_int<size_t>();                                                    // Throws
1869             size_t num_rows_to_insert = read_int<size_t>();                                         // Throws
1870             size_t prior_num_rows = read_int<size_t>();                                             // Throws
1871             bool unordered = read_bool();                                                           // Throws
1872             if (!handler.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows, unordered)) // Throws
1873                 parser_error();
1874             return;
1875         }
1876         case instr_AddRowWithKey: {
1877             size_t row_ndx = read_int<size_t>();                         // Throws
1878             size_t prior_num_rows = read_int<size_t>();                  // Throws
1879             size_t key_col_ndx = read_int<size_t>();                     // Throws
1880             int64_t key = read_int<int64_t>();                           // Throws
1881             if (!handler.add_row_with_key(row_ndx, prior_num_rows, key_col_ndx, key)) // Throws
1882                 parser_error();
1883             return;
1884         }
1885         case instr_EraseRows: {
1886             size_t row_ndx = read_int<size_t>();                                            // Throws
1887             size_t num_rows_to_erase = read_int<size_t>();                                  // Throws
1888             size_t prior_num_rows = read_int<size_t>();                                     // Throws
1889             bool unordered = read_bool();                                                   // Throws
1890             if (!handler.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, unordered)) // Throws
1891                 parser_error();
1892             return;
1893         }
1894         case instr_SwapRows: {
1895             size_t row_ndx_1 = read_int<size_t>();        // Throws
1896             size_t row_ndx_2 = read_int<size_t>();        // Throws
1897             if (!handler.swap_rows(row_ndx_1, row_ndx_2)) // Throws
1898                 parser_error();
1899             return;
1900         }
1901         case instr_MoveRow: {
1902             size_t from_ndx = read_int<size_t>();    // Throws
1903             size_t to_ndx = read_int<size_t>();      // Throws
1904             if (!handler.move_row(from_ndx, to_ndx)) // Throws
1905                 parser_error();
1906             return;
1907         }
1908         case instr_MergeRows: {
1909             size_t row_ndx = read_int<size_t>();           // Throws
1910             size_t new_row_ndx = read_int<size_t>();       // Throws
1911             if (!handler.merge_rows(row_ndx, new_row_ndx)) // Throws
1912                 parser_error();
1913             return;
1914         }
1915         case instr_SelectTable: {
1916             int levels = read_int<int>(); // Throws
1917             if (levels < 0 || levels > m_max_levels)
1918                 parser_error();
1919             m_path.reserve(0, 2 * levels); // Throws
1920             size_t* path = m_path.data();
1921             size_t group_level_ndx = read_int<size_t>(); // Throws
1922             for (int i = 0; i != levels; ++i) {
1923                 size_t col_ndx = read_int<size_t>(); // Throws
1924                 size_t row_ndx = read_int<size_t>(); // Throws
1925                 path[2 * i + 0] = col_ndx;
1926                 path[2 * i + 1] = row_ndx;
1927             }
1928             if (!handler.select_table(group_level_ndx, levels, path)) // Throws
1929                 parser_error();
1930             return;
1931         }
1932         case instr_ClearTable: {
1933             size_t old_size = read_int<size_t>();   // Throws
1934             if (!handler.clear_table(old_size)) // Throws
1935                 parser_error();
1936             return;
1937         }
1938         case instr_LinkListSet: {
1939             size_t link_ndx = read_int<size_t>();                    // Throws
1940             size_t value = read_int<size_t>();                       // Throws
1941             size_t prior_size = read_int<size_t>();                  // Throws
1942             if (!handler.link_list_set(link_ndx, value, prior_size)) // Throws
1943                 parser_error();
1944             return;
1945         }
1946         case instr_LinkListSetAll: {
1947             // todo, log that it's a SetAll we're doing
1948             size_t size = read_int<size_t>(); // Throws
1949             for (size_t i = 0; i < size; i++) {
1950                 size_t link = read_int<size_t>();          // Throws
1951                 if (!handler.link_list_set(i, link, size)) // Throws
1952                     parser_error();
1953             }
1954             return;
1955         }
1956         case instr_LinkListInsert: {
1957             size_t link_ndx = read_int<size_t>();                       // Throws
1958             size_t value = read_int<size_t>();                          // Throws
1959             size_t prior_size = read_int<size_t>();                     // Throws
1960             if (!handler.link_list_insert(link_ndx, value, prior_size)) // Throws
1961                 parser_error();
1962             return;
1963         }
1964         case instr_LinkListMove: {
1965             size_t from_link_ndx = read_int<size_t>();               // Throws
1966             size_t to_link_ndx = read_int<size_t>();                 // Throws
1967             if (!handler.link_list_move(from_link_ndx, to_link_ndx)) // Throws
1968                 parser_error();
1969             return;
1970         }
1971         case instr_LinkListSwap: {
1972             size_t link1_ndx = read_int<size_t>();             // Throws
1973             size_t link2_ndx = read_int<size_t>();             // Throws
1974             if (!handler.link_list_swap(link1_ndx, link2_ndx)) // Throws
1975                 parser_error();
1976             return;
1977         }
1978         case instr_LinkListErase: {
1979             size_t link_ndx = read_int<size_t>();               // Throws
1980             size_t prior_size = read_int<size_t>();             // Throws
1981             if (!handler.link_list_erase(link_ndx, prior_size)) // Throws
1982                 parser_error();
1983             return;
1984         }
1985         case instr_LinkListNullify: {
1986             size_t link_ndx = read_int<size_t>();                 // Throws
1987             size_t prior_size = read_int<size_t>();               // Throws
1988             if (!handler.link_list_nullify(link_ndx, prior_size)) // Throws
1989                 parser_error();
1990             return;
1991         }
1992         case instr_LinkListClear: {
1993             size_t old_list_size = read_int<size_t>();   // Throws
1994             if (!handler.link_list_clear(old_list_size)) // Throws
1995                 parser_error();
1996             return;
1997         }
1998         case instr_SelectLinkList: {
1999             size_t col_ndx = read_int<size_t>();                                     // Throws
2000             size_t row_ndx = read_int<size_t>();                                     // Throws
2001             size_t target_group_level_ndx = read_int<size_t>();                      // Throws
2002             if (!handler.select_link_list(col_ndx, row_ndx, target_group_level_ndx)) // Throws
2003                 parser_error();
2004             return;
2005         }
2006         case instr_MoveColumn: {
2007             // FIXME: remove this in the next breaking change.
2008             // This instruction is no longer supported and not used by either
2009             // bindings or sync, so if we see it here, there was a problem parsing.
2010             parser_error();
2011             return;
2012         }
2013         case instr_AddSearchIndex: {
2014             size_t col_ndx = read_int<size_t>();    // Throws
2015             if (!handler.add_search_index(col_ndx)) // Throws
2016                 parser_error();
2017             return;
2018         }
2019         case instr_RemoveSearchIndex: {
2020             size_t col_ndx = read_int<size_t>();       // Throws
2021             if (!handler.remove_search_index(col_ndx)) // Throws
2022                 parser_error();
2023             return;
2024         }
2025         case instr_SetLinkType: {
2026             size_t col_ndx = read_int<size_t>(); // Throws
2027             int link_type = read_int<int>();     // Throws
2028             if (!is_valid_link_type(link_type))
2029                 parser_error();
2030             if (!handler.set_link_type(col_ndx, LinkType(link_type))) // Throws
2031                 parser_error();
2032             return;
2033         }
2034         case instr_InsertColumn:
2035         case instr_InsertNullableColumn: {
2036             size_t col_ndx = read_int<size_t>(); // Throws
2037             int type = read_int<int>();          // Throws
2038             if (!is_valid_data_type(type))
2039                 parser_error();
2040             if (REALM_UNLIKELY(type == type_Link || type == type_LinkList))
2041                 parser_error();
2042             StringData name = read_string(m_string_buffer); // Throws
2043             bool nullable = (Instruction(instr) == instr_InsertNullableColumn);
2044             if (REALM_UNLIKELY(nullable && (type == type_Mixed))) {
2045                 // Nullability not supported for Mixed columns.
2046                 parser_error();
2047             }
2048             if (!handler.insert_column(col_ndx, DataType(type), name, nullable)) // Throws
2049                 parser_error();
2050             return;
2051         }
2052         case instr_InsertLinkColumn: {
2053             size_t col_ndx = read_int<size_t>(); // Throws
2054             int type = read_int<int>();          // Throws
2055             if (!is_valid_data_type(type))
2056                 parser_error();
2057             if (REALM_UNLIKELY(type != type_Link && type != type_LinkList))
2058                 parser_error();
2059             size_t link_target_table_ndx = read_int<size_t>(); // Throws
2060             size_t backlink_col_ndx = read_int<size_t>();      // Throws
2061             StringData name = read_string(m_string_buffer);    // Throws
2062             if (!handler.insert_link_column(col_ndx, DataType(type), name, link_target_table_ndx,
2063                                             backlink_col_ndx)) // Throws
2064                 parser_error();
2065             return;
2066         }
2067         case instr_EraseColumn: {
2068             size_t col_ndx = read_int<size_t>(); // Throws
2069             if (!handler.erase_column(col_ndx))  // Throws
2070                 parser_error();
2071             return;
2072         }
2073         case instr_EraseLinkColumn: {
2074             size_t col_ndx = read_int<size_t>();                                              // Throws
2075             size_t link_target_table_ndx = read_int<size_t>();                                // Throws
2076             size_t backlink_col_ndx = read_int<size_t>();                                     // Throws
2077             if (!handler.erase_link_column(col_ndx, link_target_table_ndx, backlink_col_ndx)) // Throws
2078                 parser_error();
2079             return;
2080         }
2081         case instr_RenameColumn: {
2082             size_t col_ndx = read_int<size_t>();            // Throws
2083             StringData name = read_string(m_string_buffer); // Throws
2084             if (!handler.rename_column(col_ndx, name))      // Throws
2085                 parser_error();
2086             return;
2087         }
2088         case instr_SelectDescriptor: {
2089             int levels = read_int<int>(); // Throws
2090             if (levels < 0 || levels > m_max_levels)
2091                 parser_error();
2092             m_path.reserve(0, levels); // Throws
2093             size_t* path = m_path.data();
2094             for (int i = 0; i != levels; ++i) {
2095                 size_t col_ndx = read_int<size_t>(); // Throws
2096                 path[i] = col_ndx;
2097             }
2098             if (!handler.select_descriptor(levels, path)) // Throws
2099                 parser_error();
2100             return;
2101         }
2102         case instr_InsertGroupLevelTable: {
2103             size_t table_ndx = read_int<size_t>();                              // Throws
2104             size_t num_tables = read_int<size_t>();                             // Throws
2105             StringData name = read_string(m_string_buffer);                     // Throws
2106             if (!handler.insert_group_level_table(table_ndx, num_tables, name)) // Throws
2107                 parser_error();
2108             return;
2109         }
2110         case instr_EraseGroupLevelTable: {
2111             size_t table_ndx = read_int<size_t>();                             // Throws
2112             size_t prior_num_tables = read_int<size_t>();                      // Throws
2113             if (!handler.erase_group_level_table(table_ndx, prior_num_tables)) // Throws
2114                 parser_error();
2115             return;
2116         }
2117         case instr_RenameGroupLevelTable: {
2118             size_t table_ndx = read_int<size_t>();                      // Throws
2119             StringData new_name = read_string(m_string_buffer);         // Throws
2120             if (!handler.rename_group_level_table(table_ndx, new_name)) // Throws
2121                 parser_error();
2122             return;
2123         }
2124         case instr_MoveGroupLevelTable: {
2125             // This instruction is no longer supported and not used by either
2126             // bindings or sync, so if we see it here, there was a problem parsing.
2127             // FIXME: remove this in the next breaking change.
2128             parser_error();
2129             return;
2130         }
2131         case instr_OptimizeTable: {
2132             if (!handler.optimize_table()) // Throws
2133                 parser_error();
2134             return;
2135         }
2136     }
2137
2138     throw BadTransactLog();
2139 }
2140
2141
2142 template <class T>
2143 T TransactLogParser::read_int()
2144 {
2145     T value = 0;
2146     int part = 0;
2147     const int max_bytes = (std::numeric_limits<T>::digits + 1 + 6) / 7;
2148     for (int i = 0; i != max_bytes; ++i) {
2149         char c;
2150         if (!read_char(c))
2151             goto bad_transact_log;
2152         part = static_cast<unsigned char>(c);
2153         if (0xFF < part)
2154             goto bad_transact_log; // Only the first 8 bits may be used in each byte
2155         if ((part & 0x80) == 0) {
2156             T p = part & 0x3F;
2157             if (util::int_shift_left_with_overflow_detect(p, i * 7))
2158                 goto bad_transact_log;
2159             value |= p;
2160             break;
2161         }
2162         if (i == max_bytes - 1)
2163             goto bad_transact_log; // Too many bytes
2164         value |= T(part & 0x7F) << (i * 7);
2165     }
2166     if (part & 0x40) {
2167         // The real value is negative. Because 'value' is positive at
2168         // this point, the following negation is guaranteed by C++11
2169         // to never overflow. See C99+TC3 section 6.2.6.2 paragraph 2.
2170         REALM_DIAG_PUSH();
2171         REALM_DIAG_IGNORE_UNSIGNED_MINUS();
2172         value = -value;
2173         REALM_DIAG_POP();
2174         if (util::int_subtract_with_overflow_detect(value, 1))
2175             goto bad_transact_log;
2176     }
2177     return value;
2178
2179 bad_transact_log:
2180     throw BadTransactLog();
2181 }
2182
2183
2184 inline void TransactLogParser::read_bytes(char* data, size_t size)
2185 {
2186     for (;;) {
2187         const size_t avail = m_input_end - m_input_begin;
2188         if (size <= avail)
2189             break;
2190         realm::safe_copy_n(m_input_begin, avail, data);
2191         if (!next_input_buffer())
2192             throw BadTransactLog();
2193         data += avail;
2194         size -= avail;
2195     }
2196     const char* to = m_input_begin + size;
2197     realm::safe_copy_n(m_input_begin, size, data);
2198     m_input_begin = to;
2199 }
2200
2201
2202 inline BinaryData TransactLogParser::read_buffer(util::StringBuffer& buf, size_t size)
2203 {
2204     const size_t avail = m_input_end - m_input_begin;
2205     if (avail >= size) {
2206         m_input_begin += size;
2207         return BinaryData(m_input_begin - size, size);
2208     }
2209
2210     buf.clear();
2211     buf.resize(size); // Throws
2212     read_bytes(buf.data(), size);
2213     return BinaryData(buf.data(), size);
2214 }
2215
2216
2217 inline bool TransactLogParser::read_bool()
2218 {
2219     return read_int<char>();
2220 }
2221
2222
2223 inline float TransactLogParser::read_float()
2224 {
2225     static_assert(std::numeric_limits<float>::is_iec559 &&
2226                       sizeof(float) * std::numeric_limits<unsigned char>::digits == 32,
2227                   "Unsupported 'float' representation");
2228     float value;
2229     read_bytes(reinterpret_cast<char*>(&value), sizeof value); // Throws
2230     return value;
2231 }
2232
2233
2234 inline double TransactLogParser::read_double()
2235 {
2236     static_assert(std::numeric_limits<double>::is_iec559 &&
2237                       sizeof(double) * std::numeric_limits<unsigned char>::digits == 64,
2238                   "Unsupported 'double' representation");
2239     double value;
2240     read_bytes(reinterpret_cast<char*>(&value), sizeof value); // Throws
2241     return value;
2242 }
2243
2244
2245 inline StringData TransactLogParser::read_string(util::StringBuffer& buf)
2246 {
2247     size_t size = read_int<size_t>(); // Throws
2248
2249     if (size > Table::max_string_size)
2250         parser_error();
2251
2252     BinaryData buffer = read_buffer(buf, size);
2253     return StringData{buffer.data(), size};
2254 }
2255
2256 inline Timestamp TransactLogParser::read_timestamp()
2257 {
2258     int64_t seconds = read_int<int64_t>();     // Throws
2259     int32_t nanoseconds = read_int<int32_t>(); // Throws
2260     return Timestamp(seconds, nanoseconds);
2261 }
2262
2263
2264 inline BinaryData TransactLogParser::read_binary(util::StringBuffer& buf)
2265 {
2266     size_t size = read_int<size_t>(); // Throws
2267
2268     return read_buffer(buf, size);
2269 }
2270
2271
2272 inline void TransactLogParser::read_mixed(Mixed* mixed)
2273 {
2274     DataType type = DataType(read_int<int>()); // Throws
2275     switch (type) {
2276         case type_Int: {
2277             int_fast64_t value = read_int<int64_t>(); // Throws
2278             mixed->set_int(value);
2279             return;
2280         }
2281         case type_Bool: {
2282             bool value = read_bool(); // Throws
2283             mixed->set_bool(value);
2284             return;
2285         }
2286         case type_Float: {
2287             float value = read_float(); // Throws
2288             mixed->set_float(value);
2289             return;
2290         }
2291         case type_Double: {
2292             double value = read_double(); // Throws
2293             mixed->set_double(value);
2294             return;
2295         }
2296         case type_OldDateTime: {
2297             int_fast64_t value = read_int<int_fast64_t>(); // Throws
2298             mixed->set_olddatetime(value);
2299             return;
2300         }
2301         case type_Timestamp: {
2302             Timestamp value = read_timestamp(); // Throws
2303             mixed->set_timestamp(value);
2304             return;
2305         }
2306         case type_String: {
2307             StringData value = read_string(m_string_buffer); // Throws
2308             mixed->set_string(value);
2309             return;
2310         }
2311         case type_Binary: {
2312             BinaryData value = read_binary(m_string_buffer); // Throws
2313             mixed->set_binary(value);
2314             return;
2315         }
2316         case type_Table: {
2317             *mixed = Mixed::subtable_tag();
2318             return;
2319         }
2320         case type_Mixed:
2321             break;
2322         case type_Link:
2323         case type_LinkList:
2324             // FIXME: Need to handle new link types here
2325             break;
2326     }
2327     throw BadTransactLog();
2328 }
2329
2330
2331 inline bool TransactLogParser::next_input_buffer()
2332 {
2333     return m_input->next_block(m_input_begin, m_input_end);
2334 }
2335
2336
2337 inline bool TransactLogParser::read_char(char& c)
2338 {
2339     if (m_input_begin == m_input_end && !next_input_buffer())
2340         return false;
2341     c = *m_input_begin++;
2342     return true;
2343 }
2344
2345
2346 inline bool TransactLogParser::is_valid_data_type(int type)
2347 {
2348     switch (DataType(type)) {
2349         case type_Int:
2350         case type_Bool:
2351         case type_Float:
2352         case type_Double:
2353         case type_String:
2354         case type_Binary:
2355         case type_OldDateTime:
2356         case type_Timestamp:
2357         case type_Table:
2358         case type_Mixed:
2359         case type_Link:
2360         case type_LinkList:
2361             return true;
2362     }
2363     return false;
2364 }
2365
2366
2367 inline bool TransactLogParser::is_valid_link_type(int type)
2368 {
2369     switch (LinkType(type)) {
2370         case link_Strong:
2371         case link_Weak:
2372             return true;
2373     }
2374     return false;
2375 }
2376
2377
2378 class TransactReverser {
2379 public:
2380     bool select_table(size_t group_level_ndx, size_t levels, const size_t* path)
2381     {
2382         sync_table();
2383         m_encoder.select_table(group_level_ndx, levels, path);
2384         m_pending_ts_instr = get_inst();
2385         return true;
2386     }
2387
2388     bool select_descriptor(size_t levels, const size_t* path)
2389     {
2390         sync_descriptor();
2391         m_encoder.select_descriptor(levels, path);
2392         m_pending_ds_instr = get_inst();
2393         return true;
2394     }
2395
2396     bool insert_group_level_table(size_t table_ndx, size_t num_tables, StringData)
2397     {
2398         sync_table();
2399         m_encoder.erase_group_level_table(table_ndx, num_tables + 1);
2400         append_instruction();
2401         return true;
2402     }
2403
2404     bool erase_group_level_table(size_t table_ndx, size_t num_tables)
2405     {
2406         sync_table();
2407         m_encoder.insert_group_level_table(table_ndx, num_tables - 1, "");
2408         append_instruction();
2409         return true;
2410     }
2411
2412     bool rename_group_level_table(size_t, StringData)
2413     {
2414         sync_table();
2415         return true;
2416     }
2417
2418     bool optimize_table()
2419     {
2420         return true; // No-op
2421     }
2422
2423     bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool unordered)
2424     {
2425         size_t num_rows_to_erase = num_rows_to_insert;
2426         size_t prior_num_rows_2 = prior_num_rows + num_rows_to_insert;
2427         m_encoder.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows_2, unordered); // Throws
2428         append_instruction();
2429         return true;
2430     }
2431
2432     bool add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t, int64_t)
2433     {
2434         bool unordered = true;
2435         m_encoder.erase_rows(row_ndx, 1, prior_num_rows + 1, unordered); // Throws
2436         append_instruction();
2437         return true;
2438     }
2439
2440     bool erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool unordered)
2441     {
2442         size_t num_rows_to_insert = num_rows_to_erase;
2443         // Number of rows in table after removal, but before inverse insertion
2444         size_t prior_num_rows_2 = prior_num_rows - num_rows_to_erase;
2445         m_encoder.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows_2, unordered); // Throws
2446         append_instruction();
2447         return true;
2448     }
2449
2450     bool swap_rows(size_t row_ndx_1, size_t row_ndx_2)
2451     {
2452         m_encoder.swap_rows(row_ndx_1, row_ndx_2);
2453         append_instruction();
2454         return true;
2455     }
2456
2457     bool move_row(size_t from_ndx, size_t to_ndx)
2458     {
2459         m_encoder.move_row(to_ndx, from_ndx);
2460         append_instruction();
2461         return true;
2462     }
2463
2464     bool merge_rows(size_t row_ndx, size_t new_row_ndx)
2465     {
2466         // There is no instruction we can generate here to change back.
2467         // However, we do need to refresh accessors for any tables
2468         // connected through backlinks, so we generate updates on each
2469         // affected row by merging to itself.
2470         m_encoder.merge_rows(row_ndx, row_ndx);
2471         append_instruction();
2472         m_encoder.merge_rows(new_row_ndx, new_row_ndx);
2473         append_instruction();
2474         return true;
2475     }
2476
2477     bool set_int(size_t col_ndx, size_t row_ndx, int_fast64_t value, Instruction variant, size_t prior_num_rows)
2478     {
2479         m_encoder.set_int(col_ndx, row_ndx, value, variant, prior_num_rows);
2480         append_instruction();
2481         return true;
2482     }
2483
2484     bool add_int(size_t col_ndx, size_t row_ndx, int_fast64_t value)
2485     {
2486         m_encoder.add_int(col_ndx, row_ndx, -value);
2487         append_instruction();
2488         return true;
2489     }
2490
2491     bool set_bool(size_t col_ndx, size_t row_ndx, bool value, Instruction variant)
2492     {
2493         m_encoder.set_bool(col_ndx, row_ndx, value, variant);
2494         append_instruction();
2495         return true;
2496     }
2497
2498     bool set_float(size_t col_ndx, size_t row_ndx, float value, Instruction variant)
2499     {
2500         m_encoder.set_float(col_ndx, row_ndx, value, variant);
2501         append_instruction();
2502         return true;
2503     }
2504
2505     bool set_double(size_t col_ndx, size_t row_ndx, double value, Instruction variant)
2506     {
2507         m_encoder.set_double(col_ndx, row_ndx, value, variant);
2508         append_instruction();
2509         return true;
2510     }
2511
2512     bool set_string(size_t col_ndx, size_t row_ndx, StringData value, Instruction variant, size_t prior_num_rows)
2513     {
2514         m_encoder.set_string(col_ndx, row_ndx, value, variant, prior_num_rows);
2515         append_instruction();
2516         return true;
2517     }
2518
2519     bool set_binary(size_t col_ndx, size_t row_ndx, BinaryData value, Instruction variant)
2520     {
2521         m_encoder.set_binary(col_ndx, row_ndx, value, variant);
2522         append_instruction();
2523         return true;
2524     }
2525
2526     bool set_olddatetime(size_t col_ndx, size_t row_ndx, OldDateTime value, Instruction variant)
2527     {
2528         m_encoder.set_olddatetime(col_ndx, row_ndx, value, variant);
2529         append_instruction();
2530         return true;
2531     }
2532
2533     bool set_timestamp(size_t col_ndx, size_t row_ndx, Timestamp value, Instruction variant)
2534     {
2535         m_encoder.set_timestamp(col_ndx, row_ndx, value, variant);
2536         append_instruction();
2537         return true;
2538     }
2539
2540     bool set_table(size_t col_ndx, size_t row_ndx, Instruction variant)
2541     {
2542         m_encoder.set_table(col_ndx, row_ndx, variant);
2543         append_instruction();
2544         return true;
2545     }
2546
2547     bool set_mixed(size_t col_ndx, size_t row_ndx, const Mixed& value, Instruction variant)
2548     {
2549         m_encoder.set_mixed(col_ndx, row_ndx, value, variant);
2550         append_instruction();
2551         return true;
2552     }
2553
2554     bool set_null(size_t col_ndx, size_t row_ndx, Instruction variant, size_t prior_num_rows)
2555     {
2556         m_encoder.set_null(col_ndx, row_ndx, variant, prior_num_rows);
2557         append_instruction();
2558         return true;
2559     }
2560
2561     bool set_link(size_t col_ndx, size_t row_ndx, size_t value, size_t target_group_level_ndx, Instruction variant)
2562     {
2563         m_encoder.set_link(col_ndx, row_ndx, value, target_group_level_ndx, variant);
2564         append_instruction();
2565         return true;
2566     }
2567
2568     bool insert_substring(size_t, size_t, size_t, StringData)
2569     {
2570         return true; // No-op
2571     }
2572
2573     bool erase_substring(size_t, size_t, size_t, size_t)
2574     {
2575         return true; // No-op
2576     }
2577
2578     bool clear_table(size_t old_size)
2579     {
2580         bool unordered = false;
2581         m_encoder.insert_empty_rows(0, old_size, 0, unordered);
2582         append_instruction();
2583         return true;
2584     }
2585
2586     bool add_search_index(size_t)
2587     {
2588         return true; // No-op
2589     }
2590
2591     bool remove_search_index(size_t)
2592     {
2593         return true; // No-op
2594     }
2595
2596     bool set_link_type(size_t, LinkType)
2597     {
2598         return true; // No-op
2599     }
2600
2601     bool insert_link_column(size_t col_idx, DataType, StringData, size_t target_table_idx, size_t backlink_col_ndx)
2602     {
2603         m_encoder.erase_link_column(col_idx, target_table_idx, backlink_col_ndx);
2604         append_instruction();
2605         return true;
2606     }
2607
2608     bool erase_link_column(size_t col_idx, size_t target_table_idx, size_t backlink_col_idx)
2609     {
2610         DataType type = type_Link; // The real type of the column doesn't matter here,
2611         // but the encoder asserts that it's actually a link type.
2612         m_encoder.insert_link_column(col_idx, type, "", target_table_idx, backlink_col_idx);
2613         append_instruction();
2614         return true;
2615     }
2616
2617     bool insert_column(size_t col_idx, DataType, StringData, bool)
2618     {
2619         m_encoder.erase_column(col_idx);
2620         append_instruction();
2621         return true;
2622     }
2623
2624     bool erase_column(size_t col_idx)
2625     {
2626         m_encoder.insert_column(col_idx, DataType(), "");
2627         append_instruction();
2628         return true;
2629     }
2630
2631     bool rename_column(size_t, StringData)
2632     {
2633         return true; // No-op
2634     }
2635
2636     bool select_link_list(size_t col_ndx, size_t row_ndx, size_t link_target_group_level_ndx)
2637     {
2638         sync_linkview();
2639         m_encoder.select_link_list(col_ndx, row_ndx, link_target_group_level_ndx);
2640         m_pending_lv_instr = get_inst();
2641         return true;
2642     }
2643
2644     bool link_list_set(size_t row, size_t value, size_t prior_size)
2645     {
2646         m_encoder.link_list_set(row, value, prior_size);
2647         append_instruction();
2648         return true;
2649     }
2650
2651     bool link_list_insert(size_t link_ndx, size_t, size_t prior_size)
2652     {
2653         m_encoder.link_list_erase(link_ndx, prior_size + 1);
2654         append_instruction();
2655         return true;
2656     }
2657
2658     bool link_list_move(size_t from_link_ndx, size_t to_link_ndx)
2659     {
2660         m_encoder.link_list_move(from_link_ndx, to_link_ndx);
2661         append_instruction();
2662         return true;
2663     }
2664
2665     bool link_list_swap(size_t link1_ndx, size_t link2_ndx)
2666     {
2667         m_encoder.link_list_swap(link1_ndx, link2_ndx);
2668         append_instruction();
2669         return true;
2670     }
2671
2672     bool link_list_erase(size_t link_ndx, size_t prior_size)
2673     {
2674         m_encoder.link_list_insert(link_ndx, 0, prior_size - 1);
2675         append_instruction();
2676         return true;
2677     }
2678
2679     bool link_list_clear(size_t old_list_size)
2680     {
2681         // Append in reverse order because the reversed log is itself applied
2682         // in reverse, and this way it generates all back-insertions rather than
2683         // all front-insertions
2684         for (size_t i = old_list_size; i > 0; --i) {
2685             m_encoder.link_list_insert(i - 1, 0, old_list_size - i);
2686             append_instruction();
2687         }
2688         return true;
2689     }
2690
2691     bool nullify_link(size_t col_ndx, size_t row_ndx, size_t target_group_level_ndx)
2692     {
2693         size_t value = 0;
2694         // FIXME: Is zero this right value to pass here, or should
2695         // TransactReverser::nullify_link() also have taken a
2696         // `target_group_level_ndx` argument.
2697         m_encoder.set_link(col_ndx, row_ndx, value, target_group_level_ndx);
2698         append_instruction();
2699         return true;
2700     }
2701
2702     bool link_list_nullify(size_t link_ndx, size_t prior_size)
2703     {
2704         m_encoder.link_list_insert(link_ndx, 0, prior_size - 1);
2705         append_instruction();
2706         return true;
2707     }
2708
2709 private:
2710     _impl::TransactLogBufferStream m_buffer;
2711     _impl::TransactLogEncoder m_encoder{m_buffer};
2712     struct Instr {
2713         size_t begin;
2714         size_t end;
2715     };
2716     std::vector<Instr> m_instructions;
2717     size_t current_instr_start = 0;
2718     Instr m_pending_ts_instr{0, 0};
2719     Instr m_pending_ds_instr{0, 0};
2720     Instr m_pending_lv_instr{0, 0};
2721
2722     Instr get_inst()
2723     {
2724         Instr instr;
2725         instr.begin = current_instr_start;
2726         current_instr_start = transact_log_size();
2727         instr.end = current_instr_start;
2728         return instr;
2729     }
2730
2731     size_t transact_log_size() const
2732     {
2733         REALM_ASSERT_3(m_encoder.write_position(), >=, m_buffer.transact_log_data());
2734         return m_encoder.write_position() - m_buffer.transact_log_data();
2735     }
2736
2737     void append_instruction()
2738     {
2739         m_instructions.push_back(get_inst());
2740     }
2741
2742     void append_instruction(Instr instr)
2743     {
2744         m_instructions.push_back(instr);
2745     }
2746
2747     void sync_select(Instr& pending_instr)
2748     {
2749         if (pending_instr.begin != pending_instr.end) {
2750             append_instruction(pending_instr);
2751             pending_instr = {0, 0};
2752         }
2753     }
2754
2755     void sync_linkview()
2756     {
2757         sync_select(m_pending_lv_instr);
2758     }
2759
2760     void sync_descriptor()
2761     {
2762         sync_linkview();
2763         sync_select(m_pending_ds_instr);
2764     }
2765
2766     void sync_table()
2767     {
2768         sync_descriptor();
2769         sync_select(m_pending_ts_instr);
2770     }
2771
2772     friend class ReversedNoCopyInputStream;
2773 };
2774
2775
2776 class ReversedNoCopyInputStream : public NoCopyInputStream {
2777 public:
2778     ReversedNoCopyInputStream(TransactReverser& reverser)
2779         : m_instr_order(reverser.m_instructions)
2780     {
2781         // push any pending select_table or select_descriptor into the buffer
2782         reverser.sync_table();
2783
2784         m_buffer = reverser.m_buffer.transact_log_data();
2785         m_current = m_instr_order.size();
2786     }
2787
2788     bool next_block(const char*& begin, const char*& end) override
2789     {
2790         if (m_current != 0) {
2791             m_current--;
2792             begin = m_buffer + m_instr_order[m_current].begin;
2793             end = m_buffer + m_instr_order[m_current].end;
2794             return (end > begin);
2795         }
2796         return false;
2797     }
2798
2799 private:
2800     const char* m_buffer;
2801     std::vector<TransactReverser::Instr>& m_instr_order;
2802     size_t m_current;
2803 };
2804
2805 } // namespace _impl
2806 } // namespace realm
2807
2808 #endif // REALM_IMPL_TRANSACT_LOG_HPP