private TRecord[] ReadTable(Stream peStream) { var columns = new ColumnIndexAttribute[ColumnCount]; var fields = new FieldInfo[ColumnCount]; // map the fields to columns foreach (var fld in typeof(TRecord).GetFields()) { var indexAttr = fld .GetCustomAttributes(false) .OfType <ColumnIndexAttribute>() .FirstOrDefault(); if (indexAttr != null) { int index = indexAttr.Index; if (columns[index] != null || fields[index] != null) { throw new InvalidOperationException($"Duplicate field index {index} in record type {typeof(TRecord).FullName}"); } columns[index] = indexAttr; fields[index] = fld; } } if (columns.Any(c => c == null) || fields.Any(f => f == null)) { throw new InvalidOperationException($"Could not map all columns of record type {typeof(TRecord).FullName}."); } // map the stream data to fields. var records = new TRecord[RowCount]; const int MaxFieldSize = 4; var fieldBytes = new byte[MaxFieldSize]; for (int row = 0; row < RowCount; row++) { // note: in the case that TRecord is a struct, we box + unbox it so we can populate via reflection. object rec = new TRecord(); int recBytes = 0; for (int col = 0; col < ColumnCount; col++) { CMiniColDef colDef = _tableDef.m_Def.m_pColDefs[col]; if (recBytes != colDef.m_oColumn) { // skip past any padding bytes. peStream.Seek(colDef.m_oColumn - recBytes, SeekOrigin.Current); recBytes = colDef.m_oColumn; } peStream.Read(fieldBytes, 0, colDef.m_cbColumn); fields[col].SetValue(rec, ConvertBytes(fieldBytes, colDef)); recBytes += colDef.m_cbColumn; } records[row] = (TRecord)rec; } return(records); }
private object ConvertBytes(byte[] bytes, CMiniColDef column) { // read RIDs, CodedTokens if (column.m_Type < MetaModel.iCodedTokenMax) { if (column.m_cbColumn == 2) { return(BitConverter.ToUInt16(bytes, 0)); } else if (column.m_cbColumn == 4) { return(BitConverter.ToUInt32(bytes, 0)); } throw new ArgumentException($"Unexpected data size for RID or Coded Token: {column.m_cbColumn} bytes"); } else { // read types with specific values. ColumnType type = (ColumnType)column.m_Type; switch (type) { case ColumnType.String: case ColumnType.Guid: case ColumnType.Blob: if (column.m_cbColumn == 2) { return(BitConverter.ToUInt16(bytes, 0)); } else if (column.m_cbColumn == 4) { return(BitConverter.ToUInt32(bytes, 0)); } break; case ColumnType.SHORT: if (column.m_cbColumn == 2) { return(BitConverter.ToInt16(bytes, 0)); } break; case ColumnType.USHORT: if (column.m_cbColumn == 2) { return(BitConverter.ToUInt16(bytes, 0)); } break; case ColumnType.LONG: if (column.m_cbColumn == 4) { return(BitConverter.ToInt32(bytes, 0)); } break; case ColumnType.ULONG: if (column.m_cbColumn == 4) { return(BitConverter.ToUInt32(bytes, 0)); } break; case ColumnType.BYTE: if (column.m_cbColumn == 1) { return(bytes[0]); } break; default: throw new ArgumentException($"Unrecognized column type '{type}'."); } throw new ArgumentException($"Unexpected data size for type {type}: {column.m_cbColumn} bytes"); } }