diff options
author | Runxi Yu <me@runxiyu.org> | 2025-03-22 22:04:25 +0800 |
---|---|---|
committer | Runxi Yu <me@runxiyu.org> | 2025-03-22 22:04:25 +0800 |
commit | cf735091ac163cbaafda8a12ead568bf4ed8abbf (patch) | |
tree | fda3c9f96040991730d090a3a05809decc2588b1 | |
parent | Cache tree-building (diff) | |
download | forge-cf735091ac163cbaafda8a12ead568bf4ed8abbf.tar.gz forge-cf735091ac163cbaafda8a12ead568bf4ed8abbf.tar.zst forge-cf735091ac163cbaafda8a12ead568bf4ed8abbf.zip |
Reuse the cache for /tree
-rw-r--r-- | cache.go | 31 | ||||
-rw-r--r-- | http_handle_repo_index.go | 29 | ||||
-rw-r--r-- | http_handle_repo_tree.go | 121 |
3 files changed, 112 insertions, 69 deletions
diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..e745884 --- /dev/null +++ b/cache.go @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// SPDX-FileContributor: Runxi Yu <https://runxiyu.org> + +package main + +import ( + "html/template" + + "github.com/dgraph-io/ristretto/v2" + "go.lindenii.runxiyu.org/lindenii-common/clog" +) + +type treeReadmeCacheEntry struct { + DisplayTree []displayTreeEntry + ReadmeFilename string + ReadmeRendered template.HTML +} + +var treeReadmeCache *ristretto.Cache[[]byte, treeReadmeCacheEntry] + +func init() { + var err error + treeReadmeCache, err = ristretto.NewCache(&ristretto.Config[[]byte, treeReadmeCacheEntry]{ + NumCounters: 1e4, + MaxCost: 1 << 30, + BufferItems: 64, + }) + if err != nil { + clog.Fatal(1, "Error initializing indexPageCache: "+err.Error()) + } +} diff --git a/http_handle_repo_index.go b/http_handle_repo_index.go index 44151a0..9285e6e 100644 --- a/http_handle_repo_index.go +++ b/http_handle_repo_index.go @@ -4,40 +4,17 @@ package main import ( - "html/template" "iter" "net/http" "strings" "time" - "github.com/dgraph-io/ristretto/v2" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/storer" - "go.lindenii.runxiyu.org/lindenii-common/clog" ) -type indexPageCacheEntry struct { - DisplayTree []displayTreeEntry - ReadmeFilename string - ReadmeRendered template.HTML -} - -var indexPageCache *ristretto.Cache[[]byte, indexPageCacheEntry] - -func init() { - var err error - indexPageCache, err = ristretto.NewCache(&ristretto.Config[[]byte, indexPageCacheEntry]{ - NumCounters: 1e4, - MaxCost: 1 << 30, - BufferItems: 64, - }) - if err != nil { - clog.Fatal(1, "Error initializing indexPageCache: "+err.Error()) - } -} - func httpHandleRepoIndex(writer http.ResponseWriter, _ *http.Request, params map[string]any) { var repo *git.Repository var repoName string @@ -83,7 +60,7 @@ func httpHandleRepoIndex(writer http.ResponseWriter, _ *http.Request, params map commitIterSeq, params["commits_err"] = commitIterSeqErr(commitIter) params["commits"] = iterSeqLimit(commitIterSeq, 3) - if value, found := indexPageCache.Get(refHashSlice); found { + if value, found := treeReadmeCache.Get(refHashSlice); found { params["files"] = value.DisplayTree params["readme_filename"] = value.ReadmeFilename params["readme"] = value.ReadmeRendered @@ -104,12 +81,12 @@ func httpHandleRepoIndex(writer http.ResponseWriter, _ *http.Request, params map params["readme_filename"] = readmeFilename params["readme"] = readmeRendered - entry := indexPageCacheEntry{ + entry := treeReadmeCacheEntry{ DisplayTree: displayTree, ReadmeFilename: readmeFilename, ReadmeRendered: readmeRendered, } - indexPageCache.Set(refHashSlice, entry, cost) + treeReadmeCache.Set(refHashSlice, entry, cost) } no_ref: diff --git a/http_handle_repo_tree.go b/http_handle_repo_tree.go index 229b029..91d7e68 100644 --- a/http_handle_repo_tree.go +++ b/http_handle_repo_tree.go @@ -9,6 +9,7 @@ import ( "net/http" "path" "strings" + "time" "github.com/alecthomas/chroma/v2" chromaHTML "github.com/alecthomas/chroma/v2/formatters/html" @@ -23,6 +24,7 @@ func httpHandleRepoTree(writer http.ResponseWriter, request *http.Request, param var rawPathSpec, pathSpec string var repo *git.Repository var refHash plumbing.Hash + var refHashSlice []byte var commitObject *object.Commit var tree *object.Tree var err error @@ -35,60 +37,93 @@ func httpHandleRepoTree(writer http.ResponseWriter, request *http.Request, param errorPage500(writer, params, "Error getting ref hash: "+err.Error()) return } - if commitObject, err = repo.CommitObject(refHash); err != nil { - errorPage500(writer, params, "Error getting commit object: "+err.Error()) - return - } - if tree, err = commitObject.Tree(); err != nil { - errorPage500(writer, params, "Error getting file tree: "+err.Error()) - return - } + refHashSlice = refHash[:] var target *object.Tree if pathSpec == "" { - target = tree - } else { - if target, err = tree.Tree(pathSpec); err != nil { - var file *object.File - var fileContent string - var lexer chroma.Lexer - var iterator chroma.Iterator - var style *chroma.Style - var formatter *chromaHTML.Formatter - var formattedHTML template.HTML - - if file, err = tree.File(pathSpec); err != nil { - errorPage500(writer, params, "Error retrieving path: "+err.Error()) + if value, found := treeReadmeCache.Get(refHashSlice); found { + params["files"] = value.DisplayTree + params["readme_filename"] = value.ReadmeFilename + params["readme"] = value.ReadmeRendered + } else { + if commitObject, err = repo.CommitObject(refHash); err != nil { + errorPage500(writer, params, "Error getting commit object: "+err.Error()) return } - if redirectNoDir(writer, request) { + if tree, err = commitObject.Tree(); err != nil { + errorPage500(writer, params, "Error getting file tree: "+err.Error()) return } - if fileContent, err = file.Contents(); err != nil { - errorPage500(writer, params, "Error reading file: "+err.Error()) - return - } - lexer = chromaLexers.Match(pathSpec) - if lexer == nil { - lexer = chromaLexers.Fallback - } - if iterator, err = lexer.Tokenise(nil, fileContent); err != nil { - errorPage500(writer, params, "Error tokenizing code: "+err.Error()) - return - } - var formattedHTMLStr bytes.Buffer - style = chromaStyles.Get("autumn") - formatter = chromaHTML.New(chromaHTML.WithClasses(true), chromaHTML.TabWidth(8)) - if err = formatter.Format(&formattedHTMLStr, style, iterator); err != nil { - errorPage500(writer, params, "Error formatting code: "+err.Error()) - return + + start := time.Now() + displayTree := makeDisplayTree(tree) + readmeFilename, readmeRendered := renderReadmeAtTree(tree) + cost := time.Since(start).Nanoseconds() + + params["files"] = displayTree + params["readme_filename"] = readmeFilename + params["readme"] = readmeRendered + + entry := treeReadmeCacheEntry{ + DisplayTree: displayTree, + ReadmeFilename: readmeFilename, + ReadmeRendered: readmeRendered, } - formattedHTML = template.HTML(formattedHTMLStr.Bytes()) //#nosec G203 - params["file_contents"] = formattedHTML + treeReadmeCache.Set(refHashSlice, entry, cost) + } + + renderTemplate(writer, "repo_tree_dir", params) + return + } - renderTemplate(writer, "repo_tree_file", params) + if commitObject, err = repo.CommitObject(refHash); err != nil { + errorPage500(writer, params, "Error getting commit object: "+err.Error()) + return + } + if tree, err = commitObject.Tree(); err != nil { + errorPage500(writer, params, "Error getting file tree: "+err.Error()) + return + } + if target, err = tree.Tree(pathSpec); err != nil { + var file *object.File + var fileContent string + var lexer chroma.Lexer + var iterator chroma.Iterator + var style *chroma.Style + var formatter *chromaHTML.Formatter + var formattedHTML template.HTML + + if file, err = tree.File(pathSpec); err != nil { + errorPage500(writer, params, "Error retrieving path: "+err.Error()) + return + } + if redirectNoDir(writer, request) { + return + } + if fileContent, err = file.Contents(); err != nil { + errorPage500(writer, params, "Error reading file: "+err.Error()) + return + } + lexer = chromaLexers.Match(pathSpec) + if lexer == nil { + lexer = chromaLexers.Fallback + } + if iterator, err = lexer.Tokenise(nil, fileContent); err != nil { + errorPage500(writer, params, "Error tokenizing code: "+err.Error()) + return + } + var formattedHTMLStr bytes.Buffer + style = chromaStyles.Get("autumn") + formatter = chromaHTML.New(chromaHTML.WithClasses(true), chromaHTML.TabWidth(8)) + if err = formatter.Format(&formattedHTMLStr, style, iterator); err != nil { + errorPage500(writer, params, "Error formatting code: "+err.Error()) return } + formattedHTML = template.HTML(formattedHTMLStr.Bytes()) //#nosec G203 + params["file_contents"] = formattedHTML + + renderTemplate(writer, "repo_tree_file", params) + return } if len(rawPathSpec) != 0 && rawPathSpec[len(rawPathSpec)-1] != '/' { |