added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / sync / transform.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_SYNC_TRANSFORM_HPP
22 #define REALM_SYNC_TRANSFORM_HPP
23
24 #include <stddef.h>
25
26 #include <realm/util/buffer.hpp>
27 #include <realm/impl/cont_transact_hist.hpp>
28 #include <realm/group_shared.hpp>
29 #include <realm/chunked_binary.hpp>
30
31 namespace realm {
32 namespace sync {
33
34 struct Changeset;
35
36 /// Represents an entry in the history of changes in a sync-enabled Realm
37 /// file. Server and client use different history formats, but this class is
38 /// used both on the server and the client side. Each history entry corresponds
39 /// to a version of the Realm state. For server and client-side histories, these
40 /// versions are referred to as *server versions* and *client versions*
41 /// respectively. These versions may, or may not correspond to Realm snapshot
42 /// numbers (on the server-side they currently do not).
43 ///
44 /// FIXME: Move this class into a separate header
45 /// (`<realm/sync/history_entry.hpp>`).
46 class HistoryEntry {
47 public:
48     using timestamp_type  = uint_fast64_t;
49     using file_ident_type = uint_fast64_t;
50     using version_type    = _impl::History::version_type;
51
52     /// The time of origination of the changes referenced by this history entry,
53     /// meassured as the number of milliseconds since 2015-01-01T00:00:00Z, not
54     /// including leap seconds. For changes of local origin, this is the local
55     /// time at the point when the local transaction was committed. For changes
56     /// of remote origin, it is the remote time of origin at the client
57     /// identified by `origin_client_file_ident`.
58     timestamp_type origin_timestamp;
59
60     /// For changes of local origin, this is the identifier of the local
61     /// file. On the client side, the special value, zero, is used as a stand-in
62     /// for the actual file identifier. This is necessary because changes may
63     /// occur on the client-side before it obtains the the actual identifier
64     /// from the server. Depending on context, the special value, zero, will, or
65     /// will not have been replaced by the actual local file identifier.
66     ///
67     /// For changes of remote origin, this is the identifier of the file in the
68     /// context of which this change originated. This may be a client, or a
69     /// server-side file. For example, when a change "travels" from client file
70     /// A with identifier 2, through the server, to client file B with
71     /// identifier 3, then `origin_client_file_ident` will be 2 on the server
72     /// and in client file A. On the other hand, if the change originates on the
73     /// server, and the server-side file identifier is 1, then
74     /// `origin_client_file_ident` will be 1 in both client files.
75     ///
76     /// FIXME: Rename this member to `origin_file_ident`. It is no longer
77     /// necessarily a client-side file.
78     file_ident_type origin_client_file_ident;
79
80     /// For changes of local origin on the client side, this is the last server
81     /// version integrated locally prior to this history entry. In other words,
82     /// it is a copy of `remote_version` of the last preceding history entry
83     /// that carries changes of remote origin, or zero if there is no such
84     /// preceding history entry.
85     ///
86     /// For changes of local origin on the server-side, this is always zero.
87     ///
88     /// For changes of remote origin, this is the version produced within the
89     /// remote-side Realm file by the change that gave rise to this history
90     /// entry. The remote-side Realm file is not always the same Realm file from
91     /// which the change originated. On the client side, the remote side is
92     /// always the server side, and `remote_version` is always a server version
93     /// (since clients do not speak directly to each other). On the server side,
94     /// the remote side is always a client side, and `remote_version` is always
95     /// a client version.
96     version_type remote_version;
97
98     /// Referenced memory is not owned by this class.
99     ChunkedBinaryData changeset;
100 };
101
102
103 /// The interface between the sync history and the operational transformer
104 /// (Transformer).
105 class TransformHistory {
106 public:
107     using timestamp_type  = HistoryEntry::timestamp_type;
108     using file_ident_type = HistoryEntry::file_ident_type;
109     using version_type    = HistoryEntry::version_type;
110
111     /// Get the first history entry where the produced version is greater than
112     /// `begin_version` and less than or equal to `end_version`, and whose
113     /// changeset is neither empty, nor produced by integration of a changeset
114     /// received from the specified remote peer.
115     ///
116     /// If \a buffer is non-null, memory will be allocated and transferred to
117     /// \a buffer. The changeset will be copied into the newly allocated memory.
118     ///
119     /// If \a buffer is null, the changeset is not copied out of the Realm,
120     /// and entry.changeset.data() does not point to the changeset.
121     /// The changeset in the Realm could be chunked, hence it is not possible
122     /// to point to it with BinaryData. entry.changeset.size() always gives the
123     /// size of the changeset.
124     ///
125     /// \param begin_version, end_version The range of versions to consider. If
126     /// `begin_version` is equal to `end_version`, this is the empty range. If
127     /// `begin_version` is zero, it means that everything preceeding
128     /// `end_version` is to be considered, which is again the empty range if
129     /// `end_version` is also zero. Zero is is special value in that no
130     /// changeset produces that version. It is an error if `end_version`
131     /// preceeds `begin_version`, or if `end_version` is zero and
132     /// `begin_version` is not.
133     ///
134     /// \param not_from_remote_client_file_ident Skip entries whose changeset is
135     /// produced by integration of changesets received from this remote
136     /// peer. Zero if the remote peer is the server, otherwise the peer
137     /// identifier of a client.
138     ///
139     /// \param only_nonempty Skip entries with empty changesets.
140     ///
141     /// \return The version produced by the changeset of the located history
142     /// entry, or zero if no history entry exists matching the specified
143     /// criteria.
144     virtual version_type find_history_entry(version_type begin_version, version_type end_version,
145                                             file_ident_type not_from_remote_client_file_ident,
146                                             bool only_nonempty, HistoryEntry& entry) const noexcept = 0;
147
148     /// Get the specified reciprocal changeset. The targeted history entry is
149     /// the one whose untransformed changeset produced the specified version.
150     ///
151     /// \param remote_client_file_ident Zero if the remote peer is the server,
152     /// otherwise the peer identifier of a client.
153     virtual ChunkedBinaryData get_reciprocal_transform(version_type version,
154                                                        file_ident_type remote_client_file_ident) const = 0;
155
156     /// Replace the specified reciprocally transformed changeset. The targeted
157     /// history entry is the one whose untransformed changeset produced the
158     /// specified version.
159     ///
160     /// \param remote_client_file_ident See get_reciprocal_transform().
161     ///
162     /// \param encoded_changeset The new reciprocally transformed changeset.
163     virtual void set_reciprocal_transform(version_type version,
164                                           file_ident_type remote_client_file_ident,
165                                           BinaryData encoded_changeset) = 0;
166
167     virtual ~TransformHistory() noexcept {}
168 };
169
170
171 class TransformError; // Exception
172
173 class Transformer {
174 public:
175     using timestamp_type  = HistoryEntry::timestamp_type;
176     using file_ident_type = HistoryEntry::file_ident_type;
177     using version_type    = HistoryEntry::version_type;
178
179     class RemoteChangeset;
180     class Reporter;
181
182     /// Produce an operationally transformed version of the specified changesets,
183     /// which are assumed to be of remote origin, and received from remote peer
184     /// P. Note that P is not necessarily the peer from which the changes
185     /// originated.
186     ///
187     /// Operational transformation is carried out between the specified
188     /// changesets and all causally unrelated changesets in the local history. A
189     /// changeset in the local history is causally unrelated if, and only if it
190     /// occurs after the local changeset that produced
191     /// `remote_changeset.last_integrated_local_version` and is not a produced
192     /// by integration of a changeset received from P. This assumes that
193     /// `remote_changeset.last_integrated_local_version` is set to the local
194     /// version produced by the last local changeset, that was integrated by P
195     /// before P produced the specified changeset.
196     ///
197     /// The operational transformation is reciprocal (two-way), so it also
198     /// transforms the causally unrelated local changesets. This process does
199     /// not modify the history itself (the changesets available through
200     /// TransformHistory::get_history_entry()), instead the reciprocally
201     /// transformed changesets are stored separately, and individually for each
202     /// remote peer, such that they can participate in transformation of the
203     /// next incoming changeset from P. Note that the peer identifier of P can
204     /// be derived from `origin_client_file_ident` and information about whether
205     /// the local peer is a server or a client.
206     ///
207     /// In general, if A and B are two causally unrelated (alternative)
208     /// changesets based on the same version V, then the operational
209     /// transformation between A and B produces changesets A' and B' such that
210     /// both of the concatenated changesets A + B' and B + A' produce the same
211     /// final state when applied to V. Operational transformation is meaningful
212     /// only when carried out between alternative changesets based on the same
213     /// version.
214     ///
215     /// \return The size of the transformed version of the specified
216     /// changesets. Upon return, the transformed changesets are concatenated
217     /// and placed in \a output_buffer.
218     ///
219     /// \throw TransformError Thrown if operational transformation fails due to
220     /// a problem with the specified changeset.
221     virtual void transform_remote_changesets(TransformHistory&,
222                                              version_type current_local_version,
223                                              Changeset* changesets,
224                                              std::size_t num_changesets,
225                                              Reporter* = nullptr) = 0;
226
227     virtual ~Transformer() noexcept {}
228 };
229
230 /// \param local_client_file_ident The server assigned local client file
231 /// identifier. This must be zero on the server-side, and only on the
232 /// server-side.
233 std::unique_ptr<Transformer> make_transformer(Transformer::file_ident_type local_client_file_ident);
234
235 class Transformer::RemoteChangeset {
236 public:
237     /// The version produced on the remote peer by this changeset.
238     ///
239     /// On the server, the remote peer is the client from which the changeset
240     /// originated, and `remote_version` is the client version produced by the
241     /// changeset on that client.
242     ///
243     /// On a client, the remote peer is the server, and `remote_version` is the
244     /// server version produced by this changeset on the server. Since the
245     /// server is never the originator of changes, this changeset must in turn
246     /// have been produced on the server by integration of a changeset uploaded
247     /// by some other client.
248     version_type remote_version = 0;
249
250     /// The last local version that has been integrated into `remote_version`.
251     ///
252     /// A local version, L, has been integrated into a remote version, R, when,
253     /// and only when L is the latest local version such that all preceeding
254     /// changesets in the local history have been integrated by the remote peer
255     /// prior to R.
256     ///
257     /// On the server, this is the last server version integrated into the
258     /// client version `remote_version`. On a client, it is the last client
259     /// version integrated into the server version `remote_version`.
260     version_type last_integrated_local_version = 0;
261
262     /// The changeset itself.
263     ChunkedBinaryData data;
264
265     /// Same meaning as `HistoryEntry::origin_timestamp`.
266     timestamp_type origin_timestamp = 0;
267
268     /// Same meaning as `HistoryEntry::origin_client_file_ident`.
269     file_ident_type origin_client_file_ident = 0;
270
271     /// If the changeset was compacted during download, the size of the original
272     /// changeset.
273     size_t original_changeset_size = 0;
274
275     RemoteChangeset() {}
276     RemoteChangeset(version_type rv, version_type lv, ChunkedBinaryData d, timestamp_type ot,
277                     file_ident_type fi);
278 };
279
280 class Transformer::Reporter {
281 public:
282     virtual void report_merges(long num_merges) = 0;
283 };
284
285
286 void parse_remote_changeset(const Transformer::RemoteChangeset&, Changeset&);
287
288
289
290
291 // Implementation
292
293 class TransformError: public std::runtime_error {
294 public:
295     TransformError(const std::string& message):
296         std::runtime_error(message)
297     {
298     }
299 };
300
301 inline Transformer::RemoteChangeset::RemoteChangeset(version_type rv, version_type lv,
302                                                      ChunkedBinaryData d, timestamp_type ot,
303                                                      file_ident_type fi):
304     remote_version(rv),
305     last_integrated_local_version(lv),
306     data(d),
307     origin_timestamp(ot),
308     origin_client_file_ident(fi)
309 {
310 }
311
312 } // namespace sync
313 } // namespace realm
314
315 #endif // REALM_SYNC_TRANSFORM_HPP