/// <summary> /// Gets all node list from any index node (go forward and backward) /// </summary> public IEnumerable <IndexNode> GetNodeList(IndexNode node, bool includeInitial) { var next = node.NextNode; var prev = node.PrevNode; // returns some initial node if (includeInitial) { yield return(node); } // go forward while (next.IsEmpty == false) { var n = this.GetNode(next); next = n.NextNode; yield return(n); } // go backward while (prev.IsEmpty == false) { var p = this.GetNode(prev); prev = p.PrevNode; yield return(p); } }
protected override void ReadContent(ByteReader reader) { this.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(); } this.Nodes.Add(node.Position.Index, node); } }
/// <summary> /// Create a new index and returns head page address (skip list) /// </summary> public CollectionIndex CreateIndex(CollectionIndex index) { // get a new index page for first index page var page = _pager.NewPage <IndexPage>(); // create a empty node with full max level var node = new IndexNode(IndexNode.MAX_LEVEL_LENGTH) { Key = new IndexKey(null), Page = page }; node.Position = new PageAddress(page.PageID, 0); // add as first node page.Nodes.Add(node.Position.Index, node); // add/remove indexPage on freelist if has space _pager.AddOrRemoveToFreeList(page.FreeBytes > BasePage.RESERVED_BYTES, page, index.Page, ref index.FreeIndexPageID); // point the head node to this new node position index.HeadNode = node.Position; index.Page.IsDirty = true; page.IsDirty = true; return(index); }
/// <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); }
internal virtual void Delete(CollectionPage col, IndexNode node) { // read dataBlock var dataBlock = this.Database.Data.Read(node.DataBlock, false); // lets remove all indexes that point to this in dataBlock foreach (var index in col.GetIndexes(true)) { this.Database.Indexer.Delete(index, dataBlock.IndexRef[index.Slot]); } // remove object data this.Database.Data.Delete(col, node.DataBlock); }
/// <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> /// Go first/last occurence 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); }
internal virtual void Delete(CollectionPage col, IndexNode node) { // read dataBlock var dataBlock = _engine.Data.Read(node.DataBlock, false); // lets remove all indexes that point to this in dataBlock for (byte i = 0; i < col.Indexes.Length; i++) { var index = col.Indexes[i]; if (!index.IsEmpty) { _engine.Indexer.Delete(index, dataBlock.IndexRef[i]); } } // remove object data _engine.Data.Delete(col, node.DataBlock); }
/// <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, Position = new PageAddress(page.PageID, 0) }; // add as first node page.Nodes.Add(head.Position.Index, head); // update freebytes + item count (for head) page.UpdateItemCount(); // 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) { // creating a new index node var node = new IndexNode(level) { Key = key, KeyLength = key.GetBytesCount() }; if (node.KeyLength > MAX_INDEX_LENGTH) { throw LiteException.IndexKeyTooLong(); } // get a free page to insert my index node var page = _pager.GetFreePage <IndexPage>(index.FreeIndexPageID, node.Length); node.Position = new PageAddress(page.PageID, page.Nodes.NextIndex()); node.Page = page; // add index node to page page.Nodes.Add(node.Position.Index, node); // update freebytes + items count page.UpdateItemCount(); // now, let's link my index node on right place var cur = this.GetNode(index.HeadNode); // scan from top left for (var i = IndexNode.MAX_LEVEL_LENGTH - 1; i >= 0; i--) { // for(; <while_not_this>; <do_this>) { ... } for (; cur.Next[i].IsEmpty == false; cur = this.GetNode(cur.Next[i])) { // read next node to compare var diff = this.GetNode(cur.Next[i]).Key.CompareTo(key); // if unique and diff = 0, throw index exception (must rollback transaction - others nodes can be dirty) if (diff == 0 && index.Options.Unique) { throw LiteException.IndexDuplicateKey(index.Field, key); } if (diff == 1) { break; } } if (i <= (level - 1)) // level == length { // cur = current (imediatte before - prev) // node = new inserted node // next = next node (where cur is poiting) 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; next.Page.IsDirty = true; } cur.Page.IsDirty = true; } } // add/remove indexPage on freelist if has space _pager.AddOrRemoveToFreeList(page.FreeBytes > IndexPage.INDEX_RESERVED_BYTES, page, index.Page, ref index.FreeIndexPageID); page.IsDirty = true; return(node); }
/// <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); 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.Position = new PageAddress(page.PageID, page.Nodes.NextIndex()); node.Page = page; // add index node to page page.Nodes.Add(node.Position.Index, node); // update freebytes + items count page.UpdateItemCount(); // 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 = IndexNode.MAX_LEVEL_LENGTH - 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 (imediatte before - prev) // node = new inserted node // next = next node (where cur is poiting) _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); }
/// <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) { // call AddNode with key value return(this.AddNode(index, key, this.FlipCoin(), last)); }
/// <summary> /// Insert a new node index inside a index. Use skip list /// </summary> public IndexNode AddNode(CollectionIndex index, object value) { // create persist value - used on key var key = new IndexKey(value); var level = this.FlipCoin(); // creating a new index node var node = new IndexNode(level) { Key = key }; // get a free page to insert my index node var page = _pager.GetFreePage <IndexPage>(index.FreeIndexPageID, node.Length); node.Position = new PageAddress(page.PageID, page.Nodes.NextIndex()); node.Page = page; // add index node to page page.Nodes.Add(node.Position.Index, node); // now, let's link my index node on right place var cur = this.GetNode(index.HeadNode); // scan from top left for (int i = IndexNode.MAX_LEVEL_LENGTH - 1; i >= 0; i--) { // for(; <while_not_this>; <do_this>) { ... } for (; cur.Next[i].IsEmpty == false; cur = this.GetNode(cur.Next[i])) { // read next node to compare var diff = this.GetNode(cur.Next[i]).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 new LiteException(string.Format("Cannot insert duplicate key in unique index '{0}'. The duplicate value is '{1}'.", index.Field, value)); } if (diff == 1) { break; } } if (i <= (level - 1)) // level == length { // cur = current (imediatte before - prev) // node = new inserted node // next = next node (where cur is poiting) 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; next.Page.IsDirty = true; } cur.Page.IsDirty = true; } } // add/remove indexPage on freelist if has space _pager.AddOrRemoveToFreeList(page.FreeBytes > IndexPage.RESERVED_BYTES, page, index.Page, ref index.FreeIndexPageID); page.IsDirty = true; return(node); }
/// <summary> /// Wrtie any BsonValue. Use 1 byte for data type, 1 byte for length (optional), 0-255 bytes to value. /// For document or array, use BufferWriter /// </summary> public static void WriteIndexKey(this BufferSlice buffer, BsonValue value, int offset) { ENSURE(IndexNode.GetKeyLength(value, true) <= MAX_INDEX_KEY_LENGTH, $"index key must have less than {MAX_INDEX_KEY_LENGTH} bytes"); if (value.IsString) { var str = value.AsString; var strLength = (ushort)Encoding.UTF8.GetByteCount(str); ExtendedLengthHelper.WriteLength(BsonType.String, strLength, out var typeByte, out var lengthByte); buffer[offset++] = typeByte; buffer[offset++] = lengthByte; buffer.Write(str, offset); } else if (value.IsBinary) { var arr = value.AsBinary; ExtendedLengthHelper.WriteLength(BsonType.Binary, (ushort)arr.Length, out var typeByte, out var lengthByte); buffer[offset++] = typeByte; buffer[offset++] = lengthByte; buffer.Write(arr, offset); } else { buffer[offset++] = (byte)value.Type; switch (value.Type) { case BsonType.Null: case BsonType.MinValue: case BsonType.MaxValue: break; case BsonType.Int32: buffer.Write(value.AsInt32, offset); break; case BsonType.Int64: buffer.Write(value.AsInt64, offset); break; case BsonType.Double: buffer.Write(value.AsDouble, offset); break; case BsonType.Decimal: buffer.Write(value.AsDecimal, offset); break; case BsonType.Document: using (var w = new BufferWriter(buffer)) { w.Skip(offset); // skip offset from buffer w.WriteDocument(value.AsDocument, true); } break; case BsonType.Array: using (var w = new BufferWriter(buffer)) { w.Skip(offset); // skip offset from buffer w.WriteArray(value.AsArray, true); } break; case BsonType.ObjectId: buffer.Write(value.AsObjectId, offset); break; case BsonType.Guid: buffer.Write(value.AsGuid, offset); break; case BsonType.Boolean: buffer[offset] = (value.AsBoolean) ? (byte)1 : (byte)0; break; case BsonType.DateTime: buffer.Write(value.AsDateTime, offset); break; default: throw new NotImplementedException(); } } }