public YDoc RestoreDocument(YDoc originDoc, YDocOptions opts = null) { if (originDoc.Gc) { // We should try to restore a GC-ed document, because some of the restored items might have their content deleted. throw new Exception("originDoc must not be garbage collected"); } using var encoder = new UpdateEncoderV2(); originDoc.Transact(tr => { int size = StateVector.Count(kvp => kvp.Value /* clock */ > 0); encoder.RestWriter.WriteVarUint((uint)size); // Splitting the structs before writing them to the encoder. foreach (var kvp in StateVector) { int client = kvp.Key; int clock = kvp.Value; if (clock == 0) { continue; } if (clock < originDoc.Store.GetState(client)) { tr.Doc.Store.GetItemCleanStart(tr, new ID(client, clock)); } var structs = originDoc.Store.Clients[client]; var lastStructIndex = StructStore.FindIndexSS(structs, clock - 1); // Write # encoded structs. encoder.RestWriter.WriteVarUint((uint)(lastStructIndex + 1)); encoder.WriteClient(client); // First clock written is 0. encoder.RestWriter.WriteVarUint(0); for (int i = 0; i <= lastStructIndex; i++) { structs[i].Write(encoder, 0); } } DeleteSet.Write(encoder); }); var newDoc = new YDoc(opts ?? originDoc.CloneOptionsWithNewGuid()); newDoc.ApplyUpdateV2(encoder.ToArray(), transactionOrigin: "snapshot"); return(newDoc); }
public void ReadAndApplyDeleteSet(IDSDecoder decoder, Transaction transaction) { var unappliedDs = new DeleteSet(); var numClients = decoder.Reader.ReadVarUint(); for (int i = 0; i < numClients; i++) { decoder.ResetDsCurVal(); var client = (int)decoder.Reader.ReadVarUint(); var numberOfDeletes = decoder.Reader.ReadVarUint(); if (!Clients.TryGetValue(client, out var structs)) { structs = new List <AbstractStruct>(); // NOTE: Clients map is not updated. } var state = GetState(client); for (int deleteIndex = 0; deleteIndex < numberOfDeletes; deleteIndex++) { var clock = decoder.ReadDsClock(); var clockEnd = clock + decoder.ReadDsLength(); if (clock < state) { if (state < clockEnd) { unappliedDs.Add(client, state, clockEnd - state); } var index = StructStore.FindIndexSS(structs, clock); // We can ignore the case of GC and Delete structs, because we are going to skip them. var str = structs[index]; // Split the first item if necessary. if (!str.Deleted && str.Id.Clock < clock) { var splitItem = (str as Item).SplitItem(transaction, clock - str.Id.Clock); structs.Insert(index + 1, splitItem); // Increase, we now want to use the next struct. index++; } while (index < structs.Count) { str = structs[index++]; if (str.Id.Clock < clockEnd) { if (!str.Deleted) { if (clockEnd < str.Id.Clock + str.Length) { var splitItem = (str as Item).SplitItem(transaction, clockEnd - str.Id.Clock); structs.Insert(index, splitItem); } str.Delete(transaction); } } else { break; } } } else { unappliedDs.Add(client, clock, clockEnd - clock); } } } if (unappliedDs.Clients.Count > 0) { // @TODO: No need for encoding+decoding ds anymore. using var unappliedDsEncoder = new DSEncoderV2(); unappliedDs.Write(unappliedDsEncoder); _pendingDeleteReaders.Add(new DSDecoderV2(new MemoryStream(unappliedDsEncoder.ToArray()))); } }