/// <param name="cellBytes">If property is external, byte[0] means empty data item</param> public void SetPropertyValue(int rowIndex, int columnIndex, byte[] propertyBytes) { TableColumnDescriptor columnDescriptor = m_tcInfo.rgTCOLDESC[columnIndex]; if (columnDescriptor.IsStoredExternally) { byte[] cellBytes = GetInternalCellBytes(rowIndex, columnIndex); HeapOrNodeID heapOrNodeID; if (cellBytes != null) { heapOrNodeID = new HeapOrNodeID(cellBytes); } else { heapOrNodeID = new HeapOrNodeID(HeapID.EmptyHeapID); } HeapOrNodeID newHeapOrNodeID = NodeStorageHelper.StoreExternalProperty(this.File, m_heap, ref m_subnodeBTree, heapOrNodeID, propertyBytes); // we call SetInternalCellBytes even when oldHeapID.Value == newHeapID.Value, // this will make sure the CEB will be updated SetInternalCellBytes(rowIndex, columnIndex, LittleEndianConverter.GetBytes(newHeapOrNodeID.Value)); } else { SetInternalCellBytes(rowIndex, columnIndex, propertyBytes); } }
public bool IsCellInUse(int rowIndex, PropertyID propertyID, PropertyTypeName propertyType) { int columnIndex = GetColumnIndexByPropertyTag(propertyID, propertyType); TableColumnDescriptor columnDescriptor = m_tcInfo.rgTCOLDESC[columnIndex]; byte[] rowBytes = GetRowBytes(rowIndex); return(IsCellInUse(columnDescriptor, rowBytes)); }
public bool IsCellInUse(TableColumnDescriptor columnDescriptor, byte[] rowBytes) { int cebByteOffset = m_tcInfo.CellExistenceBlockStartOffset + columnDescriptor.iBit / 8; // from MSB to LSB (as suggested by MS-PST, page 68) int cebBitOffset = 7 - columnDescriptor.iBit % 8; return((rowBytes[cebByteOffset] & (0x01 << cebBitOffset)) > 0); }
public void RemoveProperty(int rowIndex, PropertyID propertyID, PropertyTypeName propertyType) { int columnIndex = GetColumnIndexByPropertyTag(propertyID, propertyType); TableColumnDescriptor columnDescriptor = m_tcInfo.rgTCOLDESC[columnIndex]; if (columnDescriptor.IsStoredExternally) { RemoveExternalProperty(rowIndex, columnIndex); } byte[] rowBytes = GetRowBytes(rowIndex); UpdateCellExistenceBlock(columnDescriptor, rowBytes, false); SetRowBytes(rowIndex, rowBytes); }
public int FindColumnIndexByPropertyTag(PropertyID propertyID, PropertyTypeName propertyType) { for (int index = 0; index < m_tcInfo.rgTCOLDESC.Count; index++) { TableColumnDescriptor descriptor = m_tcInfo.rgTCOLDESC[index]; if (descriptor.PropertyID == propertyID && descriptor.PropertyType == propertyType) { return(index); } } return(-1); }
private void UpdateCellExistenceBlock(TableColumnDescriptor columnDescriptor, byte[] rowBytes, bool isInUse) { int cebByteOffset = m_tcInfo.CellExistenceBlockStartOffset + columnDescriptor.iBit / 8; // from MSB to LSB (as suggested by MS-PST, page 68) int cebBitOffset = 7 - columnDescriptor.iBit % 8; if (isInUse) { rowBytes[cebByteOffset] |= (byte)(0x01 << cebBitOffset); } else { rowBytes[cebByteOffset] &= (byte)~(0x01 << cebBitOffset); } }
public byte[] GetInternalCellBytes(int rowIndex, int columnIndex) { TableColumnDescriptor columnDescriptor = m_tcInfo.rgTCOLDESC[columnIndex]; byte[] rowBytes = GetRowBytes(rowIndex); bool inUse = IsCellInUse(columnDescriptor, rowBytes); if (!inUse) { return(null); } else { int cellLength = columnDescriptor.cbData; byte[] cellBytes = new byte[cellLength]; int cellOffset = columnDescriptor.ibData; Array.Copy(rowBytes, cellOffset, cellBytes, 0, cellBytes.Length); return(cellBytes); } }
private void SetInternalCellBytes(int rowIndex, int columnIndex, byte[] cellBytes) { TableColumnDescriptor columnDescriptor = m_tcInfo.rgTCOLDESC[columnIndex]; int cellLength = columnDescriptor.cbData; if (cellBytes.Length == cellLength) { byte[] rowBytes = GetRowBytes(rowIndex); int cellOffset = columnDescriptor.ibData; Array.Copy(cellBytes, 0, rowBytes, cellOffset, cellBytes.Length); // update CEB: UpdateCellExistenceBlock(columnDescriptor, rowBytes, true); SetRowBytes(rowIndex, rowBytes); } else { throw new InvalidPropertyException("Invalid cell length"); } }
public byte[] GetPropertyValue(int rowIndex, int columnIndex) { TableColumnDescriptor columnDescriptor = m_tcInfo.rgTCOLDESC[columnIndex]; byte[] cellBytes = GetInternalCellBytes(rowIndex, columnIndex); if (cellBytes == null) { return(null); } else { if (columnDescriptor.IsStoredExternally) { HeapOrNodeID heapOrNodeID = new HeapOrNodeID(cellBytes); return(NodeStorageHelper.GetExternalPropertyBytes(m_heap, m_subnodeBTree, heapOrNodeID)); } else { return(cellBytes); } } }
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; } }
/// <summary> /// For discovery purposes /// </summary> public List <string> ListTable() { List <string> result = new List <string>(); result.Add("Number of Columns: " + this.ColumnCount); for (int index = 0; index < m_tcInfo.rgTCOLDESC.Count; index++) { TableColumnDescriptor descriptor = m_tcInfo.rgTCOLDESC[index]; result.Add(String.Format("Column {0}, Property Type: {1}, PropertyName: {2}, Data Length: {3}, Offset: {4}, iBit: {5}", index, descriptor.PropertyType, GetPropertyIDString((ushort)descriptor.PropertyID), descriptor.cbData, descriptor.ibData, descriptor.iBit)); } result.Add("Number of Rows: " + m_rowIndex.Count); result.Add("4-byte entries length: " + (m_tcInfo.rgib[TableContextInfo.TCI_4b])); result.Add("2-byte entries length: " + (m_tcInfo.rgib[TableContextInfo.TCI_2b] - m_tcInfo.rgib[TableContextInfo.TCI_4b])); result.Add("1-byte entries length: " + (m_tcInfo.rgib[TableContextInfo.TCI_1b] - m_tcInfo.rgib[TableContextInfo.TCI_2b])); result.Add("Row length (net): " + m_tcInfo.rgib[TableContextInfo.TCI_1b]); result.Add("Row length: " + this.RowLength); for (int rowIndex = 0; rowIndex < m_rowIndex.Count; rowIndex++) { result.Add("--------------------------------------------------------------------------------"); result.Add("Row ID: " + m_rowIndex[rowIndex].dwRowID); result.Add("Data Length: " + m_rowIndex[rowIndex].DataLength); result.Add("Row Index: " + m_rowIndex[rowIndex].dwRowIndex); for (int columnIndex = 0; columnIndex < m_tcInfo.rgTCOLDESC.Count; columnIndex++) { TableColumnDescriptor descriptor = m_tcInfo.rgTCOLDESC[columnIndex]; PropertyTypeName propertyType = descriptor.PropertyType; PropertyID propertyID = descriptor.PropertyID; string value; if (IsCellInUse(rowIndex, propertyID, propertyType)) { if (propertyType == PropertyTypeName.PtypBoolean) { bool boolValue = GetBooleanProperty(rowIndex, propertyID).Value; value = boolValue.ToString(); } else if (propertyType == PropertyTypeName.PtypInteger16) { value = GetInt16Property(rowIndex, propertyID).ToString() + " (Int16)"; } else if (propertyType == PropertyTypeName.PtypInteger32) { value = GetInt32Property(rowIndex, propertyID).ToString(); } else if (propertyType == PropertyTypeName.PtypInteger64) { value = GetInt64Property(rowIndex, propertyID).ToString() + " (Int64)"; } else if (propertyType == PropertyTypeName.PtypTime) { value = GetDateTimeProperty(rowIndex, propertyID).ToString(); } else if (propertyType == PropertyTypeName.PtypString) { value = GetStringProperty(rowIndex, propertyID); } else if (propertyType == PropertyTypeName.PtypBinary) { value = StringHelper.GetByteArrayString(GetBytesProperty(rowIndex, propertyID)); } else { value = "-" + propertyType.ToString(); } } else { value = "(Unused)"; } result.Add(GetPropertyIDString((ushort)propertyID) + ": " + value); } } return(result); }
/// <summary> /// Add column to an empty table context. /// If this is a Contents Table, the caller should call UpdateMessage() afterwards. /// Similarly, for attachment table, the caller should call UpdateAttachment(). /// </summary> public void AddPropertyColumn(PropertyID propertyID, PropertyTypeName propertyType) { TableColumnDescriptor newColumnDescriptor = new TableColumnDescriptor(); newColumnDescriptor.PropertyID = propertyID; newColumnDescriptor.PropertyType = propertyType; newColumnDescriptor.iBit = (byte)m_tcInfo.ColumnCount; newColumnDescriptor.cbData = (byte)GetPropertyDataLength(propertyType); // Set the ibData: // http://social.msdn.microsoft.com/Forums/en-US/os_binaryfile/thread/a5f9c653-40f5-4638-85d3-00c54607d984/ // PidTagLtpRowId and PidTagLtpRowVer must not be relocated if (newColumnDescriptor.DataLengthGroup == TableContextInfo.TCI_4b) { newColumnDescriptor.ibData = m_tcInfo.rgib[TableContextInfo.TCI_4b]; } else if (newColumnDescriptor.DataLengthGroup == TableContextInfo.TCI_2b) { newColumnDescriptor.ibData = m_tcInfo.rgib[TableContextInfo.TCI_2b]; } else { newColumnDescriptor.ibData = m_tcInfo.rgib[TableContextInfo.TCI_1b]; } // We call GetRedistributedRows() before adding the new column: List <byte[]> rows = GetRedistributedRows(newColumnDescriptor.ibData, newColumnDescriptor.cbData); // add the new column m_tcInfo.rgTCOLDESC.Add(newColumnDescriptor); // redistribute column descriptions ushort offset = (ushort)(newColumnDescriptor.ibData + newColumnDescriptor.cbData); for (int groupIndex = newColumnDescriptor.DataLengthGroup + 1; groupIndex < 3; groupIndex++) { for (int index = 0; index < m_tcInfo.rgTCOLDESC.Count; index++) { TableColumnDescriptor descriptor = m_tcInfo.rgTCOLDESC[index]; if (groupIndex == descriptor.DataLengthGroup) { // changes to descriptor will be saved when calling UpdateTableContextInfo() descriptor.ibData = offset; offset += descriptor.cbData; } } } // update the group ending offset m_tcInfo.UpdateDataLayout(); m_rowsPerBlock = (int)Math.Floor((double)DataBlock.MaximumDataLength / RowLength); // Update the rows data if (!m_tcInfo.hnidRows.IsEmpty) { if (m_tcInfo.hnidRows.IsHeapID) { m_heap.RemoveItemFromHeap(m_tcInfo.hnidRows.HeapID); CreateSubnodeForRows(); } else { if (m_subnodeRows == null) { m_subnodeRows = m_subnodeBTree.GetSubnode(m_tcInfo.hnidRows.NodeID); } m_subnodeRows.Delete(); // this will set the subnode data-tree to null // New data tree will be created when the first row will be added m_subnodeBTree.UpdateSubnodeEntry(m_tcInfo.hnidRows.NodeID, null, null); } for (int index = 0; index < rows.Count; index++) { AddRowToSubnode(index, rows[index]); } } UpdateTableContextInfo(); }
public static int Compare(TableColumnDescriptor a, TableColumnDescriptor b) { return(a.Tag.CompareTo(b.Tag)); }