From 3e884f5f3d42cbc4874a04da31dde10314b0cfad Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Thu, 26 Mar 2026 09:17:14 +0000 Subject: format: Move commitgraph and packfile here --- format/commitgraph/read/bloom.go | 117 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 format/commitgraph/read/bloom.go (limited to 'format/commitgraph/read/bloom.go') diff --git a/format/commitgraph/read/bloom.go b/format/commitgraph/read/bloom.go new file mode 100644 index 00000000..12dd6db3 --- /dev/null +++ b/format/commitgraph/read/bloom.go @@ -0,0 +1,117 @@ +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. +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 +} -- cgit v1.3.1-10-gc9f91