Example #1
0
 public BTreeOnHeap(HeapOnNode heap, HeapID bTreeHeaderHeapID)
 {
     m_heap = heap;
     m_bTreeHeaderHeapID = bTreeHeaderHeapID;
     byte[] headerBytes = m_heap.GetHeapItem(bTreeHeaderHeapID);
     BTreeHeader = new BTreeOnHeapHeader(headerBytes);
 }
Example #2
0
        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));
        }
Example #4
0
        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);
                }
            }
        }
Example #5
0
        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);
 }
Example #7
0
        public void InsertIndexRecord(BTreeOnHeapIndex index, byte[] key, HeapID hidNextLevel)
        {
            BTreeOnHeapIndexRecord indexRecord = new BTreeOnHeapIndexRecord();

            indexRecord.key          = key;
            indexRecord.hidNextLevel = hidNextLevel;
            InsertIndexRecord(index, indexRecord);
        }
Example #8
0
 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);
 }
Example #12
0
        // 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);
        }
Example #13
0
        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);
            }
        }
Example #14
0
        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);
            }
        }
Example #15
0
        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);
        }
Example #16
0
        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);
            }
        }
Example #17
0
 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);
     }
 }
Example #18
0
        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");
            }
        }
Example #19
0
        /// <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));
        }
Example #21
0
        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;
        }
Example #24
0
        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
        }
Example #25
0
        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);
            }
        }
Example #26
0
 public byte[] GetHeapItem(HeapID heapID)
 {
     return(m_heap.GetHeapItem(heapID));
 }
Example #27
0
 public TableContextInfo()
 {
     bType       = OnHeapTypeName.bTypeTC;
     hidRowIndex = HeapID.EmptyHeapID;
     hnidRows    = new HeapOrNodeID(HeapID.EmptyHeapID);
 }
Example #28
0
 public void UpdateRootHeapID(HeapID newRootHeapID)
 {
     BTreeHeader.hidRoot = newRootHeapID;
     UpdateBTreeHeader();
 }
Example #29
0
 public void RemoveItemFromHeap(HeapID heapID)
 {
     m_heap.RemoveItemFromHeap(heapID);
 }
Example #30
0
 public HeapID ReplaceHeapItem(HeapID heapID, byte[] itemBytes)
 {
     return(m_heap.ReplaceHeapItem(heapID, itemBytes));
 }