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