public void RemoveEntry(byte[] key) { if (!m_rootRecord.IsParentNode) { int index = CollationHelper.FindIndexInLeafNode(m_rootRecord.IndexEntries, key, m_rootRecord.CollationRule); if (index >= 0) { m_rootRecord.IndexEntries.RemoveAt(index); } m_volume.UpdateFileRecord(m_fileRecord); } else { int indexOfEntryToRemove; KeyValuePairList <int, IndexRecord> path = FindRemovalPath(key, out indexOfEntryToRemove); if (path == null) { return; } if ((path.Count > 0 && path[path.Count - 1].Value.IsParentNode) || path.Count == 0) { // We find the rightmost leaf entry in the left branch and put it instead. // Note: Excluding the root of the branch, the rightmost leaf entry in the branch collates last. KeyValuePairList <int, IndexRecord> pathToLeaf = FindPathToRightmostLeaf(path, indexOfEntryToRemove); IndexRecord leaf = pathToLeaf[pathToLeaf.Count - 1].Value; IndexEntry entryToRemoveFromLeaf = leaf.IndexEntries[leaf.IndexEntries.Count - 1]; leaf.IndexEntries.RemoveAt(leaf.IndexEntries.Count - 1); long leafRecordIndex = ConvertToRecordIndex(leaf.RecordVBN); // Note: CHKDSK does not accept an empty IndexRecord, however, we must not call RemovePointer just yet because it might affect the parent as well. WriteIndexRecord(leafRecordIndex, leaf); if (path.Count == 0) { m_rootRecord.IndexEntries[indexOfEntryToRemove].FileReference = entryToRemoveFromLeaf.FileReference; m_rootRecord.IndexEntries[indexOfEntryToRemove].Key = entryToRemoveFromLeaf.Key; m_volume.UpdateFileRecord(m_fileRecord); } else { path[path.Count - 1].Value.IndexEntries[indexOfEntryToRemove].FileReference = entryToRemoveFromLeaf.FileReference; path[path.Count - 1].Value.IndexEntries[indexOfEntryToRemove].Key = entryToRemoveFromLeaf.Key; long recordIndex = ConvertToRecordIndex(path[path.Count - 1].Value.RecordVBN); WriteIndexRecord(recordIndex, path[path.Count - 1].Value); } if (leaf.IndexEntries.Count == 0) { int indexOfLeafPointer = pathToLeaf[pathToLeaf.Count - 1].Key; RemovePointer(pathToLeaf.GetRange(0, pathToLeaf.Count - 1), indexOfLeafPointer); DeallocateIndexRecord(leafRecordIndex); } } else { int indexInParentRecord = path[path.Count - 1].Key; IndexRecord leaf = path[path.Count - 1].Value; leaf.IndexEntries.RemoveAt(indexOfEntryToRemove); long recordIndex = ConvertToRecordIndex(leaf.RecordVBN); if (leaf.IndexEntries.Count > 0) { WriteIndexRecord(recordIndex, leaf); } else { path.RemoveAt(path.Count - 1); RemovePointer(path, indexInParentRecord); DeallocateIndexRecord(recordIndex); } } } }
/// <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); } } }