Fast implementation of Git in pure Go codeberg.org/lindenii/furgit
git go
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

object/tree: Add helpers and cleanup

Runxi Yu aa7e2873 c874533e

+137 -45
+19 -19
object/tree/entry.go
··· 2 2 3 3 import ( 4 4 "bytes" 5 + "slices" 5 6 6 7 objectid "codeberg.org/lindenii/furgit/object/id" 7 8 ) ··· 16 17 } 17 18 18 19 func (tree *Tree) entry(name []byte, searchIsTree bool) *TreeEntry { 19 - low, high := 0, len(tree.Entries)-1 20 - for low <= high { 21 - mid := low + (high-low)/2 22 - entry := &tree.Entries[mid] 23 - 24 - cmp := TreeEntryNameCompare(entry.Name, entry.Mode, name, searchIsTree) 25 - if cmp == 0 { 26 - if bytes.Equal(entry.Name, name) { 27 - return entry 28 - } 29 - 30 - return nil 31 - } 20 + index, ok := slices.BinarySearchFunc(tree.Entries, treeEntrySearch{ 21 + name: name, 22 + isTree: searchIsTree, 23 + }, func(entry TreeEntry, search treeEntrySearch) int { 24 + return TreeEntryNameCompare(entry.Name, entry.Mode, search.name, search.isTree) 25 + }) 26 + if !ok { 27 + return nil 28 + } 32 29 33 - if cmp < 0 { 34 - low = mid + 1 35 - } else { 36 - high = mid - 1 37 - } 30 + entry := &tree.Entries[index] 31 + if !bytes.Equal(entry.Name, name) { 32 + return nil 38 33 } 39 34 40 - return nil 35 + return entry 36 + } 37 + 38 + type treeEntrySearch struct { 39 + name []byte 40 + isTree bool 41 41 }
+7 -7
object/tree/insert.go
··· 2 2 3 3 import ( 4 4 "fmt" 5 - "sort" 5 + "slices" 6 6 ) 7 7 8 8 // InsertEntry inserts a tree entry while preserving Git ordering. ··· 15 15 16 16 newEntry.Name = append([]byte(nil), newEntry.Name...) 17 17 18 - newIsTree := newEntry.Mode == FileModeDir 19 - insertAt := sort.Search(len(tree.Entries), func(i int) bool { 20 - return TreeEntryNameCompare(tree.Entries[i].Name, tree.Entries[i].Mode, newEntry.Name, newIsTree) >= 0 18 + insertAt, _ := slices.BinarySearchFunc(tree.Entries, treeEntrySearch{ 19 + name: newEntry.Name, 20 + isTree: newEntry.Mode == FileModeDir, 21 + }, func(entry TreeEntry, search treeEntrySearch) int { 22 + return TreeEntryNameCompare(entry.Name, entry.Mode, search.name, search.isTree) 21 23 }) 22 - tree.Entries = append(tree.Entries, TreeEntry{}) 23 - copy(tree.Entries[insertAt+1:], tree.Entries[insertAt:]) 24 - tree.Entries[insertAt] = newEntry 24 + tree.Entries = slices.Insert(tree.Entries, insertAt, newEntry) 25 25 26 26 return nil 27 27 }
-12
object/tree/mode.go
··· 10 10 FileModeSymlink FileMode = 0o120000 11 11 FileModeGitlink FileMode = 0o160000 12 12 ) 13 - 14 - // IsBlobLike reports whether mode names one blob-like tree entry kind. 15 - // 16 - // Blob-like entries store blob object IDs as their targets. 17 - func (mode FileMode) IsBlobLike() bool { 18 - switch mode { 19 - case FileModeRegular, FileModeExecutable, FileModeSymlink: 20 - return true 21 - default: 22 - return false 23 - } 24 - }
+9
object/tree/mode_details.go
··· 1 + package tree 2 + 3 + type fileModeDetails struct { 4 + isBlobLike bool 5 + } 6 + 7 + func (mode FileMode) details() fileModeDetails { 8 + return fileModeTable[mode] 9 + }
+8
object/tree/mode_is_blob_like.go
··· 1 + package tree 2 + 3 + // IsBlobLike reports whether mode names one blob-like tree entry kind. 4 + // 5 + // Blob-like entries store blob object IDs as their targets. 6 + func (mode FileMode) IsBlobLike() bool { 7 + return mode.details().isBlobLike 8 + }
+19
object/tree/mode_table.go
··· 1 + package tree 2 + 3 + var fileModeTable = map[FileMode]fileModeDetails{ //nolint:gochecknoglobals 4 + FileModeDir: { 5 + isBlobLike: false, 6 + }, 7 + FileModeRegular: { 8 + isBlobLike: true, 9 + }, 10 + FileModeExecutable: { 11 + isBlobLike: true, 12 + }, 13 + FileModeSymlink: { 14 + isBlobLike: true, 15 + }, 16 + FileModeGitlink: { 17 + isBlobLike: false, 18 + }, 19 + }
+14
object/tree/path_append.go
··· 1 + package tree 2 + 3 + // AppendPath appends path to dst as one slash-separated byte path. 4 + func AppendPath(dst []byte, path [][]byte) []byte { 5 + for i := range path { 6 + if i > 0 { 7 + dst = append(dst, '/') 8 + } 9 + 10 + dst = append(dst, path[i]...) 11 + } 12 + 13 + return dst 14 + }
+16
object/tree/path_clone.go
··· 1 + package tree 2 + 3 + import ( 4 + "bytes" 5 + "slices" 6 + ) 7 + 8 + // ClonePath returns one deep copy of path. 9 + func ClonePath(path [][]byte) [][]byte { 10 + cloned := slices.Clone(path) 11 + for i := range cloned { 12 + cloned[i] = bytes.Clone(cloned[i]) 13 + } 14 + 15 + return cloned 16 + }
+19
object/tree/path_prefix.go
··· 1 + package tree 2 + 3 + import ( 4 + "bytes" 5 + "slices" 6 + ) 7 + 8 + // HasPathPrefix reports whether path begins with prefix as whole components. 9 + func HasPathPrefix(path, prefix [][]byte) bool { 10 + if len(prefix) == 0 { 11 + return true 12 + } 13 + 14 + if len(path) < len(prefix) { 15 + return false 16 + } 17 + 18 + return slices.EqualFunc(path[:len(prefix)], prefix, bytes.Equal) 19 + }
+19
object/tree/path_split.go
··· 1 + package tree 2 + 3 + import ( 4 + "bytes" 5 + ) 6 + 7 + // SplitPath splits one slash-separated tree path into components. 8 + func SplitPath(path []byte) [][]byte { 9 + if len(path) == 0 { 10 + return nil 11 + } 12 + 13 + parts := bytes.Split(path, []byte{'/'}) 14 + for i := range parts { 15 + parts[i] = bytes.Clone(parts[i]) 16 + } 17 + 18 + return parts 19 + }
+7 -7
object/tree/remove.go
··· 3 3 import ( 4 4 "bytes" 5 5 "fmt" 6 + "slices" 6 7 ) 7 8 8 9 // RemoveEntry removes a tree entry by name. ··· 11 12 return fmt.Errorf("object: tree: entry %q not found", name) 12 13 } 13 14 14 - for i := range tree.Entries { 15 - if bytes.Equal(tree.Entries[i].Name, name) { 16 - copy(tree.Entries[i:], tree.Entries[i+1:]) 17 - tree.Entries = tree.Entries[:len(tree.Entries)-1] 18 - 19 - return nil 20 - } 15 + index := slices.IndexFunc(tree.Entries, func(entry TreeEntry) bool { 16 + return bytes.Equal(entry.Name, name) 17 + }) 18 + if index >= 0 { 19 + tree.Entries = slices.Delete(tree.Entries, index, index+1) 20 + return nil 21 21 } 22 22 23 23 return fmt.Errorf("object: tree: entry %q not found", name)