/// <summary> /// The recursive portion of the find key algorithm invoked by the FindKey method of the parent Index. /// </summary> public bool FindKey(Stream AKey, object ACompareContext, SearchPath ASearchPath, out int AEntryNumber) { ASearchPath.Add(this); if (NodeType == IndexNodeType.Routing) { // Perform a binary search among the keys in this node to determine which streamid to follow for the next node bool LResult = NodeSearch(AKey, ACompareContext, out AEntryNumber); // If the key was found, use the given entry number, otherwise, use the one before the given entry AEntryNumber = LResult ? AEntryNumber : (AEntryNumber - 1); return (new IndexNode ( FProcess, FIndex, IndexUtility.GetStreamID(Data(AEntryNumber), 0) ).FindKey ( AKey, ACompareContext, ASearchPath, out AEntryNumber )); } else { // Perform a binary search among the keys in this node to determine which entry, if any, is equal to the given key return(NodeSearch(AKey, ACompareContext, out AEntryNumber)); } }
private void DeallocateNode(ServerProcess AProcess, StreamID AStreamID) { using (IndexNode LIndexNode = new IndexNode(AProcess, this, AStreamID)) { for (int LEntryIndex = LIndexNode.EntryCount - 1; LEntryIndex >= 0; LEntryIndex--) { if (LIndexNode.NodeType == IndexNodeType.Routing) { if (LEntryIndex > 0) { DisposeKey(AProcess, LIndexNode.Key(LEntryIndex)); } DeallocateNode(AProcess, IndexUtility.GetStreamID(LIndexNode.Data(LEntryIndex), 0)); } else { DisposeKey(AProcess, LIndexNode.Key(LEntryIndex)); DisposeData(AProcess, LIndexNode.Data(LEntryIndex)); if (LIndexNode.NextNode == StreamID.Null) { FTailID = LIndexNode.PriorNode; } else { using (IndexNode LNextNode = new IndexNode(AProcess, this, LIndexNode.NextNode)) { LNextNode.PriorNode = LIndexNode.PriorNode; } } if (LIndexNode.PriorNode == StreamID.Null) { FHeadID = LIndexNode.NextNode; } else { using (IndexNode LPriorNode = new IndexNode(AProcess, this, LIndexNode.PriorNode)) { LPriorNode.NextNode = LIndexNode.NextNode; } } } } } AProcess.StreamManager.Deallocate(AStreamID); }
private unsafe void InternalInsert(ServerProcess AProcess, SearchPath ASearchPath, int AEntryNumber, Stream AKey, Stream AData) { // Walk back up the search path, inserting data and splitting pages as necessary IndexNode LNewIndexNode; for (int LIndex = ASearchPath.Count - 1; LIndex >= 0; LIndex--) { if (ASearchPath[LIndex].EntryCount >= ASearchPath[LIndex].MaxEntries) { // Allocate a new node using (LNewIndexNode = AllocateNode(AProcess, ASearchPath[LIndex].NodeType)) { // Thread it into the list of leaves, if necessary if (LNewIndexNode.NodeType == IndexNodeType.Data) { LNewIndexNode.PriorNode = ASearchPath[LIndex].StreamID; LNewIndexNode.NextNode = ASearchPath[LIndex].NextNode; ASearchPath[LIndex].NextNode = LNewIndexNode.StreamID; if (LNewIndexNode.NextNode == StreamID.Null) { FTailID = LNewIndexNode.StreamID; } else { using (IndexNode LNextIndexNode = new IndexNode(AProcess, this, LNewIndexNode.NextNode)) { LNextIndexNode.PriorNode = LNewIndexNode.StreamID; } } } // Insert the upper half of the entries from ASearchPath[LIndex] into the new index node int LEntryCount = ASearchPath[LIndex].EntryCount; int LEntryPivot = LEntryCount / 2; for (int LEntryIndex = LEntryPivot; LEntryIndex < LEntryCount; LEntryIndex++) { LNewIndexNode.InternalInsert(ASearchPath[LIndex].Key(LEntryIndex), ASearchPath[LIndex].Data(LEntryIndex), LEntryIndex - LEntryPivot); // The internal call prevents the RowsMoved event from being fired } // Remove the upper half of the entries from ASearchPath[LIndex] for (int LEntryIndex = LEntryCount - 1; LEntryIndex >= LEntryPivot; LEntryIndex--) { ASearchPath[LIndex].InternalDelete(LEntryIndex); // The internal call prevents the data inside from being passed to the DisposeXXX methods, and prevents the RowDeleted event from being fired } // Notify index clients of the data change RowsMoved(ASearchPath[LIndex].StreamID, LEntryPivot, LEntryCount - 1, LNewIndexNode.StreamID, -LEntryPivot); // Insert the new entry into the appropriate node if (AEntryNumber >= LEntryPivot) { LNewIndexNode.Insert(AKey, AData, AEntryNumber - LEntryPivot); } else { ASearchPath[LIndex].Insert(AKey, AData, AEntryNumber); } // Reset the AKey, AData and AEntryNumber variables for the next round // The key for the entry one level up is the first key for the newly allocated node AKey = new MemoryStream(KeyLength); AKey.SetLength(KeyLength); CopyKey(AProcess, LNewIndexNode.Key(0), AKey); // The data is the StreamID of the newly allocated node AData = new MemoryStream(sizeof(StreamID)); IndexUtility.SetStreamID(AData, 0, LNewIndexNode.StreamID); } if (LIndex == 0) { // Allocate a new root node and grow the height of the tree by 1 using (LNewIndexNode = AllocateNode(AProcess, IndexNodeType.Routing)) { LNewIndexNode.Insert(AKey, AData, 0); AKey = new MemoryStream(KeyLength); AKey.SetLength(KeyLength); AData = new MemoryStream(DataLength); AData.SetLength(KeyLength); IndexUtility.SetStreamID(AData, 0, ASearchPath[LIndex].StreamID); LNewIndexNode.Insert(AKey, AData, 0); FRootID = LNewIndexNode.StreamID; FHeight++; } } else { bool LResult = ASearchPath[LIndex - 1].NodeSearch(AKey, null, out AEntryNumber); // At this point we should be guaranteed to have a routing key which does not exist in the parent node if (LResult) { throw new IndexException(IndexException.Codes.DuplicateRoutingKey); } } } else { ASearchPath[LIndex].Insert(AKey, AData, AEntryNumber); break; } } }