added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / sync / instructions.hpp
1 /*************************************************************************
2  *
3  * REALM CONFIDENTIAL
4  * __________________
5  *
6  *  [2011] - [2015] Realm Inc
7  *  All Rights Reserved.
8  *
9  * NOTICE:  All information contained herein is, and remains
10  * the property of Realm Incorporated and its suppliers,
11  * if any.  The intellectual and technical concepts contained
12  * herein are proprietary to Realm Incorporated
13  * and its suppliers and may be covered by U.S. and Foreign Patents,
14  * patents in process, and are protected by trade secret or copyright law.
15  * Dissemination of this information or reproduction of this material
16  * is strictly forbidden unless prior written permission is obtained
17  * from Realm Incorporated.
18  *
19  **************************************************************************/
20
21 #ifndef REALM_IMPL_INSTRUCTIONS_HPP
22 #define REALM_IMPL_INSTRUCTIONS_HPP
23
24 #include <vector>
25 #include <unordered_map>
26 #include <iosfwd> // string conversion, debug prints
27 #include <memory> // shared_ptr
28 #include <type_traits>
29
30 #include <realm/util/string_buffer.hpp>
31 #include <realm/string_data.hpp>
32 #include <realm/binary_data.hpp>
33 #include <realm/data_type.hpp>
34 #include <realm/timestamp.hpp>
35 #include <realm/sync/object_id.hpp>
36 #include <realm/impl/input_stream.hpp>
37 #include <realm/table_ref.hpp>
38 #include <realm/link_view_fwd.hpp>
39
40 namespace realm {
41 namespace sync {
42
43 // CAUTION: Any change to the order or number of instructions is a
44 // protocol-breaking change!
45 #define REALM_FOR_EACH_INSTRUCTION_TYPE(X) \
46     X(SelectTable) \
47     X(SelectContainer) \
48     X(AddTable) \
49     X(EraseTable) \
50     X(CreateObject) \
51     X(EraseObject) \
52     X(Set) \
53     X(AddInteger) \
54     X(InsertSubstring) \
55     X(EraseSubstring) \
56     X(ClearTable) \
57     X(AddColumn) \
58     X(EraseColumn) \
59     X(ContainerSet) \
60     X(ContainerInsert) \
61     X(ContainerMove) \
62     X(ContainerSwap) \
63     X(ContainerErase) \
64     X(ContainerClear) \
65
66 enum class ContainerType { none=0, links=1, array=2, dict=3 };
67
68 struct Instruction {
69     // Base classes for instructions with common fields. They enable the merge
70     // algorithm to reuse some code without resorting to templates, and can be
71     // combined to allow optimal memory layout of instructions (size <= 64).
72     struct PayloadInstructionBase;
73     struct ObjectInstructionBase;
74     struct FieldInstructionBase;
75
76 #define REALM_DECLARE_INSTRUCTION_STRUCT(X) struct X;
77     REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DECLARE_INSTRUCTION_STRUCT)
78 #undef REALM_DECLARE_INSTRUCTION_STRUCT
79
80     enum class Type: uint8_t {
81 #define REALM_DEFINE_INSTRUCTION_TYPE(X) X,
82     REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_TYPE)
83 #undef REALM_DEFINE_INSTRUCTION_TYPE
84     };
85
86     struct Payload;
87     template <Type t> struct GetType;
88     template <class T> struct GetInstructionType;
89
90     Instruction() {}
91     template <class T>
92     Instruction(T instr);
93
94     static const size_t max_instruction_size = 63;
95     std::aligned_storage_t<max_instruction_size, 8> m_storage;
96     Type type;
97
98     template <class F>
99     void visit(F&& lambda);
100     template <class F>
101     void visit(F&& lambda) const;
102
103     template <class T> T& get_as()
104     {
105         REALM_ASSERT(type == GetInstructionType<T>::value);
106         return *reinterpret_cast<T*>(&m_storage);
107     }
108
109     template <class T>
110     const T& get_as() const
111     {
112         return const_cast<Instruction*>(this)->template get_as<T>();
113     }
114 };
115
116 // 0x3f is the largest value that fits in a single byte in the variable-length
117 // encoded integer instruction format.
118 static constexpr uint8_t InstrTypeInternString = 0x3f;
119
120 // This instruction code is only ever used internally by the Changeset class
121 // to allow insertion/removal while keeping iterators stable. Should never
122 // make it onto the wire.
123 static constexpr uint8_t InstrTypeMultiInstruction = 0xff;
124
125 struct StringBufferRange {
126     uint32_t offset, size;
127
128     bool operator==(const StringBufferRange&) = delete;
129     bool operator!=(const StringBufferRange&) = delete;
130 };
131
132 struct InternString {
133     static const InternString npos;
134     explicit constexpr InternString(uint32_t v = uint32_t(-1)): value(v) {}
135
136     uint32_t value;
137
138     // Disabling comparison for safety, because it is usually not what you want.
139     bool operator==(const InternString&) = delete;
140     bool operator!=(const InternString&) = delete;
141 };
142
143 struct Instruction::Payload {
144     struct Link {
145         sync::ObjectID target; // can be nothing = null
146         InternString target_table;
147     };
148
149     union Data {
150         bool boolean;
151         int64_t integer;
152         float fnum;
153         double dnum;
154         StringBufferRange str;
155         Timestamp timestamp;
156         Link link;
157
158         Data() noexcept {}
159         Data(const Data&) noexcept = default;
160         Data& operator=(const Data&) noexcept = default;
161     };
162     Data data;
163     int8_t type; // -1 = null, -2 = implicit_nullify
164
165     Payload(): Payload(realm::util::none) {}
166     explicit Payload(bool value)      noexcept: type(type_Bool) { data.boolean = value; }
167     explicit Payload(int64_t value)   noexcept: type(type_Int) { data.integer = value; }
168     explicit Payload(float value)     noexcept: type(type_Float) { data.fnum = value; }
169     explicit Payload(double value)    noexcept: type(type_Double) { data.dnum = value; }
170     explicit Payload(Timestamp value) noexcept: type(type_Timestamp) { data.timestamp = value; }
171     explicit Payload(Link value)      noexcept: type(type_Link) { data.link = value; }
172     explicit Payload(StringBufferRange value) noexcept: type(type_String) { data.str = value; }
173     explicit Payload(realm::util::None, bool implicit_null = false) noexcept {
174         type = (implicit_null ? -2 : -1);
175     }
176
177     Payload(const Payload&) noexcept = default;
178     Payload& operator=(const Payload&) noexcept = default;
179
180     bool is_null() const;
181     bool is_implicit_null() const;
182 };
183
184 struct Instruction::ObjectInstructionBase {
185     sync::ObjectID object;
186 };
187
188 struct Instruction::FieldInstructionBase
189     : Instruction::ObjectInstructionBase
190 {
191     InternString field;
192 };
193
194 struct Instruction::PayloadInstructionBase {
195     Payload payload;
196 };
197
198
199
200 struct Instruction::SelectTable {
201     InternString table;
202 };
203
204 struct Instruction::SelectContainer
205     : Instruction::FieldInstructionBase
206 {
207     InternString link_target_table;
208 };
209
210 struct Instruction::AddTable {
211     InternString table;
212     InternString primary_key_field;
213     DataType primary_key_type;
214     bool has_primary_key;
215     bool primary_key_nullable;
216 };
217
218 struct Instruction::EraseTable {
219     InternString table;
220 };
221
222 struct Instruction::CreateObject
223     : Instruction::PayloadInstructionBase
224     , Instruction::ObjectInstructionBase
225 {
226     bool has_primary_key;
227 };
228
229 struct Instruction::EraseObject
230     : Instruction::ObjectInstructionBase
231 {};
232
233 struct Instruction::Set
234     : Instruction::PayloadInstructionBase
235     , Instruction::FieldInstructionBase
236 {
237     bool is_default;
238 };
239
240 struct Instruction::AddInteger
241     : Instruction::FieldInstructionBase
242 {
243     int64_t value;
244 };
245
246 struct Instruction::InsertSubstring
247     : Instruction::FieldInstructionBase
248 {
249     StringBufferRange value;
250     uint32_t pos;
251 };
252
253 struct Instruction::EraseSubstring
254     : Instruction::FieldInstructionBase
255 {
256     uint32_t pos;
257     uint32_t size;
258 };
259
260 struct Instruction::ClearTable {
261 };
262
263 struct Instruction::ContainerSet {
264     Instruction::Payload payload;
265     uint32_t ndx;
266     uint32_t prior_size;
267 };
268
269 struct Instruction::ContainerInsert {
270     // payload carries the value in case of LinkList
271     // payload is empty in case of Array, Dict or any other container type
272     Instruction::Payload payload;
273     uint32_t ndx;
274     uint32_t prior_size;
275 };
276
277 struct Instruction::ContainerMove {
278     uint32_t ndx_1;
279     uint32_t ndx_2;
280 };
281
282 struct Instruction::ContainerErase {
283     uint32_t ndx;
284     uint32_t prior_size;
285     bool implicit_nullify;
286 };
287
288 struct Instruction::ContainerSwap {
289     uint32_t ndx_1;
290     uint32_t ndx_2;
291 };
292
293 struct Instruction::ContainerClear {
294     uint32_t prior_size;
295 };
296
297 // If container_type != ContainerType::none, creates a subtable:
298 // +---+---+-------+
299 // | a | b |   c   |
300 // +---+---+-------+
301 // |   |   | +---+ |
302 // |   |   | | v | |
303 // |   |   | +---+ |
304 // | 1 | 2 | | 3 | |
305 // |   |   | | 4 | |
306 // |   |   | | 5 | |
307 // |   |   | +---+ |
308 // +---+---+-------+
309 struct Instruction::AddColumn {
310     InternString field;
311     InternString link_target_table;
312     DataType type;
313     ContainerType container_type;
314     bool nullable;
315 };
316
317 struct Instruction::EraseColumn {
318     InternString field;
319 };
320
321 struct InstructionHandler {
322     /// Notify the handler that an InternString meta-instruction was found.
323     virtual void set_intern_string(uint32_t index, StringBufferRange) = 0;
324
325     /// Notify the handler of the string value. The handler guarantees that the
326     /// returned string range is valid at least until the next invocation of
327     /// add_string_range().
328     ///
329     /// Instances of `StringBufferRange` passed to operator() after invoking
330     /// this function are assumed to refer to ranges in this buffer.
331     virtual StringBufferRange add_string_range(StringData) = 0;
332
333     /// Handle an instruction.
334     virtual void operator()(const Instruction&) = 0;
335 };
336
337
338 /// Implementation:
339
340 #if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 // GCC 4.x does not support std::is_trivially_copyable
341 #define REALM_CHECK_TRIVIALLY_COPYABLE(X) static_assert(std::is_trivially_copyable<Instruction::X>::value, #X" Instructions must be trivially copyable.");
342     REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_CHECK_TRIVIALLY_COPYABLE)
343 #undef REALM_CHECK_TRIVIALLY_COPYABLE
344 #endif // __GNUC__
345
346 #ifdef _WIN32 // FIXME: Fails in VS. 
347 #define REALM_CHECK_INSTRUCTION_SIZE(X)
348 #else
349 #define REALM_CHECK_INSTRUCTION_SIZE(X) static_assert(sizeof(Instruction::X) <= Instruction::max_instruction_size, #X" Instruction too big.");
350     REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_CHECK_INSTRUCTION_SIZE)
351 #undef REALM_CHECK_INSTRUCTION_SIZE
352 #endif
353
354 #define REALM_DEFINE_INSTRUCTION_GET_TYPE(X) \
355     template <> struct Instruction::GetType<Instruction::Type::X> { using Type = Instruction::X; }; \
356     template <> struct Instruction::GetInstructionType<Instruction::X> { static const Instruction::Type value = Instruction::Type::X; };
357     REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_GET_TYPE)
358 #undef REALM_DEFINE_INSTRUCTION_GET_TYPE
359
360
361 template <class T>
362 Instruction::Instruction(T instr): type(GetInstructionType<T>::value)
363 {
364     new(&m_storage) T(std::move(instr));
365 }
366
367 template <class F>
368 void Instruction::visit(F&& lambda)
369 {
370     switch (type) {
371 #define REALM_VISIT_INSTRUCTION(X) \
372         case Type::X: return lambda(get_as<Instruction::X>());
373         REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_VISIT_INSTRUCTION)
374 #undef REALM_VISIT_INSTRUCTION
375     }
376     REALM_UNREACHABLE();
377 }
378
379 template <class F>
380 void Instruction::visit(F&& lambda) const
381 {
382     const_cast<Instruction*>(this)->visit(std::forward<F>(lambda));
383 }
384
385 std::ostream& operator<<(std::ostream&, Instruction::Type);
386
387 } // namespace _impl
388 } // namespace realm
389
390 #endif // REALM_IMPL_INSTRUCTIONS_HPP