public BTreeOnHeap(HeapOnNode heap, HeapID bTreeHeaderHeapID) { m_heap = heap; m_bTreeHeaderHeapID = bTreeHeaderHeapID; byte[] headerBytes = m_heap.GetHeapItem(bTreeHeaderHeapID); BTreeHeader = new BTreeOnHeapHeader(headerBytes); }
public BTreeOnHeapLeaf <T> GetLeaf(HeapID leafHeapID, BTreeOnHeapIndexRecord leafIndexRecord) { BTreeOnHeapLeaf <T> result; if (!m_leavesCache.ContainsKey(leafHeapID.Value)) { BTreeOnHeapLeaf <T> leaf = GetLeafFromHeap(leafHeapID); if (leaf != null) { m_leavesCache.Add(leafHeapID.Value, leaf); } result = leaf; } else { result = m_leavesCache[leafHeapID.Value]; } if (result != null && leafIndexRecord != null) { // The BTH index node might have changed since the leaf was stored, // We must not use the index node stored in the cache. result.Index = leafIndexRecord.Index; } return(result); }
// We no longer create new TCs, we simply use the template and modify to another data tree public static TableContext CreateNewTableContext(PSTFile file, List <TableColumnDescriptor> columns) { HeapOnNode heap = HeapOnNode.CreateNewHeap(file); TableContextInfo tcInfo = new TableContextInfo(); tcInfo.rgTCOLDESC = columns; tcInfo.UpdateDataLayout(); HeapID newUserRoot = heap.AddItemToHeap(tcInfo.GetBytes()); // The heap header may have just been updated HeapOnNodeHeader header = heap.HeapHeader; header.bClientSig = OnHeapTypeName.bTypeTC; header.hidUserRoot = newUserRoot; heap.UpdateHeapHeader(header); BTreeOnHeapHeader bTreeHeader = new BTreeOnHeapHeader(); bTreeHeader.cbKey = TableContextRowID.RecordKeyLength; bTreeHeader.cbEnt = TableContextRowID.RecordDataLength; tcInfo.hidRowIndex = heap.AddItemToHeap(bTreeHeader.GetBytes()); // this will replace the item in place (as they have the same size since number of columns was not modified) heap.ReplaceHeapItem(header.hidUserRoot, tcInfo.GetBytes()); return(new TableContext(heap, null)); }
public void DeleteFromIndexRecord(BTreeOnHeapIndex index, HeapID heapID) { int recordIndex = index.GetIndexOfRecord(heapID); index.Records.RemoveAt(recordIndex); if (index.Records.Count > 0) { // will always replace in place (smaller size) ReplaceHeapItem(index.HeapID, index.GetBytes()); } else { if (index.ParentIndex == null) { // this is the root node BTreeHeader.hidRoot = HeapID.EmptyHeapID; BTreeHeader.bIdxLevels = 0; UpdateBTreeHeader(); } else { DeleteFromIndexRecord(index.ParentIndex, index.HeapID); } } }
public void InsertIndexRecord(BTreeOnHeapIndex index, BTreeOnHeapIndexRecord record) { HeapID existingHeapID = index.HeapID; if (index.Records.Count < index.MaximumNumberOfRecords) { int insertIndex = index.InsertSorted(record); index.HeapID = ReplaceHeapItem(existingHeapID, index.GetBytes()); if (index.HeapID.Value != existingHeapID.Value) { if (index.ParentIndex == null) { // this is the root node UpdateRootHeapID(index.HeapID); } else { UpdateIndexRecord(index.ParentIndex, existingHeapID, index.HeapID, index.NodeKey); } } else if (insertIndex == 0 && index.ParentIndex != null) { // Node key has been modified, we must update the parent UpdateIndexRecord(index.ParentIndex, existingHeapID, index.HeapID, index.NodeKey); } } else { // The node is full, we have to split it BTreeOnHeapIndex newNode = index.Split(); if (record.CompareTo(newNode.NodeKey) > 0) { newNode.InsertSorted(record); } else { int insertIndex = index.InsertSorted(record); if (insertIndex == 0 && index.ParentIndex != null) { // Node key has been modified, we must update the parent UpdateIndexRecord(index.ParentIndex, index.HeapID, index.HeapID, index.NodeKey); } } if (index.ParentIndex == null) { // this is a root page and it's full, we have to create a new root index.ParentIndex = CreateNewRoot(index.NodeKey); } // Item will be replaced in place, because it has less items than before ReplaceHeapItem(index.HeapID, index.GetBytes()); HeapID newNodeHeapID = AddItemToHeap(newNode.GetBytes()); // We made sure we have a parent to add our new page to InsertIndexRecord(index.ParentIndex, newNode.NodeKey, newNodeHeapID); } }
public HeapOnNodeHeader(byte[] buffer, int offset) { ibHnpm = LittleEndianConverter.ToUInt16(buffer, offset + 0); bSig = ByteReader.ReadByte(buffer, offset + 2); bClientSig = (OnHeapTypeName)ByteReader.ReadByte(buffer, offset + 3); hidUserRoot = new HeapID(buffer, offset + 4); rgbFillLevel = HeapOnNodeHelper.ReadFillLevelMap(buffer, offset + 8, 8); }
public void InsertIndexRecord(BTreeOnHeapIndex index, byte[] key, HeapID hidNextLevel) { BTreeOnHeapIndexRecord indexRecord = new BTreeOnHeapIndexRecord(); indexRecord.key = key; indexRecord.hidNextLevel = hidNextLevel; InsertIndexRecord(index, indexRecord); }
public BTreeOnHeapHeader(byte[] buffer, int offset) { bType = (OnHeapTypeName)ByteReader.ReadByte(buffer, offset + 0); cbKey = ByteReader.ReadByte(buffer, offset + 1); cbEnt = ByteReader.ReadByte(buffer, offset + 2); bIdxLevels = ByteReader.ReadByte(buffer, offset + 3); hidRoot = new HeapID(buffer, offset + 4); }
/// <summary> /// New rows are always added at the end /// </summary> /// <returns>Row index</returns> public int AddRow(uint rowID, byte[] newRowBytes) { int rowIndex = m_rowIndex.Count; if (m_tcInfo.hnidRows.IsEmpty) { m_tcInfo.hnidRows = new HeapOrNodeID(m_heap.AddItemToHeap(newRowBytes)); UpdateTableContextInfo(); } else if (m_tcInfo.hnidRows.IsHeapID) { byte[] oldRows = m_heap.GetHeapItem(m_tcInfo.hnidRows.HeapID); if (oldRows.Length + RowLength <= HeapOnNode.MaximumAllocationLength) { byte[] newRows = new byte[oldRows.Length + RowLength]; Array.Copy(oldRows, newRows, oldRows.Length); Array.Copy(newRowBytes, 0, newRows, oldRows.Length, RowLength); HeapID oldHeapID = m_tcInfo.hnidRows.HeapID; HeapID newHeapID = m_heap.ReplaceHeapItem(oldHeapID, newRows); if (oldHeapID.Value != newHeapID.Value) { // update the Table Context Info structure to point to the new rows m_tcInfo.hnidRows = new HeapOrNodeID(newHeapID); UpdateTableContextInfo(); } } else { // we must move the rows from the heap to a subnode byte[] rows = Heap.GetHeapItem(m_tcInfo.hnidRows.HeapID); // remove the old rows from the heap m_heap.RemoveItemFromHeap(m_tcInfo.hnidRows.HeapID); CreateSubnodeForRows(); for (int index = 0; index < RowCount; index++) { byte[] rowBytes = new byte[RowLength]; Array.Copy(rows, index * RowLength, rowBytes, 0, RowLength); AddRowToSubnode(index, rowBytes); } // add the new row AddRowToSubnode(rowIndex, newRowBytes); } } else { // indicates that the item is stored in the subnode block, and the NID is the local NID under the subnode BTree AddRowToSubnode(rowIndex); } AddRowToRowIndex(rowID, rowIndex); return(rowIndex); }
public void UpdateTableContextInfo() { HeapID newUserRootHeapID = m_heap.ReplaceHeapItem(m_heap.HeapHeader.hidUserRoot, m_tcInfo.GetBytes()); if (m_heap.HeapHeader.hidUserRoot != newUserRootHeapID) { HeapOnNodeHeader heapHeader = m_heap.HeapHeader; heapHeader.hidUserRoot = newUserRootHeapID; m_heap.UpdateHeapHeader(heapHeader); } }
public int GetIndexOfRecord(HeapID hidNextLevel) { for (int index = 0; index < Records.Count; index++) { if (Records[index].hidNextLevel.Value == hidNextLevel.Value) { return(index); } } return(-1); }
// Note: The PC is simply a BTH with cbKey set to 2 and cbEnt set to 6 public List <T> GetAll() { List <T> result = new List <T>(); List <byte[]> leaves = new List <byte[]>(); if (BTreeHeader.bIdxLevels > 0) { KeyValuePairList <byte[], byte> parents = new KeyValuePairList <byte[], byte>(); parents.Add(GetHeapItem(BTreeHeader.hidRoot), BTreeHeader.bIdxLevels); while (parents.Count > 0) { byte[] parentBytes = parents[0].Key; byte level = parents[0].Value; int offset = 0; while (offset < parentBytes.Length) { HeapID hid = new HeapID(parentBytes, offset + BTreeHeader.cbKey); byte[] bytes = GetHeapItem(hid); if (level == 1) { leaves.Add(bytes); } else { parents.Add(bytes, (byte)(level - 1)); } offset += BTreeHeader.cbKey + HeapID.Length; } parents.RemoveAt(0); } } else { leaves.Add(GetHeapItem(BTreeHeader.hidRoot)); } foreach (byte[] leafBytes in leaves) { int offset = 0; while (offset < leafBytes.Length) { T record = BTreeOnHeapDataRecord.CreateInstance <T>(leafBytes, offset); result.Add(record); offset += BTreeHeader.cbKey + BTreeHeader.cbEnt; } } return(result); }
public HeapOrNodeID(byte[] buffer, int offset) { HeapID tempHID = new HeapID(buffer, offset); if (tempHID.hidType == NodeTypeName.NID_TYPE_HID) { m_heapID = tempHID; } else { m_nodeID = new NodeID(buffer, offset); } }
public void UpdateIndexRecord(BTreeOnHeapIndex index, HeapID oldHeapID, HeapID newHeapID, byte[] newKey) { int recordIndex = index.GetIndexOfRecord(oldHeapID); index.Records[recordIndex].hidNextLevel = newHeapID; index.Records[recordIndex].key = newKey; // will always replace in place (same size) ReplaceHeapItem(index.HeapID, index.GetBytes()); if (recordIndex == 0 && index.ParentIndex != null) { UpdateIndexRecord(index.ParentIndex, index.HeapID, index.HeapID, index.NodeKey); } }
public void RemoveItemFromHeap(HeapID heapID) { int blockIndex = heapID.hidBlockIndex; HeapOnNodeBlockData blockData = GetBlockData(blockIndex); // We can't remove the HeapItem, because then the HeapID of the subsequent items will be incorrect // So instead we put an empty item instead of the item we wish to remove. // (We must not forget to update the HNPAGEMAP's cFree, otherwise outlook will report that the pst is corrupt) // hidIndex is one-based blockData.HeapItems[heapID.hidIndex - 1] = new byte[0]; CompactBlockData(blockData); UpdateBuffer(blockIndex, blockData); }
public HeapID ReplaceHeapItem(HeapID heapID, byte[] itemBytes) { bool success = TryReplacingHeapItemInPlace(heapID, itemBytes); if (!success) { // no room for the replacement item in the current block RemoveItemFromHeap(heapID); return(AddItemToHeap(itemBytes)); } else { return(heapID); } }
public BTreeOnHeapLeaf <T> GetLeafFromHeap(HeapID leafHeapID) { // The hid is set to zero if the BTH is empty if (leafHeapID.Value == 0) { return(null); } else { byte[] leafBytes = GetHeapItem(leafHeapID); BTreeOnHeapLeaf <T> result = new BTreeOnHeapLeaf <T>(leafBytes); result.HeapID = leafHeapID; return(result); } }
public byte[] GetHeapItem(HeapID hid) { HeapOnNodeBlockData blockData = GetBlockData(hid.hidBlockIndex); // hidIndex is one-based if (hid.hidIndex == 0) { throw new ArgumentOutOfRangeException("hidIndex", "PST is corrupted, hidIndex cannot be 0"); } else if (hid.hidIndex - 1 < blockData.HeapItems.Count) { return(blockData.HeapItems[hid.hidIndex - 1]); } else { throw new ArgumentOutOfRangeException("hidIndex", "PST is corrupted, hidIndex is out of range"); } }
/// <summary> /// Replace item in-place, to avoid issues, item should not be larger than the old item /// </summary> /// <returns>true if success</returns> private bool TryReplacingHeapItemInPlace(HeapID heapID, byte[] itemBytes) { int blockIndex = heapID.hidBlockIndex; HeapOnNodeBlockData blockData = GetBlockData(blockIndex); // hidIndex is one-based int oldSize = blockData.HeapItems[heapID.hidIndex - 1].Length; int newSize = itemBytes.Length; if (newSize - oldSize <= blockData.AvailableSpace) { blockData.HeapItems[heapID.hidIndex - 1] = itemBytes; UpdateBuffer(blockIndex, blockData); return(true); } else { return(false); } }
/// <param name="subnodeBTree">Subnode BTree that will be associated with the new PC</param> public static PropertyContext CreateNewPropertyContext(PSTFile file, SubnodeBTree subnodeBTree) { HeapOnNode heap = HeapOnNode.CreateNewHeap(file); BTreeOnHeapHeader bTreeHeader = new BTreeOnHeapHeader(); bTreeHeader.cbKey = PropertyContextRecord.RecordKeyLength; bTreeHeader.cbEnt = PropertyContextRecord.RecordDataLength; HeapID newUserRoot = heap.AddItemToHeap(bTreeHeader.GetBytes()); // The heap header may have just been updated HeapOnNodeHeader header = heap.HeapHeader; header.bClientSig = OnHeapTypeName.bTypePC; header.hidUserRoot = newUserRoot; heap.UpdateHeapHeader(header); heap.FlushToDataTree(); return(new PropertyContext(heap, subnodeBTree)); }
public TableContextInfo(byte[] buffer) { bType = (OnHeapTypeName)ByteReader.ReadByte(buffer, 0); byte cCols = ByteReader.ReadByte(buffer, 1); int position = 2; for (int index = 0; index < 4; index++) { rgib[index] = LittleEndianConverter.ToUInt16(buffer, position); position += 2; } hidRowIndex = new HeapID(buffer, 10); hnidRows = new HeapOrNodeID(buffer, 14); // hidIndex - deprecated position = 22; for (int index = 0; index < cCols; index++) { TableColumnDescriptor descriptor = new TableColumnDescriptor(buffer, position); rgTCOLDESC.Add(descriptor); position += TableColumnDescriptor.Length; } }
public void SetRowBytes(int rowIndex, byte[] rowBytes) { if (rowIndex >= RowCount) { throw new ArgumentException("Invalid rowIndex"); } int rowLength = this.RowLength; if (m_tcInfo.hnidRows.IsHeapID) { // the RowMatrix is stored in the data tree byte[] rows = m_heap.GetHeapItem(m_tcInfo.hnidRows.HeapID); int rowOffset = (int)rowIndex * rowLength; Array.Copy(rowBytes, 0, rows, rowOffset, rowLength); HeapID oldHeapID = m_tcInfo.hnidRows.HeapID; // this will replace the item in place (as they have the same size) m_heap.ReplaceHeapItem(oldHeapID, rows); } else { // indicates that the item is stored in the subnode block, and the NID is the local NID under the subnode BTree NodeID rowsNodeID = m_tcInfo.hnidRows.NodeID; if (m_subnodeRows == null) { m_subnodeRows = m_subnodeBTree.GetSubnode(rowsNodeID); } int blockIndex = (int)(rowIndex / m_rowsPerBlock); int inBlockRowIndex = (int)(rowIndex % m_rowsPerBlock); DataBlock block = m_subnodeRows.DataTree.GetDataBlock(blockIndex); int offset = inBlockRowIndex * rowLength; Array.Copy(rowBytes, 0, block.Data, offset, rowLength); m_subnodeRows.DataTree.UpdateDataBlock(blockIndex, block.Data); } }
public byte[] rgbFillLevel = new byte[8]; // 4 bytes, 8 entries public HeapOnNodeHeader() { bSig = HeapOnNodeBlockSignature; // heap signature hidUserRoot = HeapID.EmptyHeapID; }
public HeapID hidRoot; // This is the HID that points to the BTH root for this BTHHEADER. public BTreeOnHeapHeader() { bType = OnHeapTypeName.bTypeBTH; hidRoot = HeapID.EmptyHeapID; // Set to 0 if the BTH is empty }
public void AddRecord(T record) { BTreeOnHeapLeaf <T> leaf = FindLeaf(record.Key); if (leaf == null) // BTH is empty { leaf = new BTreeOnHeapLeaf <T>(); leaf.InsertSorted(record); HeapID rootHeapID = AddItemToHeap(leaf.GetBytes()); UpdateRootHeapID(rootHeapID); } else if (leaf.Records.Count < leaf.MaximumNumberOfRecords) { int insertIndex = leaf.InsertSorted(record); HeapID existingHeapID = leaf.HeapID; leaf.HeapID = ReplaceHeapItem(leaf.HeapID, leaf.GetBytes()); m_leavesCache[leaf.HeapID.Value] = leaf; if (leaf.HeapID.Value != existingHeapID.Value) { if (leaf.Index == null) { // this is the root node UpdateRootHeapID(leaf.HeapID); } else { // update the parent UpdateIndexRecord(leaf.Index, existingHeapID, leaf.HeapID, leaf.NodeKey); } m_leavesCache.Remove(existingHeapID.Value); } else if (insertIndex == 0 && leaf.Index != null) { // Node key has been modified, we must update the parent UpdateIndexRecord(leaf.Index, leaf.HeapID, leaf.HeapID, leaf.NodeKey); } } else { // The node is full, we have to split it BTreeOnHeapLeaf <T> newNode = leaf.Split(); if (record.CompareTo(newNode.NodeKey) > 0) { newNode.InsertSorted(record); } else { int insertIndex = leaf.InsertSorted(record); if (insertIndex == 0 && leaf.Index != null) { // Node key has been modified, we must update the parent UpdateIndexRecord(leaf.Index, leaf.HeapID, leaf.HeapID, leaf.NodeKey); } } if (leaf.Index == null) { // this is a root page and it's full, we have to create a new root leaf.Index = CreateNewRoot(leaf.NodeKey); } // Item will be replaced in place, because it has less items than before ReplaceHeapItem(leaf.HeapID, leaf.GetBytes()); m_leavesCache[leaf.HeapID.Value] = leaf; HeapID newNodeHeapID = AddItemToHeap(newNode.GetBytes()); // We made sure we have a parent to add our new page to InsertIndexRecord(leaf.Index, newNode.NodeKey, newNodeHeapID); } }
public byte[] GetHeapItem(HeapID heapID) { return(m_heap.GetHeapItem(heapID)); }
public TableContextInfo() { bType = OnHeapTypeName.bTypeTC; hidRowIndex = HeapID.EmptyHeapID; hnidRows = new HeapOrNodeID(HeapID.EmptyHeapID); }
public void UpdateRootHeapID(HeapID newRootHeapID) { BTreeHeader.hidRoot = newRootHeapID; UpdateBTreeHeader(); }
public void RemoveItemFromHeap(HeapID heapID) { m_heap.RemoveItemFromHeap(heapID); }
public HeapID ReplaceHeapItem(HeapID heapID, byte[] itemBytes) { return(m_heap.ReplaceHeapItem(heapID, itemBytes)); }