Fast implementation of Git in pure Go
1package read
2
3import (
4 "encoding/binary"
5
6 "codeberg.org/lindenii/furgit/commitgraph/bloom"
7 "codeberg.org/lindenii/furgit/internal/intconv"
8)
9
10// HasBloom reports whether any layer has changed-path Bloom data.
11func (reader *Reader) HasBloom() bool {
12 for i := range reader.layers {
13 layer := &reader.layers[i]
14 if layer.chunkBloomIndex != nil && layer.chunkBloomData != nil && layer.bloomSettings != nil {
15 return true
16 }
17 }
18
19 return false
20}
21
22// BloomVersion returns the changed-path Bloom hash version, or 0 if absent.
23func (reader *Reader) BloomVersion() uint8 {
24 for i := len(reader.layers) - 1; i >= 0; i-- {
25 layer := &reader.layers[i]
26 if layer.bloomSettings != nil {
27 version, err := intconv.Uint32ToUint8(layer.bloomSettings.HashVersion)
28 if err != nil {
29 return 0
30 }
31
32 return version
33 }
34 }
35
36 return 0
37}
38
39// BloomFilterAt returns one commit's changed-path Bloom filter.
40//
41// Returns BloomUnavailableError when this commit graph has no Bloom data.
42func (reader *Reader) BloomFilterAt(pos Position) (*bloom.Filter, error) {
43 layer, err := reader.layerByPosition(pos)
44 if err != nil {
45 return nil, err
46 }
47
48 if layer.chunkBloomIndex == nil || layer.chunkBloomData == nil || layer.bloomSettings == nil {
49 return nil, &BloomUnavailableError{Pos: pos}
50 }
51
52 start, end, err := bloomRange(layer, pos.Index)
53 if err != nil {
54 return nil, err
55 }
56
57 filter := bloom.NewFilter(
58 layer.chunkBloomData[bloom.DataHeaderSize+start:bloom.DataHeaderSize+end],
59 *layer.bloomSettings,
60 )
61
62 return filter, nil
63}
64
65func bloomRange(layer *layer, commitIndex uint32) (int, int, error) {
66 off64 := uint64(commitIndex) * 4
67
68 off, err := intconv.Uint64ToInt(off64)
69 if err != nil {
70 return 0, 0, err
71 }
72
73 end := binary.BigEndian.Uint32(layer.chunkBloomIndex[off : off+4])
74
75 var start uint32
76
77 if commitIndex > 0 {
78 prevOff64 := uint64(commitIndex-1) * 4
79
80 prevOff, err := intconv.Uint64ToInt(prevOff64)
81 if err != nil {
82 return 0, 0, err
83 }
84
85 start = binary.BigEndian.Uint32(layer.chunkBloomIndex[prevOff : prevOff+4])
86 }
87
88 if end < start {
89 return 0, 0, &MalformedError{Path: layer.path, Reason: "invalid BIDX range"}
90 }
91
92 bdatLen := len(layer.chunkBloomData) - bloom.DataHeaderSize
93
94 bdatLenU32, err := intconv.IntToUint32(bdatLen)
95 if err != nil {
96 return 0, 0, err
97 }
98
99 if end > bdatLenU32 {
100 return 0, 0, &MalformedError{Path: layer.path, Reason: "BIDX range out of BDAT bounds"}
101 }
102
103 startInt, err := intconv.Uint64ToInt(uint64(start))
104 if err != nil {
105 return 0, 0, err
106 }
107
108 endInt, err := intconv.Uint64ToInt(uint64(end))
109 if err != nil {
110 return 0, 0, err
111 }
112
113 return startInt, endInt, nil
114}