Skip to content

Commit

Permalink
feat: enhance read all function for less malloc
Browse files Browse the repository at this point in the history
  • Loading branch information
vicanso committed Dec 22, 2021
1 parent 0f34717 commit 4c8272e
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 1 deletion.
26 changes: 26 additions & 0 deletions elton.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,3 +696,29 @@ func Compose(handlerList ...Handler) Handler {
return c.Next()
}
}

// copy from io.ReadAll
// ReadAll reads from r until an error or EOF and returns the data it read.
// A successful call returns err == nil, not err == EOF. Because ReadAll is
// defined to read from src until EOF, it does not treat an EOF from Read
// as an error to be reported.
func ReadAllInitCap(r io.Reader, initCap int) ([]byte, error) {
if initCap <= 0 {
initCap = 512
}
b := make([]byte, 0, initCap)
for {
if len(b) == cap(b) {
// Add more capacity (let append pick how much).
b = append(b, 0)[:len(b)]
}
n, err := r.Read(b[len(b):cap(b)])
b = b[:len(b)+n]
if err != nil {
if err == io.EOF {
err = nil
}
return b, err
}
}
}
36 changes: 36 additions & 0 deletions elton_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,42 @@ func TestGracefulClose(t *testing.T) {
})
}

func TestReadAllInitCap(t *testing.T) {
assert := assert.New(t)

buf := &bytes.Buffer{}
for i := 0; i < 1024*1024; i++ {
buf.Write([]byte("hello world!"))
}
result := buf.Bytes()

data, err := ReadAllInitCap(buf, 1024*100)
assert.Nil(err)
assert.Equal(result, data)

data, err = ReadAllInitCap(bytes.NewBufferString("hello world!"), 1024*100)
assert.Nil(err)
assert.Equal([]byte("hello world!"), data)
}

func BenchmarkReadAllInitCap(b *testing.B) {
buf := &bytes.Buffer{}
for i := 0; i < 1024*1024; i++ {
buf.Write([]byte("hello world!"))
}
result := buf.Bytes()
size := buf.Len()
for i := 0; i < b.N; i++ {
data, err := ReadAllInitCap(bytes.NewBuffer(result), 1024*1024)
if err != nil {
panic(err)
}
if len(data) != size {
panic(errors.New("data is invalid"))
}
}
}

// https://stackoverflow.com/questions/50120427/fail-unit-tests-if-coverage-is-below-certain-percentage
func TestMain(m *testing.M) {
// call flag.Parse() here if TestMain uses flags
Expand Down
9 changes: 8 additions & 1 deletion middleware/body_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"

"github.com/vicanso/elton"
Expand Down Expand Up @@ -301,7 +302,13 @@ func NewBodyParser(config BodyParserConfig) elton.Handler {
}
defer r.Close()
var body []byte
body, err := ioutil.ReadAll(r)
initCapSize := 0
contentLength := c.GetRequestHeader(elton.HeaderContentLength)
// 如果请求头有指定了content length,则根据content length来分配[]byte大小
if contentLength != "" {
initCapSize, _ = strconv.Atoi(contentLength)
}
body, err := elton.ReadAllInitCap(r, initCapSize)
if err != nil {
if hes.IsError(err) {
return err
Expand Down
2 changes: 2 additions & 0 deletions middleware/body_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"io/ioutil"
"net/http"
"net/http/httptest"
"strconv"
"strings"
"testing"

Expand Down Expand Up @@ -355,6 +356,7 @@ func TestBodyParserMiddleware(t *testing.T) {
body := `{"name": "tree.xie"}`
req := httptest.NewRequest("POST", "https://aslant.site/", strings.NewReader(body))
req.Header.Set(elton.HeaderContentType, "application/json")
req.Header.Set(elton.HeaderContentLength, strconv.Itoa(len(body)))
c := elton.NewContext(nil, req)
c.Next = next
return c
Expand Down

0 comments on commit 4c8272e

Please sign in to comment.