main de06cbd

cache.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package git
4
5import "sync"
6
7// cacheShards is the number of independently locked LRU lists.
8const cacheShards = 8
9
10type cacheKey struct {
11	pack uint32
12	off  uint64
13}
14
15type cacheEntry struct {
16	key        cacheKey
17	typ        Type
18	data       []byte // immutable once cached
19	prev, next *cacheEntry
20}
21
22// deltaCache is an LRU cache of delta bases bounded in bytes. Cached slices
23// are never modified or recycled, so a reader may keep using one after it is
24// evicted.
25type deltaCache struct {
26	shards [cacheShards]cacheShard
27}
28
29type cacheShard struct {
30	mu         sync.Mutex
31	m          map[cacheKey]*cacheEntry
32	head, tail *cacheEntry // head is the most recently used
33	bytes      int64
34	budget     int64
35}
36
37func newDeltaCache(budget int64) *deltaCache {
38	c := &deltaCache{}
39	for i := range c.shards {
40		c.shards[i].budget = budget / cacheShards
41		c.shards[i].m = make(map[cacheKey]*cacheEntry)
42	}
43	return c
44}
45
46func (c *deltaCache) shard(k cacheKey) *cacheShard {
47	h := uint64(k.pack)*0x9e3779b97f4a7c15 ^ k.off*0xff51afd7ed558ccd
48	return &c.shards[(h>>32)%cacheShards]
49}
50
51func (c *deltaCache) get(k cacheKey) (Type, []byte, bool) {
52	s := c.shard(k)
53	if s.budget <= 0 {
54		return 0, nil, false
55	}
56	s.mu.Lock()
57	e := s.m[k]
58	if e == nil {
59		s.mu.Unlock()
60		return 0, nil, false
61	}
62	s.moveFront(e)
63	typ, data := e.typ, e.data
64	s.mu.Unlock()
65	return typ, data, true
66}
67
68// fits reports whether an object of n bytes can be cached.
69func (c *deltaCache) fits(n int) bool {
70	return int64(n)+64 <= c.shards[0].budget
71}
72
73// putOwned stores data, which the caller must not modify afterwards.
74func (c *deltaCache) putOwned(k cacheKey, typ Type, data []byte) {
75	s := c.shard(k)
76	n := int64(len(data)) + 64
77	if n > s.budget {
78		return
79	}
80	cp := data
81	s.mu.Lock()
82	defer s.mu.Unlock()
83	if e := s.m[k]; e != nil {
84		s.moveFront(e)
85		return
86	}
87	e := &cacheEntry{key: k, typ: typ, data: cp}
88	s.m[k] = e
89	s.pushFront(e)
90	s.bytes += n
91	for s.bytes > s.budget && s.tail != nil {
92		old := s.tail
93		s.unlink(old)
94		delete(s.m, old.key)
95		s.bytes -= int64(len(old.data)) + 64
96	}
97}
98
99func (s *cacheShard) unlink(e *cacheEntry) {
100	if e.prev != nil {
101		e.prev.next = e.next
102	} else {
103		s.head = e.next
104	}
105	if e.next != nil {
106		e.next.prev = e.prev
107	} else {
108		s.tail = e.prev
109	}
110	e.prev, e.next = nil, nil
111}
112
113func (s *cacheShard) pushFront(e *cacheEntry) {
114	e.next = s.head
115	if s.head != nil {
116		s.head.prev = e
117	}
118	s.head = e
119	if s.tail == nil {
120		s.tail = e
121	}
122}
123
124func (s *cacheShard) moveFront(e *cacheEntry) {
125	if s.head == e {
126		return
127	}
128	s.unlink(e)
129	s.pushFront(e)
130}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 2

↑↓ moveenter openesc close