aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-04 08:26:56 +0800
committerGravatar Runxi Yu2026-03-04 08:59:53 +0800
commitab7501be34032fb9e5c48726a68ae90a917af9eb (patch)
tree20d005647569befea8133e953c3270e8fd2a2a5b /internal
parent*: gofumpt (diff)
signatureNo signature
*: Lint
Diffstat (limited to 'internal')
-rw-r--r--internal/adler32/adler32_amd64.go8
-rw-r--r--internal/adler32/adler32_generic.go5
-rw-r--r--internal/adler32/bench_test.go3
-rw-r--r--internal/bufpool/buffers.go17
-rw-r--r--internal/bufpool/buffers_test.go9
-rw-r--r--internal/intconv/intconv.go4
-rw-r--r--internal/iolimit/expect_length_reader.go5
-rw-r--r--internal/iolimit/expect_length_reader_test.go8
-rw-r--r--internal/lru/lru.go14
-rw-r--r--internal/lru/lru_test.go31
-rw-r--r--internal/testgit/algorithms.go1
-rw-r--r--internal/testgit/repo_cat_file.go1
-rw-r--r--internal/testgit/repo_commit_tree.go5
-rw-r--r--internal/testgit/repo_hash_object.go2
-rw-r--r--internal/testgit/repo_make_commit.go1
-rw-r--r--internal/testgit/repo_make_single_file_tree.go1
-rw-r--r--internal/testgit/repo_mktree.go2
-rw-r--r--internal/testgit/repo_new.go4
-rw-r--r--internal/testgit/repo_refs.go4
-rw-r--r--internal/testgit/repo_repack.go1
-rw-r--r--internal/testgit/repo_rev_parse.go2
-rw-r--r--internal/testgit/repo_run.go7
-rw-r--r--internal/testgit/repo_tag_annotated.go1
-rw-r--r--internal/zlib/reader.go50
-rw-r--r--internal/zlib/writer.go39
25 files changed, 211 insertions, 14 deletions
diff --git a/internal/adler32/adler32_amd64.go b/internal/adler32/adler32_amd64.go
index cb67f21c..7dfab299 100644
--- a/internal/adler32/adler32_amd64.go
+++ b/internal/adler32/adler32_amd64.go
@@ -27,8 +27,10 @@ func New() hash.Hash32 {
if !hasAVX2 {
return adler32.New()
}
+
d := new(digest)
d.Reset()
+
return d
}
@@ -36,6 +38,7 @@ func (d *digest) MarshalBinary() ([]byte, error) {
b := make([]byte, 0, marshaledSize)
b = append(b, magic...)
b = binary.BigEndian.AppendUint32(b, uint32(*d))
+
return b, nil
}
@@ -43,10 +46,13 @@ func (d *digest) UnmarshalBinary(b []byte) error {
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
return errors.New("hash/adler32: invalid hash state identifier")
}
+
if len(b) != marshaledSize {
return errors.New("hash/adler32: invalid hash state size")
}
+
*d = digest(binary.BigEndian.Uint32(b[len(magic):]))
+
return nil
}
@@ -62,6 +68,7 @@ func (d *digest) Write(data []byte) (nn int, err error) {
h := update(uint32(*d), data)
*d = digest(h)
}
+
return len(data), nil
}
@@ -76,5 +83,6 @@ func Checksum(data []byte) uint32 {
if hasAVX2 && len(data) >= 64 {
return adler32_avx2(1, data)
}
+
return adler32.Checksum(data)
}
diff --git a/internal/adler32/adler32_generic.go b/internal/adler32/adler32_generic.go
index 0908d8f7..56e3ff8b 100644
--- a/internal/adler32/adler32_generic.go
+++ b/internal/adler32/adler32_generic.go
@@ -16,11 +16,13 @@ const (
// Add p to the running checksum d.
func update(d uint32, p []byte) uint32 {
s1, s2 := d&0xffff, d>>16
+
for len(p) > 0 {
var q []byte
if len(p) > nmax {
p, q = p[:nmax], p[nmax:]
}
+
for len(p) >= 4 {
s1 += uint32(p[0])
s2 += s1
@@ -32,13 +34,16 @@ func update(d uint32, p []byte) uint32 {
s2 += s1
p = p[4:]
}
+
for _, x := range p {
s1 += uint32(x)
s2 += s1
}
+
s1 %= mod
s2 %= mod
p = q
}
+
return s2<<16 | s1
}
diff --git a/internal/adler32/bench_test.go b/internal/adler32/bench_test.go
index 6c6f75ea..d2aebe8f 100644
--- a/internal/adler32/bench_test.go
+++ b/internal/adler32/bench_test.go
@@ -10,7 +10,7 @@ const benchmarkSize = 64 * 1024
var data = make([]byte, benchmarkSize)
-func init() {
+func init() { //nolint:gochecknoinits
for i := range benchmarkSize {
data[i] = byte(i % 256)
}
@@ -18,6 +18,7 @@ func init() {
func BenchmarkChecksum(b *testing.B) {
b.ReportAllocs()
+
for b.Loop() {
adler32.Checksum(data)
}
diff --git a/internal/bufpool/buffers.go b/internal/bufpool/buffers.go
index a5c27b67..91e30a31 100644
--- a/internal/bufpool/buffers.go
+++ b/internal/bufpool/buffers.go
@@ -62,9 +62,11 @@ var bufferPools = func() []sync.Pool {
capCopy := classCap
pools[i].New = func() any {
buf := make([]byte, 0, capCopy)
+
return &buf
}
}
+
return pools
}()
@@ -80,9 +82,11 @@ func Borrow(capHint int) Buffer {
if capHint < DefaultBufferCap {
capHint = DefaultBufferCap
}
+
classIdx, classCap, pooled := classFor(capHint)
if !pooled {
newBuf := make([]byte, 0, capHint)
+
return Buffer{buf: newBuf, pool: unpooled}
}
//nolint:forcetypeassert
@@ -90,7 +94,9 @@ func Borrow(capHint int) Buffer {
if cap(*buf) < classCap {
*buf = make([]byte, 0, classCap)
}
+
slice := (*buf)[:0]
+
return Buffer{buf: slice, pool: poolIndex(classIdx)} //#nosec G115
}
@@ -110,6 +116,7 @@ func (buf *Buffer) Resize(n int) {
if n < 0 {
n = 0
}
+
buf.ensureCapacity(n)
buf.buf = buf.buf[:n]
}
@@ -122,6 +129,7 @@ func (buf *Buffer) Append(src []byte) {
if len(src) == 0 {
return
}
+
start := len(buf.buf)
buf.ensureCapacity(start + len(src))
buf.buf = buf.buf[:start+len(src)]
@@ -144,6 +152,7 @@ func (buf *Buffer) Release() {
if buf.buf == nil {
return
}
+
buf.returnToPool()
buf.buf = nil
buf.pool = unpooled
@@ -157,20 +166,26 @@ func (buf *Buffer) ensureCapacity(needed int) {
if cap(buf.buf) >= needed {
return
}
+
classIdx, classCap, pooled := classFor(needed)
+
var newBuf []byte
+
if pooled {
//nolint:forcetypeassert
raw := bufferPools[classIdx].Get().(*[]byte)
if cap(*raw) < classCap {
*raw = make([]byte, 0, classCap)
}
+
newBuf = (*raw)[:len(buf.buf)]
} else {
newBuf = make([]byte, len(buf.buf), classCap)
}
+
copy(newBuf, buf.buf)
buf.returnToPool()
+
buf.buf = newBuf
if pooled {
buf.pool = poolIndex(classIdx) //#nosec G115
@@ -185,6 +200,7 @@ func classFor(size int) (idx, classCap int, ok bool) {
return i, class, true
}
}
+
return -1, size, false
}
@@ -192,6 +208,7 @@ func (buf *Buffer) returnToPool() {
if buf.pool == unpooled {
return
}
+
tmp := buf.buf[:0]
bufferPools[int(buf.pool)].Put(&tmp)
}
diff --git a/internal/bufpool/buffers_test.go b/internal/bufpool/buffers_test.go
index 70861d33..224fa98c 100644
--- a/internal/bufpool/buffers_test.go
+++ b/internal/bufpool/buffers_test.go
@@ -15,19 +15,23 @@ func TestBorrowBufferResizeAndAppend(t *testing.T) {
b.Append([]byte("alpha"))
b.Append([]byte("beta"))
+
if got := string(b.Bytes()); got != "alphabeta" {
t.Fatalf("unexpected contents: %q", got)
}
b.Resize(3)
+
if got := string(b.Bytes()); got != "alp" {
t.Fatalf("resize shrink mismatch: %q", got)
}
b.Resize(8)
+
if len(b.Bytes()) != 8 {
t.Fatalf("expected len 8 after grow, got %d", len(b.Bytes()))
}
+
if prefix := string(b.Bytes()[:3]); prefix != "alp" {
t.Fatalf("prefix lost after grow: %q", prefix)
}
@@ -39,6 +43,7 @@ func TestBorrowBufferRelease(t *testing.T) {
b := Borrow(DefaultBufferCap / 2)
b.Append([]byte("data"))
b.Release()
+
if b.buf != nil {
t.Fatal("expected buffer cleared after release")
}
@@ -59,9 +64,11 @@ func TestBorrowUsesLargerPools(t *testing.T) {
if b.pool != poolIndex(classIdx) {
t.Fatalf("expected pooled buffer in class %d, got %d", classIdx, b.pool)
}
+
if cap(b.buf) != classCap {
t.Fatalf("expected capacity %d, got %d", classCap, cap(b.buf))
}
+
b.Release()
b2 := Borrow(request)
@@ -70,6 +77,7 @@ func TestBorrowUsesLargerPools(t *testing.T) {
if b2.pool != poolIndex(classIdx) {
t.Fatalf("expected pooled buffer in class %d on reuse, got %d", classIdx, b2.pool)
}
+
if cap(b2.buf) != classCap {
t.Fatalf("expected capacity %d on reuse, got %d", classCap, cap(b2.buf))
}
@@ -82,6 +90,7 @@ func TestGrowingBufferStaysPooled(t *testing.T) {
defer b.Release()
b.Append(make([]byte, DefaultBufferCap*3))
+
if b.pool == unpooled {
t.Fatal("buffer should stay pooled after growth within limit")
}
diff --git a/internal/intconv/intconv.go b/internal/intconv/intconv.go
index 8bc77d8e..67f99a14 100644
--- a/internal/intconv/intconv.go
+++ b/internal/intconv/intconv.go
@@ -11,6 +11,7 @@ func Uint64ToInt(v uint64) (int, error) {
if v > uint64(math.MaxInt) {
return 0, fmt.Errorf("intconv: uint64 %d overflows int", v)
}
+
return int(v), nil
}
@@ -19,6 +20,7 @@ func UintptrToInt(v uintptr) (int, error) {
if v > uintptr(math.MaxInt) {
return 0, fmt.Errorf("intconv: uintptr %d overflows int", v)
}
+
return int(v), nil
}
@@ -27,6 +29,7 @@ func IntToUint64(v int) (uint64, error) {
if v < 0 {
return 0, fmt.Errorf("intconv: int %d is negative", v)
}
+
return uint64(v), nil
}
@@ -35,5 +38,6 @@ func Int64ToInt32(v int64) (int32, error) {
if v < math.MinInt32 || v > math.MaxInt32 {
return 0, fmt.Errorf("intconv: int64 %d overflows int32", v)
}
+
return int32(v), nil
}
diff --git a/internal/iolimit/expect_length_reader.go b/internal/iolimit/expect_length_reader.go
index 477c207f..288e0e62 100644
--- a/internal/iolimit/expect_length_reader.go
+++ b/internal/iolimit/expect_length_reader.go
@@ -39,13 +39,16 @@ func (reader *expectLengthReader) Read(dst []byte) (int, error) {
if reader.remaining == 0 {
var probe [1]byte
+
n, err := reader.src.Read(probe[:])
if n > 0 {
return 0, ErrExpectedLengthExceeded
}
+
if err == nil {
return 0, nil
}
+
return 0, err
}
@@ -66,9 +69,11 @@ func (reader *expectLengthReader) Read(dst []byte) (int, error) {
if reader.remaining > 0 {
return n, io.ErrUnexpectedEOF
}
+
if n > 0 {
return n, nil
}
+
return 0, io.EOF
}
diff --git a/internal/iolimit/expect_length_reader_test.go b/internal/iolimit/expect_length_reader_test.go
index 503c88ed..e2cfeab0 100644
--- a/internal/iolimit/expect_length_reader_test.go
+++ b/internal/iolimit/expect_length_reader_test.go
@@ -13,15 +13,18 @@ func TestExpectLengthReaderExact(t *testing.T) {
t.Parallel()
r := iolimit.ExpectLengthReader(bytes.NewReader([]byte("hello")), 5)
+
got, err := io.ReadAll(r)
if err != nil {
t.Fatalf("ReadAll error: %v", err)
}
+
if !bytes.Equal(got, []byte("hello")) {
t.Fatalf("ReadAll = %q, want %q", got, "hello")
}
buf := make([]byte, 1)
+
n, err := r.Read(buf)
if n != 0 || !errors.Is(err, io.EOF) {
t.Fatalf("post-boundary Read = (%d,%v), want (0,EOF)", n, err)
@@ -32,6 +35,7 @@ func TestExpectLengthReaderShort(t *testing.T) {
t.Parallel()
r := iolimit.ExpectLengthReader(bytes.NewReader([]byte("hey")), 5)
+
_, err := io.ReadAll(r)
if !errors.Is(err, io.ErrUnexpectedEOF) {
t.Fatalf("ReadAll error = %v, want ErrUnexpectedEOF", err)
@@ -43,15 +47,18 @@ func TestExpectLengthReaderLongDetectedOnNextRead(t *testing.T) {
r := iolimit.ExpectLengthReader(bytes.NewReader([]byte("hello!")), 5)
buf := make([]byte, 5)
+
n, err := io.ReadFull(r, buf)
if err != nil {
t.Fatalf("ReadFull error: %v", err)
}
+
if n != 5 || !bytes.Equal(buf, []byte("hello")) {
t.Fatalf("ReadFull = (%d,%q), want (5,hello)", n, buf)
}
probe := make([]byte, 1)
+
n, err = r.Read(probe)
if n != 0 || !errors.Is(err, iolimit.ErrExpectedLengthExceeded) {
t.Fatalf("overflow Read = (%d,%v), want (0,ErrExpectedLengthExceeded)", n, err)
@@ -63,6 +70,7 @@ func TestExpectLengthReaderEmptyExpected(t *testing.T) {
r := iolimit.ExpectLengthReader(bytes.NewReader(nil), 0)
buf := make([]byte, 1)
+
n, err := r.Read(buf)
if n != 0 || !errors.Is(err, io.EOF) {
t.Fatalf("Read = (%d,%v), want (0,EOF)", n, err)
diff --git a/internal/lru/lru.go b/internal/lru/lru.go
index 585aaa3f..fcbab646 100644
--- a/internal/lru/lru.go
+++ b/internal/lru/lru.go
@@ -39,9 +39,11 @@ func New[K comparable, V any](maxWeight int64, weightFn WeightFunc[K, V], onEvic
if maxWeight < 0 {
panic("lru: negative max weight")
}
+
if weightFn == nil {
panic("lru: nil weight function")
}
+
return &Cache[K, V]{
maxWeight: maxWeight,
weightFn: weightFn,
@@ -61,6 +63,7 @@ func (cache *Cache[K, V]) Add(key K, value V) bool {
if w < 0 {
panic("lru: negative entry weight")
}
+
if w > cache.maxWeight {
return false
}
@@ -79,6 +82,7 @@ func (cache *Cache[K, V]) Add(key K, value V) bool {
cache.weight += w
cache.evictOverBudget()
+
return true
}
@@ -87,8 +91,10 @@ func (cache *Cache[K, V]) Get(key K) (V, bool) {
elem, ok := cache.items[key]
if !ok {
var zero V
+
return zero, false
}
+
cache.lru.MoveToBack(elem)
//nolint:forcetypeassert
return elem.Value.(*entry[K, V]).value, true
@@ -99,6 +105,7 @@ func (cache *Cache[K, V]) Peek(key K) (V, bool) {
elem, ok := cache.items[key]
if !ok {
var zero V
+
return zero, false
}
//nolint:forcetypeassert
@@ -110,9 +117,12 @@ func (cache *Cache[K, V]) Remove(key K) (V, bool) {
elem, ok := cache.items[key]
if !ok {
var zero V
+
return zero, false
}
+
ent := cache.removeElem(elem)
+
return ent.value, true
}
@@ -148,6 +158,7 @@ func (cache *Cache[K, V]) SetMaxWeight(maxWeight int64) {
if maxWeight < 0 {
panic("lru: negative max weight")
}
+
cache.maxWeight = maxWeight
cache.evictOverBudget()
}
@@ -158,6 +169,7 @@ func (cache *Cache[K, V]) evictOverBudget() {
if elem == nil {
return
}
+
cache.removeElem(elem)
}
}
@@ -167,9 +179,11 @@ func (cache *Cache[K, V]) removeElem(elem *list.Element) *entry[K, V] {
ent := elem.Value.(*entry[K, V])
cache.lru.Remove(elem)
delete(cache.items, ent.key)
+
cache.weight -= ent.weight
if cache.onEvict != nil {
cache.onEvict(ent.key, ent.value)
}
+
return ent
}
diff --git a/internal/lru/lru_test.go b/internal/lru/lru_test.go
index adfec403..006a32b8 100644
--- a/internal/lru/lru_test.go
+++ b/internal/lru/lru_test.go
@@ -27,9 +27,11 @@ func TestCacheEvictsLRUAndGetUpdatesRecency(t *testing.T) {
if _, ok := cache.Peek("a"); ok {
t.Fatalf("expected a to be evicted")
}
+
if _, ok := cache.Peek("b"); !ok {
t.Fatalf("expected b to be present")
}
+
if _, ok := cache.Peek("c"); !ok {
t.Fatalf("expected c to be present")
}
@@ -37,14 +39,17 @@ func TestCacheEvictsLRUAndGetUpdatesRecency(t *testing.T) {
if _, ok := cache.Get("b"); !ok {
t.Fatalf("Get(b) should hit")
}
+
cache.Add("d", testValue{weight: 4, label: "d"})
if _, ok := cache.Peek("c"); ok {
t.Fatalf("expected c to be evicted after b was touched")
}
+
if _, ok := cache.Peek("b"); !ok {
t.Fatalf("expected b to remain present")
}
+
if _, ok := cache.Peek("d"); !ok {
t.Fatalf("expected d to be present")
}
@@ -60,11 +65,13 @@ func TestCachePeekDoesNotUpdateRecency(t *testing.T) {
if _, ok := cache.Peek("a"); !ok {
t.Fatalf("Peek(a) should hit")
}
+
cache.Add("c", testValue{weight: 2, label: "c"})
if _, ok := cache.Peek("a"); ok {
t.Fatalf("expected a to be evicted; Peek must not update recency")
}
+
if _, ok := cache.Peek("b"); !ok {
t.Fatalf("expected b to remain present")
}
@@ -74,6 +81,7 @@ func TestCacheReplaceAndResize(t *testing.T) {
t.Parallel()
var evicted []string
+
cache := lru.New[string, testValue](10, weightFn, func(key string, value testValue) {
evicted = append(evicted, key+":"+value.label)
})
@@ -85,17 +93,21 @@ func TestCacheReplaceAndResize(t *testing.T) {
if cache.Weight() != 10 {
t.Fatalf("Weight() = %d, want 10", cache.Weight())
}
+
if got, ok := cache.Peek("a"); !ok || got.label != "new" {
t.Fatalf("Peek(a) = (%+v,%v), want new,true", got, ok)
}
+
if !slices.Equal(evicted, []string{"a:old"}) {
t.Fatalf("evicted = %v, want [a:old]", evicted)
}
cache.SetMaxWeight(8)
+
if _, ok := cache.Peek("b"); ok {
t.Fatalf("expected b to be evicted after shrinking max weight")
}
+
if !slices.Equal(evicted, []string{"a:old", "b:b"}) {
t.Fatalf("evicted = %v, want [a:old b:b]", evicted)
}
@@ -105,6 +117,7 @@ func TestCacheRejectsOversizedWithoutMutation(t *testing.T) {
t.Parallel()
var evicted []string
+
cache := lru.New[string, testValue](5, weightFn, func(key string, value testValue) {
evicted = append(evicted, key)
})
@@ -113,12 +126,15 @@ func TestCacheRejectsOversizedWithoutMutation(t *testing.T) {
if ok := cache.Add("b", testValue{weight: 6, label: "b"}); ok {
t.Fatalf("Add oversized should return false")
}
+
if got, ok := cache.Peek("a"); !ok || got.label != "a" {
t.Fatalf("cache should remain unchanged after oversized add")
}
+
if cache.Weight() != 3 {
t.Fatalf("Weight() = %d, want 3", cache.Weight())
}
+
if len(evicted) != 0 {
t.Fatalf("evicted = %v, want none", evicted)
}
@@ -126,9 +142,11 @@ func TestCacheRejectsOversizedWithoutMutation(t *testing.T) {
if ok := cache.Add("a", testValue{weight: 6, label: "new"}); ok {
t.Fatalf("oversized replace should return false")
}
+
if got, ok := cache.Peek("a"); !ok || got.label != "a" {
t.Fatalf("existing key should remain unchanged after oversized replace")
}
+
if len(evicted) != 0 {
t.Fatalf("evicted = %v, want none", evicted)
}
@@ -138,6 +156,7 @@ func TestCacheRemoveAndClear(t *testing.T) {
t.Parallel()
var evicted []string
+
cache := lru.New[string, testValue](10, weightFn, func(key string, value testValue) {
evicted = append(evicted, key)
})
@@ -150,11 +169,13 @@ func TestCacheRemoveAndClear(t *testing.T) {
if !ok || removed.label != "b" {
t.Fatalf("Remove(b) = (%+v,%v), want b,true", removed, ok)
}
+
if cache.Len() != 2 || cache.Weight() != 6 {
t.Fatalf("post-remove Len/Weight = %d/%d, want 2/6", cache.Len(), cache.Weight())
}
cache.Clear()
+
if cache.Len() != 0 || cache.Weight() != 0 {
t.Fatalf("post-clear Len/Weight = %d/%d, want 0/0", cache.Len(), cache.Weight())
}
@@ -170,45 +191,55 @@ func TestCachePanicsForInvalidConfiguration(t *testing.T) {
t.Run("negative max", func(t *testing.T) {
t.Parallel()
+
defer func() {
if recover() == nil {
t.Fatalf("expected panic")
}
}()
+
_ = lru.New[string, testValue](-1, weightFn, nil)
})
t.Run("nil weight function", func(t *testing.T) {
t.Parallel()
+
defer func() {
if recover() == nil {
t.Fatalf("expected panic")
}
}()
+
_ = lru.New[string, testValue](1, nil, nil)
})
t.Run("negative entry weight", func(t *testing.T) {
t.Parallel()
+
cache := lru.New[string, testValue](10, func(_ string, _ testValue) int64 {
return -1
}, nil)
+
defer func() {
if recover() == nil {
t.Fatalf("expected panic")
}
}()
+
cache.Add("x", testValue{weight: 1, label: "x"})
})
t.Run("set negative max", func(t *testing.T) {
t.Parallel()
+
cache := lru.New[string, testValue](10, weightFn, nil)
+
defer func() {
if recover() == nil {
t.Fatalf("expected panic")
}
}()
+
cache.SetMaxWeight(-1)
})
}
diff --git a/internal/testgit/algorithms.go b/internal/testgit/algorithms.go
index 81af4f75..5534aad0 100644
--- a/internal/testgit/algorithms.go
+++ b/internal/testgit/algorithms.go
@@ -9,6 +9,7 @@ import (
// ForEachAlgorithm runs a subtest for every supported algorithm.
func ForEachAlgorithm(t *testing.T, fn func(t *testing.T, algo objectid.Algorithm)) {
t.Helper()
+
for _, algo := range objectid.SupportedAlgorithms() {
t.Run(algo.String(), func(t *testing.T) {
fn(t, algo)
diff --git a/internal/testgit/repo_cat_file.go b/internal/testgit/repo_cat_file.go
index 9cc56db6..1325cf6f 100644
--- a/internal/testgit/repo_cat_file.go
+++ b/internal/testgit/repo_cat_file.go
@@ -9,5 +9,6 @@ import (
// CatFile returns raw output from git cat-file.
func (testRepo *TestRepo) CatFile(tb testing.TB, mode string, id objectid.ObjectID) []byte {
tb.Helper()
+
return testRepo.RunBytes(tb, "cat-file", mode, id.String())
}
diff --git a/internal/testgit/repo_commit_tree.go b/internal/testgit/repo_commit_tree.go
index 763474c2..5eee21ba 100644
--- a/internal/testgit/repo_commit_tree.go
+++ b/internal/testgit/repo_commit_tree.go
@@ -9,16 +9,21 @@ import (
// CommitTree creates a commit from a tree and message, optionally with parents.
func (testRepo *TestRepo) CommitTree(tb testing.TB, tree objectid.ObjectID, message string, parents ...objectid.ObjectID) objectid.ObjectID {
tb.Helper()
+
args := make([]string, 0, 2+2*len(parents)+2)
+
args = append(args, "commit-tree", tree.String())
for _, p := range parents {
args = append(args, "-p", p.String())
}
+
args = append(args, "-m", message)
hex := testRepo.Run(tb, args...)
+
id, err := objectid.ParseHex(testRepo.algo, hex)
if err != nil {
tb.Fatalf("parse commit-tree output %q: %v", hex, err)
}
+
return id
}
diff --git a/internal/testgit/repo_hash_object.go b/internal/testgit/repo_hash_object.go
index 10a05381..bc2def72 100644
--- a/internal/testgit/repo_hash_object.go
+++ b/internal/testgit/repo_hash_object.go
@@ -10,9 +10,11 @@ import (
func (testRepo *TestRepo) HashObject(tb testing.TB, objType string, body []byte) objectid.ObjectID {
tb.Helper()
hex := testRepo.RunInput(tb, body, "hash-object", "-t", objType, "-w", "--stdin")
+
id, err := objectid.ParseHex(testRepo.algo, hex)
if err != nil {
tb.Fatalf("parse git hash-object output %q: %v", hex, err)
}
+
return id
}
diff --git a/internal/testgit/repo_make_commit.go b/internal/testgit/repo_make_commit.go
index a569dfb1..c8bdc428 100644
--- a/internal/testgit/repo_make_commit.go
+++ b/internal/testgit/repo_make_commit.go
@@ -11,5 +11,6 @@ func (testRepo *TestRepo) MakeCommit(tb testing.TB, message string) (objectid.Ob
tb.Helper()
blobID, treeID := testRepo.MakeSingleFileTree(tb, "file.txt", []byte("commit-body\n"))
commitID := testRepo.CommitTree(tb, treeID, message)
+
return blobID, treeID, commitID
}
diff --git a/internal/testgit/repo_make_single_file_tree.go b/internal/testgit/repo_make_single_file_tree.go
index 7c53c658..e7a235a7 100644
--- a/internal/testgit/repo_make_single_file_tree.go
+++ b/internal/testgit/repo_make_single_file_tree.go
@@ -13,5 +13,6 @@ func (testRepo *TestRepo) MakeSingleFileTree(tb testing.TB, fileName string, fil
blobID := testRepo.HashObject(tb, "blob", fileContent)
treeInput := fmt.Sprintf("100644 blob %s\t%s\n", blobID.String(), fileName)
treeID := testRepo.Mktree(tb, treeInput)
+
return blobID, treeID
}
diff --git a/internal/testgit/repo_mktree.go b/internal/testgit/repo_mktree.go
index 34e6388d..565a0083 100644
--- a/internal/testgit/repo_mktree.go
+++ b/internal/testgit/repo_mktree.go
@@ -10,9 +10,11 @@ import (
func (testRepo *TestRepo) Mktree(tb testing.TB, input string) objectid.ObjectID {
tb.Helper()
hex := testRepo.RunInput(tb, []byte(input), "mktree")
+
id, err := objectid.ParseHex(testRepo.algo, hex)
if err != nil {
tb.Fatalf("parse mktree output %q: %v", hex, err)
}
+
return id
}
diff --git a/internal/testgit/repo_new.go b/internal/testgit/repo_new.go
index 8120a9a2..8a71e406 100644
--- a/internal/testgit/repo_new.go
+++ b/internal/testgit/repo_new.go
@@ -21,6 +21,7 @@ type RepoOptions struct {
// NewRepo creates a temporary repository initialized with the requested options.
func NewRepo(tb testing.TB, opts RepoOptions) *TestRepo {
tb.Helper()
+
algo := opts.ObjectFormat
if algo.Size() == 0 {
tb.Fatalf("invalid algorithm: %v", algo)
@@ -47,10 +48,13 @@ func NewRepo(tb testing.TB, opts RepoOptions) *TestRepo {
if opts.Bare {
args = append(args, "--bare")
}
+
if opts.RefFormat != "" {
args = append(args, "--ref-format="+opts.RefFormat)
}
+
args = append(args, dir)
testRepo.runBytes(tb, nil, "", args...)
+
return testRepo
}
diff --git a/internal/testgit/repo_refs.go b/internal/testgit/repo_refs.go
index eb09a78b..66e08561 100644
--- a/internal/testgit/repo_refs.go
+++ b/internal/testgit/repo_refs.go
@@ -28,6 +28,7 @@ func (testRepo *TestRepo) SymbolicRef(tb testing.TB, name, target string) {
// PackRefs runs git pack-refs with args.
func (testRepo *TestRepo) PackRefs(tb testing.TB, args ...string) {
tb.Helper()
+
cmd := append([]string{"pack-refs"}, args...)
testRepo.Run(tb, cmd...)
}
@@ -35,10 +36,13 @@ func (testRepo *TestRepo) PackRefs(tb testing.TB, args ...string) {
// ShowRef returns lines from git show-ref output.
func (testRepo *TestRepo) ShowRef(tb testing.TB, args ...string) []string {
tb.Helper()
+
cmd := append([]string{"show-ref"}, args...)
+
out := testRepo.Run(tb, cmd...)
if strings.TrimSpace(out) == "" {
return nil
}
+
return strings.Split(strings.TrimSpace(out), "\n")
}
diff --git a/internal/testgit/repo_repack.go b/internal/testgit/repo_repack.go
index 29fa8a4f..7773ac13 100644
--- a/internal/testgit/repo_repack.go
+++ b/internal/testgit/repo_repack.go
@@ -5,6 +5,7 @@ import "testing"
// Repack runs "git repack" with args in the repository.
func (testRepo *TestRepo) Repack(tb testing.TB, args ...string) {
tb.Helper()
+
cmdArgs := make([]string, 0, len(args)+1)
cmdArgs = append(cmdArgs, "repack")
cmdArgs = append(cmdArgs, args...)
diff --git a/internal/testgit/repo_rev_parse.go b/internal/testgit/repo_rev_parse.go
index bebdfa8e..3bee6108 100644
--- a/internal/testgit/repo_rev_parse.go
+++ b/internal/testgit/repo_rev_parse.go
@@ -10,9 +10,11 @@ import (
func (testRepo *TestRepo) RevParse(tb testing.TB, spec string) objectid.ObjectID {
tb.Helper()
hex := testRepo.Run(tb, "rev-parse", spec)
+
id, err := objectid.ParseHex(testRepo.algo, hex)
if err != nil {
tb.Fatalf("parse rev-parse output %q: %v", hex, err)
}
+
return id
}
diff --git a/internal/testgit/repo_run.go b/internal/testgit/repo_run.go
index 8022835e..162a0d72 100644
--- a/internal/testgit/repo_run.go
+++ b/internal/testgit/repo_run.go
@@ -11,12 +11,14 @@ import (
func (testRepo *TestRepo) Run(tb testing.TB, args ...string) string {
tb.Helper()
out := testRepo.runBytes(tb, nil, testRepo.dir, args...)
+
return strings.TrimSpace(string(out))
}
// RunBytes executes git and returns raw output bytes.
func (testRepo *TestRepo) RunBytes(tb testing.TB, args ...string) []byte {
tb.Helper()
+
return testRepo.runBytes(tb, nil, testRepo.dir, args...)
}
@@ -24,12 +26,14 @@ func (testRepo *TestRepo) RunBytes(tb testing.TB, args ...string) []byte {
func (testRepo *TestRepo) RunInput(tb testing.TB, stdin []byte, args ...string) string {
tb.Helper()
out := testRepo.runBytes(tb, stdin, testRepo.dir, args...)
+
return strings.TrimSpace(string(out))
}
// RunInputBytes executes git with stdin and returns raw output bytes.
func (testRepo *TestRepo) RunInputBytes(tb testing.TB, stdin []byte, args ...string) []byte {
tb.Helper()
+
return testRepo.runBytes(tb, stdin, testRepo.dir, args...)
}
@@ -38,13 +42,16 @@ func (testRepo *TestRepo) runBytes(tb testing.TB, stdin []byte, dir string, args
//nolint:noctx
cmd := exec.Command("git", args...) //#nosec G204
cmd.Dir = dir
+
cmd.Env = testRepo.env
if stdin != nil {
cmd.Stdin = bytes.NewReader(stdin)
}
+
out, err := cmd.CombinedOutput()
if err != nil {
tb.Fatalf("git %v failed: %v\n%s", args, err, out)
}
+
return out
}
diff --git a/internal/testgit/repo_tag_annotated.go b/internal/testgit/repo_tag_annotated.go
index a3ffafa6..7e9bfbf5 100644
--- a/internal/testgit/repo_tag_annotated.go
+++ b/internal/testgit/repo_tag_annotated.go
@@ -11,5 +11,6 @@ import (
func (testRepo *TestRepo) TagAnnotated(tb testing.TB, name string, target objectid.ObjectID, message string) objectid.ObjectID {
tb.Helper()
testRepo.Run(tb, "tag", "-a", name, target.String(), "-m", message)
+
return testRepo.RevParse(tb, fmt.Sprintf("refs/tags/%s", name))
}
diff --git a/internal/zlib/reader.go b/internal/zlib/reader.go
index 5d6dcd88..e4babb9e 100644
--- a/internal/zlib/reader.go
+++ b/internal/zlib/reader.go
@@ -63,6 +63,7 @@ var (
var readerPool = sync.Pool{
New: func() any {
r := new(reader)
+
return r
},
}
@@ -89,14 +90,17 @@ func NewReader(r io.Reader) (io.ReadCloser, error) {
// If the compressed data refers to a different dictionary, NewReaderDict returns [ErrDictionary].
func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, error) {
v := readerPool.Get()
+
z, ok := v.(*reader)
if !ok {
panic("zlib: pool returned unexpected type")
}
+
err := z.Reset(r, dict)
if err != nil {
return nil, err
}
+
return z, nil
}
@@ -106,30 +110,40 @@ func (z *reader) Read(p []byte) (int, error) {
}
var n int
+
n, z.err = z.decompressor.Read(p)
- if _, err := z.digest.Write(p[0:n]); err != nil {
+
+ _, err := z.digest.Write(p[0:n])
+ if err != nil {
z.err = err
+
return n, z.err
}
+
if !errors.Is(z.err, io.EOF) {
// In the normal case we return here.
return n, z.err
}
// Finished file; check checksum.
- if _, err := io.ReadFull(z.r, z.scratch[0:4]); err != nil {
- if err == io.EOF {
+ _, err = io.ReadFull(z.r, z.scratch[0:4])
+ if err != nil {
+ if errors.Is(err, io.EOF) {
err = io.ErrUnexpectedEOF
}
+
z.err = err
+
return n, z.err
}
// ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
checksum := binary.BigEndian.Uint32(z.scratch[:4])
if checksum != z.digest.Sum32() {
z.err = ErrChecksum
+
return n, z.err
}
+
return n, io.EOF
}
@@ -140,12 +154,14 @@ func (z *reader) Close() error {
if z.err != nil && !errors.Is(z.err, io.EOF) {
return z.err
}
+
z.err = z.decompressor.Close()
if z.err != nil {
return z.err
}
readerPool.Put(z)
+
return nil
}
@@ -163,13 +179,17 @@ func (z *reader) Reset(r io.Reader, dict []byte) error {
if errors.Is(z.err, io.EOF) {
z.err = io.ErrUnexpectedEOF
}
+
return z.err
}
+
h := binary.BigEndian.Uint16(z.scratch[:2])
if (z.scratch[0]&0x0f != zlibDeflate) || (z.scratch[0]>>4 > zlibMaxWindow) || (h%31 != 0) {
z.err = ErrHeader
+
return z.err
}
+
haveDict := z.scratch[1]&0x20 != 0
if haveDict {
_, z.err = io.ReadFull(z.r, z.scratch[0:4])
@@ -177,31 +197,41 @@ func (z *reader) Reset(r io.Reader, dict []byte) error {
if errors.Is(z.err, io.EOF) {
z.err = io.ErrUnexpectedEOF
}
+
return z.err
}
+
checksum := binary.BigEndian.Uint32(z.scratch[:4])
if checksum != adler32.Checksum(dict) {
z.err = ErrDictionary
+
return z.err
}
}
- if z.decompressor == nil {
- if haveDict {
- z.decompressor = flate.NewReaderDict(z.r, dict)
- } else {
- z.decompressor = flate.NewReader(z.r)
- }
- } else {
+ if z.decompressor != nil {
resetter, ok := z.decompressor.(flate.Resetter)
if !ok {
panic("zlib: pooled decompressor does not implement flate.Resetter")
}
+
z.err = resetter.Reset(z.r, dict)
if z.err != nil {
return z.err
}
+
+ z.digest = adler32.New()
+
+ return nil
+ }
+
+ if haveDict {
+ z.decompressor = flate.NewReaderDict(z.r, dict)
+ } else {
+ z.decompressor = flate.NewReader(z.r)
}
+
z.digest = adler32.New()
+
return nil
}
diff --git a/internal/zlib/writer.go b/internal/zlib/writer.go
index 75a8ec1d..bfc52889 100644
--- a/internal/zlib/writer.go
+++ b/internal/zlib/writer.go
@@ -52,6 +52,7 @@ var writerPool = sync.Pool{
// Writes may be buffered and not flushed until Close.
func NewWriter(w io.Writer) *Writer {
z, _ := NewWriterLevelDict(w, DefaultCompression, nil)
+
return z
}
@@ -74,7 +75,9 @@ func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) {
if level < HuffmanOnly || level > BestCompression {
return nil, fmt.Errorf("zlib: invalid compression level: %d", level)
}
+
v := writerPool.Get()
+
z, ok := v.(*Writer)
if !ok {
panic("zlib: pool returned unexpected type")
@@ -86,6 +89,7 @@ func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) {
if !reuseCompressor {
z.compressor = nil
}
+
if z.digest != nil {
z.digest.Reset()
}
@@ -100,6 +104,7 @@ func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) {
if z.compressor != nil {
z.compressor.Reset(w)
}
+
return z, nil
}
@@ -112,9 +117,11 @@ func (z *Writer) Reset(w io.Writer) {
if z.compressor != nil {
z.compressor.Reset(w)
}
+
if z.digest != nil {
z.digest.Reset()
}
+
z.err = nil
z.scratch = [4]byte{}
z.wroteHeader = false
@@ -127,21 +134,29 @@ func (z *Writer) Write(p []byte) (n int, err error) {
if !z.wroteHeader {
z.err = z.writeHeader()
}
+
if z.err != nil {
return 0, z.err
}
+
if len(p) == 0 {
return 0, nil
}
+
n, err = z.compressor.Write(p)
if err != nil {
z.err = err
+
return n, err
}
- if _, err = z.digest.Write(p); err != nil {
+
+ _, err = z.digest.Write(p)
+ if err != nil {
z.err = err
+
return 0, z.err
}
+
return n, err
}
@@ -150,10 +165,13 @@ func (z *Writer) Flush() error {
if !z.wroteHeader {
z.err = z.writeHeader()
}
+
if z.err != nil {
return z.err
}
+
z.err = z.compressor.Flush()
+
return z.err
}
@@ -163,22 +181,27 @@ func (z *Writer) Close() error {
if !z.wroteHeader {
z.err = z.writeHeader()
}
+
if z.err != nil {
return z.err
}
+
z.err = z.compressor.Close()
if z.err != nil {
return z.err
}
+
checksum := z.digest.Sum32()
// ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
binary.BigEndian.PutUint32(z.scratch[:], checksum)
+
_, z.err = z.w.Write(z.scratch[0:4])
if z.err != nil {
return z.err
}
writerPool.Put(z)
+
return nil
}
@@ -205,20 +228,28 @@ func (z *Writer) writeHeader() (err error) {
default:
panic("unreachable")
}
+
if z.dict != nil {
z.scratch[1] |= 1 << 5
}
+
z.scratch[1] += uint8(31 - binary.BigEndian.Uint16(z.scratch[:2])%31) //#nosec G115
- if _, err = z.w.Write(z.scratch[0:2]); err != nil {
+
+ _, err = z.w.Write(z.scratch[0:2])
+ if err != nil {
return err
}
+
if z.dict != nil {
// The next four bytes are the Adler-32 checksum of the dictionary.
binary.BigEndian.PutUint32(z.scratch[:], adler32.Checksum(z.dict))
- if _, err = z.w.Write(z.scratch[0:4]); err != nil {
+
+ _, err = z.w.Write(z.scratch[0:4])
+ if err != nil {
return err
}
}
+
if z.compressor == nil {
// Initialize deflater unless the Writer is being reused
// after a Reset call.
@@ -226,7 +257,9 @@ func (z *Writer) writeHeader() (err error) {
if err != nil {
return err
}
+
z.digest = adler32.New()
}
+
return nil
}