public void can_look_up_paths_by_value_from_serialised_data() { // you can assign the same value to multiple paths // this could be quite useful, but I'd like to be able to // reverse the process -- see what paths an objects is bound // to. Could be a simple set of scans (slow) or a restructuring // of the internal data. var source = new ReverseTrie <ByteString>(); source.Add("very different", "value0"); source.Add("my/path/1", "value1"); source.Add("my/path/2", "value2"); source.Add("b - very different", "value0"); source.Add("my/other/path", "value3"); source.Add("my/other/path/longer", "value4"); source.Add("another/path/for/3", "value3"); source.Add("z - very different", "value0"); var bytes = source.Freeze(); var reconstituted = new ReverseTrie <ByteString>(); bytes.Seek(0, SeekOrigin.Begin); reconstituted.Defrost(bytes); var result = string.Join(", ", reconstituted.GetPathsForEntry("value3")); Assert.That(result, Is.EqualTo("my/other/path, another/path/for/3")); }
/// <summary> /// Remove a path binding if it exists. If the path is not bound, nothing happens. /// Linked documents are not removed. /// </summary> public void UnbindPath(string exactPath) { _pathLookupCache = null; lock (_fslock) { var pathLink = GetPathLookupLink(); var pathIndex = new ReverseTrie <SerialGuid>(); if (!pathLink.TryGetLink(0, out var pathPageId)) { return; } pathIndex.Defrost(GetStream(pathPageId)); // Unbind the path pathIndex.Delete(exactPath); // Write back to new chain var newPageId = WriteStream(pathIndex.Freeze()); // Update version link pathLink.WriteNewLink(newPageId, out var expired); SetPathLookupLink(pathLink); ReleaseChain(expired); Flush(); } }
[NotNull] private ReverseTrie <SerialGuid> GetPathLookupIndex() { var pathIndex = _pathLookupCache; if (pathIndex != null) { return(pathIndex); } lock (_fslock) { var pathLink = GetPathLookupLink(); pathIndex = new ReverseTrie <SerialGuid>(); if (pathLink.TryGetLink(0, out var pathPageId)) { pathIndex.Defrost(GetStream(pathPageId)); } _pathLookupCache = pathIndex; } return(pathIndex); }
public void serialisation_and_restoring() { var subject = new ReverseTrie <ByteString>(); var rawLength = 0; // Fill it up for (int i = 0; i < 100; i++) { var key = $"Path number {i}"; var value = $"{i}"; rawLength += key.Length + value.Length; subject.Add(key, ByteString.Wrap(value)); } var frozen = subject.Freeze(); frozen.Seek(0, SeekOrigin.Begin); Console.WriteLine($"Encoding 100 paths in {frozen.Length} bytes. Raw input was {rawLength} bytes"); Console.WriteLine(frozen.ToHexString()); subject = null; var result = new ReverseTrie <ByteString>(); frozen.Seek(0, SeekOrigin.Begin); result.Defrost(frozen); // Check every result for (int i = 0; i < 100; i++) { var key = $"Path number {i}"; var expected = $"{i}"; Assert.That(result.Get(key)?.ToString(), Is.EqualTo(expected), $"Lost data at path '{key}'"); } }
/// <summary> /// Bind an exact path to a document ID. /// If an existing document was bound to the same path, its ID will be returned /// </summary> /// <param name="path">Exact path for document</param> /// <param name="documentId">new document id</param> /// <param name="previousDocId">old document id that has been replaced, if any.</param> public void BindPath(string path, Guid documentId, out Guid?previousDocId) { previousDocId = null; if (string.IsNullOrEmpty(path)) { throw new Exception("Path must not be null or empty"); } _pathLookupCache = null; lock (_fslock) { // Read current path document (if it exists) var pathLink = GetPathLookupLink(); var pathIndex = new ReverseTrie <SerialGuid>(); if (pathLink.TryGetLink(0, out var pathPageId)) { pathIndex.Defrost(GetStream(pathPageId)); } // Bind the path var serialGuid = pathIndex.Add(path, documentId); if (serialGuid != null) { previousDocId = serialGuid.Value; } // Write back to new chain var newPageId = WriteStream(pathIndex.Freeze()); // Update version link pathLink.WriteNewLink(newPageId, out var expired); SetPathLookupLink(pathLink); ReleaseChain(expired); Flush(); } }