package object

import (
	"bytes"
	"context"
	"errors"
	"io"
	"sort"
	"strings"
	"testing"

	fixtures "github.com/go-git/go-git-fixtures/v6"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"

	"github.com/go-git/go-git/v6/internal/pathutil"
	"github.com/go-git/go-git/v6/plumbing"
	"github.com/go-git/go-git/v6/plumbing/cache"
	"github.com/go-git/go-git/v6/plumbing/filemode"
	"github.com/go-git/go-git/v6/plumbing/storer"
	"github.com/go-git/go-git/v6/storage/filesystem"
	"github.com/go-git/go-git/v6/storage/memory"
)

type TreeSuite struct {
	suite.Suite
	BaseObjectsSuite
	Tree *Tree
}

func TestTreeSuite(t *testing.T) {
	t.Parallel()
	suite.Run(t, new(TreeSuite))
}

func (s *TreeSuite) SetupSuite() {
	s.BaseObjectsSuite.SetupSuite(s.T())
	hash := plumbing.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c")

	s.Tree = s.tree(hash)
}

func (s *TreeSuite) TestDecode() {
	s.Len(s.Tree.Entries, 8)
	s.Equal(".gitignore", s.Tree.Entries[0].Name)
	s.Equal("32858aad3c383ed1ff0a0f9bdf231d54a00c9e88", s.Tree.Entries[0].Hash.String())
	s.Equal(filemode.Regular, s.Tree.Entries[0].Mode)
	s.Equal("go", s.Tree.Entries[4].Name)
	s.Equal("a39771a7651f97faf5c72e08224d857fc35133db", s.Tree.Entries[4].Hash.String())
	s.Equal(filemode.Dir, s.Tree.Entries[4].Mode)
}

func (s *TreeSuite) TestDecodeNonTree() {
	hash := plumbing.NewHash("9a48f23120e880dfbe41f7c9b7b708e9ee62a492")
	blob, err := s.Storer.EncodedObject(plumbing.BlobObject, hash)
	s.NoError(err)

	tree := &Tree{}
	err = tree.Decode(blob)
	s.ErrorIs(err, ErrUnsupportedObject)
}

func (s *TreeSuite) TestType() {
	s.Equal(plumbing.TreeObject, s.Tree.Type())
}

func (s *TreeSuite) TestTree() {
	expectedEntry, err := s.Tree.entry("vendor")
	s.NoError(err)
	expected := expectedEntry.Hash

	obtainedTree, err := s.Tree.Tree("vendor")
	s.NoError(err)
	s.Equal(expected, obtainedTree.Hash)
}

func (s *TreeSuite) TestTreeNotFound() {
	d, err := s.Tree.Tree("not-found")
	s.Nil(d)
	s.ErrorIs(err, ErrDirectoryNotFound)
}

func (s *TreeSuite) TestTreeFailsWithExistingFiles() {
	_, err := s.Tree.File("LICENSE")
	s.NoError(err)

	d, err := s.Tree.Tree("LICENSE")
	s.Nil(d)
	s.ErrorIs(err, ErrDirectoryNotFound)
}

func (s *TreeSuite) TestFile() {
	f, err := s.Tree.File("LICENSE")
	s.NoError(err)
	s.Equal("LICENSE", f.Name)
}

func (s *TreeSuite) TestFileNotFound() {
	f, err := s.Tree.File("not-found")
	s.Nil(f)
	s.ErrorIs(err, ErrFileNotFound)
}

func (s *TreeSuite) TestFileFailsWithExistingTrees() {
	_, err := s.Tree.Tree("vendor")
	s.NoError(err)

	f, err := s.Tree.File("vendor")
	s.Nil(f)
	s.ErrorIs(err, ErrFileNotFound)
}

func (s *TreeSuite) TestSize() {
	size, err := s.Tree.Size("LICENSE")
	s.NoError(err)
	s.Equal(int64(1072), size)
}

func (s *TreeSuite) TestFiles() {
	var count int
	err := s.Tree.Files().ForEach(func(_ *File) error {
		count++
		return nil
	})

	s.NoError(err)
	s.Equal(9, count)
}

func (s *TreeSuite) TestFindEntry() {
	e, err := s.Tree.FindEntry("vendor/foo.go")
	s.NoError(err)
	s.Equal("foo.go", e.Name)
}

func (s *TreeSuite) TestFindEntryNotFound() {
	e, err := s.Tree.FindEntry("not-found")
	s.Nil(e)
	s.ErrorIs(err, ErrEntryNotFound)
	e, err = s.Tree.FindEntry("not-found/not-found/not-found")
	s.Nil(e)
	s.ErrorIs(err, ErrDirectoryNotFound)
}

// countingStorer wraps a storer and counts EncodedObject calls per hash
type countingStorer struct {
	storer.EncodedObjectStorer
	calls map[plumbing.Hash]int
}

func newCountingStorer(s storer.EncodedObjectStorer) *countingStorer {
	return &countingStorer{
		EncodedObjectStorer: s,
		calls:               make(map[plumbing.Hash]int),
	}
}

func (cs *countingStorer) EncodedObject(t plumbing.ObjectType, h plumbing.Hash) (plumbing.EncodedObject, error) {
	cs.calls[h]++
	return cs.EncodedObjectStorer.EncodedObject(t, h)
}

// TestFindEntryCacheDepth1 verifies that the tree path cache works for
// paths with only one directory level (e.g., "vendor/foo.go").
// This test ensures that repeated lookups in the same directory reuse
// the cached subtree instead of fetching it from the storer each time.
func (s *TreeSuite) TestFindEntryCacheDepth1() {
	cs := newCountingStorer(s.Storer)

	hash := plumbing.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c")
	tree, err := GetTree(cs, hash)
	s.Require().NoError(err)

	vendorEntry, err := tree.entry("vendor")
	s.Require().NoError(err)
	vendorHash := vendorEntry.Hash

	// Previous calls to GetTree and entry may pollute the "calls" state, reset it to start afresh.
	cs.calls = make(map[plumbing.Hash]int)

	e1, err := tree.FindEntry("vendor/foo.go")
	s.Require().NoError(err)
	s.Equal("foo.go", e1.Name)

	s.Equal(1, cs.calls[vendorHash], "first FindEntry should fetch vendor tree once")

	e2, err := tree.FindEntry("vendor/foo.go")
	s.Require().NoError(err)
	s.Equal("foo.go", e2.Name)

	s.Equal(1, cs.calls[vendorHash], "second FindEntry should reuse cached vendor tree")
}

// TestFindEntryCacheDepth2 verifies that the tree path cache works for
// paths with two different files in the same directory ("json/short.json"
// and "json/long.json") .
// This complements TestFindEntryCacheDepth1 by testing multiple access with
// different path in the same directory.
func (s *TreeSuite) TestFindEntryCacheDepth2() {
	cs := newCountingStorer(s.Storer)

	hash := plumbing.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c")
	tree, err := GetTree(cs, hash)
	s.Require().NoError(err)

	jsonEntry, err := tree.entry("json")
	s.Require().NoError(err)
	jsonHash := jsonEntry.Hash

	cs.calls = make(map[plumbing.Hash]int)

	e1, err := tree.FindEntry("json/short.json")
	s.Require().NoError(err)
	s.Equal("short.json", e1.Name)

	s.Equal(1, cs.calls[jsonHash], "first FindEntry should fetch json tree once")

	e2, err := tree.FindEntry("json/long.json")
	s.Require().NoError(err)
	s.Equal("long.json", e2.Name)

	s.Equal(1, cs.calls[jsonHash], "second FindEntry should reuse cached json tree")
}

// Overrides returned plumbing.EncodedObject for given hash.
// Otherwise, delegates to actual storer to get real object
type fakeStorer struct {
	storer.EncodedObjectStorer
	hash plumbing.Hash
	fake fakeEncodedObject
}

func (fs fakeStorer) EncodedObject(t plumbing.ObjectType, h plumbing.Hash) (plumbing.EncodedObject, error) {
	if fs.hash == h {
		return fs.fake, nil
	}
	return fs.EncodedObjectStorer.EncodedObject(t, h)
}

// Overrides reader of plumbing.EncodedObject to simulate read error
type fakeEncodedObject struct{ plumbing.EncodedObject }

func (fe fakeEncodedObject) Reader() (io.ReadCloser, error) {
	return nil, errors.New("Simulate encoded object can't be read")
}

func (s *TreeSuite) TestDir() {
	vendor, err := s.Tree.dir("vendor")
	s.NoError(err)

	t, err := GetTree(s.Tree.s, s.Tree.ID())
	s.NoError(err)
	o, err := t.s.EncodedObject(plumbing.AnyObject, vendor.ID())
	s.NoError(err)

	t.s = fakeStorer{t.s, vendor.ID(), fakeEncodedObject{o}}
	_, err = t.dir("vendor")
	s.NotNil(err)
}

// This plumbing.EncodedObject implementation has a reader that only returns 6
// bytes at a time, this should simulate the conditions when a read
// returns less bytes than asked, for example when reading a hash which
// is bigger than 6 bytes.
type SortReadObject struct {
	t    plumbing.ObjectType
	h    plumbing.Hash
	cont []byte
	sz   int64
}

func (o *SortReadObject) Hash() plumbing.Hash           { return o.h }
func (o *SortReadObject) Type() plumbing.ObjectType     { return o.t }
func (o *SortReadObject) SetType(t plumbing.ObjectType) { o.t = t }
func (o *SortReadObject) Size() int64                   { return o.sz }
func (o *SortReadObject) SetSize(s int64)               { o.sz = s }
func (o *SortReadObject) Content() []byte               { return o.cont }
func (o *SortReadObject) Reader() (io.ReadCloser, error) {
	return &SortReadCloser{pos: 0, data: o.cont}, nil
}
func (o *SortReadObject) Writer() (io.WriteCloser, error)   { return o, nil }
func (o *SortReadObject) Write(p []byte) (n int, err error) { return len(p), nil }
func (o *SortReadObject) Close() error                      { return nil }

// a ReadCloser that only returns 6 bytes at a time, to simulate incomplete reads.
type SortReadCloser struct {
	pos  int
	data []byte
}

func (o *SortReadCloser) Close() error { return nil }
func (o *SortReadCloser) Read(p []byte) (int, error) {
	if o.pos == len(o.data) {
		return 0, io.EOF
	}

	sz := len(p)
	remaining := len(o.data) - o.pos
	if sz > 6 { // don't read more than 6 bytes at a time
		sz = 6
	}
	if sz > remaining {
		sz = remaining
	}

	src := o.data[o.pos : o.pos+sz]
	nw := copy(p, src)
	o.pos += nw

	return nw, nil
}

func (s *TreeSuite) TestTreeEntriesSorted() {
	tree := &Tree{
		Entries: []TreeEntry{
			{Name: "foo", Mode: filemode.Regular, Hash: plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d")},
			{Name: "bar", Mode: filemode.Regular, Hash: plumbing.NewHash("c029517f6300c2da0f4b651b8642506cd6aaf45d")},
			{Name: "baz", Mode: filemode.Regular, Hash: plumbing.NewHash("d029517f6300c2da0f4b651b8642506cd6aaf45d")},
		},
	}

	{
		s.False(sort.IsSorted(TreeEntrySorter(tree.Entries)))
		obj := &plumbing.MemoryObject{}
		err := tree.Encode(obj)
		s.ErrorIs(err, ErrEntriesNotSorted)
	}

	{
		sort.Sort(TreeEntrySorter(tree.Entries))
		obj := &plumbing.MemoryObject{}
		err := tree.Encode(obj)
		s.NoError(err)
	}
}

func (s *TreeSuite) TestTreeDecodeEncodeIdempotent() {
	trees := []*Tree{
		{
			Entries: []TreeEntry{
				{Name: "foo", Mode: filemode.Regular, Hash: plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d")},
				{Name: "bar", Mode: filemode.Regular, Hash: plumbing.NewHash("c029517f6300c2da0f4b651b8642506cd6aaf45d")},
				{Name: "baz", Mode: filemode.Regular, Hash: plumbing.NewHash("d029517f6300c2da0f4b651b8642506cd6aaf45d")},
			},
		},
	}
	for _, tree := range trees {
		sort.Sort(TreeEntrySorter(tree.Entries))
		obj := &plumbing.MemoryObject{}
		err := tree.Encode(obj)
		s.NoError(err)
		newTree := &Tree{}
		err = newTree.Decode(obj)
		s.NoError(err)
		tree.Hash = obj.Hash()
		s.EqualExportedValues(tree, newTree)
	}
}

func (s *TreeSuite) TestTreeDiff() {
	f := fixtures.ByURL("https://github.com/src-d/go-git.git").One()
	dotgit, err := f.DotGit()
	s.Require().NoError(err)
	storer := filesystem.NewStorage(dotgit, cache.NewObjectLRUDefault())
	defer func() { _ = storer.Close() }()
	commit, err := GetCommit(storer, plumbing.NewHash("89f8bda31d29767a6d6ba8f9d0dfb941d598e843"))
	s.NoError(err)

	tree, err := commit.Tree()
	s.NoError(err)

	parentCommit, err := commit.Parent(0)
	s.NoError(err)

	parentTree, err := parentCommit.Tree()
	s.NoError(err)

	ch, err := parentTree.Diff(tree)
	s.NoError(err)

	s.Len(ch, 3)
	s.Equal("examples/object_storage/main.go", ch[0].From.Name)
	s.Equal("examples/storage/main.go", ch[0].To.Name)

	ch, err = parentTree.DiffContext(context.Background(), tree)
	s.NoError(err)
	s.Len(ch, 3)
}

func (s *TreeSuite) TestTreeIter() {
	encIter, err := s.Storer.IterEncodedObjects(plumbing.TreeObject)
	s.NoError(err)
	iter := NewTreeIter(s.Storer, encIter)

	trees := []*Tree{}
	iter.ForEach(func(t *Tree) error {
		t.s = nil
		trees = append(trees, t)
		return nil
	})

	s.True(len(trees) > 0)
	iter.Close()

	encIter, err = s.Storer.IterEncodedObjects(plumbing.TreeObject)
	s.NoError(err)
	iter = NewTreeIter(s.Storer, encIter)

	i := 0
	for {
		t, err := iter.Next()
		if err == io.EOF {
			break
		}

		t.s = nil
		s.NoError(err)
		s.Equal(trees[i], t)
		i++
	}

	iter.Close()
}

func (s *TreeSuite) TestTreeWalkerNext() {
	commit, err := GetCommit(s.Storer, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
	s.NoError(err)
	tree, err := commit.Tree()
	s.NoError(err)

	walker := NewTreeWalker(tree, true, nil)
	for _, e := range treeWalkerExpects {
		name, entry, err := walker.Next()
		if err == io.EOF {
			break
		}

		s.NoError(err)
		s.Equal(e.Path, name)
		s.Equal(e.Name, entry.Name)
		s.Equal(e.Mode, entry.Mode)
		s.Equal(e.Hash, entry.Hash.String())

		s.Equal(e.Tree, walker.Tree().ID().String())
	}
}

func (s *TreeSuite) TestTreeWalkerNextSkipSeen() {
	commit, err := GetCommit(s.Storer, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
	s.NoError(err)
	tree, err := commit.Tree()
	s.NoError(err)

	seen := map[plumbing.Hash]bool{
		plumbing.NewHash(treeWalkerExpects[0].Hash): true,
	}
	walker := NewTreeWalker(tree, true, seen)
	for _, e := range treeWalkerExpects[1:] {
		name, entry, err := walker.Next()
		if err == io.EOF {
			break
		}

		s.NoError(err)
		s.Equal(e.Path, name)
		s.Equal(e.Name, entry.Name)
		s.Equal(e.Mode, entry.Mode)
		s.Equal(e.Hash, entry.Hash.String())

		s.Equal(e.Tree, walker.Tree().ID().String())
	}
}

func (s *TreeSuite) TestTreeWalkerNextNonRecursive() {
	commit := s.commit(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
	tree, err := commit.Tree()
	s.NoError(err)

	var count int
	walker := NewTreeWalker(tree, false, nil)
	for {
		name, entry, err := walker.Next()
		if err == io.EOF {
			break
		}

		s.NoError(err)
		s.NotEqual("", name)
		s.NotNil(entry)

		s.Equal("a8d315b2b1c615d43042c3a62402b8a54288cf5c", walker.Tree().ID().String())

		count++
	}

	s.Equal(8, count)
}

func (s *TreeSuite) TestPatchContext_ToNil() {
	commit := s.commit(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
	tree, err := commit.Tree()
	s.NoError(err)

	patch, err := tree.PatchContext(context.Background(), nil)
	s.NoError(err)

	s.Equal(242971, len(patch.String()))
}

func (s *TreeSuite) TestTreeWalkerNextSubmodule() {
	dotgit2, err := fixtures.ByURL("https://github.com/git-fixtures/submodule.git").One().DotGit()
	s.Require().NoError(err)
	st := filesystem.NewStorage(dotgit2, cache.NewObjectLRUDefault())
	defer func() { _ = st.Close() }()

	hash := plumbing.NewHash("b685400c1f9316f350965a5993d350bc746b0bf4")
	commit, err := GetCommit(st, hash)
	s.NoError(err)

	tree, err := commit.Tree()
	s.NoError(err)

	expected := []string{
		".gitmodules",
		"README.md",
		"basic",
		"itself",
	}

	var count int
	walker := NewTreeWalker(tree, true, nil)
	defer walker.Close()

	for {
		name, entry, err := walker.Next()
		if err == io.EOF {
			break
		}

		s.NoError(err)
		s.NotNil(entry)
		s.Equal(expected[count], name)

		count++
	}

	s.Equal(4, count)
}

var treeWalkerExpects = []struct {
	Path             string
	Mode             filemode.FileMode
	Name, Hash, Tree string
}{{
	Path: ".gitignore", Mode: filemode.Regular, Name: ".gitignore",
	Hash: "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
}, {
	Path: "CHANGELOG", Mode: filemode.Regular, Name: "CHANGELOG",
	Hash: "d3ff53e0564a9f87d8e84b6e28e5060e517008aa", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
}, {
	Path: "LICENSE", Mode: filemode.Regular, Name: "LICENSE",
	Hash: "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
}, {
	Path: "binary.jpg", Mode: filemode.Regular, Name: "binary.jpg",
	Hash: "d5c0f4ab811897cadf03aec358ae60d21f91c50d", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
}, {
	Path: "go", Mode: filemode.Dir, Name: "go",
	Hash: "a39771a7651f97faf5c72e08224d857fc35133db", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
}, {
	Path: "go/example.go", Mode: filemode.Regular, Name: "example.go",
	Hash: "880cd14280f4b9b6ed3986d6671f907d7cc2a198", Tree: "a39771a7651f97faf5c72e08224d857fc35133db",
}, {
	Path: "json", Mode: filemode.Dir, Name: "json",
	Hash: "5a877e6a906a2743ad6e45d99c1793642aaf8eda", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
}, {
	Path: "json/long.json", Mode: filemode.Regular, Name: "long.json",
	Hash: "49c6bb89b17060d7b4deacb7b338fcc6ea2352a9", Tree: "5a877e6a906a2743ad6e45d99c1793642aaf8eda",
}, {
	Path: "json/short.json", Mode: filemode.Regular, Name: "short.json",
	Hash: "c8f1d8c61f9da76f4cb49fd86322b6e685dba956", Tree: "5a877e6a906a2743ad6e45d99c1793642aaf8eda",
}, {
	Path: "php", Mode: filemode.Dir, Name: "php",
	Hash: "586af567d0bb5e771e49bdd9434f5e0fb76d25fa", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
}, {
	Path: "php/crappy.php", Mode: filemode.Regular, Name: "crappy.php",
	Hash: "9a48f23120e880dfbe41f7c9b7b708e9ee62a492", Tree: "586af567d0bb5e771e49bdd9434f5e0fb76d25fa",
}, {
	Path: "vendor", Mode: filemode.Dir, Name: "vendor",
	Hash: "cf4aa3b38974fb7d81f367c0830f7d78d65ab86b", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
}, {
	Path: "vendor/foo.go", Mode: filemode.Regular, Name: "foo.go",
	Hash: "9dea2395f5403188298c1dabe8bdafe562c491e3", Tree: "cf4aa3b38974fb7d81f367c0830f7d78d65ab86b",
}}

// When decoding a tree we were not checking the return value of read
// when reading hashes.  As a hash is quite small, it worked well nearly
// all the time.
//
// I have found some examples of repos where the read is incomplete and
// the tree decode fails, for example
// http://github.com/sqlcipher/sqlcipher.git, object
// 0ba19d22411289293ab5c012891529967d7c933e.
//
// This tests is performed with that object but using a SortReadObject to
// simulate incomplete reads on all platforms and operating systems.
func (s *TreeSuite) TestTreeDecodeReadBug() {
	// TODO: Review decoding of Trees with partial hashes
	s.T().Skip("skipping tests of trees with partial hashes")

	cont := []byte{
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x61, 0x6c, 0x74,
		0x65, 0x72, 0x2e, 0x63, 0x0, 0xa4, 0x9d, 0x33, 0x49, 0xd7,
		0xe2, 0x3f, 0xb5, 0x81, 0x19, 0x4f, 0x4c, 0xb5, 0x9a, 0xc0,
		0xd5, 0x1b, 0x2, 0x1f, 0x78, 0x31, 0x30, 0x30, 0x36, 0x34,
		0x34, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x2e,
		0x63, 0x0, 0x9a, 0x3e, 0x95, 0x97, 0xdb, 0xb, 0x3, 0x20,
		0x77, 0xc9, 0x1d, 0x96, 0x9d, 0x22, 0xc6, 0x27, 0x3f, 0x70,
		0x2a, 0xc, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x61,
		0x74, 0x74, 0x61, 0x63, 0x68, 0x2e, 0x63, 0x0, 0xb8, 0xe1,
		0x21, 0x99, 0xb5, 0x7d, 0xe8, 0x11, 0xea, 0xe0, 0xd0, 0x61,
		0x42, 0xd5, 0xac, 0x4f, 0xd4, 0x30, 0xb1, 0xd8, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x61, 0x75, 0x74, 0x68, 0x2e,
		0x63, 0x0, 0xd3, 0x8b, 0xb8, 0x36, 0xa7, 0x84, 0xfb, 0xfa,
		0xb6, 0xab, 0x7b, 0x3, 0xd4, 0xe6, 0xdd, 0x43, 0xed, 0xc4,
		0x1f, 0xa7, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x62,
		0x61, 0x63, 0x6b, 0x75, 0x70, 0x2e, 0x63, 0x0, 0x25, 0x2f,
		0x61, 0xcf, 0xca, 0xa8, 0xfc, 0xf3, 0x13, 0x7e, 0x8, 0xed,
		0x68, 0x47, 0xdc, 0xfe, 0x1d, 0xc1, 0xde, 0x54, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x62, 0x69, 0x74, 0x76, 0x65,
		0x63, 0x2e, 0x63, 0x0, 0x52, 0x18, 0x4a, 0xa9, 0x64, 0xce,
		0x18, 0x98, 0xf3, 0x5d, 0x1b, 0x3d, 0x87, 0x87, 0x1c, 0x2d,
		0xe, 0xf4, 0xc5, 0x3d, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
		0x20, 0x62, 0x74, 0x6d, 0x75, 0x74, 0x65, 0x78, 0x2e, 0x63,
		0x0, 0xd8, 0x7d, 0x4d, 0x5f, 0xee, 0xb6, 0x30, 0x7a, 0xec,
		0xdc, 0x9a, 0x83, 0x11, 0x14, 0x89, 0xab, 0x30, 0xc6, 0x78,
		0xc3, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x62, 0x74,
		0x72, 0x65, 0x65, 0x2e, 0x63, 0x0, 0x3c, 0xa6, 0x5, 0x83,
		0xe3, 0xc8, 0xe3, 0x12, 0x0, 0xf9, 0x73, 0xe0, 0xe9, 0xc4,
		0x53, 0x62, 0x58, 0xb2, 0x64, 0x39, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x62, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x68,
		0x0, 0xac, 0xe0, 0xf8, 0xcd, 0x21, 0x77, 0x70, 0xa2, 0xf6,
		0x6b, 0x2e, 0xb8, 0x71, 0xbb, 0xc5, 0xfd, 0xc6, 0xfc, 0x2b,
		0x68, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x62, 0x74,
		0x72, 0x65, 0x65, 0x49, 0x6e, 0x74, 0x2e, 0x68, 0x0, 0xce,
		0x3c, 0x54, 0x93, 0xf8, 0xca, 0xd0, 0xbc, 0x54, 0x8a, 0xe8,
		0xe4, 0x4e, 0x51, 0x28, 0x31, 0xd8, 0xfa, 0xc4, 0x31, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x62, 0x75, 0x69, 0x6c,
		0x64, 0x2e, 0x63, 0x0, 0x3c, 0x91, 0xcd, 0xcf, 0xdb, 0x7b,
		0x1, 0x7c, 0xbc, 0x2d, 0x5c, 0x29, 0x57, 0x1a, 0x98, 0x27,
		0xd, 0xe0, 0x71, 0xe6, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
		0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e,
		0x63, 0x0, 0xd4, 0xc, 0x65, 0xcb, 0x92, 0x45, 0x80, 0x29,
		0x6a, 0xd0, 0x69, 0xa0, 0x4b, 0xf9, 0xc9, 0xe9, 0x53, 0x4e,
		0xca, 0xa7, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63,
		0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2e, 0x63, 0x0,
		0x9e, 0x91, 0x40, 0x8, 0x5c, 0x0, 0x46, 0xed, 0x3b, 0xf6,
		0xf4, 0x48, 0x52, 0x20, 0x69, 0x2d, 0xca, 0x17, 0x43, 0xc5,
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63, 0x72, 0x79,
		0x70, 0x74, 0x6f, 0x2e, 0x63, 0x0, 0x25, 0x51, 0xe6, 0xba,
		0x2, 0x39, 0xf8, 0x5a, 0x35, 0x77, 0x96, 0xa8, 0xdd, 0xa8,
		0xca, 0x3e, 0x29, 0x70, 0x93, 0xf8, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e,
		0x68, 0x0, 0xf7, 0x1f, 0x53, 0x2c, 0xdc, 0x44, 0x8f, 0xa,
		0x1d, 0xd5, 0xc6, 0xef, 0xf5, 0xfb, 0xd3, 0x3a, 0x91, 0x55,
		0xaa, 0x97, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63,
		0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x63, 0x63, 0x2e, 0x63,
		0x0, 0x53, 0x7d, 0xf7, 0xe3, 0xb3, 0x6a, 0xb5, 0xcf, 0xdd,
		0x6f, 0xca, 0x40, 0x28, 0xeb, 0xca, 0xe1, 0x86, 0x87, 0xd6,
		0x4d, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63, 0x72,
		0x79, 0x70, 0x74, 0x6f, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x2e,
		0x63, 0x0, 0xa5, 0x89, 0x27, 0xc7, 0x6e, 0xf6, 0x20, 0x56,
		0x77, 0xbe, 0x5c, 0x1a, 0x8e, 0x80, 0xc9, 0x83, 0x56, 0xb3,
		0xa9, 0xd3, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63,
		0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x6c, 0x69, 0x62, 0x74,
		0x6f, 0x6d, 0x63, 0x72, 0x79, 0x70, 0x74, 0x2e, 0x63, 0x0,
		0x1a, 0x33, 0x83, 0xe0, 0x1, 0xa7, 0x21, 0x11, 0xc3, 0xf6,
		0x61, 0x92, 0x22, 0xb0, 0x65, 0xf4, 0xbd, 0x1, 0xb, 0xe1,
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x63, 0x72, 0x79,
		0x70, 0x74, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73,
		0x6c, 0x2e, 0x63, 0x0, 0xd0, 0x19, 0x81, 0x3b, 0x47, 0x6c,
		0x52, 0xd0, 0x20, 0xe2, 0xc0, 0xac, 0xd5, 0x24, 0xe9, 0xea,
		0x3d, 0xf, 0xb9, 0xfe, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
		0x20, 0x63, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x63, 0x0, 0x60,
		0x59, 0x5f, 0xf8, 0x8d, 0x92, 0xf7, 0x8, 0x26, 0x4, 0xfb,
		0xd9, 0xdf, 0x9a, 0xfe, 0xa1, 0x6a, 0xe8, 0x6f, 0xf, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x64, 0x61, 0x74, 0x65,
		0x2e, 0x63, 0x0, 0x75, 0x8d, 0xd7, 0xc8, 0x9b, 0xca, 0x39,
		0x37, 0xa9, 0xd, 0x70, 0x6e, 0xa9, 0x82, 0xce, 0x3a, 0xcf,
		0x11, 0xd1, 0x83, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x2e, 0x63, 0x0, 0x63,
		0x4e, 0x11, 0x55, 0x63, 0xae, 0x12, 0xba, 0x65, 0x58, 0xcc,
		0xc5, 0x12, 0xae, 0xd6, 0x31, 0xc0, 0x66, 0xba, 0xd8, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x65, 0x78, 0x70, 0x72,
		0x2e, 0x63, 0x0, 0x66, 0x3, 0x97, 0xe0, 0x78, 0xae, 0x48,
		0xb2, 0xe7, 0x17, 0x5e, 0x33, 0x85, 0x67, 0x78, 0x19, 0x72,
		0x2d, 0xdd, 0x6c, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x66, 0x61, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x0, 0xc3, 0x2,
		0x8c, 0x4f, 0x93, 0x6e, 0xdf, 0x96, 0x71, 0x2d, 0xbe, 0x73,
		0xa0, 0x76, 0x62, 0xf0, 0xa2, 0x6b, 0x1d, 0xa, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x66, 0x6b, 0x65, 0x79, 0x2e,
		0x63, 0x0, 0xac, 0x35, 0xbc, 0x19, 0x4c, 0xde, 0xb1, 0x27,
		0x98, 0x9b, 0x9, 0x40, 0x35, 0xce, 0xe0, 0x6f, 0x57, 0x37,
		0x6f, 0x5e, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x66,
		0x75, 0x6e, 0x63, 0x2e, 0x63, 0x0, 0xc0, 0x2f, 0x9, 0x6a,
		0xda, 0xd5, 0xbc, 0xe9, 0xac, 0x83, 0xd3, 0x5f, 0xf, 0x46,
		0x9, 0xd6, 0xf6, 0xd4, 0x3b, 0xe5, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e,
		0x63, 0x0, 0x7b, 0x2, 0xcf, 0x21, 0x30, 0xe0, 0xd1, 0xa7,
		0xb8, 0x89, 0xd8, 0x44, 0xc, 0xcc, 0x82, 0x8, 0xf7, 0xb6,
		0x7b, 0xf9, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x68,
		0x61, 0x73, 0x68, 0x2e, 0x63, 0x0, 0xe8, 0x1d, 0xcf, 0x95,
		0xe4, 0x38, 0x48, 0xfa, 0x70, 0x86, 0xb7, 0xf7, 0x81, 0xc0,
		0x90, 0xad, 0xc7, 0xe6, 0xca, 0x8e, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x68, 0x61, 0x73, 0x68, 0x2e, 0x68, 0x0,
		0x82, 0xb7, 0xc5, 0x8c, 0x71, 0x9, 0xb, 0x54, 0x7e, 0x10,
		0x17, 0x42, 0xaa, 0x9, 0x51, 0x73, 0x9f, 0xf2, 0xee, 0xe7,
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x68, 0x77, 0x74,
		0x69, 0x6d, 0x65, 0x2e, 0x68, 0x0, 0xb8, 0xbc, 0x5a, 0x29,
		0x5b, 0xe3, 0xfa, 0xc8, 0x35, 0x1f, 0xa9, 0xf0, 0x8a, 0x77,
		0x57, 0x9d, 0x59, 0xc9, 0xa8, 0xe4, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x2e,
		0x63, 0x0, 0x9a, 0x56, 0x61, 0xf5, 0x9a, 0x72, 0x95, 0x2b,
		0xe6, 0xc1, 0x67, 0xa0, 0xc2, 0xdb, 0x15, 0x9b, 0x91, 0xb7,
		0x1f, 0xae, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6a,
		0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x0, 0xfe,
		0xd2, 0x7b, 0xe3, 0xe3, 0x80, 0x55, 0xd2, 0x20, 0x43, 0x95,
		0xcd, 0xe6, 0xff, 0xc9, 0x45, 0x89, 0xfb, 0xf5, 0xe8, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6c, 0x65, 0x67, 0x61,
		0x63, 0x79, 0x2e, 0x63, 0x0, 0x94, 0x64, 0x9a, 0xe7, 0x5,
		0xab, 0x93, 0x85, 0x10, 0x8d, 0xd, 0x88, 0x7a, 0xf0, 0x75,
		0x92, 0x89, 0xfb, 0x23, 0xcb, 0x31, 0x30, 0x30, 0x36, 0x34,
		0x34, 0x20, 0x6c, 0x65, 0x6d, 0x70, 0x61, 0x72, 0x2e, 0x63,
		0x0, 0x2a, 0xfa, 0xa6, 0xce, 0xa6, 0xd8, 0x29, 0x60, 0x2c,
		0x27, 0x86, 0xc1, 0xf8, 0xa3, 0x7f, 0x56, 0x7c, 0xf6, 0xfd,
		0x53, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6c, 0x6f,
		0x61, 0x64, 0x65, 0x78, 0x74, 0x2e, 0x63, 0x0, 0xcd, 0xcf,
		0x6a, 0x93, 0xb8, 0xc4, 0xf, 0x91, 0x4b, 0x94, 0x24, 0xe,
		0xf1, 0x4c, 0xb4, 0xa3, 0xa, 0x37, 0xec, 0xa1, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x2e,
		0x63, 0x0, 0x39, 0xf6, 0x4, 0x21, 0xe6, 0x81, 0x27, 0x7c,
		0xc3, 0xdb, 0xa0, 0x9a, 0xbe, 0x7c, 0xf7, 0x90, 0xd5, 0x28,
		0xf5, 0xc3, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d,
		0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x2e, 0x63, 0x0, 0x35, 0xa4,
		0x4e, 0x5f, 0x61, 0xc2, 0xe4, 0x4c, 0x48, 0x1c, 0x62, 0x51,
		0xbd, 0xa, 0xae, 0x7a, 0xcd, 0xa4, 0xde, 0xb, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x65, 0x6d, 0x30, 0x2e,
		0x63, 0x0, 0xd, 0xb, 0x66, 0x67, 0xd6, 0xa, 0x95, 0x5a,
		0x6, 0x96, 0xdf, 0x62, 0x89, 0xb4, 0x91, 0x78, 0x96, 0x93,
		0x43, 0xaa, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d,
		0x65, 0x6d, 0x31, 0x2e, 0x63, 0x0, 0x35, 0x78, 0x49, 0x6f,
		0x33, 0x3, 0x7, 0xb2, 0x31, 0xdf, 0xb5, 0x3c, 0xc, 0x2e,
		0x1c, 0x6b, 0x32, 0x3d, 0x79, 0x1e, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x6d, 0x65, 0x6d, 0x32, 0x2e, 0x63, 0x0,
		0x26, 0x44, 0x8e, 0xa8, 0xaa, 0xe0, 0x36, 0x6a, 0xf0, 0x54,
		0x1a, 0xfe, 0xa4, 0x79, 0xb, 0x42, 0xf4, 0xa6, 0x9b, 0x5a,
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x65, 0x6d,
		0x33, 0x2e, 0x63, 0x0, 0x1a, 0x1b, 0x79, 0x1f, 0x28, 0xf8,
		0xcf, 0x3c, 0xe4, 0xf9, 0xa3, 0x5c, 0xda, 0xd7, 0xb7, 0x10,
		0x75, 0x68, 0xc7, 0x15, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
		0x20, 0x6d, 0x65, 0x6d, 0x35, 0x2e, 0x63, 0x0, 0x78, 0x3c,
		0xef, 0x61, 0x76, 0xc5, 0x9c, 0xbf, 0x30, 0x91, 0x46, 0x31,
		0x9, 0x5a, 0x1a, 0x54, 0xf4, 0xe4, 0x2e, 0x8, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x65, 0x6d, 0x6a, 0x6f,
		0x75, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x0, 0x5, 0x72,
		0x59, 0x48, 0xf6, 0x5d, 0x42, 0x7b, 0x7, 0xf7, 0xf9, 0x29,
		0xac, 0xa3, 0xff, 0x22, 0x4b, 0x17, 0x53, 0xdf, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x75, 0x74, 0x65, 0x78,
		0x2e, 0x63, 0x0, 0xb5, 0x67, 0xe7, 0xc2, 0x7e, 0xf2, 0x4,
		0x10, 0x86, 0xaf, 0xe0, 0xf6, 0x96, 0x66, 0xe2, 0x7b, 0xf5,
		0x9, 0x8a, 0x59, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x6d, 0x75, 0x74, 0x65, 0x78, 0x2e, 0x68, 0x0, 0x9, 0x78,
		0x81, 0x22, 0x52, 0x77, 0x89, 0xa, 0x9c, 0x36, 0xc2, 0x4d,
		0x41, 0xf6, 0x11, 0x4d, 0x64, 0xc0, 0x6d, 0xb3, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x75, 0x74, 0x65, 0x78,
		0x5f, 0x6e, 0x6f, 0x6f, 0x70, 0x2e, 0x63, 0x0, 0x45, 0x6e,
		0x82, 0xa2, 0x5e, 0x27, 0x1b, 0x6, 0x14, 0xe7, 0xf4, 0xf8,
		0x3c, 0x22, 0x85, 0x53, 0xb7, 0xfa, 0x1, 0x58, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x75, 0x74, 0x65, 0x78,
		0x5f, 0x75, 0x6e, 0x69, 0x78, 0x2e, 0x63, 0x0, 0xec, 0xa7,
		0x29, 0x58, 0x31, 0xc2, 0xf0, 0xee, 0x48, 0xba, 0x54, 0xd0,
		0x62, 0x91, 0x4d, 0x6, 0xa1, 0xdd, 0x8e, 0xbe, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x6d, 0x75, 0x74, 0x65, 0x78,
		0x5f, 0x77, 0x33, 0x32, 0x2e, 0x63, 0x0, 0x27, 0xd1, 0xa,
		0xf5, 0xbd, 0x33, 0x1b, 0xdb, 0x97, 0x3f, 0x61, 0x45, 0xb7,
		0x4f, 0x72, 0xb6, 0x7, 0xcf, 0xc4, 0x6e, 0x31, 0x30, 0x30,
		0x36, 0x34, 0x34, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79,
		0x2e, 0x63, 0x0, 0xfc, 0xab, 0x5b, 0xfa, 0xf0, 0x19, 0x8,
		0xd3, 0xde, 0x93, 0xfa, 0x88, 0xb5, 0xea, 0xe9, 0xe9, 0x6c,
		0xa3, 0xc8, 0xe8, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x6f, 0x73, 0x2e, 0x63, 0x0, 0xbe, 0x2e, 0xa4, 0xcf, 0xc0,
		0x19, 0x59, 0x93, 0xa3, 0x40, 0xc9, 0x2, 0xae, 0xdd, 0xf1,
		0xbe, 0x4b, 0x8e, 0xd7, 0x3a, 0x31, 0x30, 0x30, 0x36, 0x34,
		0x34, 0x20, 0x6f, 0x73, 0x2e, 0x68, 0x0, 0x7, 0xa, 0x2d,
		0xdd, 0x17, 0xf7, 0x71, 0xf9, 0x8f, 0xf8, 0xcc, 0xd6, 0xf0,
		0x33, 0xbd, 0xac, 0xc5, 0xe9, 0xf6, 0xc, 0x31, 0x30, 0x30,
		0x36, 0x34, 0x34, 0x20, 0x6f, 0x73, 0x5f, 0x63, 0x6f, 0x6d,
		0x6d, 0x6f, 0x6e, 0x2e, 0x68, 0x0, 0xf6, 0xc3, 0xe7, 0xff,
		0x89, 0x46, 0x30, 0x86, 0x40, 0x18, 0x22, 0xf4, 0x81, 0xe7,
		0xe3, 0xb8, 0x7b, 0x2c, 0x78, 0xc7, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x6f, 0x73, 0x5f, 0x75, 0x6e, 0x69, 0x78,
		0x2e, 0x63, 0x0, 0xab, 0xc2, 0x3a, 0x45, 0x2e, 0x72, 0xf7,
		0x1c, 0x76, 0xaf, 0xa9, 0x98, 0x3c, 0x3a, 0xd9, 0xd4, 0x25,
		0x61, 0x6c, 0x6d, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x6f, 0x73, 0x5f, 0x77, 0x69, 0x6e, 0x2e, 0x63, 0x0, 0xae,
		0xb0, 0x88, 0x14, 0xb3, 0xda, 0xbe, 0x81, 0xb8, 0x4c, 0xda,
		0x91, 0x85, 0x82, 0xb0, 0xf, 0xfd, 0x86, 0xe4, 0x87, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x70, 0x61, 0x67, 0x65,
		0x72, 0x2e, 0x63, 0x0, 0x61, 0x72, 0x7f, 0xaa, 0x9c, 0xf,
		0x3d, 0x56, 0x62, 0x65, 0xbe, 0x7e, 0xec, 0x5b, 0x2a, 0x35,
		0xf6, 0xa4, 0xbc, 0x9f, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
		0x20, 0x70, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x68, 0x0, 0x6f,
		0x65, 0x91, 0x36, 0xe2, 0x76, 0x7, 0x9d, 0xa4, 0x3a, 0x2e,
		0x39, 0xe1, 0xb6, 0x86, 0x37, 0xec, 0xad, 0xcf, 0x68, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x70, 0x61, 0x72, 0x73,
		0x65, 0x2e, 0x79, 0x0, 0x83, 0x10, 0xb2, 0x69, 0x89, 0xb0,
		0x5b, 0xed, 0x1e, 0x1b, 0x3, 0xda, 0x80, 0xf5, 0xc0, 0xa5,
		0x2e, 0x9a, 0xd1, 0xd2, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
		0x20, 0x70, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x0,
		0x48, 0x2a, 0x18, 0x8b, 0xee, 0x19, 0x91, 0xbc, 0x8a, 0xda,
		0xc9, 0x6a, 0x19, 0x3a, 0x53, 0xe5, 0x46, 0x2a, 0x8c, 0x10,
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x70, 0x63, 0x61,
		0x63, 0x68, 0x65, 0x2e, 0x68, 0x0, 0xf4, 0xd4, 0xad, 0x71,
		0xc1, 0xd, 0x78, 0xc6, 0xda, 0xbd, 0xe2, 0x52, 0x15, 0xcd,
		0x41, 0x5a, 0x76, 0x1, 0x48, 0xca, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x70, 0x63, 0x61, 0x63, 0x68, 0x65, 0x31,
		0x2e, 0x63, 0x0, 0x41, 0x47, 0xd2, 0xef, 0xf5, 0x5b, 0xdd,
		0x9f, 0xf7, 0xc6, 0x86, 0xc, 0x60, 0x18, 0x10, 0x20, 0x16,
		0x6c, 0x5f, 0x50, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x63, 0x0, 0x22,
		0x97, 0x71, 0x69, 0x61, 0x7d, 0x49, 0x22, 0xb3, 0x99, 0x3f,
		0x76, 0x9d, 0x90, 0xfa, 0x7b, 0xc4, 0x41, 0xea, 0x50, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x70, 0x72, 0x65, 0x70,
		0x61, 0x72, 0x65, 0x2e, 0x63, 0x0, 0xd7, 0x8d, 0x83, 0xcb,
		0xd8, 0x78, 0x97, 0xf5, 0x73, 0x30, 0x3f, 0x9f, 0x57, 0xab,
		0x8d, 0xe0, 0x24, 0xa6, 0xe3, 0xf8, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x2e,
		0x63, 0x0, 0x9f, 0x68, 0xd2, 0x4, 0xff, 0xdc, 0x9f, 0x3d,
		0x42, 0x7f, 0x80, 0xa8, 0x23, 0x9a, 0x7f, 0xa3, 0xa9, 0x8a,
		0xec, 0xbd, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x72,
		0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x2e, 0x63, 0x0, 0x23, 0x4e,
		0xbd, 0xf6, 0x58, 0xf4, 0x36, 0xcc, 0x7c, 0x68, 0xf0, 0x27,
		0xc4, 0x8b, 0xe, 0x1b, 0x9b, 0xa3, 0x4e, 0x98, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x72, 0x65, 0x73, 0x6f, 0x6c,
		0x76, 0x65, 0x2e, 0x63, 0x0, 0x91, 0xef, 0xca, 0xa1, 0xa1,
		0x6b, 0xfc, 0x98, 0xfb, 0x35, 0xd8, 0x5c, 0xad, 0x15, 0x6b,
		0x93, 0x53, 0x3e, 0x4e, 0x6, 0x31, 0x30, 0x30, 0x36, 0x34,
		0x34, 0x20, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x74, 0x2e, 0x63,
		0x0, 0x57, 0x61, 0xf9, 0x85, 0x50, 0xb1, 0x76, 0xcc, 0xe1,
		0x1d, 0xcb, 0xce, 0xc9, 0x38, 0x99, 0xa0, 0x75, 0xbb, 0x64,
		0xfd, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x65,
		0x6c, 0x65, 0x63, 0x74, 0x2e, 0x63, 0x0, 0xf3, 0xf1, 0x49,
		0x9, 0x63, 0x95, 0x5b, 0x8e, 0xd0, 0xc9, 0xfe, 0x6e, 0x1e,
		0xec, 0x83, 0x6c, 0x1a, 0x52, 0x94, 0xb4, 0x31, 0x30, 0x30,
		0x36, 0x34, 0x34, 0x20, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x2e,
		0x63, 0x0, 0x1b, 0xe2, 0x87, 0x1f, 0xed, 0x9a, 0x1f, 0xdf,
		0x1d, 0xf7, 0x19, 0x8e, 0x11, 0x25, 0x36, 0x0, 0xec, 0xba,
		0x76, 0xcc, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73,
		0x71, 0x6c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x2e, 0x68,
		0x0, 0x82, 0x75, 0x30, 0x95, 0xcd, 0x17, 0x23, 0xc5, 0xff,
		0x4f, 0x11, 0x15, 0xe4, 0x97, 0x55, 0x91, 0xee, 0x34, 0xf5,
		0xce, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x71,
		0x6c, 0x69, 0x74, 0x65, 0x2e, 0x68, 0x2e, 0x69, 0x6e, 0x0,
		0x66, 0x8, 0x82, 0x31, 0x75, 0xde, 0x5b, 0x6a, 0xd, 0x37,
		0x8f, 0xdb, 0xc, 0x38, 0x18, 0xb6, 0xab, 0x4f, 0xbf, 0x8e,
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x71, 0x6c,
		0x69, 0x74, 0x65, 0x33, 0x2e, 0x72, 0x63, 0x0, 0x96, 0x98,
		0x76, 0xda, 0x1e, 0x57, 0x14, 0x3d, 0xe0, 0xb4, 0xd1, 0xc7,
		0x62, 0x9f, 0xd3, 0x35, 0x6f, 0x2e, 0x1c, 0x96, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x71, 0x6c, 0x69, 0x74,
		0x65, 0x33, 0x65, 0x78, 0x74, 0x2e, 0x68, 0x0, 0x92, 0x8b,
		0xb3, 0xba, 0xd9, 0xdd, 0x64, 0x3c, 0x30, 0x1d, 0xd2, 0xb0,
		0xac, 0x22, 0x28, 0x7a, 0x81, 0x28, 0x48, 0x84, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x71, 0x6c, 0x69, 0x74,
		0x65, 0x49, 0x6e, 0x74, 0x2e, 0x68, 0x0, 0x59, 0x50, 0xf2,
		0x37, 0xd9, 0xf9, 0xf2, 0xd3, 0xef, 0x6b, 0xd8, 0xbe, 0x34,
		0x2d, 0xcf, 0x64, 0x89, 0x22, 0x51, 0x42, 0x31, 0x30, 0x30,
		0x36, 0x34, 0x34, 0x20, 0x73, 0x71, 0x6c, 0x69, 0x74, 0x65,
		0x4c, 0x69, 0x6d, 0x69, 0x74, 0x2e, 0x68, 0x0, 0xc7, 0xae,
		0xe5, 0x3c, 0xeb, 0xca, 0x94, 0xda, 0x51, 0xe7, 0x1a, 0x82,
		0x2e, 0xa5, 0xa6, 0xde, 0xb9, 0x3, 0x85, 0xdf, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75,
		0x73, 0x2e, 0x63, 0x0, 0x28, 0x34, 0x9e, 0x6d, 0x3d, 0x20,
		0x88, 0xe0, 0x0, 0x3b, 0x76, 0xf8, 0xa, 0x89, 0x54, 0xfa,
		0xec, 0x59, 0x30, 0xba, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
		0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x63, 0x0, 0x26,
		0xbb, 0xfb, 0x4f, 0x45, 0x6c, 0x42, 0x98, 0x25, 0x29, 0xea,
		0x1a, 0x63, 0xa0, 0x17, 0x51, 0xdd, 0x3e, 0xe9, 0x5a, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x63, 0x6c, 0x73,
		0x71, 0x6c, 0x69, 0x74, 0x65, 0x2e, 0x63, 0x0, 0xf1, 0xbb,
		0x29, 0x21, 0xda, 0xc, 0x68, 0xa4, 0xf1, 0xc8, 0xe1, 0x5c,
		0xf5, 0x66, 0xb2, 0x33, 0xe9, 0x2a, 0x51, 0x9f, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x31,
		0x2e, 0x63, 0x0, 0xa6, 0x38, 0xe4, 0x80, 0xad, 0xdf, 0x14,
		0x43, 0x9c, 0xdf, 0xa4, 0xee, 0x16, 0x4d, 0xc3, 0x1b, 0x79,
		0xf8, 0xbc, 0xac, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x74, 0x65, 0x73, 0x74, 0x32, 0x2e, 0x63, 0x0, 0xd1, 0x30,
		0xe9, 0xd0, 0x1b, 0x70, 0x24, 0xa5, 0xec, 0x6d, 0x73, 0x5,
		0x92, 0xee, 0x4d, 0x1f, 0xb0, 0x2c, 0xfd, 0xb4, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x33,
		0x2e, 0x63, 0x0, 0xe3, 0xed, 0x31, 0xc, 0x81, 0x4, 0xfe,
		0x36, 0x21, 0xce, 0xbb, 0xf, 0x51, 0xd1, 0x1, 0x45, 0x1,
		0x8d, 0x4f, 0xac, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x74, 0x65, 0x73, 0x74, 0x34, 0x2e, 0x63, 0x0, 0xa6, 0x37,
		0x5c, 0x7c, 0xc4, 0x3, 0xf6, 0xc, 0xaa, 0xb7, 0xe9, 0x59,
		0x53, 0x3e, 0x3d, 0xb1, 0xff, 0x75, 0xa, 0xe4, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x35,
		0x2e, 0x63, 0x0, 0x30, 0x3d, 0x12, 0x5, 0xb2, 0x26, 0x28,
		0x42, 0x3d, 0x98, 0x6f, 0x71, 0xe2, 0x7c, 0x7c, 0xf7, 0x14,
		0xa7, 0x45, 0xa6, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x74, 0x65, 0x73, 0x74, 0x36, 0x2e, 0x63, 0x0, 0xc1, 0x51,
		0xea, 0x42, 0x98, 0x9b, 0xb, 0xe2, 0x4e, 0xe4, 0xb9, 0xa4,
		0xbe, 0x37, 0x8b, 0x4f, 0x63, 0x6d, 0xb6, 0x41, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x37,
		0x2e, 0x63, 0x0, 0x3c, 0xd4, 0xa2, 0x24, 0xd7, 0xe8, 0xe1,
		0x6b, 0xd7, 0xcb, 0xe4, 0x9e, 0x2d, 0x3e, 0x94, 0xce, 0x9b,
		0x17, 0xbd, 0x76, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x74, 0x65, 0x73, 0x74, 0x38, 0x2e, 0x63, 0x0, 0xc5, 0x73,
		0x93, 0x32, 0xd4, 0x6e, 0x57, 0x12, 0x1d, 0xa2, 0x7c, 0x3e,
		0x88, 0xfd, 0xe7, 0x5a, 0xeb, 0x87, 0x10, 0xf7, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x39,
		0x2e, 0x63, 0x0, 0xe5, 0x99, 0x3e, 0x8f, 0xf7, 0x8f, 0x61,
		0xc2, 0x43, 0x5b, 0x6f, 0x97, 0xa3, 0xb4, 0x63, 0xe2, 0x27,
		0xc7, 0x67, 0xac, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x79, 0x6e, 0x63,
		0x2e, 0x63, 0x0, 0xb0, 0xb9, 0x43, 0x18, 0x5b, 0xfc, 0x23,
		0xc1, 0x7f, 0xd0, 0x8f, 0x55, 0x76, 0x8c, 0xac, 0x12, 0xa9,
		0xf5, 0x69, 0x51, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x65,
		0x78, 0x74, 0x2e, 0x63, 0x0, 0xb5, 0x1, 0x3f, 0x31, 0x73,
		0xa2, 0x17, 0x6e, 0x2d, 0x9f, 0xc, 0xaa, 0x99, 0x19, 0x30,
		0x36, 0xbf, 0xc3, 0x7e, 0x91, 0x31, 0x30, 0x30, 0x36, 0x34,
		0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x61, 0x63,
		0x6b, 0x75, 0x70, 0x2e, 0x63, 0x0, 0xe9, 0x67, 0x42, 0x4a,
		0x29, 0xf, 0x73, 0x8a, 0xec, 0xfd, 0xac, 0x57, 0x8e, 0x9b,
		0x87, 0xa4, 0xc4, 0xae, 0x8d, 0x7f, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x74,
		0x72, 0x65, 0x65, 0x2e, 0x63, 0x0, 0xdb, 0x72, 0x88, 0x9b,
		0x2a, 0xfb, 0x62, 0x72, 0x82, 0x8d, 0xda, 0x86, 0x6d, 0xcc,
		0xf1, 0x22, 0xa4, 0x9a, 0x72, 0x99, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x6f,
		0x6e, 0x66, 0x69, 0x67, 0x2e, 0x63, 0x0, 0x53, 0x47, 0x27,
		0xa0, 0x80, 0x42, 0xb6, 0xca, 0xd6, 0x7e, 0x26, 0x7e, 0x87,
		0xb4, 0x3, 0xa4, 0x1a, 0x73, 0xb2, 0x99, 0x31, 0x30, 0x30,
		0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x64,
		0x65, 0x6d, 0x6f, 0x76, 0x66, 0x73, 0x2e, 0x63, 0x0, 0x63,
		0x76, 0x27, 0x7, 0x1d, 0x9e, 0x28, 0xf4, 0xb3, 0x45, 0x1b,
		0xbb, 0xdd, 0xf8, 0x8, 0xd1, 0xa9, 0x12, 0x0, 0xf8, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74,
		0x5f, 0x64, 0x65, 0x76, 0x73, 0x79, 0x6d, 0x2e, 0x63, 0x0,
		0x21, 0xf0, 0xf6, 0x84, 0xd8, 0x61, 0x11, 0x67, 0x70, 0xde,
		0xfc, 0xde, 0xcd, 0x53, 0x2b, 0xa3, 0xee, 0xab, 0xa9, 0x75,
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73,
		0x74, 0x5f, 0x66, 0x73, 0x2e, 0x63, 0x0, 0x47, 0x8c, 0xad,
		0x80, 0xb1, 0x6a, 0x90, 0x9b, 0x23, 0xbd, 0x3, 0xc2, 0xda,
		0xd8, 0xb4, 0x49, 0xa7, 0x45, 0x87, 0xa1, 0x31, 0x30, 0x30,
		0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x66,
		0x75, 0x6e, 0x63, 0x2e, 0x63, 0x0, 0x6f, 0x9b, 0xb0, 0x3d,
		0xc8, 0x8a, 0x21, 0xd6, 0x58, 0xbf, 0x99, 0x99, 0xba, 0xf6,
		0x6d, 0xc1, 0xd5, 0x2e, 0xbc, 0x54, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x68, 0x65,
		0x78, 0x69, 0x6f, 0x2e, 0x63, 0x0, 0xb2, 0xb, 0x5c, 0xe7,
		0x30, 0xab, 0x7f, 0xa8, 0x0, 0xd2, 0xd0, 0xcc, 0x38, 0xc7,
		0x72, 0x75, 0x59, 0x3e, 0xbd, 0xbb, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6e,
		0x69, 0x74, 0x2e, 0x63, 0x0, 0xe3, 0x72, 0x4d, 0x8b, 0xe3,
		0x14, 0xdb, 0x9, 0xee, 0xa8, 0x4, 0xb, 0x9d, 0xdf, 0xc8,
		0xa8, 0xbe, 0xee, 0x22, 0x91, 0x31, 0x30, 0x30, 0x36, 0x34,
		0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x74,
		0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x0, 0xf5, 0xc3,
		0xd9, 0xe4, 0x5, 0x9a, 0x16, 0x56, 0x7, 0x34, 0x7, 0xe4,
		0x3a, 0x92, 0x11, 0x79, 0x99, 0x69, 0x7b, 0x93, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
		0x69, 0x6e, 0x74, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x68,
		0x0, 0x69, 0x13, 0x37, 0xd1, 0xae, 0xd6, 0x37, 0x15, 0xd6,
		0x2e, 0x76, 0x26, 0x6f, 0xf, 0x3b, 0x50, 0x8b, 0x1, 0xa,
		0x34, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65,
		0x73, 0x74, 0x5f, 0x6a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c,
		0x2e, 0x63, 0x0, 0xe8, 0x70, 0x1a, 0x4e, 0xea, 0xdb, 0x8e,
		0xad, 0x16, 0x9d, 0x60, 0x6, 0x40, 0x7d, 0x54, 0xa8, 0x98,
		0x59, 0x2d, 0x70, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x74, 0x65, 0x73, 0x74, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x65,
		0x78, 0x74, 0x2e, 0x63, 0x0, 0x11, 0x37, 0xe3, 0xa9, 0xaa,
		0xe9, 0x29, 0x6, 0xb8, 0x28, 0x9f, 0x6c, 0x3d, 0xaa, 0x61,
		0xf0, 0xd0, 0x70, 0xf5, 0x5a, 0x31, 0x30, 0x30, 0x36, 0x34,
		0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x61, 0x6c,
		0x6c, 0x6f, 0x63, 0x2e, 0x63, 0x0, 0xcf, 0x98, 0xa8, 0xfb,
		0x21, 0x82, 0xc0, 0xba, 0xf5, 0xa, 0xd5, 0x79, 0x79, 0xb6,
		0x75, 0xbb, 0x70, 0x7a, 0x93, 0xb0, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x75,
		0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x2e, 0x63, 0x0,
		0x62, 0x45, 0x41, 0xb3, 0x2a, 0x10, 0xd2, 0x1a, 0x2f, 0xd1,
		0xa, 0x35, 0xee, 0x66, 0x32, 0xbd, 0xac, 0x55, 0x2d, 0x41,
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73,
		0x74, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65,
		0x78, 0x2e, 0x68, 0x0, 0xb7, 0xe1, 0xaf, 0xea, 0x5f, 0xd7,
		0x8b, 0x87, 0x58, 0x2, 0x65, 0xf8, 0x4c, 0x81, 0x61, 0x2c,
		0xbd, 0x2, 0x5b, 0xaf, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
		0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x75, 0x74, 0x65,
		0x78, 0x2e, 0x63, 0x0, 0xc9, 0xb4, 0xa2, 0x9a, 0xb7, 0x5c,
		0x77, 0xea, 0x5f, 0x36, 0xb5, 0x19, 0x32, 0x56, 0xd7, 0xf,
		0xe6, 0x58, 0xe, 0x95, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
		0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x65, 0x66,
		0x69, 0x6c, 0x65, 0x2e, 0x63, 0x0, 0x69, 0x86, 0x74, 0x41,
		0xb8, 0xcc, 0x9a, 0x62, 0x1a, 0xf3, 0x24, 0x13, 0xfc, 0x63,
		0xda, 0x80, 0x99, 0x37, 0x64, 0xf4, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6f, 0x73,
		0x69, 0x6e, 0x73, 0x74, 0x2e, 0x63, 0x0, 0x53, 0x14, 0x33,
		0x31, 0x3e, 0xe3, 0x6c, 0x7, 0xeb, 0x21, 0xc0, 0x2f, 0x31,
		0x15, 0xcb, 0x7a, 0x37, 0x48, 0x6c, 0x79, 0x31, 0x30, 0x30,
		0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70,
		0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x0, 0x8f, 0xcf,
		0xe7, 0xe2, 0x6e, 0x3f, 0xf1, 0x74, 0x96, 0xb8, 0x40, 0xf5,
		0xd6, 0x3c, 0x75, 0x78, 0x3a, 0xff, 0x81, 0x62, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
		0x71, 0x75, 0x6f, 0x74, 0x61, 0x2e, 0x63, 0x0, 0xe5, 0x90,
		0x99, 0x6c, 0xa4, 0xb8, 0x57, 0x4a, 0xb1, 0xe4, 0x18, 0x5d,
		0x57, 0x77, 0x56, 0x66, 0x4a, 0xd2, 0x49, 0x5f, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
		0x71, 0x75, 0x6f, 0x74, 0x61, 0x2e, 0x68, 0x0, 0x2d, 0x7,
		0x67, 0xa1, 0x9a, 0xb7, 0xc3, 0xa4, 0x21, 0xcd, 0xba, 0x6a,
		0x3, 0x49, 0x20, 0x43, 0x67, 0xc2, 0x2c, 0x81, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
		0x72, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x63, 0x0, 0xf5, 0x4a,
		0xe9, 0xb0, 0x63, 0xbb, 0x73, 0x71, 0x2f, 0xcf, 0xc1, 0xc6,
		0x83, 0x2e, 0x2a, 0x50, 0xf6, 0x2a, 0x97, 0xe7, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
		0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x63, 0x0, 0x12,
		0x64, 0x44, 0x67, 0x64, 0x7d, 0x51, 0x39, 0x4a, 0x1, 0xf9,
		0xfa, 0x60, 0x37, 0x62, 0x98, 0x18, 0x54, 0x66, 0xfd, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74,
		0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x0,
		0xed, 0x8, 0x18, 0xe6, 0xf6, 0x5f, 0x27, 0x28, 0x2d, 0xc7,
		0xb1, 0xc1, 0x90, 0xec, 0x18, 0x8c, 0x89, 0x33, 0x0, 0x2b,
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73,
		0x74, 0x5f, 0x73, 0x71, 0x6c, 0x6c, 0x6f, 0x67, 0x2e, 0x63,
		0x0, 0x4a, 0xa6, 0x8b, 0x7c, 0x42, 0x93, 0x23, 0xb8, 0xee,
		0xbe, 0x6c, 0x9c, 0x2d, 0x7, 0xfc, 0x66, 0xd, 0x8d, 0x47,
		0xc9, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65,
		0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x63, 0x0,
		0xd4, 0xc9, 0x2, 0xb5, 0xea, 0x11, 0x1a, 0xd5, 0x8a, 0x73,
		0x71, 0x12, 0xc2, 0x8f, 0x0, 0x38, 0x43, 0x4c, 0x85, 0xc0,
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73,
		0x74, 0x5f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6c, 0x6f, 0x63,
		0x6b, 0x2e, 0x63, 0x0, 0x93, 0x6f, 0xca, 0xd0, 0xc5, 0x6f,
		0x6b, 0xc8, 0x58, 0x9, 0x74, 0x2f, 0x6a, 0xe1, 0xc1, 0xee,
		0xb8, 0xb7, 0xd2, 0xf1, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34,
		0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x79, 0x73, 0x63,
		0x61, 0x6c, 0x6c, 0x2e, 0x63, 0x0, 0x7c, 0x8, 0x73, 0xc1,
		0x6d, 0x84, 0x32, 0x2, 0xf3, 0xe, 0x2d, 0xb9, 0x45, 0x9f,
		0xa2, 0x99, 0x75, 0xea, 0x5e, 0x68, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x63,
		0x6c, 0x76, 0x61, 0x72, 0x2e, 0x63, 0x0, 0x12, 0x19, 0x19,
		0xc, 0x3, 0x0, 0xfd, 0x5e, 0xc7, 0xa3, 0xc5, 0x84, 0x8,
		0xf3, 0x38, 0x43, 0xd2, 0xe, 0xee, 0x15, 0x31, 0x30, 0x30,
		0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x74,
		0x68, 0x72, 0x65, 0x61, 0x64, 0x2e, 0x63, 0x0, 0x2f, 0x93,
		0x63, 0xb7, 0x50, 0x1e, 0x51, 0x19, 0x81, 0xfe, 0x32, 0x83,
		0x1f, 0xf2, 0xe8, 0xfd, 0x2f, 0x30, 0xc4, 0x93, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f,
		0x76, 0x66, 0x73, 0x2e, 0x63, 0x0, 0xfc, 0xd5, 0x77, 0x43,
		0x9c, 0xfd, 0x6c, 0x72, 0xdd, 0xe4, 0x83, 0x58, 0x92, 0x14,
		0x20, 0xcf, 0x6e, 0xf1, 0xf8, 0x6d, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x76, 0x66,
		0x73, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x63, 0x0, 0xa,
		0xac, 0xc0, 0x1f, 0xe4, 0x2e, 0x77, 0xfe, 0xb8, 0x58, 0xe4,
		0xbe, 0xd0, 0xcb, 0x7e, 0x4, 0xa4, 0x35, 0xb2, 0x10, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x65, 0x73, 0x74,
		0x5f, 0x77, 0x73, 0x64, 0x2e, 0x63, 0x0, 0x99, 0xe4, 0xa0,
		0x56, 0x58, 0x1f, 0x58, 0xf4, 0x53, 0x6f, 0xdb, 0x5a, 0x5d,
		0xf7, 0x5c, 0x74, 0x69, 0x8a, 0x81, 0x62, 0x31, 0x30, 0x30,
		0x36, 0x34, 0x34, 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x69,
		0x7a, 0x65, 0x2e, 0x63, 0x0, 0xfa, 0xea, 0x5f, 0x26, 0xc7,
		0x9c, 0x5e, 0x18, 0x8f, 0xa8, 0x7f, 0x2f, 0xdf, 0x6f, 0xf7,
		0x6a, 0x7a, 0x60, 0x6, 0xc5, 0x31, 0x30, 0x30, 0x36, 0x34,
		0x34, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x2e,
		0x63, 0x0, 0xf1, 0xff, 0x76, 0x6e, 0x20, 0x2a, 0x45, 0x18,
		0xec, 0x10, 0xe5, 0x27, 0x12, 0xc, 0xd3, 0xe, 0x83, 0xfb,
		0xd0, 0x34, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x75,
		0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x0, 0x3a, 0xb1,
		0xab, 0x2a, 0x4b, 0x65, 0xda, 0x3f, 0x19, 0x8c, 0x15, 0x84,
		0xd5, 0x4d, 0x36, 0xf1, 0x8c, 0xa1, 0x21, 0x4a, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x75, 0x74, 0x66, 0x2e, 0x63,
		0x0, 0x6d, 0x5b, 0x1b, 0xfe, 0x40, 0xc, 0x37, 0x48, 0xaa,
		0x70, 0xa3, 0xb2, 0xfd, 0x5e, 0xe, 0xac, 0x5f, 0xc0, 0x4d,
		0xe2, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x75, 0x74,
		0x69, 0x6c, 0x2e, 0x63, 0x0, 0xd8, 0x3a, 0x63, 0x1, 0x5f,
		0xd8, 0x7d, 0xcc, 0x4f, 0xb4, 0x41, 0x66, 0xfa, 0xbf, 0x2e,
		0x9b, 0xc9, 0x67, 0x1e, 0xb8, 0x31, 0x30, 0x30, 0x36, 0x34,
		0x34, 0x20, 0x76, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x2e, 0x63,
		0x0, 0x4a, 0xfb, 0x2c, 0xca, 0x64, 0xdd, 0x60, 0x76, 0x11,
		0x22, 0x2c, 0x7, 0x93, 0x2d, 0x12, 0xea, 0xcf, 0xa, 0x2c,
		0x22, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x76, 0x64,
		0x62, 0x65, 0x2e, 0x63, 0x0, 0xf3, 0x43, 0xe1, 0x3d, 0x4e,
		0x91, 0x78, 0x4b, 0x15, 0x88, 0x10, 0xc5, 0xb7, 0xd4, 0x46,
		0x84, 0xdf, 0xbf, 0xa2, 0xa5, 0x31, 0x30, 0x30, 0x36, 0x34,
		0x34, 0x20, 0x76, 0x64, 0x62, 0x65, 0x2e, 0x68, 0x0, 0xfa,
		0x7b, 0x31, 0xb7, 0x27, 0xa, 0x90, 0xd4, 0xf6, 0x37, 0x36,
		0x5a, 0xfc, 0xc9, 0xbd, 0xa1, 0xd1, 0xb1, 0xe1, 0xd6, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x76, 0x64, 0x62, 0x65,
		0x49, 0x6e, 0x74, 0x2e, 0x68, 0x0, 0x3a, 0x5b, 0x40, 0x28,
		0xbb, 0xd6, 0xc9, 0x56, 0x10, 0xd7, 0xc, 0xce, 0x3, 0x69,
		0xdf, 0xcd, 0x60, 0x7a, 0xa9, 0x0, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x76, 0x64, 0x62, 0x65, 0x61, 0x70, 0x69,
		0x2e, 0x63, 0x0, 0x7c, 0x86, 0x1e, 0x2d, 0x47, 0x21, 0x8c,
		0x91, 0x63, 0x31, 0x77, 0x77, 0xc3, 0x7, 0x21, 0x99, 0xe9,
		0xb4, 0x2, 0x80, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x76, 0x64, 0x62, 0x65, 0x61, 0x75, 0x78, 0x2e, 0x63, 0x0,
		0x2c, 0x42, 0x69, 0xa5, 0x9e, 0x6d, 0xbc, 0xe8, 0x67, 0x1c,
		0x47, 0x4f, 0x34, 0x61, 0x90, 0xbe, 0x2a, 0xe, 0x18, 0x51,
		0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x76, 0x64, 0x62,
		0x65, 0x62, 0x6c, 0x6f, 0x62, 0x2e, 0x63, 0x0, 0x2e, 0x8f,
		0xd8, 0xee, 0x74, 0x47, 0xe6, 0x46, 0x46, 0xe3, 0x49, 0x4b,
		0x4c, 0x4, 0x1d, 0x3a, 0x4a, 0xbb, 0x8, 0x85, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x76, 0x64, 0x62, 0x65, 0x6d,
		0x65, 0x6d, 0x2e, 0x63, 0x0, 0x8f, 0xc2, 0x22, 0xe2, 0xde,
		0x20, 0x50, 0x14, 0x50, 0xec, 0xea, 0x9d, 0x4e, 0xbf, 0xaa,
		0xc9, 0x81, 0x4a, 0xae, 0x59, 0x31, 0x30, 0x30, 0x36, 0x34,
		0x34, 0x20, 0x76, 0x64, 0x62, 0x65, 0x73, 0x6f, 0x72, 0x74,
		0x2e, 0x63, 0x0, 0xfd, 0xfc, 0x4a, 0x79, 0xdd, 0xc9, 0x6e,
		0x59, 0x9b, 0x1b, 0xe, 0xeb, 0xac, 0xbd, 0xb8, 0x45, 0xc6,
		0x38, 0x13, 0xb2, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
		0x76, 0x64, 0x62, 0x65, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e,
		0x63, 0x0, 0x35, 0x62, 0x77, 0xe8, 0xd2, 0x3b, 0xca, 0xdb,
		0x67, 0x6b, 0x59, 0xd1, 0xa4, 0xdc, 0xf8, 0x42, 0xfd, 0xc4,
		0xc9, 0x72, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x76,
		0x74, 0x61, 0x62, 0x2e, 0x63, 0x0, 0x95, 0x82, 0x2, 0xc3,
		0x1e, 0x24, 0x15, 0xb, 0x60, 0xf1, 0xa, 0x8a, 0xf, 0x74,
		0x41, 0xaf, 0xac, 0x3f, 0xbb, 0x1c, 0x31, 0x30, 0x30, 0x36,
		0x34, 0x34, 0x20, 0x77, 0x61, 0x6c, 0x2e, 0x63, 0x0, 0xe6,
		0x42, 0xea, 0x21, 0x5, 0xb5, 0xc5, 0x4a, 0xf3, 0x5, 0x88,
		0x9, 0x62, 0x69, 0xab, 0x75, 0xcb, 0xef, 0x8f, 0xf2, 0x31,
		0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x77, 0x61, 0x6c, 0x2e,
		0x68, 0x0, 0x9, 0x25, 0x46, 0x35, 0x4b, 0x34, 0xc0, 0xab,
		0x3d, 0x20, 0x5, 0x6a, 0x7f, 0x8a, 0x8a, 0x52, 0xe4, 0xd0,
		0xb5, 0xf5, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x77,
		0x61, 0x6c, 0x6b, 0x65, 0x72, 0x2e, 0x63, 0x0, 0xe7, 0x1e,
		0xd2, 0xac, 0x48, 0x4c, 0x91, 0x6c, 0x1c, 0xc1, 0x0, 0x7e,
		0x5e, 0x5, 0xda, 0x47, 0x1c, 0xb4, 0x95, 0x99, 0x31, 0x30,
		0x30, 0x36, 0x34, 0x34, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65,
		0x2e, 0x63, 0x0, 0xe6, 0x14, 0xf4, 0xa6, 0xd8, 0x64, 0xe7,
		0xe, 0xc4, 0x32, 0x8d, 0xb, 0xdb, 0x25, 0x4e, 0x3a, 0xc9,
		0xf0, 0xd2, 0x87,
	}
	obj := &SortReadObject{
		t:    plumbing.TreeObject,
		h:    plumbing.ZeroHash,
		cont: cont,
		sz:   5313,
	}

	expected := &Tree{
		Entries: []TreeEntry{
			{
				Name: "alter.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("a49d3349d7e23fb581194f4cb59ac0d51b21f78"),
			},
			{
				Name: "analyze.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("9a3e9597dbb32077c91d969d22c6273f702ac"),
			},
			{
				Name: "attach.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("b8e12199b57de811eae0d06142d5ac4fd430b1d8"),
			},
			{
				Name: "auth.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("d38bb836a784fbfab6ab7b3d4e6dd43edc41fa7"),
			},
			{
				Name: "backup.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("252f61cfcaa8fcf3137e8ed6847dcfe1dc1de54"),
			},
			{
				Name: "bitvec.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("52184aa964ce1898f35d1b3d87871c2def4c53d"),
			},
			{
				Name: "btmutex.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("d87d4d5feeb6307aecdc9a83111489ab30c678c3"),
			},
			{
				Name: "btree.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("3ca6583e3c8e3120f973e0e9c4536258b26439"),
			},
			{
				Name: "btree.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("ace0f8cd217770a2f66b2eb871bbc5fdc6fc2b68"),
			},
			{
				Name: "btreeInt.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("ce3c5493f8cad0bc548ae8e44e512831d8fac431"),
			},
			{
				Name: "build.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("3c91cdcfdb7b17cbc2d5c29571a9827de071e6"),
			},
			{
				Name: "callback.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("d4c65cb924580296ad069a04bf9c9e9534ecaa7"),
			},
			{
				Name: "complete.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("9e914085c046ed3bf6f4485220692dca1743c5"),
			},
			{
				Name: "crypto.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("2551e6ba239f85a357796a8dda8ca3e297093f8"),
			},
			{
				Name: "crypto.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("f71f532cdc448fa1dd5c6eff5fbd33a9155aa97"),
			},
			{
				Name: "crypto_cc.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("537df7e3b36ab5cfdd6fca4028ebcae18687d64d"),
			},
			{
				Name: "crypto_impl.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("a58927c76ef6205677be5c1a8e80c98356b3a9d3"),
			},
			{
				Name: "crypto_libtomcrypt.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("1a3383e01a72111c3f6619222b065f4bd1be1"),
			},
			{
				Name: "crypto_openssl.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("d019813b476c52d020e2c0acd524e9ea3dfb9fe"),
			},
			{
				Name: "ctime.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("60595ff88d92f78264fbd9df9afea16ae86ff"),
			},
			{
				Name: "date.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("758dd7c89bca3937a9d706ea982ce3acf11d183"),
			},
			{
				Name: "delete.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("634e115563ae12ba6558ccc512aed631c066bad8"),
			},
			{
				Name: "expr.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("66397e078ae48b2e7175e3385677819722ddd6c"),
			},
			{
				Name: "fault.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("c328c4f936edf96712dbe73a07662f0a26b1da"),
			},
			{
				Name: "fkey.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("ac35bc194cdeb127989b94035cee06f57376f5e"),
			},
			{
				Name: "func.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("c02f96adad5bce9ac83d35ff469d6f6d43be5"),
			},
			{
				Name: "global.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("7b2cf2130e0d1a7b889d844ccc828f7b67bf9"),
			},
			{
				Name: "hash.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("e81dcf95e43848fa7086b7f781c090adc7e6ca8e"),
			},
			{
				Name: "hash.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("82b7c58c719b547e101742aa951739ff2eee7"),
			},
			{
				Name: "hwtime.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("b8bc5a295be3fac8351fa9f08a77579d59c9a8e4"),
			},
			{
				Name: "insert.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("9a5661f59a72952be6c167a0c2db159b91b71fae"),
			},
			{
				Name: "journal.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("fed27be3e38055d2204395cde6ffc94589fbf5e8"),
			},
			{
				Name: "legacy.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("94649ae75ab9385108dd887af0759289fb23cb"),
			},
			{
				Name: "lempar.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("2afaa6cea6d829602c2786c1f8a37f567cf6fd53"),
			},
			{
				Name: "loadext.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("cdcf6a93b8c4f914b9424ef14cb4a3a37eca1"),
			},
			{
				Name: "main.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("39f6421e681277cc3dba09abe7cf790d528f5c3"),
			},
			{
				Name: "malloc.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("35a44e5f61c2e44c481c6251bdaae7acda4deb"),
			},
			{
				Name: "mem0.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("db6667d6a955a696df6289b49178969343aa"),
			},
			{
				Name: "mem1.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("3578496f3337b231dfb53cc2e1c6b323d791e"),
			},
			{
				Name: "mem2.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("26448ea8aae0366af0541afea479b42f4a69b5a"),
			},
			{
				Name: "mem3.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("1a1b791f28f8cf3ce4f9a35cdad7b7107568c715"),
			},
			{
				Name: "mem5.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("783cef6176c59cbf3091463195a1a54f4e42e8"),
			},
			{
				Name: "memjournal.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("5725948f65d427b7f7f929aca3ff224b1753df"),
			},
			{
				Name: "mutex.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("b567e7c27ef241086afe0f69666e27bf598a59"),
			},
			{
				Name: "mutex.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("9788122527789a9c36c24d41f6114d64c06db3"),
			},
			{
				Name: "mutex_noop.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("456e82a25e271b614e7f4f83c228553b7fa158"),
			},
			{
				Name: "mutex_unix.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("eca7295831c2f0ee48ba54d062914d6a1dd8ebe"),
			},
			{
				Name: "mutex_w32.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("27d1af5bd331bdb973f6145b74f72b67cfc46e"),
			},
			{
				Name: "notify.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("fcab5bfaf0198d3de93fa88b5eae9e96ca3c8e8"),
			},
			{
				Name: "os.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("be2ea4cfc0195993a340c92aeddf1be4b8ed73a"),
			},
			{
				Name: "os.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("7a2ddd17f771f98ff8ccd6f033bdacc5e9f6c"),
			},
			{
				Name: "os_common.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("f6c3e7ff89463086401822f481e7e3b87b2c78c7"),
			},
			{
				Name: "os_unix.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("abc23a452e72f71c76afa9983c3ad9d425616c6d"),
			},
			{
				Name: "os_win.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("aeb08814b3dabe81b84cda918582b0ffd86e487"),
			},
			{
				Name: "pager.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("61727faa9cf3d566265be7eec5b2a35f6a4bc9f"),
			},
			{
				Name: "pager.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("6f659136e27679da43a2e39e1b68637ecadcf68"),
			},
			{
				Name: "parse.y", Mode: filemode.Regular,
				Hash: plumbing.NewHash("8310b26989b05bed1e1b3da80f5c0a52e9ad1d2"),
			},
			{
				Name: "pcache.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("482a188bee1991bc8adac96a193a53e5462a8c10"),
			},
			{
				Name: "pcache.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("f4d4ad71c1d78c6dabde25215cd415a76148ca"),
			},
			{
				Name: "pcache1.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("4147d2eff55bdd9ff7c686c60181020166c5f50"),
			},
			{
				Name: "pragma.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("22977169617d4922b3993f769d90fa7bc441ea50"),
			},
			{
				Name: "prepare.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("d78d83cbd87897f573303f9f57ab8de024a6e3f8"),
			},
			{
				Name: "printf.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("9f68d24ffdc9f3d427f80a8239a7fa3a98aecbd"),
			},
			{
				Name: "random.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("234ebdf658f436cc7c68f027c48be1b9ba34e98"),
			},
			{
				Name: "resolve.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("91efcaa1a16bfc98fb35d85cad156b93533e4e6"),
			},
			{
				Name: "rowset.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("5761f98550b176cce11dcbcec93899a075bb64fd"),
			},
			{
				Name: "select.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("f3f149963955b8ed0c9fe6e1eec836c1a5294b4"),
			},
			{
				Name: "shell.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("1be2871fed9a1fdf1df7198e1125360ecba76cc"),
			},
			{
				Name: "sqlcipher.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("82753095cd1723c5ff4f1115e4975591ee34f5ce"),
			},
			{
				Name: "sqlite.h.in", Mode: filemode.Regular,
				Hash: plumbing.NewHash("668823175de5b6ad378fdbc3818b6ab4fbf8e"),
			},
			{
				Name: "sqlite3.rc", Mode: filemode.Regular,
				Hash: plumbing.NewHash("969876da1e57143de0b4d1c7629fd3356f2e1c96"),
			},
			{
				Name: "sqlite3ext.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("928bb3bad9dd643c301dd2b0ac22287a81284884"),
			},
			{
				Name: "sqliteInt.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("5950f237d9f9f2d3ef6bd8be342dcf6489225142"),
			},
			{
				Name: "sqliteLimit.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("c7aee53cebca94da51e71a822ea5a6deb9385df"),
			},
			{
				Name: "status.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("28349e6d3d2088e003b76f8a8954faec5930ba"),
			},
			{
				Name: "table.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("26bbfb4f456c42982529ea1a63a01751dd3ee95a"),
			},
			{
				Name: "tclsqlite.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("f1bb2921dac68a4f1c8e15cf566b233e92a519f"),
			},
			{
				Name: "test1.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("a638e480addf14439cdfa4ee164dc31b79f8bcac"),
			},
			{
				Name: "test2.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("d130e9d01b7024a5ec6d73592ee4d1fb02cfdb4"),
			},
			{
				Name: "test3.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("e3ed31c814fe3621cebbf51d114518d4fac"),
			},
			{
				Name: "test4.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("a6375c7cc43f6caab7e959533e3db1ff75ae4"),
			},
			{
				Name: "test5.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("303d125b22628423d986f71e27c7cf714a745a6"),
			},
			{
				Name: "test6.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("c151ea42989bbe24ee4b9a4be378b4f636db641"),
			},
			{
				Name: "test7.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("3cd4a224d7e8e16bd7cbe49e2d3e94ce9b17bd76"),
			},
			{
				Name: "test8.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("c5739332d46e57121da27c3e88fde75aeb8710f7"),
			},
			{
				Name: "test9.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("e5993e8ff78f61c2435b6f97a3b463e227c767ac"),
			},
			{
				Name: "test_async.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("b0b943185bfc23c17fd08f55768cac12a9f56951"),
			},
			{
				Name: "test_autoext.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("b513f3173a2176e2d9fcaa99193036bfc37e91"),
			},
			{
				Name: "test_backup.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("e967424a29f738aecfdac578e9b87a4c4ae8d7f"),
			},
			{
				Name: "test_btree.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("db72889b2afb6272828dda866dccf122a49a7299"),
			},
			{
				Name: "test_config.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("534727a08042b6cad67e267e87b43a41a73b299"),
			},
			{
				Name: "test_demovfs.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("63762771d9e28f4b3451bbbddf88d1a9120f8"),
			},
			{
				Name: "test_devsym.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("21f0f684d861116770defcdecd532ba3eeaba975"),
			},
			{
				Name: "test_fs.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("478cad80b16a909b23bd3c2dad8b449a74587a1"),
			},
			{
				Name: "test_func.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("6f9bb03dc88a21d658bf9999baf66dc1d52ebc54"),
			},
			{
				Name: "test_hexio.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("b2b5ce730ab7fa80d2d0cc38c77275593ebdbb"),
			},
			{
				Name: "test_init.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("e3724d8be314db9eea84b9ddfc8a8beee2291"),
			},
			{
				Name: "test_intarray.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("f5c3d9e459a16567347e43a92117999697b93"),
			},
			{
				Name: "test_intarray.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("691337d1aed63715d62e76266ff3b508b1a34"),
			},
			{
				Name: "test_journal.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("e8701a4eeadb8ead169d606407d54a898592d70"),
			},
			{
				Name: "test_loadext.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("1137e3a9aae9296b8289f6c3daa61f0d070f55a"),
			},
			{
				Name: "test_malloc.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("cf98a8fb2182c0baf5ad57979b675bb707a93b0"),
			},
			{
				Name: "test_multiplex.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("624541b32a10d21a2fd1a35ee6632bdac552d41"),
			},
			{
				Name: "test_multiplex.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("b7e1afea5fd78b8758265f84c81612cbd25baf"),
			},
			{
				Name: "test_mutex.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("c9b4a29ab75c77ea5f36b5193256d7fe658e95"),
			},
			{
				Name: "test_onefile.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("69867441b8cc9a621af32413fc63da80993764f4"),
			},
			{
				Name: "test_osinst.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("531433313ee36c7eb21c02f3115cb7a37486c79"),
			},
			{
				Name: "test_pcache.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("8fcfe7e26e3ff17496b840f5d63c75783aff8162"),
			},
			{
				Name: "test_quota.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("e590996ca4b8574ab1e4185d577756664ad2495f"),
			},
			{
				Name: "test_quota.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("2d767a19ab7c3a421cdba6a349204367c22c81"),
			},
			{
				Name: "test_rtree.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("f54ae9b063bb73712fcfc1c6832e2a50f62a97e7"),
			},
			{
				Name: "test_schema.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("12644467647d51394a1f9fa60376298185466fd"),
			},
			{
				Name: "test_server.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("ed818e6f65f27282dc7b1c190ec188c893302b"),
			},
			{
				Name: "test_sqllog.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("4aa68b7c429323b8eebe6c9c2d7fc66d8d47c9"),
			},
			{
				Name: "test_stat.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("d4c92b5ea111ad58a737112c28f038434c85c0"),
			},
			{
				Name: "test_superlock.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("936fcad0c56f6bc8589742f6ae1c1eeb8b7d2f1"),
			},
			{
				Name: "test_syscall.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("7c873c16d84322f3e2db9459fa29975ea5e68"),
			},
			{
				Name: "test_tclvar.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("121919c30fd5ec7a3c5848f33843d2eee15"),
			},
			{
				Name: "test_thread.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("2f9363b7501e511981fe32831ff2e8fd2f30c493"),
			},
			{
				Name: "test_vfs.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("fcd577439cfd6c72dde48358921420cf6ef1f86d"),
			},
			{
				Name: "test_vfstrace.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("aacc01fe42e77feb858e4bed0cb7e4a435b210"),
			},
			{
				Name: "test_wsd.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("99e4a056581f58f4536fdb5a5df75c74698a8162"),
			},
			{
				Name: "tokenize.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("faea5f26c79c5e188fa87f2fdf6ff76a7a606c5"),
			},
			{
				Name: "trigger.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("f1ff766e202a4518ec10e52712cd3e83fbd034"),
			},
			{
				Name: "update.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("3ab1ab2a4b65da3f198c1584d54d36f18ca1214a"),
			},
			{
				Name: "utf.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("6d5b1bfe40c3748aa70a3b2fd5eeac5fc04de2"),
			},
			{
				Name: "util.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("d83a6315fd87dcc4fb44166fabf2e9bc9671eb8"),
			},
			{
				Name: "vacuum.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("4afb2cca64dd607611222c7932d12eacfa2c22"),
			},
			{
				Name: "vdbe.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("f343e13d4e91784b158810c5b7d44684dfbfa2a5"),
			},
			{
				Name: "vdbe.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("fa7b31b727a90d4f637365afcc9bda1d1b1e1d6"),
			},
			{
				Name: "vdbeInt.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("3a5b4028bbd6c95610d7cce369dfcd607aa90"),
			},
			{
				Name: "vdbeapi.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("7c861e2d47218c9163317777c372199e9b4280"),
			},
			{
				Name: "vdbeaux.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("2c4269a59e6dbce8671c474f346190be2ae1851"),
			},
			{
				Name: "vdbeblob.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("2e8fd8ee7447e64646e3494b4c41d3a4abb885"),
			},
			{
				Name: "vdbemem.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("8fc222e2de20501450ecea9d4ebfaac9814aae59"),
			},
			{
				Name: "vdbesort.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("fdfc4a79ddc96e599b1beebacbdb845c63813b2"),
			},
			{
				Name: "vdbetrace.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("356277e8d23bcadb676b59d1a4dcf842fdc4c972"),
			},
			{
				Name: "vtab.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("95822c31e2415b60f1a8af7441afac3fbb1c"),
			},
			{
				Name: "wal.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("e642ea215b5c54af358896269ab75cbef8ff2"),
			},
			{
				Name: "wal.h", Mode: filemode.Regular,
				Hash: plumbing.NewHash("92546354b34c0ab3d2056a7f8a8a52e4d0b5f5"),
			},
			{
				Name: "walker.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("e71ed2ac484c916c1cc107e5e5da471cb49599"),
			},
			{
				Name: "where.c", Mode: filemode.Regular,
				Hash: plumbing.NewHash("e614f4a6d864e7ec4328dbdb254e3ac9f0d287"),
			},
		},
	}

	var obtained Tree
	err := obtained.Decode(obj)
	s.Require().NoError(err)

	if !s.Equal(expected.Entries, obtained.Entries) {
		for i := 0; i < len(expected.Entries); i++ {
			s.Equal(expected.Entries[i], obtained.Entries[i])
		}
	}
}

type rawTreeEntry struct {
	mode string
	name string
	hash []byte
}

func encodeRawTreeEntries(entries ...rawTreeEntry) []byte {
	var buf bytes.Buffer
	for _, e := range entries {
		buf.WriteString(e.mode)
		buf.WriteByte(' ')
		buf.WriteString(e.name)
		buf.WriteByte(0)
		buf.Write(e.hash)
	}
	return buf.Bytes()
}

func newRawTreeObject(t *testing.T, body []byte) *plumbing.MemoryObject {
	t.Helper()

	obj := &plumbing.MemoryObject{}
	obj.SetType(plumbing.TreeObject)
	w, err := obj.Writer()
	require.NoError(t, err)
	_, err = w.Write(body)
	require.NoError(t, err)
	require.NoError(t, w.Close())

	return obj
}

func decodeRawTree(t *testing.T, body []byte) (*Tree, error) {
	t.Helper()

	obj := newRawTreeObject(t, body)

	var tree Tree
	err := tree.Decode(obj)
	return &tree, err
}

func TestTreeDecodeRejectsMalformedEntries(t *testing.T) {
	t.Parallel()

	hash := bytes.Repeat([]byte{0xAA}, 20)

	cases := []struct {
		name string
		body []byte
	}{
		{
			name: "short object id",
			body: append(append([]byte{}, []byte("100644 foo\x00")...), hash[:19]...),
		},
		{
			name: "empty filename",
			body: encodeRawTreeEntries(rawTreeEntry{"100644", "", hash}),
		},
		{
			name: "missing filename terminator",
			body: append(append([]byte{}, []byte("100644 foo")...), hash...),
		},
		{
			name: "malformed mode",
			body: encodeRawTreeEntries(rawTreeEntry{"10064x", "foo", hash}),
		},
		{
			name: "trailing partial entry",
			body: append(encodeRawTreeEntries(rawTreeEntry{"100644", "foo", hash}), []byte("100644 trailing")...),
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			tree, err := decodeRawTree(t, tc.body)
			require.ErrorIs(t, err, ErrMalformedTree)
			assert.NotNil(t, tree)
		})
	}
}

func TestTreeDecodeCanonicalizesModes(t *testing.T) {
	t.Parallel()

	hash := bytes.Repeat([]byte{0xAA}, 20)
	body := encodeRawTreeEntries(
		rawTreeEntry{"100664", "regular", hash},
		rawTreeEntry{"100111", "executable", hash},
		rawTreeEntry{"120777", "symlink", hash},
		rawTreeEntry{"040755", "directory", hash},
		rawTreeEntry{"42", "submodule", hash},
	)

	tree, err := decodeRawTree(t, body)
	require.NoError(t, err)
	require.Len(t, tree.Entries, 5)
	assert.Equal(t, filemode.Regular, tree.Entries[0].Mode)
	assert.Equal(t, filemode.Executable, tree.Entries[1].Mode)
	assert.Equal(t, filemode.Symlink, tree.Entries[2].Mode)
	assert.Equal(t, filemode.Dir, tree.Entries[3].Mode)
	assert.Equal(t, filemode.Submodule, tree.Entries[4].Mode)
}

func TestTreeDecodeEmptyClearsExistingEntries(t *testing.T) {
	t.Parallel()

	obj := &plumbing.MemoryObject{}
	obj.SetType(plumbing.TreeObject)

	tree := &Tree{
		Entries: []TreeEntry{
			{Name: "stale", Mode: filemode.Regular},
		},
	}

	require.NoError(t, tree.Decode(obj))
	assert.Empty(t, tree.Entries)
}

func TestTreeDecodeClearsExistingState(t *testing.T) {
	t.Parallel()

	store := memory.NewStorage()
	obj := &plumbing.MemoryObject{}
	obj.SetType(plumbing.TreeObject)

	tree := &Tree{
		Hash: plumbing.NewHash("1111111111111111111111111111111111111111"),
		Entries: []TreeEntry{
			{Name: "stale", Mode: filemode.Regular},
		},
		s:             store,
		t:             map[string]*Tree{"stale": {}},
		entriesSorted: false,
	}

	require.NoError(t, tree.Decode(obj))
	assert.Equal(t, obj.Hash(), tree.Hash)
	assert.Empty(t, tree.Entries)
	assert.Same(t, store, tree.s)
	assert.Nil(t, tree.t)
	assert.True(t, tree.entriesSorted)
}

func TestTreeDecodeClearsPathCache(t *testing.T) {
	t.Parallel()

	store := memory.NewStorage()
	oldFileHash := bytes.Repeat([]byte{0xAA}, 20)
	newFileHash := bytes.Repeat([]byte{0xBB}, 20)

	oldSubtree := newRawTreeObject(t, encodeRawTreeEntries(
		rawTreeEntry{"100644", "old", oldFileHash},
	))
	oldSubtreeHash, err := store.SetEncodedObject(oldSubtree)
	require.NoError(t, err)

	oldDir := newRawTreeObject(t, encodeRawTreeEntries(
		rawTreeEntry{"40000", "sub", oldSubtreeHash.Bytes()},
	))
	oldDirHash, err := store.SetEncodedObject(oldDir)
	require.NoError(t, err)

	oldRoot := newRawTreeObject(t, encodeRawTreeEntries(
		rawTreeEntry{"40000", "dir", oldDirHash.Bytes()},
	))

	newSubtree := newRawTreeObject(t, encodeRawTreeEntries(
		rawTreeEntry{"100644", "new", newFileHash},
	))
	newSubtreeHash, err := store.SetEncodedObject(newSubtree)
	require.NoError(t, err)

	newDir := newRawTreeObject(t, encodeRawTreeEntries(
		rawTreeEntry{"40000", "sub", newSubtreeHash.Bytes()},
	))
	newDirHash, err := store.SetEncodedObject(newDir)
	require.NoError(t, err)

	newRoot := newRawTreeObject(t, encodeRawTreeEntries(
		rawTreeEntry{"40000", "dir", newDirHash.Bytes()},
	))

	tree := &Tree{s: store}
	require.NoError(t, tree.Decode(oldRoot))
	got, err := tree.FindEntry("dir/sub/old")
	require.NoError(t, err)
	assert.Equal(t, 0, got.Hash.Compare(oldFileHash))

	require.NoError(t, tree.Decode(newRoot))
	got, err = tree.FindEntry("dir/sub/new")
	require.NoError(t, err)
	assert.Equal(t, 0, got.Hash.Compare(newFileHash))
}

func TestTreeFindEntryDuplicates(t *testing.T) {
	t.Parallel()

	hashA := bytes.Repeat([]byte{0xAA}, 20)
	hashB := bytes.Repeat([]byte{0xBB}, 20)
	hashC := bytes.Repeat([]byte{0xCC}, 20)

	cases := []struct {
		name      string
		body      []byte
		lookup    string
		wantHash  []byte
		wantMode  filemode.FileMode
		wantCount int
	}{
		{
			name: "two regular files with same name",
			body: encodeRawTreeEntries(
				rawTreeEntry{"100644", "foo", hashA},
				rawTreeEntry{"100644", "foo", hashB},
			),
			lookup:    "foo",
			wantHash:  hashA,
			wantMode:  filemode.Regular,
			wantCount: 2,
		},
		{
			name: "triplicate regular file",
			body: encodeRawTreeEntries(
				rawTreeEntry{"100644", "foo", hashA},
				rawTreeEntry{"100644", "foo", hashB},
				rawTreeEntry{"100644", "foo", hashC},
			),
			lookup:    "foo",
			wantHash:  hashA,
			wantMode:  filemode.Regular,
			wantCount: 3,
		},
		{
			name: "file shadows later directory of same name",
			body: encodeRawTreeEntries(
				rawTreeEntry{"100644", "foo", hashA},
				rawTreeEntry{"40000", "foo", hashB},
			),
			lookup:    "foo",
			wantHash:  hashA,
			wantMode:  filemode.Regular,
			wantCount: 2,
		},
		{
			name: "directory shadows later file of same name",
			body: encodeRawTreeEntries(
				rawTreeEntry{"40000", "foo", hashA},
				rawTreeEntry{"100644", "foo", hashB},
			),
			lookup:    "foo",
			wantHash:  hashA,
			wantMode:  filemode.Dir,
			wantCount: 2,
		},
		{
			name: "duplicate is not first entry",
			body: encodeRawTreeEntries(
				rawTreeEntry{"100644", "bar", hashC},
				rawTreeEntry{"100644", "foo", hashA},
				rawTreeEntry{"100644", "foo", hashB},
			),
			lookup:    "foo",
			wantHash:  hashA,
			wantMode:  filemode.Regular,
			wantCount: 3,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			obj := &plumbing.MemoryObject{}
			obj.SetType(plumbing.TreeObject)
			w, err := obj.Writer()
			require.NoError(t, err)
			_, err = w.Write(tc.body)
			require.NoError(t, err)
			require.NoError(t, w.Close())

			var tree Tree
			require.NoError(t, tree.Decode(obj))
			require.Len(t, tree.Entries, tc.wantCount,
				"all duplicate entries must be preserved in storage order")

			got, err := tree.FindEntry(tc.lookup)
			require.NoError(t, err)
			assert.Equal(t, tc.wantMode, got.Mode)
			assert.Equal(t, 0, bytes.Compare(tc.wantHash, got.Hash.Bytes()),
				"FindEntry must return the first matching entry; got %s want %x",
				got.Hash, tc.wantHash)
		})
	}
}

func TestTreeFindEntryStopsAtUnsortedEntryPastName(t *testing.T) {
	t.Parallel()

	hashA := bytes.Repeat([]byte{0xAA}, 20)
	hashB := bytes.Repeat([]byte{0xBB}, 20)
	body := encodeRawTreeEntries(
		rawTreeEntry{"100644", "z", hashA},
		rawTreeEntry{"100644", "foo", hashB},
	)

	obj := &plumbing.MemoryObject{}
	obj.SetType(plumbing.TreeObject)
	w, err := obj.Writer()
	require.NoError(t, err)
	_, err = w.Write(body)
	require.NoError(t, err)
	require.NoError(t, w.Close())

	var tree Tree
	require.NoError(t, tree.Decode(obj))

	got, err := tree.FindEntry("foo")
	require.ErrorIs(t, err, ErrEntryNotFound)
	assert.Nil(t, got)
}

func TestTreeFindEntryFindsDirectoryAfterLexicallyEarlierFile(t *testing.T) {
	t.Parallel()

	hashA := bytes.Repeat([]byte{0xAA}, 20)
	hashB := bytes.Repeat([]byte{0xBB}, 20)
	body := encodeRawTreeEntries(
		rawTreeEntry{"100644", "foo.bar", hashA},
		rawTreeEntry{"40000", "foo", hashB},
	)

	obj := &plumbing.MemoryObject{}
	obj.SetType(plumbing.TreeObject)
	w, err := obj.Writer()
	require.NoError(t, err)
	_, err = w.Write(body)
	require.NoError(t, err)
	require.NoError(t, w.Close())

	var tree Tree
	require.NoError(t, tree.Decode(obj))

	got, err := tree.FindEntry("foo")
	require.NoError(t, err)
	assert.Equal(t, filemode.Dir, got.Mode)
	assert.Equal(t, 0, bytes.Compare(hashB, got.Hash.Bytes()))
}

// TestTreeEncodeRejectsDuplicateEntries pins the duplicate-name check
// in Tree.Encode. Two entries sharing a name — whether two files, two
// directories, or a file/dir collision where the dir's sort name `foo/`
// puts it elsewhere in the sorted order — must be rejected with
// ErrDuplicateEntry rather than written to the object.
func TestTreeEncodeRejectsDuplicateEntries(t *testing.T) {
	t.Parallel()

	hashA, ok := plumbing.FromBytes(bytes.Repeat([]byte{0xAA}, 20))
	require.True(t, ok)
	hashB, ok := plumbing.FromBytes(bytes.Repeat([]byte{0xBB}, 20))
	require.True(t, ok)

	cases := []struct {
		name    string
		entries []TreeEntry
	}{
		{
			name: "two files with the same name",
			entries: []TreeEntry{
				{Name: "foo", Mode: filemode.Regular, Hash: hashA},
				{Name: "foo", Mode: filemode.Regular, Hash: hashB},
			},
		},
		{
			name: "file and directory with the same name",
			entries: []TreeEntry{
				{Name: "foo", Mode: filemode.Regular, Hash: hashA},
				{Name: "foo", Mode: filemode.Dir, Hash: hashB},
			},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			tree := &Tree{Entries: tc.entries}
			sort.Sort(TreeEntrySorter(tree.Entries))
			obj := &plumbing.MemoryObject{}
			err := tree.Encode(obj)
			require.ErrorIs(t, err, ErrDuplicateEntry)
		})
	}
}

// TestTreeEncodeRejectsDangerousNames pins the entry-name validation in
// Tree.Encode: a tree assembled in memory with components that
// pathutil.ValidTreePath refuses must fail at the encoder boundary so
// the library cannot produce such tree objects through its API.
func TestTreeEncodeRejectsDangerousNames(t *testing.T) {
	t.Parallel()

	hash, ok := plumbing.FromBytes(bytes.Repeat([]byte{0xAA}, 20))
	require.True(t, ok)

	cases := []struct {
		name  string
		entry string
	}{
		{"final-component .git", ".git"},
		{"parent traversal", ".."},
		{"current directory", "."},
		{"NTFS trailing space on .git", ".git "},
		{"NTFS short-name alias git~1", "git~1"},
		{"HFS+ zero-width character in .git", ".g\u200cit"},
		{"embedded NUL in name", "foo\x00bar"},
		{"control character", "foo\x01bar"},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			tree := &Tree{
				Entries: []TreeEntry{{Name: tc.entry, Mode: filemode.Regular, Hash: hash}},
			}
			obj := &plumbing.MemoryObject{}
			err := tree.Encode(obj)
			require.ErrorIs(t, err, pathutil.ErrInvalidPath)
		})
	}
}

// TestTreeEntryFileRejectsDangerousNames pins the validation gate added
// to TreeEntryFile: any caller materialising a *File from a *TreeEntry
// must fail closed when the entry's name is a shape that
// pathutil.ValidTreePath rejects, regardless of whether the underlying
// blob fetch would succeed. The blob is staged before each case so a
// missing-blob error cannot mask a missing validator.
func TestTreeEntryFileRejectsDangerousNames(t *testing.T) {
	t.Parallel()

	store := memory.NewStorage()
	blob := &plumbing.MemoryObject{}
	blob.SetType(plumbing.BlobObject)
	bw, err := blob.Writer()
	require.NoError(t, err)
	_, err = bw.Write([]byte("payload"))
	require.NoError(t, err)
	require.NoError(t, bw.Close())
	blobHash, err := store.SetEncodedObject(blob)
	require.NoError(t, err)

	cases := []struct {
		name      string
		entryName string
	}{
		{"empty name", ""},
		{".git", ".git"},
		{"git~1 short name", "git~1"},
		{"NTFS trailing space", ".git "},
		{"NTFS trailing dot", ".git."},
		{"NTFS alternate data stream", ".git::$INDEX_ALLOCATION"},
		{"NTFS trailing space on git~1", "git~1 "},
		{"NTFS alternate data stream on git~1", "git~1::$DATA"},
		{"HFS+ zero-width character", ".g\u200cit"},
		{"dot-dot traversal", ".."},
		{"single dot", "."},
		{"control character", "foo\x01bar"},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			tree := &Tree{s: store}
			f, err := tree.TreeEntryFile(&TreeEntry{
				Name: tc.entryName,
				Mode: filemode.Regular,
				Hash: blobHash,
			})
			assert.Nil(t, f, "TreeEntryFile should not return a *File for %q", tc.entryName)
			assert.ErrorIs(t, err, pathutil.ErrInvalidPath)
		})
	}
}

// TestTreeWalkerNextRejectsDangerousNames pins the validation gate
// added to TreeWalker.Next: enumeration must surface an attacker-shaped
// entry name as an error rather than handing the joined path to the
// caller. Inspection-only callers that legitimately need to enumerate
// raw entries can still read Tree.Entries directly.
func TestTreeWalkerNextRejectsDangerousNames(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name      string
		entryName string
	}{
		{".git", ".git"},
		{"git~1 short name", "git~1"},
		{"NTFS trailing space", ".git "},
		{"NTFS alternate data stream", ".git::$INDEX_ALLOCATION"},
		{"NTFS trailing space on git~1", "git~1 "},
		{"HFS+ zero-width character", ".g\u200cit"},
		{"dot-dot traversal", ".."},
		{"control character", "foo\x01bar"},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			tree := &Tree{
				Entries: []TreeEntry{
					{
						Name: tc.entryName,
						Mode: filemode.Regular,
						Hash: plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
					},
				},
			}
			walker := NewTreeWalker(tree, true, nil)
			defer walker.Close()

			_, _, err := walker.Next()
			assert.ErrorIs(t, err, pathutil.ErrInvalidPath)
		})
	}
}

// TestTreeValidateAcceptsWellFormed pins the negative case: a tree
// with canonical modes, sorted unique entries, and ordinary names
// passes Validate cleanly. Anchors the rest of the rule-by-rule
// coverage as deviations from this baseline.
func TestTreeValidateAcceptsWellFormed(t *testing.T) {
	t.Parallel()

	hash := bytes.Repeat([]byte{0xAA}, 20)
	body := encodeRawTreeEntries(
		rawTreeEntry{"100644", "alpha", hash},
		rawTreeEntry{"100755", "beta", hash},
		rawTreeEntry{"120000", "gamma", hash},
		rawTreeEntry{"40000", "subdir", hash},
		rawTreeEntry{"160000", "submod", hash},
	)
	tree, err := decodeRawTree(t, body)
	require.NoError(t, err)
	assert.NoError(t, tree.Validate())
}

// TestTreeValidateRejectsFsckRules exercises each rule fsck_tree
// enforces. Each case constructs a raw tree object that decodes
// cleanly (Decode is intentionally permissive) and then asserts
// Validate flags the corresponding rule. Sentinels are checked via
// errors.Is where the rule has one; otherwise the error message is
// matched.
func TestTreeValidateRejectsFsckRules(t *testing.T) {
	t.Parallel()

	hash := bytes.Repeat([]byte{0xAA}, 20)
	zero := make([]byte, 20)

	cases := []struct {
		name       string
		entries    []rawTreeEntry
		wantIs     error
		wantString string
	}{
		{
			name:       "null hash",
			entries:    []rawTreeEntry{{"100644", "file", zero}},
			wantString: "points to null hash",
		},
		{
			name:       "slash in name",
			entries:    []rawTreeEntry{{"100644", "dir/file", hash}},
			wantString: "contains a slash",
		},
		{
			name:    "dot component",
			entries: []rawTreeEntry{{"100644", ".", hash}},
			wantIs:  pathutil.ErrInvalidPath,
		},
		{
			name:    "dotdot component",
			entries: []rawTreeEntry{{"100644", "..", hash}},
			wantIs:  pathutil.ErrInvalidPath,
		},
		{
			name:    "dotgit component",
			entries: []rawTreeEntry{{"100644", ".git", hash}},
			wantIs:  pathutil.ErrInvalidPath,
		},
		{
			name: "duplicate entries",
			entries: []rawTreeEntry{
				{"100644", "foo", hash},
				{"100644", "foo", hash},
			},
			wantIs: ErrDuplicateEntry,
		},
		{
			// canonicalTreeMode maps the non-canonical 0100664 down
			// to the canonical Regular mode at Decode, so Validate
			// sees a well-formed entry and accepts it. The rule is
			// kept in the table to document that the canonicalising
			// step swallows it.
			name:       "0100664 normalises to Regular and validates",
			entries:    []rawTreeEntry{{"100664", "file", hash}},
			wantString: "",
		},
		{
			name:       "symlinked .gitmodules",
			entries:    []rawTreeEntry{{"120000", ".gitmodules", hash}},
			wantString: ".gitmodules is a symlink",
		},
		{
			name:       "symlinked .gitattributes",
			entries:    []rawTreeEntry{{"120000", ".gitattributes", hash}},
			wantString: ".gitattributes is a symlink",
		},
		{
			name:       "symlinked .gitignore",
			entries:    []rawTreeEntry{{"120000", ".gitignore", hash}},
			wantString: ".gitignore is a symlink",
		},
		{
			name:       "symlinked .mailmap",
			entries:    []rawTreeEntry{{"120000", ".mailmap", hash}},
			wantString: ".mailmap is a symlink",
		},
		{
			name: "mis-sorted entries",
			entries: []rawTreeEntry{
				{"100644", "zeta", hash},
				{"100644", "alpha", hash},
			},
			wantIs: ErrEntriesNotSorted,
		},
		{
			name:       "name too long",
			entries:    []rawTreeEntry{{"100644", strings.Repeat("a", maxTreeEntryNameLen+1), hash}},
			wantString: "exceeds 4096",
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			tree, err := decodeRawTree(t, encodeRawTreeEntries(tc.entries...))
			require.NoError(t, err, "Decode should be permissive")

			err = tree.Validate()
			if tc.wantIs == nil && tc.wantString == "" {
				assert.NoError(t, err)
				return
			}
			require.Error(t, err)
			assert.ErrorIs(t, err, ErrInvalidTree)
			if tc.wantIs != nil {
				assert.ErrorIs(t, err, tc.wantIs)
			}
			if tc.wantString != "" {
				assert.Contains(t, err.Error(), tc.wantString)
			}
		})
	}
}

// TestTreeValidateEmptyName covers the case fsck_tree calls
// has_empty_name. Tree.Decode rejects an empty entry name at parse,
// so the test constructs the in-memory tree directly to reach the
// Validate rule.
func TestTreeValidateEmptyName(t *testing.T) {
	t.Parallel()

	tree := &Tree{
		Entries: []TreeEntry{{
			Name: "",
			Mode: filemode.Regular,
			Hash: plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
		}},
	}
	err := tree.Validate()
	require.Error(t, err)
	assert.ErrorIs(t, err, ErrInvalidTree)
	assert.Contains(t, err.Error(), "empty entry name")
}

// TestTreeValidateBadInMemoryMode pins the bad-mode rule against
// programmatically-constructed entries. Wire-decoded modes go through
// canonicalTreeMode and always land on a valid value, so this rule is
// only reachable when a caller sets a non-canonical Mode by hand.
func TestTreeValidateBadInMemoryMode(t *testing.T) {
	t.Parallel()

	tree := &Tree{
		Entries: []TreeEntry{{
			Name: "file",
			Mode: filemode.FileMode(0o12345),
			Hash: plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
		}},
	}
	err := tree.Validate()
	require.Error(t, err)
	assert.ErrorIs(t, err, ErrInvalidTree)
	assert.Contains(t, err.Error(), "bad mode")
}

// TestTreeValidateReportsAllRules confirms that Validate accumulates
// multiple rule violations into a single joined error rather than
// returning on the first failure.
func TestTreeValidateReportsAllRules(t *testing.T) {
	t.Parallel()

	zero := make([]byte, 20)
	tree, err := decodeRawTree(t, encodeRawTreeEntries(
		rawTreeEntry{"100644", "..", zero}, // dotdot + null hash
	))
	require.NoError(t, err)

	verr := tree.Validate()
	require.Error(t, verr)
	assert.ErrorIs(t, verr, ErrInvalidTree)
	assert.ErrorIs(t, verr, pathutil.ErrInvalidPath)
	assert.Contains(t, verr.Error(), "null hash")
}
