using System.IO.Compression; using System.Text.Json.Serialization; using LibGit.Extensions; using LibGit.Interfaces; namespace LibGit; public class TreeObject { [JsonIgnore] public IRepoSource RepoSource { get; } public string ObjectId { get; } public TreeObject(IRepoSource repoSource, string objectId) { RepoSource = repoSource; ObjectId = objectId; } private const bool _debug = false; public string Length { get; set; } public Dictionary Entries { get; set; } = new(); public TreeObject ReadFromZlibCompressedObjFile(Stream bytes) { if (_debug) Console.WriteLine($"Decompressing {GetType().Name}"); using ZLibStream stream = new ZLibStream(bytes, CompressionMode.Decompress); using var result = new MemoryStream(); stream.CopyTo(result); stream.Flush(); stream.Close(); return ReadFromDecompressedObjFile(result); } public TreeObject ReadFromDecompressedObjFile(Stream data) { if (_debug) Console.WriteLine("Parsing tree object"); // var data = new Queue(bytes.ToArray()); int iters = 0; data.Seek(0, SeekOrigin.Begin); if (_debug) Console.WriteLine($"Iteration {iters}: starting pos: {data.Position}/+{data.Remaining()}/{data.Length}"); Length = data.ReadNullTerminatedField(asciiPrefix: "tree ").AsString(); while (data.Remaining() > 20) { if (_debug) Console.WriteLine($"readTree.Iteration {iters} ({data.Position}/+{data.Remaining()}/{data.Length})"); var entry = ReadFileEntry(data); Entries.Add(entry.Key, entry.Value); } if (data.Remaining() > 0) { Console.WriteLine($"--parseTree: Unparsed data after {iters} iteration(s) of parsing TreeObject--"); Console.WriteLine(this.ToJson()); Console.WriteLine("--HexDump of remaining data--"); data.Peek(data.Remaining()).HexDump(); //Console.WriteLine($"Unparsed data: {Encoding.UTF8.GetString(data.ToArray())}"); } data.Close(); if (_debug) { Console.WriteLine($"-- Read tree object of size {Length} --"); foreach (var x in Entries.ToList()) { Console.WriteLine($"{x.Value.Mode} {x.Value.Hash} {x.Key}"); } foreach (var x in Entries.ToList()) { Console.WriteLine($"Path: {x.Key}"); Console.WriteLine($"Mode: {x.Value.Mode}"); Console.WriteLine($"Hash: {x.Value.Hash}"); } } return this; } //parsing private static KeyValuePair ReadFileEntry(Stream data) { if (_debug) Console.WriteLine($"--tree.ReadFileEntry--"); var path = ""; TreeObjectEntry entry = new(); //entry format: \0 entry.Mode = data.ReadSpaceTerminatedField().AsString(); path = data.ReadNullTerminatedField().AsString(); entry.Hash = string.Join("", data.ReadBytes(20).Select(x => $"{x:x2}")); return new KeyValuePair(path, entry); } public class TreeObjectEntry { public string Mode { get; set; } public string Hash { get; set; } } }