Prism VCS > Tree [f4699a2]
/tree.go/
// Copyright (c) 2025, Christian Lee Seibold
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package main
import (
"bytes"
"fmt"
"os"
"path"
"sort"
"time"
"github.com/klauspost/compress/zstd"
)
type TreeObject struct {
hash Hash
entries []TreeEntry
}
func DecodeTree(vcsDirectory string, hash Hash) TreeObject {
file_contents, err := os.ReadFile(path.Join(vcsDirectory, "objects", hash.HexString()))
if err != nil {
return TreeObject{}
}
zr, _ := zstd.NewReader(bytes.NewReader(file_contents))
result := new(bytes.Buffer)
result.ReadFrom(zr)
zr.Close()
_, contents, _ := bytes.Cut(result.Bytes(), []byte{00})
entries := []TreeEntry{}
reader := bytes.NewReader(contents)
for {
entry := ParseTreeEntry(reader)
if (entry != TreeEntry{}) {
entries = append(entries, entry)
} else {
break
}
}
return TreeObject{hash, entries}
}
func (t TreeObject) FindEntryName(name string) TreeEntry {
for _, entry := range t.entries {
if entry.name == name {
return entry
}
}
return TreeEntry{}
}
func (t TreeObject) Encode() []byte {
sort.SliceStable(t.entries, func(i, j int) bool {
return caseInsensitiveStringCompare(t.entries[i].name, t.entries[j].name) == -1
})
buf := bytes.Buffer{}
for _, entry := range t.entries {
buf.Write(entry.Encode())
}
return buf.Bytes()
}
func (t TreeObject) ToIndex(modTime time.Time) *Index {
newIndex := make(Index)
for _, entry := range t.entries {
if entry.t == "blob" {
newIndex[entry.name] = IndexEntry{"c", "100644", entry.name, entry.hash, modTime}
} else if entry.t == "tree" {
// TODO
}
}
return &newIndex
}
type TreeEntry struct {
t string // blob or tree
hash Hash
name string
}
func ParseTreeEntry(reader *bytes.Reader) TreeEntry {
// Get next space character to get the mode
modebuf := bytes.Buffer{}
for {
b, err := reader.ReadByte()
if err != nil {
return TreeEntry{}
}
if b == ' ' {
break
}
modebuf.WriteByte(b)
}
// Read until NUL character into name
name := bytes.Buffer{}
for {
b, err := reader.ReadByte()
if err != nil {
return TreeEntry{}
}
if b == 0 {
break
}
name.WriteByte(b)
}
// Create array for the hash
var h Hash
_, err := reader.Read(h[:])
if err != nil {
return TreeEntry{}
}
// Create tree entry based on mode
mode := modebuf.String()
entryType := "blob"
if mode != "100644" {
entryType = "tree"
}
return TreeEntry{entryType, h, name.String()}
}
func (te TreeEntry) Encode() []byte {
mode := ""
if te.t == "blob" {
mode = "100644" // Default mode for blobs
}
buf := bytes.Buffer{}
buf.WriteString(fmt.Sprintf("%s %s\000", mode, te.name))
buf.Write(te.hash[:])
return buf.Bytes()
}