diff --git a/GitStaticPageBuilder.sln.DotSettings.user b/GitStaticPageBuilder.sln.DotSettings.user
index d2fd913..90e964e 100644
--- a/GitStaticPageBuilder.sln.DotSettings.user
+++ b/GitStaticPageBuilder.sln.DotSettings.user
@@ -1,8 +1,16 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArray_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F171934cfea6d47e7b448809a75c8111ddba800_003Fdc_003Fec70b72b_003FArray_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAvx2_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F171934cfea6d47e7b448809a75c8111ddba800_003F10_003Ff575a23a_003FAvx2_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABitConverter_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F171934cfea6d47e7b448809a75c8111ddba800_003F2c_003F3d0cc1cc_003FBitConverter_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADeflateStream_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3684b2f6cbc5487aaabe08de60bdfbc861000_003F1e_003Fbd0c2641_003FDeflateStream_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F171934cfea6d47e7b448809a75c8111ddba800_003F07_003F685e6f25_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInflater_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3684b2f6cbc5487aaabe08de60bdfbc861000_003F4c_003F22dee77c_003FInflater_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMemoryExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F171934cfea6d47e7b448809a75c8111ddba800_003F38_003F09c92a09_003FMemoryExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASpanHelpers_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F171934cfea6d47e7b448809a75c8111ddba800_003Fea_003F5fb3adee_003FSpanHelpers_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStream_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F171934cfea6d47e7b448809a75c8111ddba800_003Fea_003F084b71d4_003FStream_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F171934cfea6d47e7b448809a75c8111ddba800_003F08_003F3251f443_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F264933f5493344a9bd939d6f1fac156a83800_003F2c_003F32748566_003FThrowHelper_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AZLibStream_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3684b2f6cbc5487aaabe08de60bdfbc861000_003F01_003F92d6c825_003FZLibStream_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+ <s:Boolean x:Key="/Default/Monitoring/Counters/=System_002ERuntime/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UnloadedProject/UnloadedProjects/=7c42b766_002Dc2c9_002D44cf_002D997d_002Dc52e614ce906_0023GitStaticPageBuilder/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>
\ No newline at end of file
diff --git a/LibGit/Extensions/StreamExtensions.cs b/LibGit/Extensions/StreamExtensions.cs
index 41e4b4f..90bd35a 100644
--- a/LibGit/Extensions/StreamExtensions.cs
+++ b/LibGit/Extensions/StreamExtensions.cs
@@ -1,4 +1,5 @@
using System.IO.Compression;
+using System.Runtime.InteropServices;
namespace LibGit.Extensions;
@@ -53,18 +54,25 @@ public static class StreamExtensions
{
if (!stream.CanRead)
throw new InvalidOperationException("Can't read a non-readable stream");
- if (!stream.CanSeek)
- throw new InvalidOperationException("Can't read a non-seekable stream");
- for (long i = 0; i < count; i++)
+ var buffer = new byte[count];
+ var readCount = stream.Read(buffer, 0, (int)count);
+ for (int i = 0; i < readCount; i++)
{
- int read = stream.ReadByte();
- if (read == -1)
- yield break;
- yield return (byte)read;
+ yield return buffer[i];
}
}
+ public static byte[] ReadBlock(this Stream stream, int count)
+ {
+ if (!stream.CanRead)
+ throw new InvalidOperationException("Can't read a non-readable stream");
+
+ Span<byte> buffer = stackalloc byte[count];
+ stream.ReadExactly(buffer);
+ return buffer.ToArray();
+ }
+
public static bool StartsWith(this Stream stream, IEnumerable<byte> sequence)
{
if (!stream.CanRead)
@@ -172,13 +180,25 @@ public static class StreamExtensions
if (!stream.CanRead)
throw new InvalidOperationException("Can't read a non-readable stream");
- var bytes = stream.ReadBytes(4).ToArray();
+ Span<byte> b = stackalloc byte[4];
+ stream.ReadExactly(b);
+ return ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
+ }
- if (BitConverter.IsLittleEndian)
- Array.Reverse(bytes);
+ public static int[] MultiReadInt32BE(this Stream stream, int count)
+ {
+ if (!stream.CanRead)
+ throw new InvalidOperationException("Can't read a non-readable stream");
- // Console.WriteLine("ReadInt32BE: " + bytes.AsHexString() + " => " + BitConverter.ToInt32(bytes));
- return BitConverter.ToInt32(bytes);
+ Span<byte> b = stackalloc byte[4 * count];
+ stream.ReadExactly(b);
+ int[] results = new int[count];
+ for (int i = 0; i < count; i++)
+ {
+ results[i] = (b[i * 4 + 0] << 24) | (b[i * 4 + 1] << 16) | (b[i * 4 + 2] << 8) | b[i * 4 + 3];
+ }
+
+ return results;
}
public static uint ReadUInt32BE(this Stream stream)
@@ -186,13 +206,25 @@ public static class StreamExtensions
if (!stream.CanRead)
throw new InvalidOperationException("Can't read a non-readable stream");
- var bytes = stream.ReadBytes(4).ToArray();
+ Span<byte> b = stackalloc byte[4];
+ stream.ReadExactly(b);
+ return (uint)((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
+ }
+
+ public static uint[] MultiReadUInt32BE(this Stream stream, int count)
+ {
+ if (!stream.CanRead)
+ throw new InvalidOperationException("Can't read a non-readable stream");
- if (BitConverter.IsLittleEndian)
- Array.Reverse(bytes);
+ Span<byte> b = count >= 32767 ? new byte[4 * count] : stackalloc byte[4 * count];
+ stream.ReadExactly(b);
+ uint[] results = new uint[count];
+ for (int i = 0; i < count; i++)
+ {
+ results[i] = (uint)((b[i * 4 + 0] << 24) | (b[i * 4 + 1] << 16) | (b[i * 4 + 2] << 8) | b[i * 4 + 3]);
+ }
- // Console.WriteLine("ReadUInt32BE: " + bytes.AsHexString() + " => " + BitConverter.ToUInt32(bytes));
- return BitConverter.ToUInt32(bytes);
+ return results;
}
public static ulong ReadUInt64BE(this Stream stream)
@@ -200,13 +232,38 @@ public static class StreamExtensions
if (!stream.CanRead)
throw new InvalidOperationException("Can't read a non-readable stream");
- var bytes = stream.ReadBytes(8).ToArray();
+ Span<byte> b = stackalloc byte[8];
+ stream.ReadExactly(b);
+ return ((ulong)b[0] << 56) | ((ulong)b[1] << 48) | ((ulong)b[2] << 40) | ((ulong)b[3] << 32) |
+ ((ulong)b[4] << 24) | ((ulong)b[5] << 16) | ((ulong)b[6] << 8) | b[7];
+ }
- if (BitConverter.IsLittleEndian)
- Array.Reverse(bytes);
+ public static ulong[] MultiReadUInt64BE(this Stream stream, int count)
+ {
+ if (!stream.CanRead)
+ throw new InvalidOperationException("Can't read a non-readable stream");
+
+ Span<byte> b = count >= 16384 ? new byte[8 * count] : stackalloc byte[8 * count];
+ stream.ReadExactly(b);
+ ulong[] results = new ulong[count];
+ for (int i = 0; i < count; i++)
+ {
+ results[i] = ((ulong)b[i * 8 + 0] << 56) | ((ulong)b[i * 8 + 1] << 48) | ((ulong)b[i * 8 + 2] << 40) | ((ulong)b[i * 8 + 3] << 32) |
+ ((ulong)b[i * 8 + 4] << 24) | ((ulong)b[i * 8 + 5] << 16) | ((ulong)b[i * 8 + 6] << 8) | b[i * 8 + 7];
+ }
+
+ return results;
+ }
+
+ public static T[] MultiReadInlineArray<T>(this Stream stream, int count) where T : unmanaged
+ {
+ if (!stream.CanRead)
+ throw new InvalidOperationException("Can't read a non-readable stream");
- // Console.WriteLine("ReadUInt64BE: " + bytes.AsHexString() + " => " + BitConverter.ToUInt64(bytes));
- return BitConverter.ToUInt64(bytes);
+ Span<T> buffer = count >= 8192 ? new T[count] : stackalloc T[count];
+ Span<byte> byteBuffer = MemoryMarshal.AsBytes(buffer);
+ stream.ReadExactly(byteBuffer);
+ return buffer.ToArray();
}
//read variable length number
diff --git a/LibGit/GitPack.cs b/LibGit/GitPack.cs
index bbb53d1..9e574ee 100644
--- a/LibGit/GitPack.cs
+++ b/LibGit/GitPack.cs
@@ -1,5 +1,7 @@
+using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO.Compression;
+using System.Runtime.CompilerServices;
using LibGit.Extensions;
namespace LibGit;
@@ -7,6 +9,7 @@ namespace LibGit;
// https://shafiul.github.io//gitbook/7_the_packfile.html - easier to digest than the git documentation
public class GitPack
{
+ private const bool _log = false;
public string PackId { get; set; }
public GitRepo Repo { get; set; }
@@ -15,14 +18,17 @@ public class GitPack
public GitPackIndex Index { get; set; }
public List<GitPackObject> Objects { get; set; } = new();
- public GitPack Read(Stream packStream, Stream idxStream)
+ public GitPack Read(Stream packStream, Stream? idxStream = null, GitPackIndex? index = null)
{
- Console.Write(" Header: ");
- packStream.Peek(04).ToArray()[0..].HexDump(4);
- Console.Write("Version: ");
- packStream.Peek(08).ToArray()[4..].HexDump(4);
- Console.Write(" ObjCnt: ");
- packStream.Peek(12).ToArray()[8..].HexDump(4);
+ if (_log)
+ {
+ Console.Write(" Header: ");
+ packStream.Peek(04).ToArray()[0..].HexDump(4);
+ Console.Write("Version: ");
+ packStream.Peek(08).ToArray()[4..].HexDump(4);
+ Console.Write(" ObjCnt: ");
+ packStream.Peek(12).ToArray()[8..].HexDump(4);
+ }
if (!packStream.StartsWith("PACK"))
throw new Exception("Invalid pack file header");
@@ -31,25 +37,37 @@ public class GitPack
Version = packStream.ReadInt32BE();
ObjectCount = packStream.ReadInt32BE();
- Console.WriteLine($"Got git v{Version} pack with {ObjectCount} objects");
- Console.WriteLine("Reading index...");
+ if (_log)
+ {
+ Console.WriteLine($"Got git v{Version} pack with {ObjectCount} objects");
+ Console.WriteLine("Reading index...");
+ }
+
Index = new GitPackIndex().Read(idxStream);
- // Console.WriteLine("Pack index entries:");
- // foreach (var entry in Index.Entries.OrderByDescending(x => x.Offset))
- // {
- // Console.WriteLine($" - {entry.Sha.AsHexString().Replace(" ", "").ToLower()} @ {entry.Offset}");
- // }
-
- // Console.WriteLine(string.Join("\n - ", Index.Entries.OrderByDescending(x => x.Offset).Select(x => $"{x.Sha.AsHexString().Replace(" ", "").ToLower()} @ {x.Offset}")));
- Console.WriteLine("Reading pack objects...");
+ if (_log) Console.WriteLine("Reading pack objects...");
var ordered = Index.Entries.OrderBy(x => x.Offset).ToArray();
+ // prevent spamming the console
+ var sw = Stopwatch.StartNew();
+ var tsw = Stopwatch.StartNew();
for (int i = 0; i < ObjectCount; i++)
{
- // Console.WriteLine("Reading object " + (i + 1) + " of " + ObjectCount);
- Objects.Add(new GitPackObject().Read(packStream, ordered[i].Offset));
+ var obj = ordered[i];
+ if (sw.ElapsedMilliseconds >= 50 || i == ObjectCount - 1)
+ {
+ Console.Write($"\r[{tsw.Elapsed}] Reading object {i + 1}/{ObjectCount} ({ordered[i].Sha}) @ {obj.Offset}");
+ sw.Restart();
+ }
+
+ Objects.Add(new GitPackObject().ReadHeader(packStream, obj.Offset));
}
+ Console.WriteLine();
+
+ foreach (var group in Objects.GroupBy(x => x.ObjType))
+ {
+ Console.WriteLine($" - {group.Key}: {group.Count()} objects");
+ }
return this;
}
@@ -63,17 +81,30 @@ public class GitPack
public class GitPackIndex
{
+ private const bool _log = false;
public int Version { get; set; }
public int[] fanOutTable = new int[256];
- public List<IndexEntry> Entries { get; set; } = new List<IndexEntry>();
- public Byte[] PackSHA { get; set; } = null!;
- public Byte[] IndexSHA { get; set; } = null!;
+ public List<IndexEntry> Entries { get; set; } = [];
+ public Sha1Value PackSHA { get; set; }
+ public Sha1Value IndexSHA { get; set; }
+
+ [InlineArray(20)]
+ public struct Sha1Value
+ {
+ private byte _e0;
+
+ public override string ToString()
+ {
+ ReadOnlySpan<byte> bytes = this;
+ return Convert.ToHexStringLower(bytes);
+ }
+ }
public struct IndexEntry
{
- public byte[] Sha { get; set; }
- public uint Crc32 { get; set; }
- public ulong Offset { get; set; }
+ public Sha1Value Sha;
+ public uint Crc32;
+ public ulong Offset;
}
public GitPackIndex Read(Stream stream)
@@ -83,91 +114,48 @@ public class GitPackIndex
stream.Skip(4);
Version = stream.ReadInt32BE();
- Console.WriteLine($"Got git v{Version} pack index");
+ if (_log) Console.WriteLine($"Got git v{Version} pack index");
- //fan-out table
- for (int i = 0; i < 255; i++)
- {
- fanOutTable[i] = stream.ReadInt32BE();
- }
+ fanOutTable = stream.MultiReadInt32BE(255);
var size = stream.ReadInt32BE(); // aka "fanout[255]"
- Console.WriteLine($"Index contains {size} objects");
-
- // Console.WriteLine("Fan-out table:");
- // var tableWidth = 8;
- // if (Console.WindowWidth >= 320) tableWidth = 12;
- // else if (Console.WindowWidth >= 240) tableWidth = 10;
- // else if (Console.WindowWidth >= 160) tableWidth = 8;
- // else if (Console.WindowWidth >= 80) tableWidth = 4;
- // Console.WriteLine($"TW: {tableWidth}, CW: {Console.WindowWidth}");
- // for (int i = 0; i < 256; i++)
- // {
- // Console.Write($"[{i:X2}] {fanOutTable[i]:X8} ({fanOutTable[i].ToString(),8}) ");
- // if ((i + 1) % tableWidth == 0)
- // Console.WriteLine();
- // }
-
- // Console.WriteLine($"\t\t END OF TABLE @ {stream.Position}");
-
- for (int i = 0; i < size; i++)
- {
- // sha list
- var sha = stream.ReadBytes(20).ToArray();
- // Console.WriteLine($"OBJ {i:X4}: {sha.AsHexString()}");
- Entries.Add(new IndexEntry
- {
- Sha = sha
- });
- }
+ if (_log) Console.WriteLine($"Index contains {size} objects");
+ Entries = new List<IndexEntry>(new IndexEntry[size]);
- for (int i = 0; i < size; i++)
- {
- // crc32 list
- var crc = stream.ReadUInt32BE();
- // Console.WriteLine($"CRC {i:X4}: {crc:X8}");
- Entries[i] = new IndexEntry
- {
- Sha = Entries[i].Sha,
- Crc32 = crc
- };
- }
+ if (_log) Console.Write("Reading SHA1...");
+ var sha1Values = stream.MultiReadInlineArray<Sha1Value>(size);
+
+ if (_log) Console.Write(" CRC...");
+ var crcValues = stream.MultiReadUInt32BE(size);
+
+ if (_log) Console.Write(" OFS...");
+ var raw32 = stream.MultiReadUInt32BE(size); // uint[]
+ var raw64 = stream.MultiReadUInt64BE((int)((stream.Remaining() - 40) / sizeof(ulong)));
- for (int i = 0; i < size; i++)
+ if (_log) Console.WriteLine(" Hashes...");
+ var h = new Sha1Value();
+ stream.ReadBlock(20).CopyTo(h);
+ PackSHA = h;
+ if (_log) Console.WriteLine($"Pack SHA: {PackSHA}");
+
+ h = new Sha1Value();
+ stream.ReadBlock(20).CopyTo(h);
+ IndexSHA = h;
+ if (_log) Console.WriteLine($"Index SHA: {IndexSHA}");
+
+ if (_log) Console.WriteLine("Constructing entries...");
+ for (var i = 0; i < size; i++)
{
- // offset list
- var offset = stream.ReadInt32BE();
- // Console.WriteLine($"OFF {i:X4}: {offset}");
Entries[i] = new IndexEntry
{
- Sha = Entries[i].Sha,
- Crc32 = Entries[i].Crc32,
- Offset = (uint)offset
+ Sha = sha1Values[i],
+ Crc32 = crcValues[i],
+ Offset = (raw32[i] & 0x80000000) == 0
+ ? raw32[i]
+ : raw64[raw32[i] & 0x7FFFFFFF]
};
}
- // for (int i = 0; i < size; i++)
- // {
- // Console.WriteLine($"ENTRY {i:X4}: {Entries[i].Sha.AsHexString()} | CRC32: {Entries[i].Crc32:X8} | OFF: {Entries[i].Offset}");
- // }
-
- if (stream.Remaining() > 20)
- for (int i = 0; i < size; i++)
- {
- var entry = Entries[i];
- if ((entry.Offset & 0x80000000) == 0) continue;
-
- var largeOffset = stream.ReadUInt64BE();
- Console.WriteLine($"LARGE OFF {i:X4}: {largeOffset} (idx: {i})");
- Entries[i] = entry with { Offset = largeOffset };
- // Thread.Sleep(10);
- }
-
- PackSHA = stream.ReadBytes(20).ToArray();
- Console.WriteLine($"Pack SHA: {PackSHA.AsHexString()}");
- IndexSHA = stream.ReadBytes(20).ToArray();
- Console.WriteLine($"Index SHA: {IndexSHA.AsHexString()}");
-
return this;
}
}
@@ -176,24 +164,25 @@ public class GitPackObject
{
private const bool _debug = false;
- public GitPackObject Read(Stream stream, ulong offset)
+ public GitPackObject ReadHeader(Stream stream, ulong offset)
{
+ Offset = offset;
stream.Seek((long)offset, SeekOrigin.Begin);
if (_debug) Console.WriteLine($"Reading pack object at offset {offset}, stream position {stream.Position}");
var headerPos = stream.Position;
- var data = stream.ReadBytes(1).First();
+ var data = stream.ReadByte();
if (_debug) Console.WriteLine($"data: {data:X8} ({data}/{data:b8})");
-
+
//format: 1 bit continue, 3 bits type, 4 bits size (A), continued by up to 3 more bytes of size (B, C and D), A is least significant
ObjType = (GitObjectType)((data >> 4) & 0b0000_0111);
var sizeBits = data & 0b0000_1111; // Lower 4 bits are the initial size
var restOfSize = (data & 0b1000_0000) != 0 ? stream.ReadVLQ() : 0;
UncompressedSize = (restOfSize << 4) | sizeBits;
-
+
// handle delta objects
if (ObjType == GitObjectType.RefDelta)
{
- RefDeltaBaseObjectId = stream.ReadBytes(20).ToArray();
+ RefDeltaBaseObjectId = stream.ReadBlock(20);
if (_debug) Console.WriteLine($"Ref delta base object id: {RefDeltaBaseObjectId.AsHexString()}");
}
else if (ObjType == GitObjectType.OffsDelta)
@@ -201,20 +190,27 @@ public class GitPackObject
OffsDeltaBaseOffset = stream.ReadGitPackOffsetModifiedVLQ();
if (_debug) Console.WriteLine($"Offset delta base offset: {OffsDeltaBaseOffset}");
}
-
- var dataPos = stream.Position;
- if (_debug) Console.WriteLine($"pack objType: {ObjType} ({(int)ObjType}), uncompressed size: {UncompressedSize}, position: HDR={headerPos}, DATA={dataPos}");
- // stream.Peek(Size).Take(16).ToArray().HexDump(16);
- // stream.Skip(Size);
+
+ // var dataPos = stream.Position;
+ // if (_debug) Console.WriteLine($"pack objType: {ObjType} ({(int)ObjType}), uncompressed size: {UncompressedSize}, position: HDR={headerPos}, DATA={dataPos}");
+
+ DataOffset = (ulong) stream.Position;
+
+ return this;
+ }
+
+ public GitPackObject Read(Stream stream, ulong offset)
+ {
+ ReadHeader(stream, offset);
try
{
using var zlibStream = new ZLibStream(stream, CompressionMode.Decompress, true);
- var decompressedData = new byte[UncompressedSize];
+ var decompressedData = new Span<byte>(new byte[UncompressedSize]);
int totalRead = 0;
while (totalRead < UncompressedSize)
{
- int bytesRead = zlibStream.Read(decompressedData, totalRead, UncompressedSize - totalRead);
+ int bytesRead = zlibStream.Read(decompressedData); //, totalRead, UncompressedSize - totalRead);
if (bytesRead == 0)
throw new Exception("Unexpected end of zlib stream");
totalRead += bytesRead;
@@ -223,25 +219,28 @@ public class GitPackObject
catch (Exception ex)
{
Console.WriteLine($"Error during zlib decompression: {ex.Message}");
- stream.Seek(headerPos - 5, SeekOrigin.Begin);
+ stream.Seek((long)Offset, SeekOrigin.Begin);
stream.Peek(32).HexDump();
throw;
}
- var endPos = stream.Position;
- if (_debug) Console.WriteLine($"Decompressed data ({UncompressedSize} bytes/{endPos - dataPos} compressed, stream @ {endPos}):");
+ // var endPos = stream.Position;
+ // if (_debug) Console.WriteLine($"Decompressed data ({UncompressedSize} bytes/{endPos - dataPos} compressed, stream @ {endPos}):");
// if (_debug) decompressedData.ToArray().HexDump(32);
// Environment.Exit(1);
return this;
}
+ public ulong Offset { get; set; }
+ public ulong DataOffset { get; set; }
+
public GitObjectType ObjType { get; set; }
public int UncompressedSize { get; set; }
public byte[]? RefDeltaBaseObjectId { get; set; }
-
+
public int? OffsDeltaBaseOffset { get; set; }
}
diff --git a/LibGit/LibGit.csproj b/LibGit/LibGit.csproj
index a55e5be..c87aeab 100644
--- a/LibGit/LibGit.csproj
+++ b/LibGit/LibGit.csproj
@@ -4,6 +4,7 @@
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
diff --git a/LibGitTest/LibGitTest.csproj b/LibGitTest/LibGitTest.csproj
index fddc46c..5f1d18e 100644
--- a/LibGitTest/LibGitTest.csproj
+++ b/LibGitTest/LibGitTest.csproj
@@ -5,6 +5,7 @@
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
+ <PublishAOT>true</PublishAOT>
</PropertyGroup>
<ItemGroup>
diff --git a/LibGitTest/Program.cs b/LibGitTest/Program.cs
index 3d87631..18459c7 100644
--- a/LibGitTest/Program.cs
+++ b/LibGitTest/Program.cs
@@ -1,11 +1,10 @@
// See https://aka.ms/new-console-template for more information
-using LibGit;
-using LibGit.Extensions;
using LibGitTest;
Console.WriteLine("Hello, World!");
-//await Test1.Run();
+// await Test1.Run();
//await Test2.Run();
-await Test3.Run();
\ No newline at end of file
+// await Test3.Run();
+await Test4.Run();
\ No newline at end of file
diff --git a/LibGitTest/Test1.cs b/LibGitTest/Test1.cs
index ebd6f7e..71de9df 100644
--- a/LibGitTest/Test1.cs
+++ b/LibGitTest/Test1.cs
@@ -8,7 +8,7 @@ public class Test1
public static async Task Run()
{
Console.WriteLine("Test1 running");
- var repo = new GitRepo(new FileRepoSource(@"/home/Rory/git/matrix/MatrixRoomUtils.git"));
+ var repo = new GitRepo(new FileRepoSource(@"/home/Rory/git/spacebar/server-master/.git"));
// var repo = new GitRepo(new WebRepoSource("https://git.rory.gay/MatrixRoomUtils.git/"));
var commit = await repo.GetCommit("HEAD");
diff --git a/LibGitTest/Test3.cs b/LibGitTest/Test3.cs
index 24d6986..fee7230 100644
--- a/LibGitTest/Test3.cs
+++ b/LibGitTest/Test3.cs
@@ -7,7 +7,7 @@ public class Test3
public static async Task Run()
{
Console.WriteLine("Test3 running");
- var repo = new GitRepo(new FileRepoSource(@"/home/Rory/git/spacebar/server-master/.git"));
+ var repo = new GitRepo(new FileRepoSource(@"/home/Rory/git/nixpkgs-Draupnir/.git"));
var packs = repo.GetPacks().GetAsyncEnumerator();
int count = 0;
while(await packs.MoveNextAsync())
diff --git a/LibGitTest/Test4.cs b/LibGitTest/Test4.cs
new file mode 100644
index 0000000..5a9e47f
--- /dev/null
+++ b/LibGitTest/Test4.cs
@@ -0,0 +1,29 @@
+using LibGit;
+
+namespace LibGitTest;
+
+public class Test4
+{
+ public static async Task Run()
+ {
+ Console.WriteLine("Test4 running");
+ var repo = new GitRepo(new FileRepoSource(@"/home/Rory/git/spacebar/server-master/.git"));
+ var packs = repo.GetPacks().GetAsyncEnumerator();
+ int count = 0;
+ if (Directory.Exists("out-git"))
+ Directory.Delete("out-git", true);
+ Directory.CreateDirectory("out-git");
+ Directory.CreateDirectory("out-git/objects");
+ while (await packs.MoveNextAsync())
+ {
+ count += packs.Current.ObjectCount;
+ foreach (var gitPackObject in packs.Current.Index.Entries)
+ {
+ var item = packs.Current.Objects.First(x => x.Offset == gitPackObject.Offset);
+ Console.WriteLine($"{item.ObjType}");
+ }
+ }
+
+ Console.WriteLine($"Read {count} objects from pack files.");
+ }
+}
\ No newline at end of file
|