File: GitDataReader\GitRefTableReader.Primitives.cs
Web Access
Project: src\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.

using System;
using System.IO;
using System.Text;

namespace Microsoft.Build.Tasks.Git;

internal sealed partial class GitRefTableReader
{
    private const int SizeOfUInt24 = 3;

    private readonly byte[] _buffer = new byte[64];

    private long Position
    {
        get => stream.Position;
        set => stream.Position = value;
    }

    private long Length
        => stream.Length;

    internal byte ReadByte()
    {
        var result = stream.ReadByte();
        if (result == -1)
        {
            throw new EndOfStreamException();
        }

        return (byte)result;
    }

    /// <summary>
    /// See https://git-scm.com/docs/reftable#_varint_encoding
    /// and "offset encoding" in https://git-scm.com/docs/pack-format#_original_version_1_pack_idx_files_have_the_following_format
    /// </summary>
    internal int ReadVarInt()
    {
        long result = -1;

        while (true)
        {
            var b = ReadByte();

            result = (result + 1) << 7 | (long)(b & 0x7f);
            if (result > int.MaxValue)
            {
                throw new InvalidDataException();
            }

            if ((b & 0x80) == 0)
            {
                return (int)result;
            }
        }
    }

    internal ushort ReadUInt16BE()
    {
        ReadExactly(_buffer, sizeof(ushort));
        return (ushort)(_buffer[0] << 8 | _buffer[1]);
    }

    internal int ReadUInt24BE()
    {
        ReadExactly(_buffer, 3);
        return _buffer[0] << 16 | _buffer[1] << 8 | _buffer[2];
    }

    internal uint ReadUInt32BE()
    {
        ReadExactly(_buffer, sizeof(uint));
        return
            (uint)_buffer[0] << 24 |
            (uint)_buffer[1] << 16 |
            (uint)_buffer[2] << 8 |
            _buffer[3];
    }

    internal ulong ReadUInt64BE()
    {
        ReadExactly(_buffer, sizeof(ulong));
        return
            (ulong)_buffer[0] << 56 |
            (ulong)_buffer[1] << 48 |
            (ulong)_buffer[2] << 40 |
            (ulong)_buffer[3] << 32 |
            (ulong)_buffer[4] << 24 |
            (ulong)_buffer[5] << 16 |
            (ulong)_buffer[6] << 8 |
            _buffer[7];
    }

    internal string ReadObjectName(int objectIdSize)
    {
        ReadExactly(_buffer, objectIdSize);

        var builder = new StringBuilder(objectIdSize * 2);

        for (var i = 0; i < objectIdSize; i++)
        {
            var b = _buffer[i];
            builder.Append(CharUtils.ToHexDigit((byte)(b >> 4)));
            builder.Append(CharUtils.ToHexDigit((byte)(b & 0xf)));
        }

        return builder.ToString();
    }

    internal byte[] ReadBytes(int count)
    {
        byte[] bytes;
        try
        {
            bytes = new byte[count];
        }
        catch (OutOfMemoryException)
        {
            throw new InvalidDataException();
        }

        ReadExactly(bytes);
        return bytes;
    }

    internal void ReadExactly(byte[] buffer)
        => ReadExactly(buffer, buffer.Length);

    internal void ReadExactly(byte[] buffer, int count)
    {
        var totalRead = 0;
        while (totalRead < count)
        {
            var read = stream.Read(buffer, totalRead, count - totalRead);
            if (read == 0)
            {
                throw new EndOfStreamException();
            }

            totalRead += read;
        }
    }
}