Skip to content

Commit

Permalink
Add support for MSFZ/PDZ files (#4868)
Browse files Browse the repository at this point in the history
This adds support for reading MSFZ/PDZ files. MSFZ files are a new
container format for PDB files, which allows for efficient compression
and decompression of small fragments of a file, without decompressing
the entire file.

Co-authored-by: Arlie Davis <[email protected]>
  • Loading branch information
sivadeilra and Arlie Davis authored Aug 20, 2024
1 parent 254a730 commit c318910
Show file tree
Hide file tree
Showing 8 changed files with 656 additions and 50 deletions.
23 changes: 23 additions & 0 deletions src/Microsoft.FileFormats/PDB/IMSFFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.FileFormats.PDB
{
/// <summary>
/// An abstraction for reading both MSF (normal PDB files) and MSFZ files (compressed PDB files).
/// </summary>
internal interface IMSFFile
{
/// <summary>
/// The number of streams stored in the file. This will always be at least 1.
/// </summary>
uint NumStreams { get; }

/// <summary>
/// Gets an object which can read the given stream.
/// </summary>
/// <param name="stream">The index of the stream. This must be less than NumStreams.</param>
/// <returns>A Reader which can read the stream.</returns>
Reader GetStream(uint stream);
}
}
71 changes: 71 additions & 0 deletions src/Microsoft.FileFormats/PDB/MSFFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.FileFormats.PDB
{
/// <summary>
/// This class can read from PDB files that use the MSF container format.
/// </summary>
internal sealed class MSFFile : IMSFFile
{
private Reader[] _streams;

private MSFFile(Reader[] streams)
{
_streams = streams;
}

internal static MSFFile OpenInternal(Reader fileReader, PDBFileHeader msfFileHeader)
{
// Read the Stream Directory and build the list of streams.

uint pageSize = msfFileHeader.PageSize;

uint secondLevelPageCount = ToPageCount(pageSize, msfFileHeader.DirectorySize);
ulong pageIndicesOffset = fileReader.SizeOf<PDBFileHeader>();
PDBPagedAddressSpace secondLevelPageList = CreatePagedAddressSpace(fileReader.DataSource, fileReader.DataSource, msfFileHeader.PageSize, pageIndicesOffset, secondLevelPageCount * sizeof(uint));
PDBPagedAddressSpace directoryContent = CreatePagedAddressSpace(fileReader.DataSource, secondLevelPageList, msfFileHeader.PageSize, 0, msfFileHeader.DirectorySize);

Reader directoryReader = new(directoryContent);
ulong position = 0;
uint countStreams = directoryReader.Read<uint>(ref position);
uint[] streamSizes = directoryReader.ReadArray<uint>(ref position, countStreams);
Reader[] streams = new Reader[countStreams];
for (uint i = 0; i < streamSizes.Length; i++)
{
uint streamSize = streamSizes[i];
streams[i] = new Reader(CreatePagedAddressSpace(fileReader.DataSource, directoryContent, pageSize, position, streamSize));
position += ToPageCount(pageSize, streamSizes[i]) * sizeof(uint);
}

return new MSFFile(streams);
}

private static PDBPagedAddressSpace CreatePagedAddressSpace(IAddressSpace fileData, IAddressSpace indicesData, uint pageSize, ulong offset, uint length)
{
uint[] indices = new Reader(indicesData).ReadArray<uint>(offset, ToPageCount(pageSize, length));
return new PDBPagedAddressSpace(fileData, indices, pageSize, length);
}

private static uint ToPageCount(uint pageSize, uint size)
{
return unchecked((pageSize + size - 1) / pageSize);
}

public uint NumStreams
{
get
{
return (uint)_streams.Length;
}
}

public Reader GetStream(uint index)
{
return _streams[index];
}
}
}
Loading

0 comments on commit c318910

Please sign in to comment.