diff --git a/container/gvar/gvar_scan.go b/container/gvar/gvar_scan.go new file mode 100644 index 000000000..064dd6fce --- /dev/null +++ b/container/gvar/gvar_scan.go @@ -0,0 +1,19 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gvar + +import ( + "github.com/gogf/gf/util/gconv" +) + +// Scan automatically checks the type of `pointer` and converts `params` to `pointer`. It supports `pointer` +// with type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting. +// +// See gconv.Scan. +func (v *Var) Scan(pointer interface{}, mapping ...map[string]string) error { + return gconv.Scan(v.Val(), pointer, mapping...) +} diff --git a/container/gvar/gvar_struct.go b/container/gvar/gvar_struct.go index 2beb07572..3369b9792 100644 --- a/container/gvar/gvar_struct.go +++ b/container/gvar/gvar_struct.go @@ -21,12 +21,3 @@ func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error { func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) error { return gconv.Structs(v.Val(), pointer, mapping...) } - -// Scan automatically calls Struct or Structs function according to the type of parameter -// `pointer` to implement the converting. -// -// It calls function Struct if `pointer` is type of *struct/**struct to do the converting. -// It calls function Structs if `pointer` is type of *[]struct/*[]*struct to do the converting. -func (v *Var) Scan(pointer interface{}, mapping ...map[string]string) error { - return gconv.Scan(v.Val(), pointer, mapping...) -} diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 4331673af..ad0af9073 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -21,14 +21,18 @@ import ( // It calls function `doStructs` internally if `pointer` is type of *[]struct/*[]*struct for converting. func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { var ( - pointerType reflect.Type - pointerKind reflect.Kind + pointerType reflect.Type + pointerKind reflect.Kind + pointerValue reflect.Value ) if v, ok := pointer.(reflect.Value); ok { + pointerValue = v pointerType = v.Type() } else { - pointerType = reflect.TypeOf(pointer) + pointerValue = reflect.ValueOf(pointer) + pointerType = reflect.TypeOf(pointer) // Do not use pointerValue.Type() as pointerValue might be zero. } + if pointerType == nil { return gerror.NewCode(gcode.CodeInvalidParameter, "parameter pointer should not be nil") } @@ -36,6 +40,30 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) if pointerKind != reflect.Ptr { return gerror.NewCodef(gcode.CodeInvalidParameter, "params should be type of pointer, but got type: %v", pointerKind) } + + // Direct assignment checks! + var ( + paramsType reflect.Type + paramsValue reflect.Value + ) + if v, ok := params.(reflect.Value); ok { + paramsValue = v + paramsType = paramsValue.Type() + } else { + paramsValue = reflect.ValueOf(params) + paramsType = reflect.TypeOf(params) // Do not use paramsValue.Type() as paramsValue might be zero. + } + // If `params` and `pointer` are the same type, the do directly assignment. + // For performance enhancement purpose. + var ( + pointerValueElem = pointerValue.Elem() + ) + if pointerValueElem.CanSet() && paramsType == pointerValueElem.Type() { + pointerValueElem.Set(paramsValue) + return nil + } + + // Converting. var ( pointerElem = pointerType.Elem() pointerElemKind = pointerElem.Kind() diff --git a/util/gconv/gconv_z_unit_scan_test.go b/util/gconv/gconv_z_unit_scan_test.go index 281566d72..ffdb7e6e0 100644 --- a/util/gconv/gconv_z_unit_scan_test.go +++ b/util/gconv/gconv_z_unit_scan_test.go @@ -7,6 +7,7 @@ package gconv_test import ( + "fmt" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/test/gtest" @@ -275,3 +276,46 @@ func Test_Scan_JsonAttributes_StringArray(t *testing.T) { t.Assert(len(s.Array), 0) }) } + +func Test_Scan_SameType_Just_Assign(t *testing.T) { + // Struct. + gtest.C(t, func(t *gtest.T) { + type User struct { + Uid int + Name string + Pass1 string + Pass2 string + Pointer *int + } + var ( + int1 = 1 + int2 = 1 + user1 = new(User) + user2 *User + ) + user1.Pointer = &int1 + err := gconv.Scan(user1, &user2) + t.AssertNil(err) + t.Assert(fmt.Sprintf(`%p`, user1), fmt.Sprintf(`%p`, user2)) + t.Assert(*user1.Pointer, *user2.Pointer) + user1.Pointer = &int2 + t.Assert(*user1.Pointer, *user2.Pointer) + }) + // Map. + gtest.C(t, func(t *gtest.T) { + var ( + int1 = 1 + int2 = 1 + m1 = map[string]*int{ + "int": &int1, + } + m2 map[string]*int + ) + err := gconv.Scan(m1, &m2) + t.AssertNil(err) + t.Assert(fmt.Sprintf(`%p`, m1), fmt.Sprintf(`%p`, m2)) + t.Assert(*m1["int"], *m2["int"]) + m1["int"] = &int2 + t.Assert(*m1["int"], *m2["int"]) + }) +}