/// <summary> /// Add/Remove page into free slots on collection page. Used when some data are removed/changed /// For insert data, this method are called from GetFreePage /// </summary> public void AddOrRemoveFreeList <T>(T page, int initialSlot) where T : BasePage { ENSURE(page.PageType == PageType.Index || page.PageType == PageType.Data, "only index/data page contains free linked-list"); var newSlot = BasePage.FreeIndexSlot(page.FreeBytes); // there is no slot change - just exit (no need any change) [except if has no more items) if (newSlot == initialSlot && page.ItemsCount > 0) { return; } // select if I will get from free index list or data list var freeLists = page.PageType == PageType.Index ? _collectionPage.FreeIndexPageID : _collectionPage.FreeDataPageID; // remove from intial slot this.RemoveFreeList <T>(page, ref freeLists[initialSlot]); // if there is no items, delete page if (page.ItemsCount == 0) { this.DeletePage(page); } else { // add into current slot this.AddFreeList <T>(page, ref freeLists[newSlot]); } }
/// <summary> /// Delete a single index node - fix tree double-linked list levels /// </summary> private void DeleteSingleNode(IndexNode node) { for (int i = node.Level - 1; i >= 0; i--) { // get previous and next nodes (between my deleted node) var prevNode = this.GetNode(node.Prev[i]); var nextNode = this.GetNode(node.Next[i]); if (prevNode != null) { prevNode.SetNext((byte)i, node.Next[i]); } if (nextNode != null) { nextNode.SetPrev((byte)i, node.Prev[i]); } } // get current slot position in free list var slot = BasePage.FreeIndexSlot(node.Page.FreeBytes); node.Page.DeleteIndexNode(node.Position.Index); // update (if needed) slot position _snapshot.AddOrRemoveFreeList(node.Page, slot); }
/// <summary> /// Returns a page that contains space enough to data to insert new object - if one does not exit, creates a new page. /// Before return page, fix empty free list slot according with passed length /// </summary> public T GetFreePage <T>(int bytesLength) where T : BasePage { var length = bytesLength + BasePage.SLOT_SIZE; // add +4 bytes for footer slot // select if I will get from free index list or data list var freeLists = typeof(T) == typeof(IndexPage) ? _collectionPage.FreeIndexPageID : _collectionPage.FreeDataPageID; // get minimum slot to check for free page. Returns -1 if need NewPage var startSlot = BasePage.GetMinimumIndexSlot(length); // check for avaiable re-usable page for (int currentSlot = startSlot; currentSlot >= 0; currentSlot--) { var freePageID = freeLists[currentSlot]; // there is no free page here, try find princess in another castle if (freePageID == uint.MaxValue) { continue; } var page = this.GetPage <T>(freePageID); var newSlot = BasePage.FreeIndexSlot(page.FreeBytes - length); // if slots will change, fix now if (currentSlot != newSlot) { this.RemoveFreeList <T>(page, ref freeLists[currentSlot]); this.AddFreeList <T>(page, ref freeLists[newSlot]); } ENSURE(page.FreeBytes >= length, "ensure selected page has space enougth for this content"); // mark page page as dirty page.IsDirty = true; return(page); } // if not page avaiable, create new and add in free list var newPage = this.NewPage <T>(); // get slot based on how many blocks page will have after use var slot = BasePage.FreeIndexSlot(newPage.FreeBytes - length - BasePage.SLOT_SIZE); // and add into free-list this.AddFreeList <T>(newPage, ref freeLists[slot]); return(newPage); }
/// <summary> /// Delete all datablock that contains a document (can use multiples data blocks) /// </summary> public void Delete(PageAddress blockAddress) { // delete all document blocks while (blockAddress != PageAddress.Empty) { var page = _snapshot.GetPage <DataPage>(blockAddress.PageID); var block = page.GetBlock(blockAddress.Index); var slot = BasePage.FreeIndexSlot(page.FreeBytes); // delete block inside page page.DeleteBlock(blockAddress.Index); // fix page empty list (or delete page) _snapshot.AddOrRemoveFreeList(page, slot); blockAddress = block.NextBlock; } }
/// <summary> /// Update document using same page position as reference /// </summary> public void Update(CollectionPage col, PageAddress blockAddress, BsonDocument doc) { var bytesLeft = doc.GetBytesCount(true); if (bytesLeft > MAX_DOCUMENT_SIZE) { throw new LiteException(0, "Document size exceed {0} limit", MAX_DOCUMENT_SIZE); } DataBlock lastBlock = null; var updateAddress = blockAddress; IEnumerable <BufferSlice> source() { var bytesToCopy = 0; while (bytesLeft > 0) { // if last block contains new block sequence, continue updating if (updateAddress.IsEmpty == false) { var dataPage = _snapshot.GetPage <DataPage>(updateAddress.PageID); var currentBlock = dataPage.GetBlock(updateAddress.Index); // try get full page size content bytesToCopy = Math.Min(bytesLeft, dataPage.FreeBytes + currentBlock.Buffer.Count); // get current free slot linked list var slot = BasePage.FreeIndexSlot(dataPage.FreeBytes); var updateBlock = dataPage.UpdateBlock(currentBlock, bytesToCopy); _snapshot.AddOrRemoveFreeList(dataPage, slot); yield return(updateBlock.Buffer); lastBlock = updateBlock; // go to next address (if extits) updateAddress = updateBlock.NextBlock; } else { bytesToCopy = Math.Min(bytesLeft, MAX_DATA_BYTES_PER_PAGE); var dataPage = _snapshot.GetFreePage <DataPage>(bytesToCopy + DataBlock.DATA_BLOCK_FIXED_SIZE); var insertBlock = dataPage.InsertBlock(bytesToCopy, true); if (lastBlock != null) { lastBlock.SetNextBlock(insertBlock.Position); } yield return(insertBlock.Buffer); lastBlock = insertBlock; } bytesLeft -= bytesToCopy; } // old document was bigger than current, must delete extend blocks if (lastBlock.NextBlock.IsEmpty == false) { this.Delete(lastBlock.NextBlock); } } // consume all source bytes to write BsonDocument direct into PageBuffer // must be fastest as possible using (var w = new BufferWriter(source())) { // already bytes count calculate at method start w.WriteDocument(doc, false); w.Consume(); } }