/// <summary> /// http://social.msdn.microsoft.com/Forums/en-US/os_binaryfile/thread/a5f9c653-40f5-4638-85d3-00c54607d984/ /// Outlook 2003 MUST have a valid rgbFillLevel for write operations /// </summary> private void UpdateFillLevelMap() { int blocksToProcess = Math.Min(m_dataTree.DataBlockCount, 8); HeapOnNodeHeader header = this.HeapHeader; for (int blockIndex = 0; blockIndex < blocksToProcess; blockIndex++) { if (m_blocksToUpdate.Contains(blockIndex)) { HeapOnNodeBlockData blockData = GetBlockData(blockIndex); byte fillLevel = HeapOnNodeHelper.GetBlockFillLevel(blockData); header.rgbFillLevel[blockIndex] = fillLevel; } } UpdateHeapHeader(header); for (int blockIndex = 8; blockIndex < m_dataTree.DataBlockCount; blockIndex += 128) { int blocksLeft = m_dataTree.DataBlockCount - blockIndex; blocksToProcess = Math.Min(blocksLeft, 128); HeapOnNodeBitmapHeader bitmapHeader = GetBitmapHeader(blockIndex); for (int blockOffset = 0; blockOffset < blocksToProcess; blockOffset++) { if (m_blocksToUpdate.Contains(blockIndex + blockOffset)) { HeapOnNodeBlockData blockData = GetBlockData(blockIndex + blockOffset); byte fillLevel = HeapOnNodeHelper.GetBlockFillLevel(blockData); bitmapHeader.rgbFillLevel[blockOffset] = fillLevel; } } UpdateBitmapHeader(blockIndex, bitmapHeader); blocksLeft -= blocksToProcess; } }
private void UpdateBuffer(int blockIndex, HeapOnNodeBlockData blockData) { m_buffer[blockIndex] = blockData; if (!m_blocksToUpdate.Contains(blockIndex)) { m_blocksToUpdate.Add(blockIndex); } }
public void FlushToDataTree() { UpdateFillLevelMap(); foreach (int blockIndex in m_blocksToUpdate) { HeapOnNodeBlockData blockData = m_buffer[blockIndex]; m_dataTree.UpdateDataBlock(blockIndex, blockData.GetBytes()); } m_blocksToUpdate.Clear(); }
private HeapOnNodeBlockData GetBlockData(int blockIndex) { if (!m_buffer.ContainsKey(blockIndex)) { HeapOnNodeBlockData blockData = GetBlockDataUnbuffered(blockIndex); m_buffer.Add(blockIndex, blockData); return(blockData); } else { return(m_buffer[blockIndex]); } }
/// <summary> /// We trim freed items (items at the end of the block that can be removed without corrupting HeapIDs) /// (so that new items could be added in their place) /// </summary> /// <param name="blockData"></param> private void CompactBlockData(HeapOnNodeBlockData blockData) { for (int itemIndex = blockData.HeapItems.Count - 1; itemIndex >= 0; itemIndex--) { if (blockData.HeapItems[itemIndex].Length == 0) { blockData.HeapItems.RemoveAt(itemIndex); } else { break; } } }
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 AddItemToHeap(byte[] itemBytes) { if (itemBytes.Length > MaximumAllocationLength) { throw new ArgumentException("Maximum size of a heap allocation is 3580 bytes"); } if (itemBytes.Length == 0) { return(HeapID.EmptyHeapID); } // We can use the Header / BitmapHeader to locate available space, // but the final authority is HNPAGEMAP located at the end of each block // Note: we are only required to maintain HNPAGEMAP for (int blockIndex = 0; blockIndex < m_dataTree.DataBlockCount; blockIndex++) { HeapOnNodeBlockData blockData = GetBlockData(blockIndex); // We need space for the item itself and for a place for it in the page map (2 bytes) // We also have to make sure we do not allocate more items then can be represented by hidIndex if (blockData.AvailableSpace > itemBytes.Length + 2 && blockData.HeapItems.Count < HeapID.MaximumHidIndex) { blockData.HeapItems.Add(itemBytes); UpdateBuffer(blockIndex, blockData); // hidIndex is one-based ushort hidIndex = (ushort)(blockData.HeapItems.Count); return(new HeapID((ushort)blockIndex, hidIndex)); } } // no space found in existing blocks, we need to allocate new data block int newBlockIndex = m_dataTree.DataBlockCount; HeapOnNodeBlockData newBlockData; if (newBlockIndex % 128 == 8) { newBlockData = new HeapOnNodeBitmapBlockData(); } else { newBlockData = new HeapOnNodePageBlockData(); } newBlockData.HeapItems.Add(itemBytes); this.DataTree.AddDataBlock(newBlockData.GetBytes()); // hidIndex is one-based return(new HeapID((ushort)newBlockIndex, 1)); }
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); } }
public static byte GetBlockFillLevel(HeapOnNodeBlockData blockData) { int availableSpace = blockData.AvailableSpace; if (availableSpace >= 3584) { return(0x00); } else if (availableSpace >= 2560) { return(0x01); } else if (availableSpace >= 2048) { return(0x02); } else if (availableSpace >= 1792) { return(0x03); } else if (availableSpace >= 1536) { return(0x04); } else if (availableSpace >= 1280) { return(0x05); } else if (availableSpace >= 1024) { return(0x06); } else if (availableSpace >= 768) { return(0x07); } else if (availableSpace >= 512) { return(0x08); } else if (availableSpace >= 256) { return(0x09); } else if (availableSpace >= 128) { return(0x0A); } else if (availableSpace >= 64) { return(0x0B); } else if (availableSpace >= 32) { return(0x0C); } else if (availableSpace >= 16) { return(0x0D); } else if (availableSpace >= 8) { return(0x0E); } else { return(0x0F); } }
/// <summary> /// For discovery purposes /// </summary> public int GetBlockibHnpm(int blockIndex) { HeapOnNodeBlockData blockData = GetBlockData(blockIndex); return(blockData.ibHnpm); }
public int GetItemCount(int blockIndex) { HeapOnNodeBlockData blockData = GetBlockData(blockIndex); return(blockData.HeapItems.Count); }