v0.7.0 4c16dd8

codec.go RawHistoryFolder

1package kv
2
3import (
4	"encoding/binary"
5	"errors"
6	"hash/crc32"
7	"math"
8	"sort"
9)
10
11var ErrCorrupt = errors.New("kv: corrupt store file")
12
13var fileMagic = [4]byte{'V', 'E', 'V', 'K'}
14
15const (
16	fileVersion    uint16 = 3
17	filePrefixLen         = 4 + 2 + 4
18	entryPrefixLen        = 4 + 4
19	crcLen                = 4
20)
21
22// encodeFile writes magic | version | count | (keyLen key valLen val)* | crc32.
23func encodeFile(data map[string][]byte) []byte {
24	keys := make([]string, 0, len(data))
25	for key := range data {
26		keys = append(keys, key)
27	}
28	sort.Strings(keys)
29
30	size := filePrefixLen + crcLen
31	for _, key := range keys {
32		size += entryPrefixLen + len(key) + len(data[key])
33	}
34	buf := make([]byte, 0, size)
35	buf = append(buf, fileMagic[:]...)
36	buf = binary.BigEndian.AppendUint16(buf, fileVersion)
37	buf = binary.BigEndian.AppendUint32(buf, uint32(len(keys)))
38	for _, key := range keys {
39		value := data[key]
40		buf = binary.BigEndian.AppendUint32(buf, uint32(len(key)))
41		buf = append(buf, key...)
42		buf = binary.BigEndian.AppendUint32(buf, uint32(len(value)))
43		buf = append(buf, value...)
44	}
45	return appendCRC(buf)
46}
47
48func appendCRC(payload []byte) []byte {
49	return binary.BigEndian.AppendUint32(payload, crc32.ChecksumIEEE(payload))
50}
51
52// decodeFile validates the single trailing CRC and rejects every malformed
53// existing file. Callers must not replace or truncate a file that fails here.
54func decodeFile(raw []byte) (map[string][]byte, error) {
55	if len(raw) < filePrefixLen+crcLen {
56		return nil, ErrCorrupt
57	}
58	payload := raw[:len(raw)-crcLen]
59	wantCRC := binary.BigEndian.Uint32(raw[len(raw)-crcLen:])
60	if crc32.ChecksumIEEE(payload) != wantCRC {
61		return nil, ErrCorrupt
62	}
63	if string(payload[:4]) != string(fileMagic[:]) || binary.BigEndian.Uint16(payload[4:6]) != fileVersion {
64		return nil, ErrCorrupt
65	}
66
67	count := binary.BigEndian.Uint32(payload[6:10])
68	off := filePrefixLen
69	data := make(map[string][]byte)
70	for range count {
71		keyLen, next, ok := decodeLength(payload, off)
72		if !ok {
73			return nil, ErrCorrupt
74		}
75		off = next
76		if keyLen > uint64(len(payload)-off) {
77			return nil, ErrCorrupt
78		}
79		key := string(payload[off : off+int(keyLen)])
80		off += int(keyLen)
81		valueLen, next, ok := decodeLength(payload, off)
82		if !ok {
83			return nil, ErrCorrupt
84		}
85		off = next
86		if valueLen > uint64(len(payload)-off) {
87			return nil, ErrCorrupt
88		}
89		if _, duplicate := data[key]; duplicate {
90			return nil, ErrCorrupt
91		}
92		data[key] = append([]byte(nil), payload[off:off+int(valueLen)]...)
93		off += int(valueLen)
94	}
95	if off != len(payload) {
96		return nil, ErrCorrupt
97	}
98	return data, nil
99}
100
101func decodeLength(payload []byte, off int) (uint64, int, bool) {
102	if off < 0 || len(payload)-off < 4 {
103		return 0, off, false
104	}
105	length := uint64(binary.BigEndian.Uint32(payload[off : off+4]))
106	if length > uint64(math.MaxInt) {
107		return 0, off, false
108	}
109	return length, off + 4, true
110}

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 12

Tags 11

↑↓ moveenter openesc close