File: GitDataReader\GitRefTableReader.Diagnostics.cs
Web Access
Project: src\sourcelink\src\Microsoft.Build.Tasks.Git\Microsoft.Build.Tasks.Git.csproj (Microsoft.Build.Tasks.Git)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if DEBUG

using System.Collections.Generic;
using System.IO;

namespace Microsoft.Build.Tasks.Git;

// Utilities for diagnosing reader issues.

internal sealed partial class GitRefTableReader
{
    public record Block(
        char Type,
        long Position,
        int UnalignedSize,
        List<RefRecord> RefRecords,
        List<RefIndexRecord> IndexRecords,
        List<int> RestartOffsets)
    {
        public override string ToString()
            => $"Type: '{Type}', Position: {Position}, UnalignedSize: {UnalignedSize}, Records: {RefRecords.Count + IndexRecords.Count}, RestartOffsets: {RestartOffsets.Count}";
    }

    /// <summary>
    /// Returns all ref-blocks and ref-index-blocks.
    /// </summary>
    public List<Block> GetBlocks()
    {
        var footer = ReadHeaderAndFooter();

        var blocks = new List<Block>();

        Position = footer.Header.Size;
        var blockStartPosition = 0L;

        while (Position < footer.Position)
        {
            var blockType = ReadByte();
            var unalignedSize = ReadUInt24BE();

            var refRecords = new List<RefRecord>();
            var indexRecords = new List<RefIndexRecord>();
            var restartOffsets = new List<int>();

            if (blockType is not (BlockTypeRef or BlockTypeIndex))
            {
                break;
            }

            var firstRecordPosition = Position;

            Position = blockStartPosition + unalignedSize - sizeof(ushort);
            var restartCount = ReadUInt16BE();
            Position = firstRecordPosition;

            var endOfRecords = blockStartPosition + unalignedSize - restartCount * SizeOfUInt24 - sizeof(ushort);
            while (Position < endOfRecords)
            {
                if (blockType == BlockTypeRef)
                {
                    refRecords.Add(ReadRefRecord(footer.Header, priorName: null));
                }
                else
                {
                    indexRecords.Add(ReadRefIndexRecord(priorName: null));
                }
            }

            for (var i = 0; i < restartCount; i++)
            {
                restartOffsets.Add(ReadUInt24BE());
            }

            // restart_count
            _ = ReadUInt16BE();

            blocks.Add(new Block((char)blockType, blockStartPosition, unalignedSize, refRecords, indexRecords, restartOffsets));

            // If the file is unaligned the next block starts at the end of the current block,
            // otherwise the current block is padded to block size and the next block starts after the padding.
            blockStartPosition = footer.Header.BlockSize > 0 ? blockStartPosition + footer.Header.BlockSize : Position;

            while (Position < blockStartPosition)
            {
                var padding = ReadByte();
                if (padding != 0)
                {
                    throw new InvalidDataException($"Unexpected non-zero padding byte: 0x{padding:X2}.");
                }
            }
        }

        return blocks;
    }
}

#endif