loading up the forgejo repo on tangled to test page performance
at forgejo 3.8 kB view raw
1// Copyright 2018 The Gitea Authors. All rights reserved. 2// SPDX-License-Identifier: MIT 3 4package git 5 6import ( 7 "bufio" 8 "bytes" 9 "fmt" 10 "io" 11 "strconv" 12 "strings" 13 14 "forgejo.org/modules/log" 15) 16 17// ParseTreeEntries parses the output of a `git ls-tree -l` command. 18func ParseTreeEntries(data []byte) ([]*TreeEntry, error) { 19 return parseTreeEntries(data, nil) 20} 21 22var sepSpace = []byte{' '} 23 24func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) { 25 var err error 26 entries := make([]*TreeEntry, 0, bytes.Count(data, []byte{'\n'})+1) 27 for pos := 0; pos < len(data); { 28 // expect line to be of the form: 29 // <mode> <type> <sha> <space-padded-size>\t<filename> 30 // <mode> <type> <sha>\t<filename> 31 posEnd := bytes.IndexByte(data[pos:], '\n') 32 if posEnd == -1 { 33 posEnd = len(data) 34 } else { 35 posEnd += pos 36 } 37 line := data[pos:posEnd] 38 posTab := bytes.IndexByte(line, '\t') 39 if posTab == -1 { 40 return nil, fmt.Errorf("invalid ls-tree output (no tab): %q", line) 41 } 42 43 entry := new(TreeEntry) 44 entry.ptree = ptree 45 46 entryAttrs := line[:posTab] 47 entryName := line[posTab+1:] 48 49 entryMode, entryAttrs, _ := bytes.Cut(entryAttrs, sepSpace) 50 _ /* entryType */, entryAttrs, _ = bytes.Cut(entryAttrs, sepSpace) // the type is not used, the mode is enough to determine the type 51 entryObjectID, entryAttrs, _ := bytes.Cut(entryAttrs, sepSpace) 52 if len(entryAttrs) > 0 { 53 entrySize := entryAttrs // the last field is the space-padded-size 54 entry.size, _ = strconv.ParseInt(strings.TrimSpace(string(entrySize)), 10, 64) 55 entry.sized = true 56 } 57 58 switch string(entryMode) { 59 case "100644": 60 entry.entryMode = EntryModeBlob 61 case "100755": 62 entry.entryMode = EntryModeExec 63 case "120000": 64 entry.entryMode = EntryModeSymlink 65 case "160000": 66 entry.entryMode = EntryModeCommit 67 case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons 68 entry.entryMode = EntryModeTree 69 default: 70 return nil, fmt.Errorf("unknown type: %v", string(entryMode)) 71 } 72 73 entry.ID, err = NewIDFromString(string(entryObjectID)) 74 if err != nil { 75 return nil, fmt.Errorf("invalid ls-tree output (invalid object id): %q, err: %w", line, err) 76 } 77 78 if len(entryName) > 0 && entryName[0] == '"' { 79 entry.name, err = strconv.Unquote(string(entryName)) 80 if err != nil { 81 return nil, fmt.Errorf("invalid ls-tree output (invalid name): %q, err: %w", line, err) 82 } 83 } else { 84 entry.name = string(entryName) 85 } 86 87 pos = posEnd + 1 88 entries = append(entries, entry) 89 } 90 return entries, nil 91} 92 93func catBatchParseTreeEntries(objectFormat ObjectFormat, ptree *Tree, rd *bufio.Reader, sz int64) ([]*TreeEntry, error) { 94 fnameBuf := make([]byte, 4096) 95 modeBuf := make([]byte, 40) 96 shaBuf := make([]byte, objectFormat.FullLength()) 97 entries := make([]*TreeEntry, 0, 10) 98 99loop: 100 for sz > 0 { 101 mode, fname, sha, count, err := ParseTreeLine(objectFormat, rd, modeBuf, fnameBuf, shaBuf) 102 if err != nil { 103 if err == io.EOF { 104 break loop 105 } 106 return nil, err 107 } 108 sz -= int64(count) 109 entry := new(TreeEntry) 110 entry.ptree = ptree 111 112 switch string(mode) { 113 case "100644": 114 entry.entryMode = EntryModeBlob 115 case "100755": 116 entry.entryMode = EntryModeExec 117 case "120000": 118 entry.entryMode = EntryModeSymlink 119 case "160000": 120 entry.entryMode = EntryModeCommit 121 case "40000", "40755": // git uses 40000 for tree object, but some users may get 40755 for unknown reasons 122 entry.entryMode = EntryModeTree 123 default: 124 log.Debug("Unknown mode: %v", string(mode)) 125 return nil, fmt.Errorf("unknown mode: %v", string(mode)) 126 } 127 128 entry.ID = objectFormat.MustID(sha) 129 entry.name = string(fname) 130 entries = append(entries, entry) 131 } 132 if _, err := rd.Discard(1); err != nil { 133 return entries, err 134 } 135 136 return entries, nil 137}