1 /*************************************************************************
6 * [2011] - [2015] Realm Inc
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.
19 **************************************************************************/
21 #ifndef REALM_IMPL_INSTRUCTIONS_HPP
22 #define REALM_IMPL_INSTRUCTIONS_HPP
25 #include <unordered_map>
26 #include <iosfwd> // string conversion, debug prints
27 #include <memory> // shared_ptr
28 #include <type_traits>
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>
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) \
66 enum class ContainerType { none=0, links=1, array=2, dict=3 };
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;
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
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
87 template <Type t> struct GetType;
88 template <class T> struct GetInstructionType;
94 static const size_t max_instruction_size = 63;
95 std::aligned_storage_t<max_instruction_size, 8> m_storage;
99 void visit(F&& lambda);
101 void visit(F&& lambda) const;
103 template <class T> T& get_as()
105 REALM_ASSERT(type == GetInstructionType<T>::value);
106 return *reinterpret_cast<T*>(&m_storage);
110 const T& get_as() const
112 return const_cast<Instruction*>(this)->template get_as<T>();
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;
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;
125 struct StringBufferRange {
126 uint32_t offset, size;
128 bool operator==(const StringBufferRange&) = delete;
129 bool operator!=(const StringBufferRange&) = delete;
132 struct InternString {
133 static const InternString npos;
134 explicit constexpr InternString(uint32_t v = uint32_t(-1)): value(v) {}
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;
143 struct Instruction::Payload {
145 sync::ObjectID target; // can be nothing = null
146 InternString target_table;
154 StringBufferRange str;
159 Data(const Data&) noexcept = default;
160 Data& operator=(const Data&) noexcept = default;
163 int8_t type; // -1 = null, -2 = implicit_nullify
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);
177 Payload(const Payload&) noexcept = default;
178 Payload& operator=(const Payload&) noexcept = default;
180 bool is_null() const;
181 bool is_implicit_null() const;
184 struct Instruction::ObjectInstructionBase {
185 sync::ObjectID object;
188 struct Instruction::FieldInstructionBase
189 : Instruction::ObjectInstructionBase
194 struct Instruction::PayloadInstructionBase {
200 struct Instruction::SelectTable {
204 struct Instruction::SelectContainer
205 : Instruction::FieldInstructionBase
207 InternString link_target_table;
210 struct Instruction::AddTable {
212 InternString primary_key_field;
213 DataType primary_key_type;
214 bool has_primary_key;
215 bool primary_key_nullable;
218 struct Instruction::EraseTable {
222 struct Instruction::CreateObject
223 : Instruction::PayloadInstructionBase
224 , Instruction::ObjectInstructionBase
226 bool has_primary_key;
229 struct Instruction::EraseObject
230 : Instruction::ObjectInstructionBase
233 struct Instruction::Set
234 : Instruction::PayloadInstructionBase
235 , Instruction::FieldInstructionBase
240 struct Instruction::AddInteger
241 : Instruction::FieldInstructionBase
246 struct Instruction::InsertSubstring
247 : Instruction::FieldInstructionBase
249 StringBufferRange value;
253 struct Instruction::EraseSubstring
254 : Instruction::FieldInstructionBase
260 struct Instruction::ClearTable {
263 struct Instruction::ContainerSet {
264 Instruction::Payload payload;
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;
277 struct Instruction::ContainerMove {
282 struct Instruction::ContainerErase {
285 bool implicit_nullify;
288 struct Instruction::ContainerSwap {
293 struct Instruction::ContainerClear {
297 // If container_type != ContainerType::none, creates a subtable:
309 struct Instruction::AddColumn {
311 InternString link_target_table;
313 ContainerType container_type;
317 struct Instruction::EraseColumn {
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;
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().
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;
333 /// Handle an instruction.
334 virtual void operator()(const Instruction&) = 0;
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
346 #ifdef _WIN32 // FIXME: Fails in VS.
347 #define REALM_CHECK_INSTRUCTION_SIZE(X)
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
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
362 Instruction::Instruction(T instr): type(GetInstructionType<T>::value)
364 new(&m_storage) T(std::move(instr));
368 void Instruction::visit(F&& lambda)
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
380 void Instruction::visit(F&& lambda) const
382 const_cast<Instruction*>(this)->visit(std::forward<F>(lambda));
385 std::ostream& operator<<(std::ostream&, Instruction::Type);
390 #endif // REALM_IMPL_INSTRUCTIONS_HPP