diff options
| author | 2026-03-30 05:40:58 +0000 | |
|---|---|---|
| committer | 2026-03-30 05:44:56 +0000 | |
| commit | aa7e2873aa4da53fbaa4774fdd33f5af20c1afb0 (patch) | |
| tree | 0d1367f693324ea8287ccef52ba524f8515ca146 /object | |
| parent | README, etc.: Use proper unicode hyphens :P (diff) | |
| signature | No signature | |
object/tree: Add helpers and cleanup v0.1.152
Diffstat (limited to 'object')
| -rw-r--r-- | object/tree/entry.go | 38 | ||||
| -rw-r--r-- | object/tree/insert.go | 14 | ||||
| -rw-r--r-- | object/tree/mode.go | 12 | ||||
| -rw-r--r-- | object/tree/mode_details.go | 9 | ||||
| -rw-r--r-- | object/tree/mode_is_blob_like.go | 8 | ||||
| -rw-r--r-- | object/tree/mode_table.go | 19 | ||||
| -rw-r--r-- | object/tree/path_append.go | 14 | ||||
| -rw-r--r-- | object/tree/path_clone.go | 16 | ||||
| -rw-r--r-- | object/tree/path_prefix.go | 19 | ||||
| -rw-r--r-- | object/tree/path_split.go | 19 | ||||
| -rw-r--r-- | object/tree/remove.go | 14 |
11 files changed, 137 insertions, 45 deletions
diff --git a/object/tree/entry.go b/object/tree/entry.go index 06b8d112..70f1dab7 100644 --- a/object/tree/entry.go +++ b/object/tree/entry.go @@ -2,6 +2,7 @@ package tree import ( "bytes" + "slices" objectid "codeberg.org/lindenii/furgit/object/id" ) @@ -16,26 +17,25 @@ type TreeEntry struct { } func (tree *Tree) entry(name []byte, searchIsTree bool) *TreeEntry { - low, high := 0, len(tree.Entries)-1 - for low <= high { - mid := low + (high-low)/2 - entry := &tree.Entries[mid] - - cmp := TreeEntryNameCompare(entry.Name, entry.Mode, name, searchIsTree) - if cmp == 0 { - if bytes.Equal(entry.Name, name) { - return entry - } - - return nil - } + index, ok := slices.BinarySearchFunc(tree.Entries, treeEntrySearch{ + name: name, + isTree: searchIsTree, + }, func(entry TreeEntry, search treeEntrySearch) int { + return TreeEntryNameCompare(entry.Name, entry.Mode, search.name, search.isTree) + }) + if !ok { + return nil + } - if cmp < 0 { - low = mid + 1 - } else { - high = mid - 1 - } + entry := &tree.Entries[index] + if !bytes.Equal(entry.Name, name) { + return nil } - return nil + return entry +} + +type treeEntrySearch struct { + name []byte + isTree bool } diff --git a/object/tree/insert.go b/object/tree/insert.go index 2da86514..683af4a7 100644 --- a/object/tree/insert.go +++ b/object/tree/insert.go @@ -2,7 +2,7 @@ package tree import ( "fmt" - "sort" + "slices" ) // InsertEntry inserts a tree entry while preserving Git ordering. @@ -15,13 +15,13 @@ func (tree *Tree) InsertEntry(newEntry TreeEntry) error { newEntry.Name = append([]byte(nil), newEntry.Name...) - newIsTree := newEntry.Mode == FileModeDir - insertAt := sort.Search(len(tree.Entries), func(i int) bool { - return TreeEntryNameCompare(tree.Entries[i].Name, tree.Entries[i].Mode, newEntry.Name, newIsTree) >= 0 + insertAt, _ := slices.BinarySearchFunc(tree.Entries, treeEntrySearch{ + name: newEntry.Name, + isTree: newEntry.Mode == FileModeDir, + }, func(entry TreeEntry, search treeEntrySearch) int { + return TreeEntryNameCompare(entry.Name, entry.Mode, search.name, search.isTree) }) - tree.Entries = append(tree.Entries, TreeEntry{}) - copy(tree.Entries[insertAt+1:], tree.Entries[insertAt:]) - tree.Entries[insertAt] = newEntry + tree.Entries = slices.Insert(tree.Entries, insertAt, newEntry) return nil } diff --git a/object/tree/mode.go b/object/tree/mode.go index c0bb4d75..b1cbc6bc 100644 --- a/object/tree/mode.go +++ b/object/tree/mode.go @@ -10,15 +10,3 @@ const ( FileModeSymlink FileMode = 0o120000 FileModeGitlink FileMode = 0o160000 ) - -// IsBlobLike reports whether mode names one blob-like tree entry kind. -// -// Blob-like entries store blob object IDs as their targets. -func (mode FileMode) IsBlobLike() bool { - switch mode { - case FileModeRegular, FileModeExecutable, FileModeSymlink: - return true - default: - return false - } -} diff --git a/object/tree/mode_details.go b/object/tree/mode_details.go new file mode 100644 index 00000000..02d22620 --- /dev/null +++ b/object/tree/mode_details.go @@ -0,0 +1,9 @@ +package tree + +type fileModeDetails struct { + isBlobLike bool +} + +func (mode FileMode) details() fileModeDetails { + return fileModeTable[mode] +} diff --git a/object/tree/mode_is_blob_like.go b/object/tree/mode_is_blob_like.go new file mode 100644 index 00000000..3ec3a308 --- /dev/null +++ b/object/tree/mode_is_blob_like.go @@ -0,0 +1,8 @@ +package tree + +// IsBlobLike reports whether mode names one blob-like tree entry kind. +// +// Blob-like entries store blob object IDs as their targets. +func (mode FileMode) IsBlobLike() bool { + return mode.details().isBlobLike +} diff --git a/object/tree/mode_table.go b/object/tree/mode_table.go new file mode 100644 index 00000000..e180c38f --- /dev/null +++ b/object/tree/mode_table.go @@ -0,0 +1,19 @@ +package tree + +var fileModeTable = map[FileMode]fileModeDetails{ //nolint:gochecknoglobals + FileModeDir: { + isBlobLike: false, + }, + FileModeRegular: { + isBlobLike: true, + }, + FileModeExecutable: { + isBlobLike: true, + }, + FileModeSymlink: { + isBlobLike: true, + }, + FileModeGitlink: { + isBlobLike: false, + }, +} diff --git a/object/tree/path_append.go b/object/tree/path_append.go new file mode 100644 index 00000000..609d5279 --- /dev/null +++ b/object/tree/path_append.go @@ -0,0 +1,14 @@ +package tree + +// AppendPath appends path to dst as one slash-separated byte path. +func AppendPath(dst []byte, path [][]byte) []byte { + for i := range path { + if i > 0 { + dst = append(dst, '/') + } + + dst = append(dst, path[i]...) + } + + return dst +} diff --git a/object/tree/path_clone.go b/object/tree/path_clone.go new file mode 100644 index 00000000..a4668add --- /dev/null +++ b/object/tree/path_clone.go @@ -0,0 +1,16 @@ +package tree + +import ( + "bytes" + "slices" +) + +// ClonePath returns one deep copy of path. +func ClonePath(path [][]byte) [][]byte { + cloned := slices.Clone(path) + for i := range cloned { + cloned[i] = bytes.Clone(cloned[i]) + } + + return cloned +} diff --git a/object/tree/path_prefix.go b/object/tree/path_prefix.go new file mode 100644 index 00000000..ed658cee --- /dev/null +++ b/object/tree/path_prefix.go @@ -0,0 +1,19 @@ +package tree + +import ( + "bytes" + "slices" +) + +// HasPathPrefix reports whether path begins with prefix as whole components. +func HasPathPrefix(path, prefix [][]byte) bool { + if len(prefix) == 0 { + return true + } + + if len(path) < len(prefix) { + return false + } + + return slices.EqualFunc(path[:len(prefix)], prefix, bytes.Equal) +} diff --git a/object/tree/path_split.go b/object/tree/path_split.go new file mode 100644 index 00000000..c147dd25 --- /dev/null +++ b/object/tree/path_split.go @@ -0,0 +1,19 @@ +package tree + +import ( + "bytes" +) + +// SplitPath splits one slash-separated tree path into components. +func SplitPath(path []byte) [][]byte { + if len(path) == 0 { + return nil + } + + parts := bytes.Split(path, []byte{'/'}) + for i := range parts { + parts[i] = bytes.Clone(parts[i]) + } + + return parts +} diff --git a/object/tree/remove.go b/object/tree/remove.go index 9eb42028..fe4fda32 100644 --- a/object/tree/remove.go +++ b/object/tree/remove.go @@ -3,6 +3,7 @@ package tree import ( "bytes" "fmt" + "slices" ) // RemoveEntry removes a tree entry by name. @@ -11,13 +12,12 @@ func (tree *Tree) RemoveEntry(name []byte) error { return fmt.Errorf("object: tree: entry %q not found", name) } - for i := range tree.Entries { - if bytes.Equal(tree.Entries[i].Name, name) { - copy(tree.Entries[i:], tree.Entries[i+1:]) - tree.Entries = tree.Entries[:len(tree.Entries)-1] - - return nil - } + index := slices.IndexFunc(tree.Entries, func(entry TreeEntry) bool { + return bytes.Equal(entry.Name, name) + }) + if index >= 0 { + tree.Entries = slices.Delete(tree.Entries, index, index+1) + return nil } return fmt.Errorf("object: tree: entry %q not found", name) |
