protected override void ReadContent(ByteReader reader) { _nodes = new Dictionary <ushort, IndexNode>(this.ItemCount); for (var i = 0; i < this.ItemCount; i++) { var index = reader.ReadUInt16(); var levels = reader.ReadByte(); var node = new IndexNode(levels); node.Page = this; node.Position = new PageAddress(this.PageID, index); node.Slot = reader.ReadByte(); node.PrevNode = reader.ReadPageAddress(); node.NextNode = reader.ReadPageAddress(); node.KeyLength = reader.ReadUInt16(); node.Key = reader.ReadBsonValue(node.KeyLength); node.DataBlock = reader.ReadPageAddress(); for (var j = 0; j < node.Prev.Length; j++) { node.Prev[j] = reader.ReadPageAddress(); node.Next[j] = reader.ReadPageAddress(); } _nodes.Add(node.Position.Index, node); } }
/// <summary> /// Delete node from this page and update counter and free space /// </summary> public void DeleteNode(IndexNode node) { this.ItemCount--; this.FreeBytes += node.Length; _nodes.Remove(node.Position.Index); }
/// <summary> /// Add new index node into this page. Update counters and free space /// </summary> public void AddNode(IndexNode node) { var index = _nodes.NextIndex(); node.Position = new PageAddress(this.PageID, index); this.ItemCount++; this.FreeBytes -= node.Length; _nodes.Add(index, node); }
/// <summary> /// Insert a new node index inside an collection index. Flip coin to know level /// </summary> public IndexNode AddNode(CollectionIndex index, BsonValue key, IndexNode last) { var level = this.FlipCoin(); // set index collection with max-index level if (level > index.MaxLevel) { index.MaxLevel = level; _pager.SetDirty(index.Page); } // call AddNode with key value return(this.AddNode(index, key, level, last)); }
/// <summary> /// Goto the first/last occurrence of this index value /// </summary> private IndexNode FindBoundary(CollectionIndex index, IndexNode cur, BsonValue value, int order, int level) { var last = cur; while (cur.Key.CompareTo(value) == 0) { last = cur; cur = this.GetNode(cur.NextPrev(0, order)); if (cur.IsHeadTail(index)) { break; } } return(last); }
/// <summary> /// Create a new index and returns head page address (skip list) /// </summary> public CollectionIndex CreateIndex(CollectionPage col) { // get index slot var index = col.GetFreeIndex(); // get a new index page for first index page var page = _pager.NewPage <IndexPage>(); // create a empty node with full max level var head = new IndexNode(IndexNode.MAX_LEVEL_LENGTH) { Key = BsonValue.MinValue, KeyLength = (ushort)BsonValue.MinValue.GetBytesCount(false), Slot = (byte)index.Slot, Page = page }; // add as first node page.AddNode(head); // set index page as dirty _pager.SetDirty(index.Page); // add indexPage on freelist if has space _pager.AddOrRemoveToFreeList(true, page, index.Page, ref index.FreeIndexPageID); // point the head/tail node to this new node position index.HeadNode = head.Position; // insert tail node var tail = this.AddNode(index, BsonValue.MaxValue, IndexNode.MAX_LEVEL_LENGTH, null); index.TailNode = tail.Position; return(index); }
/// <summary> /// Insert a new node index inside an collection index. /// </summary> private IndexNode AddNode(CollectionIndex index, BsonValue key, byte level, IndexNode last) { // calc key size var keyLength = key.GetBytesCount(false); // test for index key maxlength if (keyLength > MAX_INDEX_LENGTH) { throw LiteException.IndexKeyTooLong(); } // creating a new index node var node = new IndexNode(level) { Key = key, KeyLength = (ushort)keyLength, Slot = (byte)index.Slot }; // get a free page to insert my index node var page = _pager.GetFreePage <IndexPage>(index.FreeIndexPageID, node.Length); node.Page = page; // add index node to page page.AddNode(node); // now, let's link my index node on right place var cur = this.GetNode(index.HeadNode); // using as cache last IndexNode cache = null; // scan from top left for (var i = index.MaxLevel - 1; i >= 0; i--) { // get cache for last node cache = cache != null && cache.Position.Equals(cur.Next[i]) ? cache : this.GetNode(cur.Next[i]); // for(; <while_not_this>; <do_this>) { ... } for (; cur.Next[i].IsEmpty == false; cur = cache) { // get cache for last node cache = cache != null && cache.Position.Equals(cur.Next[i]) ? cache : this.GetNode(cur.Next[i]); // read next node to compare var diff = cache.Key.CompareTo(key); // if unique and diff = 0, throw index exception (must rollback transaction - others nodes can be dirty) if (diff == 0 && index.Unique) { throw LiteException.IndexDuplicateKey(index.Field, key); } if (diff == 1) { break; } } if (i <= (level - 1)) // level == length { // cur = current (immediately before - prev) // node = new inserted node // next = next node (where cur is pointing) _pager.SetDirty(cur.Page); node.Next[i] = cur.Next[i]; node.Prev[i] = cur.Position; cur.Next[i] = node.Position; var next = this.GetNode(node.Next[i]); if (next != null) { next.Prev[i] = node.Position; _pager.SetDirty(next.Page); } } } // add/remove indexPage on freelist if has space _pager.AddOrRemoveToFreeList(page.FreeBytes > IndexPage.INDEX_RESERVED_BYTES, page, index.Page, ref index.FreeIndexPageID); // if last node exists, create a double link list if (last != null) { // link new node with last node if (last.NextNode.IsEmpty == false) { // fix link pointer with has more nodes in list var next = this.GetNode(last.NextNode); next.PrevNode = node.Position; last.NextNode = node.Position; node.PrevNode = last.Position; node.NextNode = next.Position; _pager.SetDirty(next.Page); } else { last.NextNode = node.Position; node.PrevNode = last.Position; } // set last node page as dirty _pager.SetDirty(last.Page); } return(node); }