diff --git a/util/gvalid/gvalid_validator_check_value.go b/util/gvalid/gvalid_validator_check_value.go index 759e97239af..970499e0469 100644 --- a/util/gvalid/gvalid_validator_check_value.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -287,26 +287,42 @@ func (v *Validator) doCheckValueRecursively(ctx context.Context, in doCheckValue } case reflect.Slice, reflect.Array: - var array []interface{} - if gjson.Valid(in.Value) { - array = gconv.Interfaces(gconv.Bytes(in.Value)) - } else { - array = gconv.Interfaces(in.Value) - } - if len(array) == 0 { - return + loop := false + switch in.Type.Elem().Kind() { + // []struct []map + case reflect.Struct, reflect.Map: + loop = true + case reflect.Ptr: + // []*struct + // []*int + loop = true } - for _, item := range array { - v.doCheckValueRecursively(ctx, doCheckValueRecursivelyInput{ - Value: item, - Type: in.Type.Elem(), - Kind: in.Type.Elem().Kind(), - ErrorMaps: in.ErrorMaps, - ResultSequenceRules: in.ResultSequenceRules, - }) - // Bail feature. - if v.bail && len(in.ErrorMaps) > 0 { - break + // When it is a base type array, + // there is no need for recursive loop validation, + // otherwise it will cause memory leakage + // https://github.com/gogf/gf/issues/4092 + if loop { + var array []interface{} + if gjson.Valid(in.Value) { + array = gconv.Interfaces(gconv.Bytes(in.Value)) + } else { + array = gconv.Interfaces(in.Value) + } + if len(array) == 0 { + return + } + for _, item := range array { + v.doCheckValueRecursively(ctx, doCheckValueRecursivelyInput{ + Value: item, + Type: in.Type.Elem(), + Kind: in.Type.Elem().Kind(), + ErrorMaps: in.ErrorMaps, + ResultSequenceRules: in.ResultSequenceRules, + }) + // Bail feature. + if v.bail && len(in.ErrorMaps) > 0 { + break + } } } } diff --git a/util/gvalid/gvalid_z_unit_issue_test.go b/util/gvalid/gvalid_z_unit_issue_test.go index 38aea073937..2bed169cba3 100644 --- a/util/gvalid/gvalid_z_unit_issue_test.go +++ b/util/gvalid/gvalid_z_unit_issue_test.go @@ -9,6 +9,7 @@ package gvalid_test import ( "context" "fmt" + "runtime" "testing" "time" @@ -114,3 +115,37 @@ func Test_Issue3636(t *testing.T) { ) }) } + +// https://github.com/gogf/gf/issues/4092 +func Test_Issue4092(t *testing.T) { + type Model struct { + Raw []byte `v:"required"` + Test []byte `v:"foreach|in:1,2,3"` + } + gtest.C(t, func(t *gtest.T) { + const kb = 1024 + const mb = 1024 * kb + raw := make([]byte, 50*mb) + in := &Model{ + Raw: raw, + Test: []byte{40, 5, 6}, + } + err := g.Validator(). + Data(in). + Run(context.Background()) + t.Assert(err, "The Test value `6` is not in acceptable range: 1,2,3") + allocMb := getMemAlloc() + t.AssertLE(allocMb, 110) + }) +} + +func getMemAlloc() uint64 { + byteToMb := func(b uint64) uint64 { + return b / 1024 / 1024 + } + var m runtime.MemStats + runtime.ReadMemStats(&m) + // For info on each, see: https://golang.org/pkg/runtime/#MemStats + alloc := byteToMb(m.Alloc) + return alloc +}