// 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));
        }
        /// <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);
        }
        /// <param name="subnodeBTree">Note: We use ref, this way we are able to create a new subnode BTree and update the subnodeBTree the caller provided</param>
        /// <param name="heapOrNodeID">Existing HeapOrNodeID</param>
        public static HeapOrNodeID StoreExternalProperty(PSTFile file, HeapOnNode heap, ref SubnodeBTree subnodeBTree, HeapOrNodeID heapOrNodeID, byte[] propertyBytes)
        {
            // We should avoid storing items with length of 0, because those are consideref freed, and could be repurposed
            if (propertyBytes.Length == 0)
            {
                RemoveExternalProperty(heap, subnodeBTree, heapOrNodeID);
                return(new HeapOrNodeID(HeapID.EmptyHeapID));
            }

            if (heapOrNodeID.IsHeapID) // if HeapOrNodeID is empty then IsHeapID == true
            {
                if (propertyBytes.Length <= HeapOnNode.MaximumAllocationLength)
                {
                    if (heapOrNodeID.IsEmpty)
                    {
                        return(new HeapOrNodeID(heap.AddItemToHeap(propertyBytes)));
                    }
                    else
                    {
                        return(new HeapOrNodeID(heap.ReplaceHeapItem(heapOrNodeID.HeapID, propertyBytes)));
                    }
                }
                else // old data (if exist) is stored on heap, but new data needs a subnode
                {
                    if (!heapOrNodeID.IsEmpty)
                    {
                        heap.RemoveItemFromHeap(heapOrNodeID.HeapID);
                    }

                    if (subnodeBTree == null)
                    {
                        subnodeBTree = new SubnodeBTree(file);
                    }
                    DataTree dataTree = new DataTree(file);
                    dataTree.AppendData(propertyBytes);
                    dataTree.SaveChanges();

                    NodeID subnodeID = file.Header.AllocateNextNodeID(NodeTypeName.NID_TYPE_LTP);
                    subnodeBTree.InsertSubnodeEntry(subnodeID, dataTree, null);

                    return(new HeapOrNodeID(subnodeID));
                }
            }
            else // old data is stored in a subnode
            {
                Subnode subnode = subnodeBTree.GetSubnode(heapOrNodeID.NodeID);
                if (subnode.DataTree != null)
                {
                    subnode.DataTree.Delete();
                }
                subnode.DataTree = new DataTree(subnodeBTree.File);
                subnode.DataTree.AppendData(propertyBytes);
                subnode.SaveChanges(subnodeBTree);
                return(new HeapOrNodeID(heapOrNodeID.NodeID));
            }
        }
        /// <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 #5
0
 public HeapID AddItemToHeap(byte[] itemBytes)
 {
     return(m_heap.AddItemToHeap(itemBytes));
 }