/// <summary> /// Remove a mapping from a document GUID. /// The page chain is not affected. /// If no such document id is bound, nothing happens /// </summary> public void UnbindIndex(Guid documentId) { lock (_fslock) { var indexLink = GetIndexPageLink(); if (!indexLink.TryGetLink(0, out var indexTopPageId)) { return; // no index to unbind } // Search for the binding, and remove if found var currentPage = GetRawPage(indexTopPageId); while (currentPage != null) { var indexSnap = new IndexPage(); indexSnap.Defrost(currentPage.BodyStream()); var found = indexSnap.Remove(documentId); if (found) { var stream = indexSnap.Freeze(); currentPage.Write(stream, 0, stream.Length); CommitPage(currentPage); Flush(); return; } currentPage = GetRawPage(currentPage.PrevPageId); } } }
/// <summary> /// Get the top page ID for a document ID by reading the index. /// If the document ID can't be found, returns -1 /// </summary> public int GetDocumentHead(Guid documentId) { var indexLink = GetIndexPageLink(); if (!indexLink.TryGetLink(0, out var indexTopPageId)) { indexTopPageId = -1; } // Try to update an existing document var currentPage = GetRawPage(indexTopPageId); while (currentPage != null) { var indexSnap = new IndexPage(); indexSnap.Defrost(currentPage.BodyStream()); var found = indexSnap.Search(documentId, out var link); if (found && link != null) { if (link.TryGetLink(0, out var result)) { return(result); } } currentPage = GetRawPage(currentPage.PrevPageId); } return(-1); }
/// <summary> /// Map a document GUID to a page ID. /// If the document has an existing page, the versions will be incremented. /// If a version expires, the page ID will be returned in `expiredPageId` /// </summary> /// <param name="documentId">Unique ID for the document</param> /// <param name="newPageId">top page id for most recent version of the document</param> /// <param name="expiredPageId">an expired version of the document, or `-1` if no versions have expired</param> public void BindIndex(Guid documentId, int newPageId, out int expiredPageId) { lock (_fslock) { var indexLink = GetIndexPageLink(); if (!indexLink.TryGetLink(0, out var indexTopPageId)) { indexTopPageId = -1; } // Try to update an existing document var currentPage = GetRawPage(indexTopPageId); while (currentPage != null) { var indexSnap = new IndexPage(); indexSnap.Defrost(currentPage.BodyStream()); var found = indexSnap.Update(documentId, newPageId, out expiredPageId); if (found) { var stream = indexSnap.Freeze(); currentPage.Write(stream, 0, stream.Length); CommitPage(currentPage); return; } currentPage = GetRawPage(currentPage.PrevPageId); } // Try to insert a new link in an existing index page expiredPageId = -1; currentPage = GetRawPage(indexTopPageId); while (currentPage != null) { var indexSnap = new IndexPage(); indexSnap.Defrost(currentPage.BodyStream()); var found = indexSnap.TryInsert(documentId, newPageId); if (found) { var stream = indexSnap.Freeze(); currentPage.Write(stream, 0, stream.Length); CommitPage(currentPage); return; } currentPage = GetRawPage(currentPage.PrevPageId); } // need to extend into a new index, and write to a new version of the head var newIndex = new IndexPage(); var ok = newIndex.TryInsert(documentId, newPageId); if (!ok) { throw new Exception("Failed to write index to blank index page"); } var slot = new int[1]; AllocatePageBlock(slot); var newPage = GetRawPage(slot[0]) ?? throw new Exception("Failed to read newly allocated page"); newPage.PrevPageId = indexTopPageId; var newStream = newIndex.Freeze(); newPage.Write(newStream, 0, newStream.Length); CommitPage(newPage); // set new head link indexLink.WriteNewLink(newPage.PageId, out _); // Index is always extended, we never clean it up SetIndexPageLink(indexLink); Flush(); } }