Skip to content

Commit 0e2e0d8

Browse files
committed
fix memory usage
1 parent 72c8f83 commit 0e2e0d8

File tree

3 files changed

+106
-5
lines changed

3 files changed

+106
-5
lines changed

modules/git/blob.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@ func (b *Blob) GetBlobContent(limit int64) (string, error) {
3030
return "", err
3131
}
3232
defer dataRc.Close()
33-
buf := make([]byte, limit)
34-
n, _ := util.ReadAtMost(dataRc, buf)
35-
buf = buf[:n]
36-
return string(buf), nil
33+
buf, err := util.ReadWithLimit(dataRc, int(limit))
34+
return string(buf), err
3735
}
3836

3937
// GetBlobLineCount gets line count of the blob

modules/util/io.go

+38-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
package util
55

66
import (
7+
"bytes"
78
"errors"
89
"io"
910
)
1011

1112
// ReadAtMost reads at most len(buf) bytes from r into buf.
1213
// It returns the number of bytes copied. n is only less than len(buf) if r provides fewer bytes.
13-
// If EOF occurs while reading, err will be nil.
14+
// If EOF or ErrUnexpectedEOF occurs while reading, err will be nil.
1415
func ReadAtMost(r io.Reader, buf []byte) (n int, err error) {
1516
n, err = io.ReadFull(r, buf)
1617
if err == io.EOF || err == io.ErrUnexpectedEOF {
@@ -19,6 +20,42 @@ func ReadAtMost(r io.Reader, buf []byte) (n int, err error) {
1920
return n, err
2021
}
2122

23+
// ReadWithLimit reads at most "limit" bytes from r into buf.
24+
// If EOF or ErrUnexpectedEOF occurs while reading, err will be nil.
25+
func ReadWithLimit(r io.Reader, n int) (buf []byte, err error) {
26+
return readWithLimit(r, 1024, n)
27+
}
28+
29+
func readWithLimit(r io.Reader, batch, limit int) ([]byte, error) {
30+
if limit <= batch {
31+
buf := make([]byte, limit)
32+
n, err := ReadAtMost(r, buf)
33+
if err != nil {
34+
return nil, err
35+
}
36+
return buf[:n], nil
37+
}
38+
res := bytes.NewBuffer(make([]byte, 0, batch))
39+
bufFix := make([]byte, batch)
40+
eof := false
41+
for res.Len() < limit && !eof {
42+
bufTmp := bufFix
43+
if res.Len()+batch > limit {
44+
bufTmp = bufFix[:limit-res.Len()]
45+
}
46+
n, err := io.ReadFull(r, bufTmp)
47+
if err == io.EOF || err == io.ErrUnexpectedEOF {
48+
eof = true
49+
} else if err != nil {
50+
return nil, err
51+
}
52+
if _, err = res.Write(bufTmp[:n]); err != nil {
53+
return nil, err
54+
}
55+
}
56+
return res.Bytes(), nil
57+
}
58+
2259
// ErrNotEmpty is an error reported when there is a non-empty reader
2360
var ErrNotEmpty = errors.New("not-empty")
2461

modules/util/io_test.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package util
5+
6+
import (
7+
"bytes"
8+
"errors"
9+
"testing"
10+
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
type readerWithError struct {
15+
buf *bytes.Buffer
16+
}
17+
18+
func (r *readerWithError) Read(p []byte) (n int, err error) {
19+
if r.buf.Len() < 2 {
20+
return 0, errors.New("test error")
21+
}
22+
return r.buf.Read(p)
23+
}
24+
25+
func TestReadWithLimit(t *testing.T) {
26+
bs := []byte("0123456789abcdef")
27+
28+
// normal test
29+
buf, err := readWithLimit(bytes.NewBuffer(bs), 5, 2)
30+
assert.NoError(t, err)
31+
assert.Equal(t, []byte("01"), buf)
32+
33+
buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 5)
34+
assert.NoError(t, err)
35+
assert.Equal(t, []byte("01234"), buf)
36+
37+
buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 6)
38+
assert.NoError(t, err)
39+
assert.Equal(t, []byte("012345"), buf)
40+
41+
buf, err = readWithLimit(bytes.NewBuffer(bs), 5, len(bs))
42+
assert.NoError(t, err)
43+
assert.Equal(t, []byte("0123456789abcdef"), buf)
44+
45+
buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 100)
46+
assert.NoError(t, err)
47+
assert.Equal(t, []byte("0123456789abcdef"), buf)
48+
49+
// test with error
50+
buf, err = readWithLimit(&readerWithError{bytes.NewBuffer(bs)}, 5, 10)
51+
assert.NoError(t, err)
52+
assert.Equal(t, []byte("0123456789"), buf)
53+
54+
buf, err = readWithLimit(&readerWithError{bytes.NewBuffer(bs)}, 5, 100)
55+
assert.ErrorContains(t, err, "test error")
56+
assert.Empty(t, buf)
57+
58+
// test public function
59+
buf, err = ReadWithLimit(bytes.NewBuffer(bs), 2)
60+
assert.NoError(t, err)
61+
assert.Equal(t, []byte("01"), buf)
62+
63+
buf, err = ReadWithLimit(bytes.NewBuffer(bs), 9999999)
64+
assert.NoError(t, err)
65+
assert.Equal(t, []byte("0123456789abcdef"), buf)
66+
}

0 commit comments

Comments
 (0)