// 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)); }
public HeapID AddItemToHeap(byte[] itemBytes) { return(m_heap.AddItemToHeap(itemBytes)); }