/// <summary> /// Initializes a new instance of the <see cref="PdbStream"/> class. /// </summary> /// <param name="stream">PDB symbol stream.</param> public TpiStream(PdbStream stream) { Stream = stream; if (stream.Reader.BytesRemaining < TpiStreamHeader.Size) { throw new Exception("TPI Stream does not contain a header."); } Header = TpiStreamHeader.Read(stream.Reader); if (Header.Version != PdbTpiVersion.V80) { throw new Exception("Unsupported TPI Version."); } if (Header.HeaderSize != TpiStreamHeader.Size) { throw new Exception("Corrupt TPI Header size."); } if (Header.HashKeySize != 4) // 4 = sizeof(uint) { throw new Exception("TPI Stream expected 4 byte hash key size."); } if (Header.HashBucketsCount < MinTpiHashBuckets || Header.HashBucketsCount > MaxTpiHashBuckets) { throw new Exception("TPI Stream Invalid number of hash buckets."); } // The actual type records themselves come from this stream TypeRecordsSubStream = Stream.Reader.ReadSubstream(Header.TypeRecordBytes); typeRecordsSubStreamPerThread = new System.Threading.ThreadLocal <IBinaryReader>(() => TypeRecordsSubStream.Duplicate()); IBinaryReader reader = TypeRecordsSubStream; long position = reader.Position, end = reader.Length; references = new List <RecordReference>(); while (position < end) { RecordPrefix prefix = RecordPrefix.Read(reader); if (prefix.RecordLength < 2) { throw new Exception("CV corrupt record"); } TypeLeafKind kind = (TypeLeafKind)prefix.RecordKind; ushort dataLen = prefix.DataLen; references.Add(new RecordReference { DataOffset = (uint)position + RecordPrefix.Size, Kind = kind, DataLen = dataLen, }); position += dataLen + RecordPrefix.Size; reader.Move(dataLen); } typesCache = new ArrayCache <TypeRecord>(references.Count, true, ReadType); typesByKindCache = new DictionaryCache <TypeLeafKind, TypeRecord[]>(GetTypesByKind); // Hash indices, hash values, etc come from the hash stream. HashSubstream = Stream.File.GetStream(Header.HashStreamIndex)?.Reader; hashValuesCache = SimpleCache.CreateStruct(() => { if (HashSubstream != null) { // There should be a hash value for every type record, or no hashes at all. uint numHashValues = Header.HashValueBuffer.Length / 4; // 4 = sizeof(uint) if (numHashValues != references.Count && numHashValues != 0) { throw new Exception("TPI hash count does not match with the number of type records."); } HashSubstream.Position = Header.HashValueBuffer.Offset; return(HashSubstream.ReadUintArray(references.Count)); } return(null); }); typeIndexOffsetsCache = SimpleCache.CreateStruct(() => { if (HashSubstream != null) { HashSubstream.Position = Header.IndexOffsetBuffer.Offset; uint numTypeIndexOffsets = Header.IndexOffsetBuffer.Length / TypeIndexOffset.Size; TypeIndexOffset[] typeIndexOffsets = new TypeIndexOffset[numTypeIndexOffsets]; for (uint i = 0; i < typeIndexOffsets.Length; i++) { typeIndexOffsets[i] = TypeIndexOffset.Read(HashSubstream); } return(typeIndexOffsets); } return(null); }); hashAdjustersCache = SimpleCache.CreateStruct(() => { if (HashSubstream != null && Header.HashAdjustersBuffer.Length > 0) { HashSubstream.Position = Header.HashAdjustersBuffer.Offset; return(new HashTable(HashSubstream)); } return(null); }); hashTableCache = SimpleCache.CreateStruct(() => { uint[] hashes = HashValues; if (hashes != null) { // Construct hash table TypeIndexListItem[] hashTable = new TypeIndexListItem[Header.HashBucketsCount]; for (uint ti = Header.TypeIndexBegin, i = 0; ti < Header.TypeIndexEnd; ti++, i++) { uint bucket = hashes[i] % Header.HashBucketsCount; hashTable[bucket] = new TypeIndexListItem(new TypeIndex(ti), hashTable[bucket]); } // Use hash adjusters to improve hash table if (HashAdjusters != null) { var namesMap = Stream.File.InfoStream.NamesMap; foreach (var kvp in HashAdjusters.Dictionary) { uint nameIndex = kvp.Key; TypeIndex typeIndex = new TypeIndex(kvp.Value); string name = namesMap.GetString(nameIndex); uint hash = Windows.HashTable.HashStringV1(name) % (uint)hashTable.Length; // Find type index hash adjusters wants to be head for (TypeIndexListItem item = hashTable[hash], previousItem = null; item != null; previousItem = item, item = item.Next) { if (item.TypeIndex == typeIndex) { if (previousItem == null) { // Our type index is already at the head break; } previousItem.Next = item.Next; item.Next = hashTable[hash]; hashTable[hash] = item; break; } } } } return(hashTable); } return(null); }); }
/// <summary> /// Initializes a new instance of the <see cref="PdbStream"/> class. /// </summary> /// <param name="stream">PDB symbol stream.</param> public TpiStream(PdbStream stream) { Stream = stream; if (stream.Reader.BytesRemaining < TpiStreamHeader.Size) { throw new Exception("TPI Stream does not contain a header."); } Header = TpiStreamHeader.Read(stream.Reader); if (Header.Version != PdbTpiVersion.V80) { throw new Exception("Unsupported TPI Version."); } if (Header.HeaderSize != TpiStreamHeader.Size) { throw new Exception("Corrupt TPI Header size."); } if (Header.HashKeySize != 4) // 4 = sizeof(uint) { throw new Exception("TPI Stream expected 4 byte hash key size."); } if (Header.HashBucketsCount < MinTpiHashBuckets || Header.HashBucketsCount > MaxTpiHashBuckets) { throw new Exception("TPI Stream Invalid number of hash buckets."); } // The actual type records themselves come from this stream TypeRecordsSubStream = Stream.Reader.ReadSubstream(Header.TypeRecordBytes); typeRecordsSubStreamPerThread = new System.Threading.ThreadLocal <IBinaryReader>(() => TypeRecordsSubStream.Duplicate()); IBinaryReader reader = TypeRecordsSubStream; long position = reader.Position, end = reader.Length; references = new List <RecordReference>(); while (position < end) { RecordPrefix prefix = RecordPrefix.Read(reader); if (prefix.RecordLength < 2) { throw new Exception("CV corrupt record"); } TypeLeafKind kind = (TypeLeafKind)prefix.RecordKind; ushort dataLen = prefix.DataLen; references.Add(new RecordReference { DataOffset = (uint)position + RecordPrefix.Size, Kind = kind, DataLen = dataLen, }); position += dataLen + RecordPrefix.Size; reader.ReadFake(dataLen); } typesCache = new ArrayCache <TypeRecord>(references.Count, true, ReadType); typesByKindCache = new DictionaryCache <TypeLeafKind, TypeRecord[]>(GetTypesByKind); // Hash indices, hash values, etc come from the hash stream. if (Header.HashStreamIndex != InvalidStreamIndex) { if (Header.HashStreamIndex >= Stream.File.Streams.Count) { throw new Exception("Invalid TPI hash stream index."); } HashSubstream = Stream.File.Streams[Header.HashStreamIndex].Reader; } hashValuesCache = SimpleCache.CreateStruct(() => { if (HashSubstream != null) { // There should be a hash value for every type record, or no hashes at all. uint numHashValues = Header.HashValueBuffer.Length / 4; // 4 = sizeof(uint) if (numHashValues != references.Count && numHashValues != 0) { throw new Exception("TPI hash count does not match with the number of type records."); } HashSubstream.Position = Header.HashValueBuffer.Offset; return(HashSubstream.ReadUintArray(references.Count)); } return(null); }); typeIndexOffsetsCache = SimpleCache.CreateStruct(() => { if (HashSubstream != null) { HashSubstream.Position = Header.IndexOffsetBuffer.Offset; uint numTypeIndexOffsets = Header.IndexOffsetBuffer.Length / TypeIndexOffset.Size; TypeIndexOffset[] typeIndexOffsets = new TypeIndexOffset[numTypeIndexOffsets]; for (uint i = 0; i < typeIndexOffsets.Length; i++) { typeIndexOffsets[i] = TypeIndexOffset.Read(HashSubstream); } return(typeIndexOffsets); } return(null); }); hashAdjustersCache = SimpleCache.CreateStruct(() => { if (HashSubstream != null && Header.HashAdjustersBuffer.Length > 0) { HashSubstream.Position = Header.HashAdjustersBuffer.Offset; return(new HashTable(HashSubstream)); } return(null); }); }