gin icon indicating copy to clipboard operation
gin copied to clipboard

Optimize the initFormCache method of the Context struct

Open 1911860538 opened this issue 2 months ago • 0 comments

Delete unnecessary initialization of the context formCache.

Below is my local test code:

context.go

func (c *Context) initFormCache() {
	if c.formCache == nil {
		c.formCache = make(url.Values)
		req := c.Request
		if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
			if !errors.Is(err, http.ErrNotMultipart) {
				debugPrint("error on parse multipart form array: %v", err)
			}
		}
		c.formCache = req.PostForm
	}
}

func (c *Context) newInitFormCache() {
	if c.formCache == nil {
		req := c.Request
		if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
			if !errors.Is(err, http.ErrNotMultipart) {
				debugPrint("error on parse multipart form array: %v", err)
			}
		}
		c.formCache = req.PostForm
	}
}

context_test.go

func getTestInitFormCacheContext() *Context {
	return &Context{
		Request: &http.Request{
			PostForm: url.Values{
				"k0": []string{"0"},
				"k1": []string{"1", "1"},
				"k2": []string{},
			},
		},
		engine: &Engine{
			MaxMultipartMemory: 100,
		},
	}
}

// the context.Request.ParseMultipartForm method will return an error which is not http.ErrNotMultipart.
func getErrTestInitFormCacheContext() *Context {
	return &Context{
		Request: &http.Request{
			PostForm: url.Values{
				"k0": []string{"0"},
				"k1": []string{"1", "1"},
				"k2": []string{},
			},
			Header: http.Header{
				"Content-Type": []string{"application/json; charset=utf-8"},
			},
		},
		engine: &Engine{
			MaxMultipartMemory: 100,
		},
	}
}

func TestNewIntiFormCache(t *testing.T) {
	{
		ctx0 := getTestInitFormCacheContext()
		ctx0.initFormCache()

		ctx1 := getTestInitFormCacheContext()
		ctx1.newInitFormCache()

		assert.Equal(t, ctx0.formCache, ctx1.formCache)
	}

	{
		ctx0 := getTestInitFormCacheContext()
		ctx0.Request.PostForm = nil
		ctx0.initFormCache()

		ctx1 := getTestInitFormCacheContext()
		ctx1.Request.PostForm = nil
		ctx1.newInitFormCache()

		assert.Equal(t, ctx0.formCache, ctx1.formCache)
	}

	{
		ctx0 := getTestInitFormCacheContext()
		ctx0.Request.PostForm = make(url.Values)
		ctx0.initFormCache()

		ctx1 := getTestInitFormCacheContext()
		ctx1.Request.PostForm = make(url.Values)
		ctx1.newInitFormCache()

		assert.Equal(t, ctx0.formCache, ctx1.formCache)
	}
}

func TestNewIntiFormCacheWithParseMultipartFormError(t *testing.T) {
	ctx0 := getErrTestInitFormCacheContext()
	ctx0.initFormCache()

	ctx1 := getErrTestInitFormCacheContext()
	ctx1.newInitFormCache()

	assert.Equal(t, ctx0.formCache, ctx1.formCache)
}

func BenchmarkInitFormCache(b *testing.B) {
	ctx := getTestInitFormCacheContext()
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ctx.initFormCache()
	}
}

func BenchmarkNewInitFormCache(b *testing.B) {
	ctx := getTestInitFormCacheContext()
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ctx.newInitFormCache()
	}
}

test output

=== RUN   TestNewIntiFormCache
--- PASS: TestNewIntiFormCache (0.00s)
=== RUN   TestNewIntiFormCacheWithParseMultipartFormError
--- PASS: TestNewIntiFormCacheWithParseMultipartFormError (0.00s)
PASS

Process finished with the exit code 0

benchmark output

goos: linux
goarch: amd64
pkg: github.com/gin-gonic/gin
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
BenchmarkInitFormCache
BenchmarkInitFormCache-6           	719376747	         1.639 ns/op	       0 B/op	       0 allocs/op
BenchmarkNewInitFormCache
BenchmarkNewInitFormCache-6        	721553457	         1.636 ns/op	       0 B/op	       0 allocs/op
PASS

Process finished with the exit code 0

1911860538 avatar Mar 29 '24 10:03 1911860538