summary refs log tree commit diff
path: root/LibGit/TreeObject.cs
blob: 7d5eb36681fd9971ba753d0fd218f1ee48b3d7ef (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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<string, TreeObjectEntry> 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<byte>(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<string, TreeObjectEntry> ReadFileEntry(Stream data)
    {
        if (_debug) Console.WriteLine($"--tree.ReadFileEntry--");
        var path = "";
        TreeObjectEntry entry = new();
        //entry format: <mode> <path>\0<hash>
        entry.Mode = data.ReadSpaceTerminatedField().AsString();
        path = data.ReadNullTerminatedField().AsString();
        entry.Hash = string.Join("", data.ReadBytes(20).Select(x => $"{x:x2}"));

        return new KeyValuePair<string, TreeObjectEntry>(path, entry);
    }

    public class TreeObjectEntry
    {
        public string Mode { get; set; }
        public string Hash { get; set; }
    }
}