public void AddEntry(MftSegmentReference fileReference, byte[] key) { IndexEntry entry = new IndexEntry(); entry.FileReference = fileReference; entry.Key = key; if (!m_rootRecord.IsParentNode) { int insertIndex = CollationHelper.FindIndexForSortedInsert(m_rootRecord.IndexEntries, key, m_rootRecord.CollationRule); m_rootRecord.IndexEntries.Insert(insertIndex, entry); if (m_rootRecord.RecordLength >= m_volume.AttributeRecordLengthToMakeNonResident) { if (m_indexAllocationRecord == null) { m_indexAllocationRecord = (IndexAllocationRecord)m_fileRecord.CreateAttributeRecord(AttributeType.IndexAllocation, m_indexName); m_indexAllocationData = new NonResidentAttributeData(m_volume, m_fileRecord, m_indexAllocationRecord); m_bitmapRecord = m_fileRecord.CreateAttributeRecord(AttributeType.Bitmap, m_indexName); m_bitmapData = new BitmapData(m_volume, m_fileRecord, m_bitmapRecord, 0); } SplitRootIndexRecord(); } else { m_volume.UpdateFileRecord(m_fileRecord); } } else { KeyValuePairList <int, IndexRecord> path = FindInsertPath(key); IndexRecord leafRecord = path[path.Count - 1].Value; long leafRecordVBN = leafRecord.RecordVBN; int insertIndex = CollationHelper.FindIndexForSortedInsert(leafRecord.IndexEntries, key, m_rootRecord.CollationRule); leafRecord.IndexEntries.Insert(insertIndex, entry); long leafRecordIndex = ConvertToRecordIndex(leafRecordVBN); if (leafRecord.DoesFit((int)m_rootRecord.BytesPerIndexRecord)) { WriteIndexRecord(leafRecordIndex, leafRecord); } else { // Split index record SplitIndexRecord(path); } } }
/// <param name="path">Key is index in parent node</param> private void SplitIndexRecord(KeyValuePairList <int, IndexRecord> path) { int indexInParentRecord = path[path.Count - 1].Key; // We will treat the record we want to split as the right node, and create a left node IndexRecord rightNode = path[path.Count - 1].Value; long rightNodeVBN = rightNode.RecordVBN; long rightNodeIndex = ConvertToRecordIndex(rightNodeVBN); List <IndexEntry> rightNodeEntries = rightNode.IndexEntries; int splitIndex = rightNodeEntries.Count / 2; IndexEntry middleEntry = rightNodeEntries[splitIndex]; IndexRecord leftNode = new IndexRecord(); leftNode.IsParentNode = rightNode.IsParentNode; leftNode.IndexEntries = rightNodeEntries.GetRange(0, splitIndex); rightNodeEntries.RemoveRange(0, splitIndex + 1); if (rightNode.IsParentNode) { // A parent node has n keys and points to (n + 1) subnodes, // When splitting it to two nodes we will take the pointer from the entry we wish to push to the parent node, // and use it as the last pointer in the left node. IndexEntry leftNodeLastEntry = new IndexEntry(); leftNodeLastEntry.ParentNodeForm = true; leftNodeLastEntry.IsLastEntry = true; leftNodeLastEntry.SubnodeVBN = middleEntry.SubnodeVBN; leftNode.IndexEntries.Add(leftNodeLastEntry); } long leftNodeIndex = AllocateIndexRecord(); leftNode.RecordVBN = ConvertToVirtualBlockNumber(leftNodeIndex); IndexEntry newParentEntry = new IndexEntry(middleEntry.FileReference, middleEntry.Key); newParentEntry.ParentNodeForm = true; newParentEntry.SubnodeVBN = leftNode.RecordVBN; WriteIndexRecord(rightNodeIndex, rightNode); WriteIndexRecord(leftNodeIndex, leftNode); if (path.Count > 1) { IndexRecord parentRecord = path[path.Count - 2].Value; long parentRecordIndex = ConvertToRecordIndex(parentRecord.RecordVBN); List <IndexEntry> parentEntries = parentRecord.IndexEntries; parentEntries.Insert(indexInParentRecord, newParentEntry); if (parentRecord.DoesFit((int)m_rootRecord.BytesPerIndexRecord)) { WriteIndexRecord(parentRecordIndex, parentRecord); } else { // Split parent index record path.RemoveAt(path.Count - 1); SplitIndexRecord(path); } } else { m_rootRecord.IndexEntries.Insert(indexInParentRecord, newParentEntry); if (m_rootRecord.RecordLength >= m_volume.AttributeRecordLengthToMakeNonResident) { SplitRootIndexRecord(); } else { m_volume.UpdateFileRecord(m_fileRecord); } } }