├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── feature-request.md │ └── general-question.md └── workflows │ └── go.yml ├── .gitignore ├── LICENSE ├── README.md ├── cache.go ├── case.md ├── fix-panic_test.go ├── go.mod ├── go.sum ├── is_base_type.go ├── is_base_type_test.go ├── new_baseslice.go ├── new_basetype.go ├── options.go ├── pcopy.go ├── pcopy_basetype_test.go ├── pcopy_benchmark_base_map_test.go ├── pcopy_benchmark_base_slice_test.go ├── pcopy_benchmark_base_type_test.go ├── pcopy_benchmark_composite_map_test.go ├── pcopy_benchmark_getlikefavorited_test.go ├── pcopy_benchmark_getredpoint_test.go ├── pcopy_benchmark_interface_base_test.go ├── pcopy_benchmark_interface_slice_test.go ├── pcopy_benchmark_ptr_base_type1_test.go ├── pcopy_benchmark_ptr_baseslice_test.go ├── pcopy_benchmark_slice_with_struct_test.go ├── pcopy_better_to_use_test.go ├── pcopy_bug_test.go ├── pcopy_dst_ptr_struct_test.go ├── pcopy_enum_fix2_test.go ├── pcopy_fix_test.go ├── pcopy_func_test.go ├── pcopy_getlikefavorited_test.go ├── pcopy_getredpoint_test.go ├── pcopy_have_value_test.go ├── pcopy_interface_test.go ├── pcopy_map_test.go ├── pcopy_ptr_basemap_test.go ├── pcopy_ptr_baseslice_test.go ├── pcopy_ptr_basetype_test.go ├── pcopy_ptr_double_basetype_test.go ├── pcopy_ptr_struct_test.go ├── pcopy_readme_example_test.go ├── pcopy_slice_and_array_test.go ├── pcopy_slice_basetype_ptr_test.go ├── pcopy_slice_struct_ptr_test.go ├── pcopy_struct_basetype_test.go ├── pcopy_struct_test.go ├── pcopy_struct_with_struct_test.go ├── pcopy_test.go ├── pcopy_time_Time_test.go ├── pcopy_type_test.go ├── preheat.go ├── set_basemap_func.go ├── set_basemap_test.go ├── set_basemap_tmpl.go ├── set_baseslice.go ├── set_baseslice_test.go ├── set_basetype.go ├── set_composite_type.go ├── set_composite_type_slice_test.go ├── set_slice_elem_is_ptr.go ├── set_slice_elem_is_ptr_test.go └── set_slice_elem_is_ptr_tmpl.go /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F91D Bug Report" 3 | about: As a User, I want to report a Bug. 4 | labels: type/bug 5 | --- 6 | 7 | ## Bug Report 8 | 9 | Please answer these questions before submitting your issue. Thanks! 10 | 11 | ### 1. Minimal reproduce step (Required) 12 | 13 | 14 | 15 | ### 2. What did you expect to see? (Required) 16 | 17 | ### 3. What did you see instead (Required) 18 | 19 | ### 4. What is your pcopy version? (Required) 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F44F Feature Request" 3 | about: As a user, I want to request a New Feature on the product. 4 | labels: type/feature-request 5 | --- 6 | 7 | ## Feature Request 8 | 9 | **Is your feature request related to a problem? Please describe:** 10 | 11 | 12 | **Describe the feature you'd like:** 13 | 14 | 15 | **Describe alternatives you've considered:** 16 | 17 | 18 | **Teachability, Documentation, Adoption, Migration Strategy:** 19 | 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/general-question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F600 Ask a Question" 3 | about: I want to ask a question. 4 | labels: type/question 5 | --- 6 | 7 | ## General Question 8 | 9 | 20 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | 9 | build: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | go: ['1.18', '1.19', '1.20'] 14 | name: Go ${{ matrix.go }} sample 15 | 16 | steps: 17 | 18 | - name: Set up Go 1.13 19 | uses: actions/setup-go@v1 20 | with: 21 | go-version: ${{ matrix.go }} 22 | id: go 23 | 24 | - name: Check out code into the Go module directory 25 | uses: actions/checkout@v1 26 | 27 | - name: Get dependencies 28 | run: | 29 | go get -v -t -d ./... 30 | if [ -f Gopkg.toml ]; then 31 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 32 | dep ensure 33 | fi 34 | 35 | - name: Build 36 | run: go build -v . 37 | 38 | - name: Test 39 | run: go test -v -coverprofile='coverage.out' -covermode=count ./... 40 | 41 | - name: Upload coverage reports to Codecov 42 | uses: codecov/codecov-action@v3 43 | with: 44 | token: ${{secrets.CODECOV_TOKEN}} 45 | file: ./coverage.out 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | /cpu.prof 3 | /cover.cov 4 | *swp 5 | *~ 6 | *.exe 7 | *.exe~ 8 | *.dll 9 | *.so 10 | *.dylib 11 | 12 | # Test binary, built with `go test -c` 13 | *.test 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Dependency directories (remove the comment below to include it) 19 | # vendor/ 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 蚂蚁实验室 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 作用 2 | [![Go](https://github.com/antlabs/pcopy/workflows/Go/badge.svg)](https://github.com/antlabs/pcopy/actions) 3 | [![codecov](https://codecov.io/gh/antlabs/pcopy/branch/master/graph/badge.svg)](https://codecov.io/gh/antlabs/pcopy) 4 | 5 | `pcopy.Copy`主要用于两个类型间的深度拷贝, 前身是[deepcopy](https://github.com/antlabs/deepcopy) 6 | 7 | 新加预热函数。Copy时打开加速开关,达到性能提升4-10倍的效果。 8 | 9 | 警告: 10 | 11 | 高性能的同时可能会有些bug, 如果发现bug可以去掉`pcopy.WithUsePreheat()`试下, 结果不一致,可以提issue。 12 | 13 | ## feature 14 | * 高性能, 相对第一个版本提升4-10倍的性能 15 | * 支持异构结构体拷贝, dst和src可以是不同的类型,会拷贝dst和src交集的部分 16 | * 多类型支持struct/map/slice/array/int...int64/uint...uint64/ 等等 17 | 18 | ## 内容 19 | - [Installation](#Installation) 20 | - [Quick start](#quick-start) 21 | - [example](#example) 22 | - [1.拷贝slice](#copy-slice) 23 | - [2.拷贝map](#copy-map) 24 | - [3.简化业务代码开发](#simplify-business-code-development) 25 | - [性能压测](#benchmark) 26 | ## Installation 27 | ``` 28 | go get github.com/antlabs/pcopy 29 | ``` 30 | 31 | ## Quick start 32 | ```go 33 | package main 34 | 35 | import ( 36 | "fmt" 37 | "github.com/antlabs/pcopy" 38 | ) 39 | 40 | type dst struct { 41 | ID int 42 | Result string 43 | } 44 | 45 | type src struct{ 46 | ID int 47 | Text string 48 | } 49 | func main() { 50 | d, s := dst{}, src{ID:3} 51 | pcopy.Preheat(&dst{}, &src{}) // 一对类型只要预热一次 52 | pcopy.Copy(&d, &s, pcopy.WithUsePreheat()) 53 | fmt.Printf("%#v\n", d) 54 | 55 | } 56 | 57 | ``` 58 | 59 | ## copy slice 60 | ```go 61 | package main 62 | 63 | import ( 64 | "fmt" 65 | 66 | "github.com/antlabs/pcopy" 67 | ) 68 | 69 | func main() { 70 | i := []int{1, 2, 3, 4, 5, 6} 71 | var o []int 72 | 73 | pcopy.Preheat(&o, &i) 74 | pcopy.Copy(&o, &i, pcopy.WithUsePreheat()) 75 | 76 | fmt.Printf("%#v\n", o) 77 | } 78 | 79 | ``` 80 | 81 | ## copy map 82 | ```go 83 | package main 84 | 85 | import ( 86 | "fmt" 87 | 88 | "github.com/antlabs/pcopy" 89 | ) 90 | 91 | func main() { 92 | i := map[string]int{ 93 | "cat": 100, 94 | "head": 10, 95 | "tr": 3, 96 | "tail": 44, 97 | } 98 | 99 | var o map[string]int 100 | pcopy.Preheat(&o, &i) 101 | pcopy.Copy(&o, &i, pcopy.WithUsePreheat()) 102 | 103 | fmt.Printf("%#v\n", o) 104 | } 105 | 106 | ``` 107 | ## simplify business code development 108 | 经常看到,对同一个结构体的,有值更新操作,都是一堆手工if 然后赋值的代码。不仅容易出错,还累。快使用pcopy解放双手。 109 | ```go 110 | type option struct { 111 | Int int 112 | Float64 float64 113 | S string 114 | } 115 | 116 | func main() { 117 | var a, b option 118 | if b.Int != 0 { 119 | a.Int = b.Int 120 | } 121 | 122 | if b.Float64 != 0.0 { 123 | a.Float64 = b.Float64 124 | } 125 | 126 | if b.S != "" { 127 | a.S = b.S 128 | } 129 | 130 | pcopy.Preheat(&a, &b) //只要预热一次 131 | //可以约化成 132 | pcopy.Copy(&a, &b, pcopy.WithUsePreheat()) 133 | } 134 | ``` 135 | # benchmark 136 | 从零实现的pcopy相比json序列化与反序列化方式拥有更好的性能 137 | 138 | [压测仓库位置](https://github.com/1whour/deepcopy-benchmark) 139 | ``` 140 | goos: darwin 141 | goarch: arm64 142 | pkg: benchmark 143 | Benchmark_Use_reflectValue_MiniCopy-8 334728 3575 ns/op 144 | Benchmark_Use_reflectValue_DeepCopy-8 595302 1956 ns/op 145 | Benchmark_Use_reflectValue_Copier-8 203574 5860 ns/op 146 | Benchmark_Use_Ptr_jsoniter-8 821113 1477 ns/op 147 | Benchmark_Use_Ptr_pcopy-8 3390382 354.0 ns/op 148 | Benchmark_Use_Ptr_coven-8 1414197 848.7 ns/op 149 | PASS 150 | ok benchmark 9.771s 151 | ``` 152 | 153 | ### 本项目压测 154 | 从下面的压测数据可以看到,基本提供了4-10倍的性能提升 155 | ``` 156 | goos: darwin 157 | goarch: arm64 158 | pkg: github.com/antlabs/pcopy 159 | Benchmark_BaseMap_Unsafe_Pcopy-8 529747 2343 ns/op 160 | Benchmark_BaseMap_miniCopy-8 62181 19212 ns/op 161 | Benchmark_BaseMap_Reflect-8 93810 12756 ns/op 162 | Benchmark_BaseSlice_Unsafe_Pcopy-8 2013764 595.1 ns/op 163 | Benchmark_BaseSlice_miniCopy-8 154918 7728 ns/op 164 | Benchmark_BaseSlice_Reflect-8 188720 6393 ns/op 165 | Benchmark_BaseType_Unsafe_Pcopy-8 4872112 243.8 ns/op 166 | Benchmark_BaseType_MiniCopy-8 517814 2278 ns/op 167 | Benchmark_BaseType_Pcopy-8 635156 1886 ns/op 168 | Benchmark_CompositeMap_Unsafe_Pcopy-8 486253 2409 ns/op 169 | Benchmark_CompositeMap_miniCopy-8 229674 5173 ns/op 170 | Benchmark_CompositeMap_Reflect-8 475243 2490 ns/op 171 | Benchmark_GetLikeFavorited_Unsafe_Pcopy2-8 446907 2662 ns/op 172 | Benchmark_GetLikeFavorited_Unsafe_Pcopy-8 470217 2572 ns/op 173 | Benchmark_GetLikeFavorited_MiniCopy-8 85674 13989 ns/op 174 | Benchmark_GetLikeFavorited_Reflect_Pcopy-8 121603 9856 ns/op 175 | Benchmark_GetRedPoint_Unsafe_Pcopy-8 1626688 736.1 ns/op 176 | Benchmark_GetRedPoint_MiniCopy-8 650004 1871 ns/op 177 | Benchmark_GetRedPoint_Reflect_Pcopy-8 1669778 722.0 ns/op 178 | Benchmark_Interface_Unsafe_Pcopy-8 2869022 421.3 ns/op 179 | Benchmark_Interface_MiniCopy-8 413936 2704 ns/op 180 | Benchmark_Interface_Pcopy-8 440250 2688 ns/op 181 | Benchmark_Interface_BaseSlice_Unsafe_Pcopy-8 1266501 947.4 ns/op 182 | Benchmark_Interface_BaseSlice_MiniCopy-8 141610 8422 ns/op 183 | Benchmark_Interface_BaseSlice_Pcopy-8 203906 5917 ns/op 184 | Benchmark_Ptr_BaseType1_Unsafe_Pcopy-8 910153 1310 ns/op 185 | Benchmark_Ptr_BaseType1_Reflect_Pcopy-8 391117 3026 ns/op 186 | Benchmark_Ptr_BaseSlice_Unsafe_Pcopy-8 698156 1704 ns/op 187 | Benchmark_Ptr_BaseSlice_Reflect_Pcopy-8 219999 5415 ns/op 188 | Benchmark_SliceWithStruct_Unsafe_Pcopy-8 1395982 860.3 ns/op 189 | Benchmark_SliceWithStruct_miniCopy-8 163154 7298 ns/op 190 | Benchmark_SliceWithStruct_Reflect_Pcopy-8 190728 6213 ns/op 191 | ``` 192 | -------------------------------------------------------------------------------- /cache.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "reflect" 6 | "sync" 7 | "unsafe" 8 | ) 9 | 10 | type ( 11 | newBaseTypeFunc func(interface{}) interface{} 12 | setUnsafeFunc func(dstAddr, srcAddr unsafe.Pointer) 13 | setReflectFunc func(dstType, srcType reflect.Type, dst, src unsafe.Pointer, opt options, of *offsetAndFunc) error 14 | // setReflectFunc func(dstType, srcType reflect.Type, dstValType, srcValType reflect.Type, dst, src unsafe.Pointer, opt options) error 15 | setUnsafeFuncTab map[reflect.Kind]setUnsafeFunc 16 | setReflectFuncTab map[reflect.Kind]setReflectFunc 17 | setBaseMapFuncTab map[baseMapKind]setUnsafeFunc 18 | ) 19 | 20 | type baseMapKind struct { 21 | key reflect.Kind 22 | val reflect.Kind 23 | } 24 | 25 | type baseTypeTmpl struct { 26 | TypeName []string 27 | } 28 | 29 | var cacheAllFunc sync.Map 30 | 31 | type dstSrcType struct { 32 | dst reflect.Type 33 | src reflect.Type 34 | } 35 | 36 | type allFieldFunc struct { 37 | fieldFuncs []offsetAndFunc 38 | } 39 | 40 | type flag int 41 | 42 | const ( 43 | // 空值类型 44 | emptyTypeSet flag = iota 45 | baseTypeSet // 基础类型 46 | sliceTypeSet // slice类型 47 | baseSliceTypeSet // 基础类型的slice 48 | structTypeSet // 结构体类型 49 | baseMapTypeSet // 基础类型的map 50 | debugTypeSet // debug类型 51 | ) 52 | 53 | type offsetAndFunc struct { 54 | dstType reflect.Type 55 | srcType reflect.Type 56 | dstOffset int 57 | srcOffset int 58 | unsafeSet setUnsafeFunc 59 | reflectSet setReflectFunc 60 | 61 | // 找到一个复合类型 62 | nextComposite *allFieldFunc 63 | createFlag flag // 记录offsetAndFunc这个对象生成的触发点, debug时用 64 | } 65 | 66 | func saveToCache(a dstSrcType, fieldFunc *allFieldFunc) { 67 | // fmt.Printf("saveToCache: dst = %v, src = %v\n", a.dst, a.src) 68 | cacheAllFunc.LoadOrStore(a, fieldFunc) 69 | } 70 | 71 | func hasSetFromCache(a dstSrcType) (exist bool) { 72 | _, ok := cacheAllFunc.Load(a) 73 | return ok 74 | } 75 | 76 | func getFromCacheSetAndRun(a dstSrcType, dstAddr, srcAddr unsafe.Pointer, opt options) (exist bool, err error) { 77 | v, ok := cacheAllFunc.Load(a) 78 | if !ok { 79 | return false, nil 80 | } 81 | 82 | err = v.(*allFieldFunc).do(dstAddr, srcAddr, opt) 83 | // cacheFunc.do(dstAddr, srcAddr) 84 | return true, err 85 | } 86 | 87 | func newAllFieldFunc() (rv *allFieldFunc) { 88 | rv = &allFieldFunc{fieldFuncs: make([]offsetAndFunc, 0, 5)} 89 | return 90 | } 91 | 92 | func (af *allFieldFunc) append(fieldFunc offsetAndFunc) { 93 | af.fieldFuncs = append(af.fieldFuncs, fieldFunc) 94 | } 95 | 96 | func (c *allFieldFunc) do(dstBaseAddr, srcBaseAddr unsafe.Pointer, opt options) (err error) { 97 | for _, v := range c.fieldFuncs { 98 | var kind reflect.Kind 99 | 100 | if v.unsafeSet == nil && v.reflectSet == nil { 101 | goto next 102 | } 103 | 104 | kind = v.srcType.Kind() 105 | switch { 106 | // dst如果是ptr 107 | // 修改这里的代码,也要修改pcopy()函数里面的代码 108 | // 这里必须要放在第一 109 | case v.dstType.Kind() == reflect.Ptr: 110 | if err := v.reflectSet(v.dstType, v.srcType, add(dstBaseAddr, v.dstOffset), add(srcBaseAddr, v.srcOffset), opt, &v); err != nil { 111 | return err 112 | } 113 | // 这里必须放在第二 114 | case kind == reflect.Ptr: 115 | if err := v.reflectSet(v.dstType, v.srcType, add(dstBaseAddr, v.dstOffset), add(srcBaseAddr, v.srcOffset), opt, &v); err != nil { 116 | return err 117 | } 118 | // 特殊类型的struct才会进这里, 正常情况一个struct对应一个allFieldFunc 119 | case v.dstType.Kind() == reflect.Struct: 120 | if v.unsafeSet != nil { 121 | v.unsafeSet(add(dstBaseAddr, v.dstOffset), add(srcBaseAddr, v.srcOffset)) 122 | continue 123 | } 124 | // 目前是空的,预留下 125 | if err := v.reflectSet(v.dstType, v.srcType, add(dstBaseAddr, v.dstOffset), add(srcBaseAddr, v.srcOffset), opt, &v); err != nil { 126 | return err 127 | } 128 | // 处理slice 129 | case kind == reflect.Slice: 130 | // 由基础类型组成的slice 131 | if v.unsafeSet != nil { 132 | // 基础类型的slice直接一把函数搞定 133 | v.unsafeSet(add(dstBaseAddr, v.dstOffset), add(srcBaseAddr, v.srcOffset)) 134 | continue 135 | } 136 | 137 | // 复合类型的slice 138 | if v.reflectSet != nil { 139 | if err := v.reflectSet(v.dstType, v.srcType, add(dstBaseAddr, v.dstOffset), add(srcBaseAddr, v.srcOffset), opt, &v); err != nil { 140 | return err 141 | } 142 | } 143 | // 处理map 144 | case kind == reflect.Map: 145 | // 基础类型的map直接一把函数搞定 146 | if v.unsafeSet != nil { 147 | v.unsafeSet(add(dstBaseAddr, v.dstOffset), add(srcBaseAddr, v.srcOffset)) 148 | continue 149 | } 150 | 151 | // 复合类型的map 152 | if v.reflectSet != nil { 153 | if err := v.reflectSet(v.dstType, v.srcType, add(dstBaseAddr, v.dstOffset), add(srcBaseAddr, v.srcOffset), opt, &v); err != nil { 154 | return err 155 | } 156 | } 157 | // 基础类型 158 | case isBaseType(kind): 159 | v.unsafeSet(add(dstBaseAddr, v.dstOffset), add(srcBaseAddr, v.srcOffset)) 160 | 161 | // 处理interface 162 | case kind == reflect.Interface: 163 | 164 | if v.reflectSet != nil { 165 | // interface{}里面存放基础类型还是复合类型都由reflectSet函数处理 166 | if err := v.reflectSet(v.dstType, v.srcType, add(dstBaseAddr, v.dstOffset), add(srcBaseAddr, v.srcOffset), opt, &v); err != nil { 167 | return err 168 | } 169 | } 170 | 171 | } 172 | 173 | next: 174 | if v.nextComposite != nil { 175 | if err := v.nextComposite.do(dstBaseAddr, srcBaseAddr, opt); err != nil { 176 | return err 177 | } 178 | continue 179 | } 180 | } 181 | return nil 182 | } 183 | 184 | func addOffset(addr unsafe.Pointer, offset uintptr, i int) unsafe.Pointer { 185 | return add(addr, int(offset)*i) 186 | } 187 | 188 | // 基址+offset 189 | func add(addr unsafe.Pointer, offset int) unsafe.Pointer { 190 | return unsafe.Pointer(uintptr(addr) + uintptr(offset)) 191 | } 192 | 193 | // 基址-当前字段地址 求 offset 194 | func sub(base uintptr, addr uintptr) int { 195 | return int(base - uintptr(addr)) 196 | } 197 | -------------------------------------------------------------------------------- /case.md: -------------------------------------------------------------------------------- 1 | 测试点 2 | ### 1. 名字一样,类型不一样的数据类型。 3 | 1.1 slice 和 map(todo) 4 | 1.2 map 和slice(todo) 5 | 1.3 interface{} 和map(todo) 6 | 1.4 map和interface{}(todo) 7 | 1.5 array 和 slice(todo) 8 | 9 | ### 2. src是指针类型的各种排列组合 10 | 2.1 基础类型 11 | 2.1.1 dst是指针, src基础类型(done) 12 | 2.1.2 dst是指针,src是指针(done) 13 | 2.1.3 dst是基础类型,src是指针(done) 14 | 15 | 2.2 基础类型-2级指针 16 | 2.2.1 dst是指针, src基础类型(done) 17 | 2.2.2 dst是指针,src是指针(done) 18 | 2.2.3 dst是基础类型,src是指针(done) 19 | 20 | 2.3 slice类型 21 | 2.3.1 dst是指针, src基础slice类型(done) 22 | 2.3.2 dst是指针,src是指针(done) 23 | 2.3.3 dst是基础slice类型,src是指针(done) 24 | 25 | 2.4 map类型 26 | 2.4.1 dst是指针, src基础map类型(done) 27 | 2.4.2 dst是指针,src是指针(done) 28 | 2.4.3 dst是基础map类型,src是指针(done) 29 | 30 | 2.5 struct类型 31 | 2.5.1 dst是指针, src struct类型(done) 32 | 2.5.2 dst是指针,src是指针(done) 33 | 2.5.3 dst是struct类型,src是指针(done) 34 | 35 | ### 3. 特殊类型 36 | 1. map[interface{}]interface{} 37 | 2. time.Time-结构体,特殊处理了 (done) 38 | 3. []byte 和[]uint8处理一样 (done) 39 | 40 | ### 4. slice类型 41 | 1. 基础slice 42 | 1.1 dst的元素是指针slice src元素不是(done) 43 | 1.2 dst的元素不是指针 slice的元素是指针(done) 44 | 1.3 dst的元素是指针, src的元素指针(done) 45 | 46 | // 1。slice里面套基础map 47 | // type sliceMap struct { 48 | // // 1.1 slice里面套基础map,map里面套基础类型 49 | // SliceWithMap []map[string]string 50 | // // 1.2 slice里面套基础map,map里面套struct 51 | // SliceWithMapStruct []map[string]DCopyDst_BaseSlice 52 | // // 1.3 slice里面套基础map,map里面套interface 53 | // // SliceWithMapInterface []map[string]interface{} 54 | // } 55 | 56 | // 2. slice里面套map 57 | 58 | // 3. slice里面套slice 59 | 60 | // 4. slice里面套struct 61 | 62 | // 5. slice里面套interface 63 | 64 | // 6. slice里面套基础类型 65 | 66 | // 7. slice里面套slice里面套基础类型 67 | 68 | // 8. slice里面套slice里面套map 69 | 70 | // 9. slice里面套slice里面套struct 71 | 72 | // 10. slice里面套slice里面套interface 73 | 74 | // 11. slice里面套slice里面套slice 75 | -------------------------------------------------------------------------------- /fix-panic_test.go: -------------------------------------------------------------------------------- 1 | package pcopy 2 | 3 | import ( 4 | "database/sql" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | type TopicItem struct { 10 | ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` 11 | Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` 12 | CreateTime string `protobuf:"bytes,3,opt,name=CreateTime,proto3" json:"CreateTime,omitempty"` 13 | OwnerRid uint64 `protobuf:"varint,4,opt,name=OwnerRid,proto3" json:"OwnerRid,omitempty"` 14 | } 15 | 16 | type OmGroupUserTopic struct { 17 | ID int64 18 | Rid int64 19 | GroupID string 20 | TopicID string 21 | Name sql.NullString 22 | CreateTime time.Time 23 | CreateTimeMilli int64 24 | OwnerRid int64 25 | } 26 | 27 | func Test_Panic(t *testing.T) { 28 | // 类型不匹配 29 | t.Run("类型不一样导致的panic, dst是uint, src是int", func(t *testing.T) { 30 | v := &TopicItem{} 31 | Copy(v, &OmGroupUserTopic{ 32 | OwnerRid: 1, 33 | }) 34 | if v.OwnerRid != 1 { 35 | t.Fatal("OwnerRid not equal") 36 | } 37 | 38 | Preheat(&TopicItem{}, &OmGroupUserTopic{}) 39 | dst := &TopicItem{} 40 | src := &OmGroupUserTopic{OwnerRid: 1} 41 | Copy(&dst, src, WithUsePreheat()) 42 | if dst.OwnerRid != 1 { 43 | t.Fatal("OwnerRid not equal") 44 | } 45 | }) 46 | 47 | t.Run("类型不一样导致的panic, dst是int, src是uint", func(t *testing.T) { 48 | src := &TopicItem{ 49 | OwnerRid: 1, 50 | } 51 | dst := &OmGroupUserTopic{} 52 | Copy(dst, src) 53 | if dst.OwnerRid != 1 { 54 | t.Fatal("OwnerRid not equal") 55 | } 56 | 57 | Preheat(&OmGroupUserTopic{}, &TopicItem{}) 58 | dst = &OmGroupUserTopic{} 59 | src = &TopicItem{OwnerRid: 1} 60 | Copy(dst, src, WithUsePreheat()) 61 | if dst.OwnerRid != 1 { 62 | t.Fatal("OwnerRid not equal") 63 | } 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/antlabs/pcopy 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/Masterminds/sprig/v3 v3.2.3 7 | github.com/stretchr/testify v1.5.1 8 | ) 9 | 10 | require ( 11 | github.com/Masterminds/goutils v1.1.1 // indirect 12 | github.com/Masterminds/semver/v3 v3.2.0 // indirect 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/google/uuid v1.1.1 // indirect 15 | github.com/huandu/xstrings v1.3.3 // indirect 16 | github.com/imdario/mergo v0.3.11 // indirect 17 | github.com/mitchellh/copystructure v1.0.0 // indirect 18 | github.com/mitchellh/reflectwalk v1.0.0 // indirect 19 | github.com/pmezard/go-difflib v1.0.0 // indirect 20 | github.com/shopspring/decimal v1.2.0 // indirect 21 | github.com/spf13/cast v1.3.1 // indirect 22 | golang.org/x/crypto v0.31.0 // indirect 23 | gopkg.in/yaml.v2 v2.3.0 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= 2 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 3 | github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= 4 | github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= 5 | github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= 6 | github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 11 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 12 | github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= 13 | github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 14 | github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= 15 | github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 16 | github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= 17 | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 18 | github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= 19 | github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 20 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 21 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 22 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 23 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 24 | github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= 25 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 26 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 27 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 28 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 29 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 30 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 31 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 32 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 33 | golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 34 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 35 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 36 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 37 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 38 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 39 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 40 | golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 41 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 42 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 43 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 44 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 45 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 46 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 47 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 48 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 49 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 50 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 51 | golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 52 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 53 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 54 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 55 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 56 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 57 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 58 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 59 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 60 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 61 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 62 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 63 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 64 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 65 | -------------------------------------------------------------------------------- /is_base_type.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import "reflect" 5 | 6 | func isBaseType(b reflect.Kind) bool { 7 | switch b { 8 | case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 9 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 10 | reflect.String, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.Uintptr: 11 | return true 12 | } 13 | return false 14 | } 15 | 16 | func isInt64(b reflect.Kind) bool { 17 | switch b { 18 | case reflect.Int64, reflect.Uint64, reflect.Int: 19 | return true 20 | } 21 | return false 22 | } 23 | 24 | func isInt32(b reflect.Kind) bool { 25 | switch b { 26 | case reflect.Int32, reflect.Uint32: 27 | return true 28 | } 29 | return false 30 | } 31 | 32 | func isInt16(b reflect.Kind) bool { 33 | switch b { 34 | case reflect.Int16, reflect.Uint16: 35 | return true 36 | } 37 | return false 38 | } 39 | 40 | func isInt8(b reflect.Kind) bool { 41 | switch b { 42 | case reflect.Int8, reflect.Uint8: 43 | return true 44 | } 45 | return false 46 | } 47 | 48 | func isBaseSliceOrBaseSlicePtr(t reflect.Type, k *reflect.Kind) bool { 49 | if t.Kind() != reflect.Slice { 50 | return false 51 | } 52 | 53 | if isBaseType(t.Elem().Kind()) { 54 | if k != nil { 55 | *k = t.Elem().Kind() 56 | } 57 | return true 58 | } 59 | 60 | if t.Elem().Kind() != reflect.Ptr { 61 | return false 62 | } 63 | 64 | if isBaseType(t.Elem().Elem().Kind()) { 65 | if k != nil { 66 | *k = t.Elem().Elem().Kind() 67 | } 68 | return true 69 | } 70 | return false 71 | } 72 | -------------------------------------------------------------------------------- /is_base_type_test.go: -------------------------------------------------------------------------------- 1 | package pcopy 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "unsafe" 7 | ) 8 | 9 | func Test_IsInt64(t *testing.T) { 10 | if !isInt64(reflect.Int64) { 11 | t.Fatalf("reflect.Int64 is int64") 12 | i := int64(0) 13 | if unsafe.Sizeof(i) != 8 { 14 | t.Fatalf("int64 size is not 8") 15 | } 16 | } 17 | 18 | if !isInt64(reflect.Uint64) { 19 | t.Fatalf("reflect.Uint64 is int64") 20 | i := uint64(0) 21 | if unsafe.Sizeof(i) != 8 { 22 | t.Fatalf("uint64 size is not 8") 23 | } 24 | } 25 | 26 | if !isInt64(reflect.Int) { 27 | t.Fatalf("reflect.Int is int64") 28 | i := int(0) 29 | if unsafe.Sizeof(i) != 8 { 30 | t.Fatalf("int size is not 8") 31 | } 32 | } 33 | } 34 | 35 | type TestBaseTypeExDst struct { 36 | N64 uint64 37 | N32 uint32 38 | N16 uint16 39 | N8 uint8 40 | } 41 | 42 | type TestBaseTypeExSrc struct { 43 | N64 int64 44 | N32 int32 45 | N16 int16 46 | N8 int8 47 | } 48 | 49 | func Test_Base_Type_Ex(t *testing.T) { 50 | err := Preheat(&TestBaseTypeExDst{}, &TestBaseTypeExSrc{}) 51 | if err != nil { 52 | t.Fatalf("preheat error: %v", err) 53 | } 54 | d := TestBaseTypeExDst{} 55 | s := TestBaseTypeExSrc{ 56 | N64: 1, 57 | N32: 2, 58 | N16: 3, 59 | N8: 4, 60 | } 61 | need := TestBaseTypeExDst{ 62 | N64: 1, 63 | N32: 2, 64 | N16: 3, 65 | N8: 4, 66 | } 67 | err = Copy(&d, &s, WithUsePreheat()) 68 | if err != nil { 69 | t.Fatalf("copy error rv: %v", err) 70 | } 71 | if d != need { 72 | t.Fatalf("copy error: %v", d) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /new_baseslice.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "reflect" 6 | "unsafe" 7 | ) 8 | 9 | func newIntSlice(sh *reflect.SliceHeader) interface{} { 10 | rv := make([]int, sh.Len) 11 | copy(rv, *(*[]int)(unsafe.Pointer(sh))) 12 | return rv 13 | } 14 | 15 | func newInt8Slice(sh *reflect.SliceHeader) interface{} { 16 | rv := make([]int8, sh.Len) 17 | copy(rv, *(*[]int8)(unsafe.Pointer(sh))) 18 | return rv 19 | } 20 | 21 | func newInt16Slice(sh *reflect.SliceHeader) interface{} { 22 | rv := make([]int16, sh.Len) 23 | copy(rv, *(*[]int16)(unsafe.Pointer(sh))) 24 | return rv 25 | } 26 | 27 | func newInt32Slice(sh *reflect.SliceHeader) interface{} { 28 | rv := make([]int32, sh.Len) 29 | copy(rv, *(*[]int32)(unsafe.Pointer(sh))) 30 | return rv 31 | } 32 | 33 | func newInt64Slice(sh *reflect.SliceHeader) interface{} { 34 | rv := make([]int64, sh.Len) 35 | copy(rv, *(*[]int64)(unsafe.Pointer(sh))) 36 | return rv 37 | } 38 | 39 | func newUintSlice(sh *reflect.SliceHeader) interface{} { 40 | rv := make([]uint, sh.Len) 41 | copy(rv, *(*[]uint)(unsafe.Pointer(sh))) 42 | return rv 43 | } 44 | 45 | func newUint8Slice(sh *reflect.SliceHeader) interface{} { 46 | rv := make([]uint8, sh.Len) 47 | copy(rv, *(*[]uint8)(unsafe.Pointer(sh))) 48 | return rv 49 | } 50 | 51 | func newUint16Slice(sh *reflect.SliceHeader) interface{} { 52 | rv := make([]uint16, sh.Len) 53 | copy(rv, *(*[]uint16)(unsafe.Pointer(sh))) 54 | return rv 55 | } 56 | 57 | func newUint32Slice(sh *reflect.SliceHeader) interface{} { 58 | rv := make([]uint32, sh.Len) 59 | copy(rv, *(*[]uint32)(unsafe.Pointer(sh))) 60 | return rv 61 | } 62 | 63 | func newUint64Slice(sh *reflect.SliceHeader) interface{} { 64 | rv := make([]uint64, sh.Len) 65 | copy(rv, *(*[]uint64)(unsafe.Pointer(sh))) 66 | return rv 67 | } 68 | 69 | func newStringSlice(sh *reflect.SliceHeader) interface{} { 70 | rv := make([]string, sh.Len) 71 | copy(rv, *(*[]string)(unsafe.Pointer(sh))) 72 | return rv 73 | } 74 | 75 | func newFloat32Slice(sh *reflect.SliceHeader) interface{} { 76 | rv := make([]float32, sh.Len) 77 | copy(rv, *(*[]float32)(unsafe.Pointer(sh))) 78 | return rv 79 | } 80 | 81 | func newFloat64Slice(sh *reflect.SliceHeader) interface{} { 82 | rv := make([]float64, sh.Len) 83 | copy(rv, *(*[]float64)(unsafe.Pointer(sh))) 84 | return rv 85 | } 86 | 87 | func newComplex64Slice(sh *reflect.SliceHeader) interface{} { 88 | rv := make([]complex64, sh.Len) 89 | copy(rv, *(*[]complex64)(unsafe.Pointer(sh))) 90 | return rv 91 | } 92 | 93 | func newComplex128Slice(sh *reflect.SliceHeader) interface{} { 94 | rv := make([]complex128, sh.Len) 95 | copy(rv, *(*[]complex128)(unsafe.Pointer(sh))) 96 | return rv 97 | } 98 | 99 | func newBoolSlice(sh *reflect.SliceHeader) interface{} { 100 | rv := make([]bool, sh.Len) 101 | copy(rv, *(*[]bool)(unsafe.Pointer(sh))) 102 | return rv 103 | } 104 | 105 | func newUintptrSlice(sh *reflect.SliceHeader) interface{} { 106 | rv := make([]uintptr, sh.Len) 107 | copy(rv, *(*[]uintptr)(unsafe.Pointer(sh))) 108 | return rv 109 | } 110 | 111 | func getNewBaseSliceType(k reflect.Kind, sh *reflect.SliceHeader) interface{} { 112 | newBaseSliceType, ok := newBaseSliceTypeTable[k] 113 | if ok { 114 | return newBaseSliceType(sh) 115 | } 116 | return nil 117 | } 118 | 119 | var newBaseSliceTypeTable = map[reflect.Kind]func(sh *reflect.SliceHeader) interface{}{ 120 | reflect.Int: newIntSlice, 121 | reflect.Int8: newInt8Slice, 122 | reflect.Int16: newInt16Slice, 123 | reflect.Int32: newInt32Slice, 124 | reflect.Int64: newInt64Slice, 125 | reflect.Uint: newUintSlice, 126 | reflect.Uint8: newUint8Slice, 127 | reflect.Uint16: newUint16Slice, 128 | reflect.Uint32: newUint32Slice, 129 | reflect.Uint64: newUint64Slice, 130 | reflect.String: newStringSlice, 131 | reflect.Float32: newFloat32Slice, 132 | reflect.Float64: newFloat64Slice, 133 | reflect.Bool: newBoolSlice, 134 | reflect.Complex64: newComplex64Slice, 135 | reflect.Complex128: newComplex128Slice, 136 | reflect.Uintptr: newUintptrSlice, 137 | } 138 | -------------------------------------------------------------------------------- /new_basetype.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "reflect" 6 | "unsafe" 7 | ) 8 | 9 | func newUint(dst interface{}) interface{} { 10 | d := *(*uint)((*emptyInterface)(unsafe.Pointer(&dst)).word) 11 | return d 12 | } 13 | 14 | func newUint8(dst interface{}) interface{} { 15 | d := *(*uint8)((*emptyInterface)(unsafe.Pointer(&dst)).word) 16 | return d 17 | } 18 | 19 | func newUint16(dst interface{}) interface{} { 20 | d := *(*uint16)((*emptyInterface)(unsafe.Pointer(&dst)).word) 21 | return d 22 | } 23 | 24 | func newUint32(dst interface{}) interface{} { 25 | d := *(*uint32)((*emptyInterface)(unsafe.Pointer(&dst)).word) 26 | return d 27 | } 28 | 29 | func newUint64(dst interface{}) interface{} { 30 | d := *(*uint64)((*emptyInterface)(unsafe.Pointer(&dst)).word) 31 | return d 32 | } 33 | 34 | func newInt(dst interface{}) interface{} { 35 | d := *(*int)((*emptyInterface)(unsafe.Pointer(&dst)).word) 36 | return d 37 | } 38 | 39 | func newInt8(dst interface{}) interface{} { 40 | d := *(*int8)((*emptyInterface)(unsafe.Pointer(&dst)).word) 41 | return d 42 | } 43 | 44 | func newInt16(dst interface{}) interface{} { 45 | d := *(*int16)((*emptyInterface)(unsafe.Pointer(&dst)).word) 46 | return d 47 | } 48 | 49 | func newInt32(dst interface{}) interface{} { 50 | d := *(*int32)((*emptyInterface)(unsafe.Pointer(&dst)).word) 51 | return d 52 | } 53 | 54 | func newInt64(dst interface{}) interface{} { 55 | d := *(*int64)((*emptyInterface)(unsafe.Pointer(&dst)).word) 56 | return d 57 | } 58 | 59 | // func newString(dst interface{}) interface{} { 60 | // d := *(*string)((*emptyInterface)(unsafe.Pointer(&dst)).word) 61 | // return d 62 | // } 63 | 64 | func newString(dst interface{}) interface{} { 65 | d := *(*string)((*emptyInterface)(unsafe.Pointer(&dst)).word) 66 | return d 67 | } 68 | 69 | func newFloat32(dst interface{}) interface{} { 70 | d := *(*float32)((*emptyInterface)(unsafe.Pointer(&dst)).word) 71 | return d 72 | } 73 | 74 | func newFloat64(dst interface{}) interface{} { 75 | d := *(*float64)((*emptyInterface)(unsafe.Pointer(&dst)).word) 76 | return d 77 | } 78 | 79 | func newBool(dst interface{}) interface{} { 80 | d := *(*bool)((*emptyInterface)(unsafe.Pointer(&dst)).word) 81 | return d 82 | } 83 | 84 | func newComplex64(dst interface{}) interface{} { 85 | d := *(*complex64)((*emptyInterface)(unsafe.Pointer(&dst)).word) 86 | return d 87 | } 88 | 89 | func newComplex128(dst interface{}) interface{} { 90 | d := *(*complex128)((*emptyInterface)(unsafe.Pointer(&dst)).word) 91 | return d 92 | } 93 | 94 | func newUintptr(dst interface{}) interface{} { 95 | d := *(*uintptr)((*emptyInterface)(unsafe.Pointer(&dst)).word) 96 | return d 97 | } 98 | 99 | func getNewBaseType(k reflect.Kind, dst interface{}) interface{} { 100 | newBaseType, ok := newBaseTypeTable[k] 101 | if ok { 102 | return newBaseType(dst) 103 | } 104 | return nil 105 | } 106 | 107 | var newBaseTypeTable = map[reflect.Kind]newBaseTypeFunc{ 108 | reflect.Int: newInt, 109 | reflect.Int8: newInt8, 110 | reflect.Int16: newInt16, 111 | reflect.Int32: newInt32, 112 | reflect.Int64: newInt64, 113 | reflect.Uint: newUint, 114 | reflect.Uint8: newUint8, 115 | reflect.Uint16: newUint16, 116 | reflect.Uint32: newUint32, 117 | reflect.Uint64: newUint64, 118 | reflect.String: newString, 119 | reflect.Float32: newFloat32, 120 | reflect.Float64: newFloat64, 121 | reflect.Bool: newBool, 122 | reflect.Complex64: newComplex64, 123 | reflect.Complex128: newComplex128, 124 | reflect.Uintptr: newUintptr, 125 | } 126 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | type options struct { 5 | // // MaxDepth is the maximum depth to traverse. 6 | // // If MaxDepth is 0, it will be treated as no limit. 7 | // maxDepth int 8 | // // TagName is the tag name to use. 9 | // // If TagName is empty, it will be treated as no tag. 10 | // tagName string 11 | // // OnlyField is the field name to copy. 12 | // // If OnlyField is empty, it will be treated as no field. 13 | // OnlyField string 14 | preheat bool 15 | 16 | // 使用预热cache 17 | usePreheat bool 18 | } 19 | 20 | type Option func(*options) 21 | 22 | // func WithMaxDepth(maxDepth int) Option { 23 | // return func(o *options) { 24 | // o.maxDepth = maxDepth 25 | // } 26 | // } 27 | 28 | // func WithTagName(tagName string) Option { 29 | // return func(o *options) { 30 | // o.tagName = tagName 31 | // } 32 | // } 33 | 34 | func WithUsePreheat() Option { 35 | return func(o *options) { 36 | o.usePreheat = true 37 | } 38 | } 39 | 40 | // 内部函数 41 | func withPreheat() Option { 42 | return func(o *options) { 43 | o.preheat = true 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pcopy.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | "reflect" 8 | "time" 9 | "unsafe" 10 | ) 11 | 12 | // 不支持的类型 13 | var ErrUnsupportedType = errors.New("Unsupported type") 14 | 15 | // 不支持nil类型 16 | var ErrUnsupportedNil = errors.New("Unsupported nil type") 17 | 18 | // dst 和 src必须是指针类型 19 | var ErrNotPointer = errors.New("dst and src must be pointer") 20 | 21 | // 不能获取指针地址 22 | var ErrNotAddr = errors.New("dst or src type can not get address") 23 | 24 | var zeroUintptr unsafe.Pointer 25 | 26 | // pcopy结构体 27 | type pcopy struct { 28 | options 29 | } 30 | 31 | func Copy[T any, U any](dst *T, src *U, opts ...Option) error { 32 | if dst == nil || src == nil { 33 | return ErrUnsupportedNil 34 | } 35 | var opt options 36 | for _, o := range opts { 37 | o(&opt) 38 | } 39 | var dstI interface{} = dst 40 | var srcI interface{} = src 41 | 42 | return pcopyInner(dstI, srcI, opt) 43 | } 44 | 45 | func pcopyInner(dst, src interface{}, opt options) error { 46 | if dst == nil || src == nil { 47 | return ErrUnsupportedNil 48 | } 49 | 50 | dstValue := reflect.ValueOf(dst) 51 | srcValue := reflect.ValueOf(src) 52 | // 开启预热逻辑 53 | dstAddr := zeroUintptr 54 | srcAddr := zeroUintptr 55 | 56 | // 预热逻辑和预热cache逻辑都要走 57 | var of offsetAndFunc 58 | 59 | var all *allFieldFunc 60 | if opt.preheat || opt.usePreheat { 61 | if dstValue.Kind() != reflect.Ptr || srcValue.Kind() != reflect.Ptr { 62 | return ErrNotPointer 63 | } 64 | 65 | if !dstValue.Elem().CanAddr() { 66 | return fmt.Errorf("dst %w", ErrNotAddr) 67 | } 68 | 69 | if !srcValue.Elem().CanAddr() { 70 | return fmt.Errorf("src %w", ErrNotAddr) 71 | } 72 | dstAddr = unsafe.Pointer(dstValue.Elem().UnsafeAddr()) 73 | srcAddr = unsafe.Pointer(srcValue.Elem().UnsafeAddr()) 74 | 75 | dstValue = dstValue.Elem() 76 | srcValue = srcValue.Elem() 77 | 78 | // 从cache load出类型直接执行 79 | exist, err := getFromCacheSetAndRun(dstSrcType{dst: dstValue.Type(), src: srcValue.Type()}, dstAddr, srcAddr, opt) 80 | if exist || err != nil { 81 | return err 82 | } 83 | 84 | of.srcType = srcValue.Type() 85 | of.dstType = dstValue.Type() 86 | 87 | if opt.preheat { 88 | all = newAllFieldFunc() 89 | // 如果顶层对象是结构体,肯定会有预热结构 90 | // 但是是其它类型可能没有预热结构, 所以这里要判断一下 91 | if dstValue.Type().Kind() != reflect.Struct && srcValue.Type().Kind() != reflect.Struct { 92 | defer saveToCache(dstSrcType{ 93 | dst: dstValue.Type(), 94 | src: srcValue.Type(), 95 | }, 96 | all) 97 | } 98 | } 99 | 100 | // 按道理已经预热过的类型, 不会走到这里 101 | // 但这里有个特殊情况,比如多级不对称指针。为了支持这种情况,就不报错了 102 | // if opt.usePreheat { 103 | // return errors.New("usePreheat must be used with preheat") 104 | // } 105 | } 106 | 107 | d := pcopy{ 108 | options: opt, 109 | } 110 | return d.pcopy(dstValue, srcValue, dstAddr, srcAddr, of, all) 111 | } 112 | 113 | // // 需要的tag name 114 | // func haveTagName(curTabName string) bool { 115 | // return len(curTabName) > 0 116 | // } 117 | 118 | // 判断是array或slice类型 119 | func isArraySlice(v reflect.Value) bool { 120 | switch v.Kind() { 121 | case reflect.Array, reflect.Slice: 122 | return true 123 | } 124 | return false 125 | } 126 | 127 | // 拷贝slice array 128 | func (d *pcopy) cpySliceArray(dst, src reflect.Value, dstBase, srcBase unsafe.Pointer, of offsetAndFunc, all *allFieldFunc) error { 129 | // dst只能是slice和array类型 130 | if !isArraySlice(dst) { 131 | return nil 132 | } 133 | 134 | // 保存类型缓存 135 | l := src.Len() 136 | if dst.Len() > 0 && dst.Len() < src.Len() { 137 | l = dst.Len() 138 | } 139 | 140 | // 被拷贝dst类型是slice, 长度是0, src的长度有值 141 | if dst.Kind() == reflect.Slice && dst.Len() == 0 && src.Len() > 0 { 142 | dstElemType := dst.Type().Elem() 143 | newDst := reflect.MakeSlice(reflect.SliceOf(dstElemType), l, l) 144 | dst.Set(newDst) 145 | } 146 | 147 | if d.preheat { 148 | of.srcType = src.Type() 149 | of.dstType = dst.Type() 150 | // 如果是基础类型的slice, []int []string 这种 151 | if isBaseType(dst.Type().Elem().Kind()) && isBaseType(src.Type().Elem().Kind()) { 152 | of.unsafeSet = getSetBaseSliceFunc(dst.Type().Elem().Kind()) 153 | all.append(of) 154 | of.createFlag = baseSliceTypeSet 155 | return nil 156 | } 157 | // 如果任一类型是基础类型的指针类型, *[]int *[]string 这种,和基础类型的slice的组合 158 | var k reflect.Kind 159 | if isBaseSliceOrBaseSlicePtr(dst.Type(), &k) && isBaseSliceOrBaseSlicePtr(src.Type(), &k) { 160 | of.reflectSet = getSetSliceElemIsBaseTypeOrPtrFunc(k, false) 161 | of.createFlag = baseMapTypeSet 162 | all.append(of) 163 | return nil 164 | } 165 | 166 | // 处理复合类型的slice 167 | of.reflectSet = getSetCompositeFunc(dst.Type().Kind()) 168 | all.append(of) 169 | 170 | dstElemType := dst.Type().Elem() 171 | srcElemType := src.Type().Elem() 172 | exits := hasSetFromCache(dstSrcType{dst: dstElemType, src: srcElemType}) 173 | // 已经存在穿上类型的转换表,直接返回 174 | if exits { 175 | return nil 176 | } 177 | 178 | // 元素不存在转换表,需要创建 179 | return pcopyInner(reflect.New(dstElemType).Interface(), reflect.New(srcElemType).Interface(), d.options) 180 | } 181 | 182 | for i := 0; i < l; i++ { 183 | if err := d.pcopy(dst.Index(i), src.Index(i), dstBase, srcBase, of, all); err != nil { 184 | return err 185 | } 186 | } 187 | return nil 188 | } 189 | 190 | // 拷贝map 191 | func (d *pcopy) cpyMap(dst, src reflect.Value, dstBase, srcBase unsafe.Pointer, of offsetAndFunc, all *allFieldFunc) error { 192 | if dst.Kind() == reflect.Ptr { 193 | return d.cpyPtr(dst, src, dstBase, srcBase, of, all) 194 | } 195 | 196 | if dst.Kind() != src.Kind() { 197 | return nil 198 | } 199 | 200 | if !dst.CanSet() { 201 | return nil 202 | } 203 | 204 | // 检查value的类型 205 | if dst.Type().Elem().Kind() != src.Type().Elem().Kind() { 206 | return nil 207 | } 208 | 209 | // 检查key的类型 210 | if dst.Type().Key().Kind() != src.Type().Key().Kind() { 211 | return nil 212 | } 213 | 214 | if dst.IsNil() { 215 | 216 | newMap := reflect.MakeMap(src.Type()) 217 | dst.Set(newMap) 218 | } 219 | 220 | if d.preheat { 221 | // 如果是基础类型的slice 222 | of.srcType = src.Type() 223 | of.dstType = dst.Type() 224 | if isBaseType(dst.Type().Elem().Kind()) && isBaseType(src.Type().Elem().Kind()) { 225 | of.unsafeSet = getSetBaseMapFunc(src.Type().Key().Kind(), src.Type().Elem().Kind(), true) 226 | of.createFlag = baseMapTypeSet 227 | all.append(of) 228 | return nil 229 | } 230 | 231 | of.reflectSet = getSetCompositeFunc(dst.Type().Kind()) 232 | 233 | all.append(of) 234 | 235 | dstElemType := dst.Type().Elem() 236 | srcElemType := src.Type().Elem() 237 | exits := hasSetFromCache(dstSrcType{dst: dstElemType, src: srcElemType}) 238 | // 已经存在穿上类型的转换表,直接返回 239 | if exits { 240 | return nil 241 | } 242 | 243 | // 元素不存在转换表,需要创建 244 | return pcopyInner(reflect.New(dstElemType).Interface(), reflect.New(srcElemType).Interface(), d.options) 245 | } 246 | 247 | iter := src.MapRange() 248 | for iter.Next() { 249 | k := iter.Key() 250 | v := iter.Value() 251 | 252 | newVal := reflect.New(v.Type()).Elem() 253 | if err := d.pcopy(newVal, v, zeroUintptr, zeroUintptr, of, all); err != nil { 254 | return err 255 | } 256 | 257 | dst.SetMapIndex(k, newVal) 258 | } 259 | return nil 260 | } 261 | 262 | // 拷贝函数 263 | func (d *pcopy) cpyFunc(dst, src reflect.Value) error { 264 | if dst.Kind() != src.Kind() { 265 | return nil 266 | } 267 | 268 | dst.Set(src) 269 | return nil 270 | } 271 | 272 | // 拷贝结构体 273 | func (d *pcopy) cpyStruct(dst, src reflect.Value, dstBase, srcBase unsafe.Pointer, of offsetAndFunc, all *allFieldFunc) error { 274 | if dst.Kind() != src.Kind() { 275 | return nil 276 | } 277 | 278 | if dst.CanSet() { 279 | if _, ok := src.Interface().(time.Time); ok { 280 | if d.preheat { 281 | of.srcType = src.Type() 282 | of.dstType = dst.Type() 283 | of.unsafeSet = setTime 284 | of.createFlag = baseTypeSet 285 | all.append(of) 286 | return nil 287 | } 288 | 289 | dst.Set(src) 290 | } 291 | } 292 | 293 | if d.usePreheat { 294 | // 从cache load出类型直接执行 295 | exist, err := getFromCacheSetAndRun(dstSrcType{dst: dst.Type(), src: src.Type()}, dstBase, srcBase, d.options) 296 | if exist || err != nil { 297 | return nil 298 | } 299 | } 300 | 301 | typ := src.Type() 302 | for i, n := 0, src.NumField(); i < n; i++ { 303 | sf := typ.Field(i) 304 | if sf.PkgPath != "" && !sf.Anonymous { 305 | continue 306 | } 307 | 308 | // 使用src的字段名在dst里面取出reflect.Value值 309 | dstValue := dst.FieldByName(sf.Name) 310 | 311 | // dst没有src里面所有的字段,跳过 312 | if !dstValue.IsValid() { 313 | continue 314 | } 315 | 316 | // 检查结构体里面的字段是否有循环引用 317 | sField := src.Field(i) 318 | 319 | if d.preheat { 320 | // 更新这个类型的offset 321 | of.dstOffset = sub(dstValue.UnsafeAddr(), uintptr(dstBase)) 322 | of.srcOffset = sub(sField.UnsafeAddr(), uintptr(srcBase)) 323 | } 324 | 325 | if err := d.pcopy(dstValue, sField, dstBase, srcBase, of, all); err != nil { 326 | return err 327 | } 328 | } 329 | 330 | return nil 331 | } 332 | 333 | // 拷贝interface{} 334 | func (d *pcopy) cpyInterface(dst, src reflect.Value, of offsetAndFunc, all *allFieldFunc) error { 335 | if dst.Kind() != src.Kind() { 336 | return nil 337 | } 338 | 339 | src = src.Elem() 340 | newDst := reflect.New(src.Type()).Elem() 341 | 342 | newDstAddr := zeroUintptr 343 | newSrcAddr := zeroUintptr 344 | if d.usePreheat { 345 | newDstAddr = unsafe.Pointer(newDst.UnsafeAddr()) 346 | newSrcAddr = unsafe.Pointer(src.UnsafeAddr()) 347 | } 348 | 349 | if err := d.pcopy(newDst, src, newDstAddr, newSrcAddr, of, all); err != nil { 350 | return err 351 | } 352 | 353 | dst.Set(newDst) 354 | return nil 355 | } 356 | 357 | func (d *pcopy) preheatPtr(dst, src reflect.Value, of offsetAndFunc, all *allFieldFunc) error { 358 | bkDst := dst 359 | bkSrc := src 360 | for { 361 | if dst.Kind() == reflect.Ptr && dst.IsNil() { 362 | // dst.CanSet必须放到dst.IsNil判断里面 363 | // 不然会影响到struct或者map类型的指针 364 | if !dst.CanSet() { 365 | return nil 366 | } 367 | p := reflect.New(dst.Type().Elem()) 368 | dst.Set(p) 369 | } 370 | if src.Kind() == reflect.Ptr && src.IsNil() { 371 | // dst.CanSet必须放到dst.IsNil判断里面 372 | // 不然会影响到struct或者map类型的指针 373 | if !src.CanSet() { 374 | return nil 375 | } 376 | p := reflect.New(src.Type().Elem()) 377 | src.Set(p) 378 | } 379 | 380 | if src.Kind() == reflect.Ptr { 381 | src = src.Elem() 382 | } 383 | 384 | if dst.Kind() == reflect.Ptr { 385 | dst = dst.Elem() 386 | } 387 | 388 | if src.Kind() != reflect.Ptr && dst.Kind() != reflect.Ptr { 389 | break 390 | } 391 | } 392 | 393 | if src.Kind() != dst.Kind() { 394 | return nil 395 | } 396 | 397 | if isBaseType(src.Kind()) { 398 | of.unsafeSet = getSetBaseFunc(src.Kind()) 399 | } else if src.Kind() == reflect.Slice && isBaseType(src.Type().Elem().Kind()) { 400 | of.unsafeSet = getSetBaseSliceFunc(src.Type().Elem().Kind()) 401 | } else if src.Kind() == reflect.Map { 402 | of.unsafeSet = getSetBaseMapFunc(src.Type().Key().Kind(), src.Type().Elem().Kind(), false) 403 | } 404 | of.dstType = bkDst.Type() 405 | of.srcType = bkSrc.Type() 406 | of.reflectSet = getSetCompositeFunc(reflect.Ptr) 407 | all.append(of) 408 | 409 | if src.Kind() == reflect.Struct { 410 | // 预热下这个结构体 411 | exits := hasSetFromCache(dstSrcType{dst: dst.Type(), src: src.Type()}) 412 | if exits { 413 | return nil 414 | } 415 | return pcopyInner(dst.Addr().Interface(), src.Addr().Interface(), d.options) 416 | } 417 | return nil 418 | } 419 | 420 | // 拷贝指针 421 | func (d *pcopy) cpyPtr(dst, src reflect.Value, dstBase, srcBase unsafe.Pointer, of offsetAndFunc, all *allFieldFunc) error { 422 | // 解引用之后的类型如果不一样,直接返回 423 | if d.preheat { 424 | return d.preheatPtr(dst, src, of, all) 425 | } 426 | 427 | if dst.Kind() == reflect.Ptr && dst.IsNil() { 428 | // dst.CanSet必须放到dst.IsNil判断里面 429 | // 不然会影响到struct或者map类型的指针 430 | if !dst.CanSet() { 431 | return nil 432 | } 433 | p := reflect.New(dst.Type().Elem()) 434 | dst.Set(p) 435 | } 436 | 437 | if src.Kind() == reflect.Ptr { 438 | src = src.Elem() 439 | srcBase = unsafe.Pointer(src.UnsafeAddr()) 440 | } 441 | 442 | if dst.Kind() == reflect.Ptr { 443 | dst = dst.Elem() 444 | dstBase = unsafe.Pointer(dst.UnsafeAddr()) 445 | } 446 | 447 | return d.pcopy(dst, src, dstBase, srcBase, of, all) 448 | } 449 | 450 | // 其他类型 451 | func (d *pcopy) cpyDefault(dst, src reflect.Value, dstBase, srcBase unsafe.Pointer, of offsetAndFunc, all *allFieldFunc) error { 452 | if dst.Kind() != src.Kind() { 453 | // 如果是尺寸相等但类型不同的数字类型,可以进行转换 454 | if !(isInt64(dst.Kind()) && isInt64(src.Kind()) || 455 | isInt32(dst.Kind()) && isInt32(src.Kind()) || 456 | isInt16(dst.Kind()) && isInt16(src.Kind()) || 457 | isInt8(dst.Kind()) && isInt8(src.Kind())) { 458 | return nil 459 | } 460 | } 461 | 462 | if d.preheat { 463 | of.srcType = src.Type() 464 | of.dstType = dst.Type() 465 | of.unsafeSet = getSetBaseFunc(src.Kind()) 466 | of.createFlag = baseTypeSet 467 | all.append(of) 468 | return nil 469 | } 470 | 471 | switch src.Kind() { 472 | case 473 | reflect.Int, 474 | reflect.Int8, 475 | reflect.Int16, 476 | reflect.Int32, 477 | reflect.Int64: 478 | 479 | switch dst.Kind() { 480 | case 481 | reflect.Uint, 482 | reflect.Uint8, 483 | reflect.Uint16, 484 | reflect.Uint32, 485 | reflect.Uint64: 486 | dst.SetUint(uint64(src.Int())) 487 | default: 488 | dst.SetInt(src.Int()) 489 | } 490 | return nil 491 | case 492 | reflect.Uint, 493 | reflect.Uint8, 494 | reflect.Uint16, 495 | reflect.Uint32, 496 | reflect.Uint64: 497 | 498 | switch dst.Kind() { 499 | case 500 | reflect.Int, 501 | reflect.Int8, 502 | reflect.Int16, 503 | reflect.Int32, 504 | reflect.Int64: 505 | dst.SetInt(int64(src.Uint())) 506 | default: 507 | dst.SetUint(src.Uint()) 508 | } 509 | return nil 510 | case reflect.String: 511 | dst.SetString(src.String()) 512 | return nil 513 | case reflect.Bool: 514 | dst.SetBool(src.Bool()) 515 | return nil 516 | case reflect.Float32, reflect.Float64: 517 | 518 | dst.SetFloat(src.Float()) 519 | return nil 520 | } 521 | 522 | // 如果这里是枚举类型(type newType oldType),哪怕底层的数据类型(oldType)一样,set也会报错, 所以在前面加个前置判断保护下 523 | dst.Set(src) 524 | return nil 525 | } 526 | 527 | func (d *pcopy) pcopy(dst, src reflect.Value, dstBase, srcBase unsafe.Pointer, of offsetAndFunc, all *allFieldFunc) error { 528 | // 预热的时候一定要绕开这个判断, 不管src有值没值都要继续往下走 529 | // 寻找和dst匹配的字段 530 | if !(d.preheat || d.usePreheat) { 531 | if src.IsZero() { 532 | return nil 533 | } 534 | } 535 | 536 | if dst.Kind() == reflect.Ptr { 537 | return d.cpyPtr(dst, src, dstBase, srcBase, of, all) 538 | } 539 | 540 | switch src.Kind() { 541 | case reflect.Slice, reflect.Array: 542 | return d.cpySliceArray(dst, src, dstBase, srcBase, of, all) 543 | 544 | case reflect.Map: 545 | return d.cpyMap(dst, src, dstBase, srcBase, of, all) 546 | 547 | case reflect.Func: 548 | return d.cpyFunc(dst, src) 549 | 550 | case reflect.Struct: 551 | // 保存类型缓存 552 | if d.preheat { 553 | all = addNextComposite(all, of) 554 | defer saveToCache(dstSrcType{dst.Type(), src.Type()}, all) 555 | } 556 | return d.cpyStruct(dst, src, dstBase, srcBase, of, all) 557 | 558 | case reflect.Interface: 559 | if d.preheat { 560 | of.srcType = src.Type() 561 | of.dstType = dst.Type() 562 | of.reflectSet = getSetCompositeFunc(src.Kind()) 563 | all.append(of) 564 | // interface是可变类型。cache加速是把类型关系固化,所以这里只需要知道这个offset是interface就行 565 | // 后需要的类型是可变的,就没有必要分析现在interface存放的类型 566 | return nil 567 | } 568 | return d.cpyInterface(dst, src, of, all) 569 | 570 | case reflect.Ptr: 571 | return d.cpyPtr(dst, src, dstBase, srcBase, of, all) 572 | 573 | default: 574 | return d.cpyDefault(dst, src, dstBase, srcBase, of, all) 575 | } 576 | } 577 | 578 | func addNextComposite(all *allFieldFunc, of offsetAndFunc) (newAll *allFieldFunc) { 579 | of.nextComposite = newAllFieldFunc() 580 | of.createFlag = sliceTypeSet 581 | all.append(of) 582 | // 重置all 583 | of.createFlag = debugTypeSet 584 | newAll = of.nextComposite 585 | of.nextComposite = nil 586 | return 587 | } 588 | -------------------------------------------------------------------------------- /pcopy_basetype_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_OnlyBaseType(t *testing.T) { 11 | err := Preheat(new(int), new(int)) 12 | assert.NoError(t, err) 13 | var i int 14 | err = Copy(&i, newDef(3), WithUsePreheat()) 15 | assert.NoError(t, err) 16 | assert.Equal(t, 3, i) 17 | } 18 | -------------------------------------------------------------------------------- /pcopy_benchmark_base_map_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import "testing" 5 | 6 | func Benchmark_BaseMap_Unsafe_Pcopy(b *testing.B) { 7 | var dst PCopyDst_BaseMap 8 | err := Preheat(&dst, &testSrc_BaseMap) 9 | if err != nil { 10 | b.Fatal(err) 11 | } 12 | 13 | b.ResetTimer() 14 | for i := 0; i < b.N; i++ { 15 | var dst PCopyDst_BaseMap 16 | err := Copy(&dst, &testSrc_BaseMap, WithUsePreheat()) 17 | if err != nil { 18 | b.Fatal(err) 19 | } 20 | 21 | } 22 | } 23 | 24 | func Benchmark_BaseMap_RawCopy(b *testing.B) { 25 | for i := 0; i < b.N; i++ { 26 | var dst PCopyDst_BaseMap 27 | dst.A = make(map[string]string) 28 | for k, v := range testSrc_BaseMap.A { 29 | dst.A[k] = v 30 | } 31 | dst.B = make(map[string]int) 32 | for k, v := range testSrc_BaseMap.B { 33 | dst.B[k] = v 34 | } 35 | dst.C = make(map[string]float64) 36 | for k, v := range testSrc_BaseMap.C { 37 | dst.C[k] = v 38 | } 39 | dst.D = make(map[string]uint) 40 | for k, v := range testSrc_BaseMap.D { 41 | dst.D[k] = v 42 | } 43 | dst.E = make(map[string]uint8) 44 | for k, v := range testSrc_BaseMap.E { 45 | dst.E[k] = v 46 | } 47 | dst.F = make(map[string]uint16) 48 | for k, v := range testSrc_BaseMap.F { 49 | dst.F[k] = v 50 | } 51 | dst.G = make(map[string]uint32) 52 | for k, v := range testSrc_BaseMap.G { 53 | dst.G[k] = v 54 | } 55 | dst.H = make(map[string]uint64) 56 | for k, v := range testSrc_BaseMap.H { 57 | dst.H[k] = v 58 | } 59 | dst.I = make(map[string]bool) 60 | for k, v := range testSrc_BaseMap.I { 61 | dst.I[k] = v 62 | } 63 | dst.J = make(map[string]int8) 64 | for k, v := range testSrc_BaseMap.J { 65 | dst.J[k] = v 66 | } 67 | dst.K = make(map[string]int16) 68 | for k, v := range testSrc_BaseMap.K { 69 | dst.K[k] = v 70 | } 71 | dst.L = make(map[string]int32) 72 | for k, v := range testSrc_BaseMap.L { 73 | dst.L[k] = v 74 | } 75 | dst.M = make(map[string]int64) 76 | for k, v := range testSrc_BaseMap.M { 77 | dst.M[k] = v 78 | } 79 | 80 | dst.O = make(map[string]float32) 81 | for k, v := range testSrc_BaseMap.O { 82 | dst.O[k] = v 83 | } 84 | 85 | dst.BA = testSrc_BaseMap.BA 86 | dst.BB = testSrc_BaseMap.BB 87 | dst.BC = testSrc_BaseMap.BC 88 | dst.BD = testSrc_BaseMap.BD 89 | dst.BE = testSrc_BaseMap.BE 90 | dst.BF = testSrc_BaseMap.BF 91 | dst.BG = testSrc_BaseMap.BG 92 | dst.BH = testSrc_BaseMap.BH 93 | dst.BI = testSrc_BaseMap.BI 94 | dst.BJ = testSrc_BaseMap.BJ 95 | dst.BK = testSrc_BaseMap.BK 96 | dst.BL = testSrc_BaseMap.BL 97 | dst.BM = testSrc_BaseMap.BM 98 | 99 | dst.CA = append(dst.CA, testSrc_BaseMap.CA...) 100 | dst.CB = append(dst.CB, testSrc_BaseMap.CB...) 101 | dst.CC = append(dst.CC, testSrc_BaseMap.CC...) 102 | dst.CD = append(dst.CD, testSrc_BaseMap.CD...) 103 | dst.CE = append(dst.CE, testSrc_BaseMap.CE...) 104 | dst.CF = append(dst.CF, testSrc_BaseMap.CF...) 105 | dst.CG = append(dst.CG, testSrc_BaseMap.CG...) 106 | dst.CH = append(dst.CH, testSrc_BaseMap.CH...) 107 | dst.CI = append(dst.CI, testSrc_BaseMap.CI...) 108 | dst.CJ = append(dst.CJ, testSrc_BaseMap.CJ...) 109 | dst.CK = append(dst.CK, testSrc_BaseMap.CK...) 110 | dst.CL = append(dst.CL, testSrc_BaseMap.CL...) 111 | dst.CM = append(dst.CM, testSrc_BaseMap.CM...) 112 | 113 | } 114 | } 115 | 116 | func Benchmark_BaseMap_miniCopy(b *testing.B) { 117 | for i := 0; i < b.N; i++ { 118 | var dst PCopyDst_BaseMap 119 | err := miniCopy(&dst, &testSrc_BaseMap) 120 | if err != nil { 121 | b.Fatal(err) 122 | } 123 | 124 | } 125 | } 126 | 127 | func Benchmark_BaseMap_Reflect(b *testing.B) { 128 | for i := 0; i < b.N; i++ { 129 | var dst PCopyDst_BaseMap 130 | err := Copy(&dst, &testSrc_BaseMap) 131 | if err != nil { 132 | b.Fatal(err) 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /pcopy_benchmark_base_slice_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import "testing" 5 | 6 | func Benchmark_BaseSlice_Unsafe_Pcopy(b *testing.B) { 7 | var dst PCopyDst_BaseSlice 8 | err := Preheat(&dst, &testSrc_BaseSlice) 9 | if err != nil { 10 | b.Fatal(err) 11 | } 12 | 13 | b.ResetTimer() 14 | for i := 0; i < b.N; i++ { 15 | var dst PCopyDst_BaseSlice 16 | err := Copy(&dst, &testSrc_BaseSlice, WithUsePreheat()) 17 | if err != nil { 18 | b.Fatal(err) 19 | } 20 | 21 | } 22 | } 23 | 24 | func Benchmark_BaseSlice_RawCopy(b *testing.B) { 25 | for i := 0; i < b.N; i++ { 26 | var dst PCopyDst_BaseSlice 27 | dst.Bool = testSrc_BaseSlice.Bool 28 | dst.Int = testSrc_BaseSlice.Int 29 | dst.Int8 = testSrc_BaseSlice.Int8 30 | dst.Int16 = testSrc_BaseSlice.Int16 31 | dst.Int32 = testSrc_BaseSlice.Int32 32 | dst.Int64 = testSrc_BaseSlice.Int64 33 | dst.Uint = testSrc_BaseSlice.Uint 34 | dst.Uint8 = testSrc_BaseSlice.Uint8 35 | dst.Uint16 = testSrc_BaseSlice.Uint16 36 | dst.Uint32 = testSrc_BaseSlice.Uint32 37 | dst.Uint64 = testSrc_BaseSlice.Uint64 38 | dst.Float32 = testSrc_BaseSlice.Float32 39 | dst.Float64 = testSrc_BaseSlice.Float64 40 | // dst.Complex64 = testSrc_BaseSlice.Complex64 41 | // dst.Complex128 = testSrc_BaseSlice.Complex128 42 | dst.SliceBool = append(dst.SliceBool, testSrc_BaseSlice.SliceBool...) 43 | dst.SliceInt = append(dst.SliceInt, testSrc_BaseSlice.SliceInt...) 44 | dst.SliceInt8 = append(dst.SliceInt8, testSrc_BaseSlice.SliceInt8...) 45 | dst.SliceInt16 = append(dst.SliceInt16, testSrc_BaseSlice.SliceInt16...) 46 | dst.SliceInt32 = append(dst.SliceInt32, testSrc_BaseSlice.SliceInt32...) 47 | dst.SliceInt64 = append(dst.SliceInt64, testSrc_BaseSlice.SliceInt64...) 48 | dst.SliceUint = append(dst.SliceUint, testSrc_BaseSlice.SliceUint...) 49 | dst.SliceUint8 = append(dst.SliceUint8, testSrc_BaseSlice.SliceUint8...) 50 | dst.SliceUint16 = append(dst.SliceUint16, testSrc_BaseSlice.SliceUint16...) 51 | dst.SliceUint32 = append(dst.SliceUint32, testSrc_BaseSlice.SliceUint32...) 52 | dst.SliceUint64 = append(dst.SliceUint64, testSrc_BaseSlice.SliceUint64...) 53 | dst.SliceFloat32 = append(dst.SliceFloat32, testSrc_BaseSlice.SliceFloat32...) 54 | dst.SliceFloat64 = append(dst.SliceFloat64, testSrc_BaseSlice.SliceFloat64...) 55 | // dst.SliceComplex64 = append(dst.SliceComplex64, testSrc_BaseSlice.SliceComplex64...) 56 | // dst.SliceComplex128 = append(dst.SliceComplex128, testSrc_BaseSlice.SliceComplex128...) 57 | dst.String = testSrc_BaseSlice.String 58 | dst.SliceString = append(dst.SliceString, testSrc_BaseSlice.SliceString...) 59 | } 60 | } 61 | 62 | func Benchmark_BaseSlice_miniCopy(b *testing.B) { 63 | for i := 0; i < b.N; i++ { 64 | var dst PCopyDst_BaseSlice 65 | err := miniCopy(&dst, &testSrc_BaseSlice) 66 | if err != nil { 67 | b.Fatal(err) 68 | } 69 | 70 | } 71 | } 72 | 73 | func Benchmark_BaseSlice_Reflect(b *testing.B) { 74 | for i := 0; i < b.N; i++ { 75 | var dst PCopyDst_BaseSlice 76 | err := Copy(&dst, &testSrc_BaseSlice) 77 | if err != nil { 78 | b.Fatal(err) 79 | } 80 | 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /pcopy_benchmark_base_type_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "encoding/json" 6 | "testing" 7 | ) 8 | 9 | func Benchmark_BaseType_Unsafe_Pcopy(b *testing.B) { 10 | var dst PCopyDst 11 | err := Preheat(&dst, &testSrc) 12 | if err != nil { 13 | b.Fatal(err) 14 | } 15 | b.ResetTimer() 16 | for i := 0; i < b.N; i++ { 17 | var dst PCopyDst 18 | err := Copy(&dst, &testSrc, WithUsePreheat()) 19 | if err != nil { 20 | b.Fatal(err) 21 | } 22 | 23 | } 24 | } 25 | 26 | func Benchmark_BaseType_RawCopy(b *testing.B) { 27 | for i := 0; i < b.N; i++ { 28 | var dst PCopyDst 29 | dst.Bool = testSrc.Bool 30 | dst.Int = testSrc.Int 31 | dst.Int8 = testSrc.Int8 32 | dst.Int16 = testSrc.Int16 33 | dst.Int32 = testSrc.Int32 34 | dst.Int64 = testSrc.Int64 35 | dst.Uint = testSrc.Uint 36 | dst.Uint8 = testSrc.Uint8 37 | dst.Uint16 = testSrc.Uint16 38 | dst.Uint32 = testSrc.Uint32 39 | dst.Uint64 = testSrc.Uint64 40 | dst.Float32 = testSrc.Float32 41 | dst.Float64 = testSrc.Float64 42 | // dst.Complex64 = testSrc.Complex64 43 | dst.String = testSrc.String 44 | a := &dst 45 | b := a 46 | _ = b 47 | } 48 | } 49 | 50 | type testData struct { 51 | Int64 int64 `json:"int_64"` 52 | Int32 int32 `json:"int_32"` 53 | Int16 int8 `json:"int_16"` 54 | Int8 int8 `json:"int_8"` 55 | UInt8 int8 `json:"u_int_8"` 56 | UInt64 uint64 `json:"u_int_64"` 57 | UInt32 uint32 `json:"u_int_32"` 58 | UInt16 uint8 `json:"u_int_16"` 59 | S string `json:"s"` 60 | Slice []string `json:"slice"` 61 | Array []int `json:"array"` 62 | } 63 | 64 | var td = testData{ 65 | Int64: 64, 66 | Int32: 32, 67 | Int16: 16, 68 | Int8: 8, 69 | UInt8: 18, 70 | UInt64: 164, 71 | UInt32: 132, 72 | UInt16: 116, 73 | S: "test pcopy", 74 | Slice: []string{"123", "456", "789"}, 75 | Array: []int{0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, 76 | } 77 | 78 | func miniCopy(dst, src interface{}) error { 79 | bytes, err := json.Marshal(src) 80 | if err != nil { 81 | return err 82 | } 83 | 84 | return json.Unmarshal(bytes, dst) 85 | } 86 | 87 | func Benchmark_BaseType_MiniCopy(b *testing.B) { 88 | for i := 0; i < b.N; i++ { 89 | // var dst testData 90 | var dst PCopyDst 91 | err := miniCopy(&dst, &testSrc) 92 | if err != nil { 93 | b.Fatal(err) 94 | } 95 | // miniCopy(&dst, &td) 96 | } 97 | } 98 | 99 | func Benchmark_BaseType_Pcopy(b *testing.B) { 100 | for i := 0; i < b.N; i++ { 101 | // var dst testData 102 | var dst PCopyDst 103 | err := Copy(&dst, &testSrc) 104 | if err != nil { 105 | b.Fatal(err) 106 | } 107 | 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /pcopy_benchmark_composite_map_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import "testing" 5 | 6 | func Benchmark_CompositeMap_Unsafe_Pcopy(b *testing.B) { 7 | var dst test_MapWithMap_Dst 8 | err := Preheat(&dst, &local_MapWithMap_Src) 9 | if err != nil { 10 | b.Fatal(err) 11 | } 12 | 13 | b.ResetTimer() 14 | for i := 0; i < b.N; i++ { 15 | var dst test_MapWithMap_Dst 16 | err := Copy(&dst, &local_MapWithMap_Src, WithUsePreheat()) 17 | if err != nil { 18 | b.Fatal(err) 19 | } 20 | 21 | } 22 | } 23 | 24 | func Benchmark_CompositeMap_RawCopy(b *testing.B) { 25 | for i := 0; i < b.N; i++ { 26 | var dst test_MapWithMap_Dst 27 | dst.A = make(map[string]string) 28 | for k, v := range local_MapWithMap_Src.A { 29 | dst.A[k] = v 30 | } 31 | 32 | dst.B = make(map[string]map[string]string) 33 | for k, v := range local_MapWithMap_Src.B { 34 | dst.B[k] = make(map[string]string) 35 | for k1, v1 := range v { 36 | dst.B[k][k1] = v1 37 | } 38 | } 39 | 40 | dst.C = make(map[string]test_MapWithMap_Item) 41 | for k, v := range local_MapWithMap_Src.C { 42 | dst.C[k] = v 43 | } 44 | 45 | } 46 | } 47 | 48 | func Benchmark_CompositeMap_miniCopy(b *testing.B) { 49 | for i := 0; i < b.N; i++ { 50 | var dst test_MapWithMap_Dst 51 | err := miniCopy(&dst, &local_MapWithMap_Src) 52 | if err != nil { 53 | b.Fatal(err) 54 | } 55 | 56 | } 57 | } 58 | 59 | func Benchmark_CompositeMap_Reflect(b *testing.B) { 60 | for i := 0; i < b.N; i++ { 61 | var dst test_MapWithMap_Dst 62 | err := Copy(&dst, &local_MapWithMap_Src) 63 | if err != nil { 64 | b.Fatal(err) 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /pcopy_benchmark_getlikefavorited_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | ) 7 | 8 | func Benchmark_GetLikeFavorited_Unsafe_Pcopy2(b *testing.B) { 9 | err := Preheat(&GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData{}, &GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData{}) 10 | if err != nil { 11 | b.Fatal(err) 12 | } 13 | b.ResetTimer() 14 | for i := 0; i < b.N; i++ { 15 | 16 | err = Preheat(&GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData{}, &GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData{}) 17 | if err != nil { 18 | b.Fatal(err) 19 | } 20 | var dst GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData 21 | err = Copy(&dst, &local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData, WithUsePreheat()) 22 | if err != nil { 23 | b.Fatal(err) 24 | } 25 | 26 | } 27 | } 28 | 29 | func Benchmark_GetLikeFavorited_Unsafe_Pcopy(b *testing.B) { 30 | err := Preheat(&GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData{}, &GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData{}) 31 | if err != nil { 32 | b.Fatal(err) 33 | } 34 | b.ResetTimer() 35 | for i := 0; i < b.N; i++ { 36 | 37 | var dst GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData 38 | err = Copy(&dst, &local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData, WithUsePreheat()) 39 | if err != nil { 40 | b.Fatal(err) 41 | } 42 | 43 | } 44 | } 45 | 46 | func Benchmark_GetLikeFavorited_RawCopy(b *testing.B) { 47 | for i := 0; i < b.N; i++ { 48 | var dst GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData 49 | dst.Items = make([]*GetMessageLikeFavoritedRespItem, len(local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items)) 50 | for i := range local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items { 51 | dst.Items[i] = &GetMessageLikeFavoritedRespItem{ 52 | LikeType: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].LikeType, 53 | CommentType: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].CommentType, 54 | ResourceType: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].ResourceType, 55 | PostID: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].PostID, 56 | RefMessage: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].RefMessage, 57 | RefMessageID: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].RefMessageID, 58 | ParentCid: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].ParentCid, 59 | CommentModule: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].CommentModule, 60 | ArticleThumbnail: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].ArticleThumbnail, 61 | Info: &MessageWhoAndTime{ 62 | HeadPic: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].Info.HeadPic, 63 | Nickname: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].Info.Nickname, 64 | DID: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].Info.DID, 65 | Seq: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].Info.Seq, 66 | Time: local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData.Items[i].Info.Time, 67 | }, 68 | } 69 | } 70 | } 71 | } 72 | 73 | func Benchmark_GetLikeFavorited_MiniCopy(b *testing.B) { 74 | for i := 0; i < b.N; i++ { 75 | // var dst testData 76 | var dst GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData 77 | err := miniCopy(&dst, &local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData) 78 | if err != nil { 79 | b.Fatal(err) 80 | } 81 | // miniCopy(&dst, &td) 82 | } 83 | } 84 | 85 | func Benchmark_GetLikeFavorited_Reflect_Pcopy(b *testing.B) { 86 | for i := 0; i < b.N; i++ { 87 | // var dst testData 88 | var dst GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData 89 | err := Copy(&dst, &local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData) 90 | if err != nil { 91 | b.Fatal(err) 92 | } 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /pcopy_benchmark_getredpoint_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | ) 7 | 8 | func Benchmark_GetRedPoint_Unsafe_Pcopy(b *testing.B) { 9 | err := Preheat(&GetRedPointResp{}, &GetRedPointRespData{}) 10 | if err != nil { 11 | b.Fatal(err) 12 | } 13 | b.ResetTimer() 14 | for i := 0; i < b.N; i++ { 15 | rv := GetRedPointResp{} 16 | 17 | err := Copy(&rv.Data, &local_GetRedPointRespData, WithUsePreheat()) 18 | if err != nil { 19 | b.Fatal(err) 20 | } 21 | 22 | } 23 | } 24 | 25 | func Benchmark_GetRedPoint_RawCopy(b *testing.B) { 26 | for i := 0; i < b.N; i++ { 27 | var dst GetRedPointResp 28 | dst.Data = &GetRedPointResp_GetRedPointRespData{} 29 | dst.Data.Point = make(map[uint32]uint32) 30 | for k, v := range local_GetRedPointRespData.Point { 31 | dst.Data.Point[k] = v 32 | } 33 | } 34 | } 35 | 36 | func Benchmark_GetRedPoint_MiniCopy(b *testing.B) { 37 | for i := 0; i < b.N; i++ { 38 | // var dst testData 39 | rv := GetRedPointResp{} 40 | err := miniCopy(&rv.Data, local_GetRedPointRespData) 41 | if err != nil { 42 | b.Fatal(err) 43 | } 44 | // miniCopy(&dst, &td) 45 | } 46 | } 47 | 48 | func Benchmark_GetRedPoint_Reflect_Pcopy(b *testing.B) { 49 | for i := 0; i < b.N; i++ { 50 | // var dst testData 51 | rv := GetRedPointResp{} 52 | err := Copy(&rv.Data, &local_GetRedPointRespData) 53 | if err != nil { 54 | b.Fatal(err) 55 | } 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /pcopy_benchmark_interface_base_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | ) 7 | 8 | type interfaceBaseSrcTest struct { 9 | A interface{} 10 | B interface{} 11 | C interface{} 12 | D interface{} 13 | E interface{} 14 | F interface{} 15 | G interface{} 16 | H interface{} 17 | I interface{} 18 | J interface{} 19 | K interface{} 20 | L interface{} 21 | M interface{} 22 | N interface{} 23 | } 24 | 25 | type interfaceDstTest interfaceBaseSrcTest 26 | 27 | var localInterfaceSrcTest = interfaceBaseSrcTest{ 28 | A: uint(1), 29 | B: uint8(2), 30 | C: uint16(3), 31 | D: uint32(4), 32 | E: uint64(5), 33 | F: int(6), 34 | G: int8(7), 35 | H: int16(8), 36 | I: int32(9), 37 | J: int64(10), 38 | K: float32(11), 39 | L: float64(12), 40 | M: "13", 41 | N: true, 42 | } 43 | 44 | func Benchmark_Interface_Unsafe_Pcopy(b *testing.B) { 45 | var dst interfaceDstTest 46 | err := Preheat(&dst, &localInterfaceSrcTest) 47 | if err != nil { 48 | b.Fatal(err) 49 | } 50 | b.ResetTimer() 51 | for i := 0; i < b.N; i++ { 52 | var dst interfaceDstTest 53 | err := Copy(&dst, &localInterfaceSrcTest, WithUsePreheat()) 54 | if err != nil { 55 | b.Fatal(err) 56 | } 57 | 58 | } 59 | } 60 | 61 | func Benchmark_Interface_RawCopy(b *testing.B) { 62 | for i := 0; i < b.N; i++ { 63 | var dst interfaceDstTest 64 | dst.A = localInterfaceSrcTest.A.(uint) 65 | dst.B = localInterfaceSrcTest.B.(uint8) 66 | dst.C = localInterfaceSrcTest.C.(uint16) 67 | dst.D = localInterfaceSrcTest.D.(uint32) 68 | dst.E = localInterfaceSrcTest.E.(uint64) 69 | dst.F = localInterfaceSrcTest.F.(int) 70 | dst.G = localInterfaceSrcTest.G.(int8) 71 | dst.H = localInterfaceSrcTest.H.(int16) 72 | dst.I = localInterfaceSrcTest.I.(int32) 73 | dst.J = localInterfaceSrcTest.J.(int64) 74 | dst.K = localInterfaceSrcTest.K.(float32) 75 | dst.L = localInterfaceSrcTest.L.(float64) 76 | dst.M = localInterfaceSrcTest.M.(string) 77 | dst.N = localInterfaceSrcTest.N.(bool) 78 | 79 | a := &dst 80 | b := a 81 | _ = b 82 | } 83 | } 84 | 85 | func Benchmark_Interface_MiniCopy(b *testing.B) { 86 | for i := 0; i < b.N; i++ { 87 | // var dst testData 88 | var dst interfaceDstTest 89 | err := miniCopy(&dst, &localInterfaceSrcTest) 90 | if err != nil { 91 | b.Fatal(err) 92 | } 93 | // miniCopy(&dst, &td) 94 | } 95 | } 96 | 97 | func Benchmark_Interface_Pcopy(b *testing.B) { 98 | for i := 0; i < b.N; i++ { 99 | // var dst testData 100 | var dst interfaceDstTest 101 | err := Copy(&dst, &localInterfaceSrcTest) 102 | if err != nil { 103 | b.Fatal(err) 104 | } 105 | 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /pcopy_benchmark_interface_slice_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import "testing" 5 | 6 | type interfaceBaseSliceSrcTest struct { 7 | A interface{} 8 | B interface{} 9 | C interface{} 10 | D interface{} 11 | E interface{} 12 | F interface{} 13 | G interface{} 14 | H interface{} 15 | I interface{} 16 | J interface{} 17 | K interface{} 18 | L interface{} 19 | M interface{} 20 | N interface{} 21 | } 22 | 23 | type interfaceSliceDstTest interfaceBaseSliceSrcTest 24 | 25 | var localInterfaceSliceSrcTest = interfaceBaseSliceSrcTest{ 26 | A: []int{1, 2, 3, 4, 5}, 27 | B: []int8{1, 2, 3, 4, 5}, 28 | C: []int16{1, 2, 3, 4, 5}, 29 | D: []int32{1, 2, 3, 4, 5}, 30 | E: []int64{1, 2, 3, 4, 5}, 31 | F: []uint{1, 2, 3, 4, 5}, 32 | G: []uint8{1, 2, 3, 4, 5}, 33 | H: []uint16{1, 2, 3, 4, 5}, 34 | I: []uint32{1, 2, 3, 4, 5}, 35 | J: []uint64{1, 2, 3, 4, 5}, 36 | K: []float32{1, 2, 3, 4, 5}, 37 | L: []float64{1, 2, 3, 4, 5}, 38 | M: []string{"1", "2", "3", "4", "5"}, 39 | N: []bool{true, false, true, false, true}, 40 | } 41 | 42 | func Benchmark_Interface_BaseSlice_Unsafe_Pcopy(b *testing.B) { 43 | var dst interfaceSliceDstTest 44 | err := Preheat(&dst, &localInterfaceSliceSrcTest) 45 | if err != nil { 46 | b.Fatal(err) 47 | } 48 | b.ResetTimer() 49 | for i := 0; i < b.N; i++ { 50 | var dst interfaceSliceDstTest 51 | err := Copy(&dst, &localInterfaceSliceSrcTest, WithUsePreheat()) 52 | if err != nil { 53 | b.Fatal(err) 54 | } 55 | 56 | } 57 | } 58 | 59 | func Benchmark_Interface_BaseSlice_RawCopy(b *testing.B) { 60 | for i := 0; i < b.N; i++ { 61 | var dst interfaceSliceDstTest 62 | dst.A = append([]int{}, localInterfaceSliceSrcTest.A.([]int)...) 63 | dst.B = append([]int8{}, localInterfaceSliceSrcTest.B.([]int8)...) 64 | dst.C = append([]int16{}, localInterfaceSliceSrcTest.C.([]int16)...) 65 | dst.D = append([]int32{}, localInterfaceSliceSrcTest.D.([]int32)...) 66 | dst.E = append([]int64{}, localInterfaceSliceSrcTest.E.([]int64)...) 67 | dst.F = append([]uint{}, localInterfaceSliceSrcTest.F.([]uint)...) 68 | dst.G = append([]uint8{}, localInterfaceSliceSrcTest.G.([]uint8)...) 69 | dst.H = append([]uint16{}, localInterfaceSliceSrcTest.H.([]uint16)...) 70 | dst.I = append([]uint32{}, localInterfaceSliceSrcTest.I.([]uint32)...) 71 | dst.J = append([]uint64{}, localInterfaceSliceSrcTest.J.([]uint64)...) 72 | dst.K = append([]float32{}, localInterfaceSliceSrcTest.K.([]float32)...) 73 | dst.L = append([]float64{}, localInterfaceSliceSrcTest.L.([]float64)...) 74 | dst.M = append([]string{}, localInterfaceSliceSrcTest.M.([]string)...) 75 | dst.N = append([]bool{}, localInterfaceSliceSrcTest.N.([]bool)...) 76 | 77 | a := &dst 78 | b := a 79 | _ = b 80 | } 81 | } 82 | 83 | func Benchmark_Interface_BaseSlice_MiniCopy(b *testing.B) { 84 | for i := 0; i < b.N; i++ { 85 | // var dst testData 86 | var dst interfaceSliceDstTest 87 | err := miniCopy(&dst, &localInterfaceSliceSrcTest) 88 | if err != nil { 89 | b.Fatal(err) 90 | } 91 | // miniCopy(&dst, &td) 92 | } 93 | } 94 | 95 | func Benchmark_Interface_BaseSlice_Pcopy(b *testing.B) { 96 | for i := 0; i < b.N; i++ { 97 | // var dst testData 98 | var dst interfaceSliceDstTest 99 | err := Copy(&dst, &localInterfaceSliceSrcTest) 100 | if err != nil { 101 | b.Fatal(err) 102 | } 103 | 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /pcopy_benchmark_ptr_base_type1_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import "testing" 5 | 6 | func Benchmark_Ptr_BaseType1_Unsafe_Pcopy(b *testing.B) { 7 | var dst test_BaseType_ptr_Dst 8 | err := Preheat(&dst, &local_test_BaseType_ptr_Dst) 9 | if err != nil { 10 | b.Fatal(err) 11 | } 12 | 13 | b.ResetTimer() 14 | for i := 0; i < b.N; i++ { 15 | var dst test_BaseType_ptr_Dst 16 | err := Copy(&dst, &local_test_BaseType_ptr_Dst, WithUsePreheat()) 17 | if err != nil { 18 | b.Fatal(err) 19 | } 20 | 21 | } 22 | } 23 | 24 | func Benchmark_Ptr_BaseType1_RawCopy(b *testing.B) { 25 | for i := 0; i < b.N; i++ { 26 | var dst test_BaseType_ptr_Dst 27 | dst.A = newDef(*local_test_BaseType_ptr_Dst.A) 28 | dst.B = newDef(*local_test_BaseType_ptr_Dst.B) 29 | dst.C = newDef(*local_test_BaseType_ptr_Dst.C) 30 | dst.D = newDef(*local_test_BaseType_ptr_Dst.D) 31 | dst.E = newDef(*local_test_BaseType_ptr_Dst.E) 32 | dst.F = newDef(*local_test_BaseType_ptr_Dst.F) 33 | dst.G = newDef(*local_test_BaseType_ptr_Dst.G) 34 | dst.H = newDef(*local_test_BaseType_ptr_Dst.H) 35 | dst.I = newDef(*local_test_BaseType_ptr_Dst.I) 36 | dst.J = newDef(*local_test_BaseType_ptr_Dst.J) 37 | dst.K = newDef(*local_test_BaseType_ptr_Dst.K) 38 | dst.L = newDef(*local_test_BaseType_ptr_Dst.L) 39 | dst.M = newDef(*local_test_BaseType_ptr_Dst.M) 40 | dst.N = newDef(*local_test_BaseType_ptr_Dst.N) 41 | dst.O = newDef(*local_test_BaseType_ptr_Dst.O) 42 | dst.P = newDef(*local_test_BaseType_ptr_Dst.P) 43 | dst.Q = newDef(*local_test_BaseType_ptr_Dst.Q) 44 | // dst.R = newDef(*local_test_BaseType_ptr_Dst.R) 45 | } 46 | } 47 | 48 | func Benchmark_Ptr_BaseType1_miniCopy(b *testing.B) { 49 | for i := 0; i < b.N; i++ { 50 | var dst test_BaseType_ptr_Dst 51 | err := miniCopy(&dst, &local_test_BaseType_ptr_Dst) 52 | if err != nil { 53 | b.Fatal(err) 54 | } 55 | 56 | } 57 | } 58 | 59 | func Benchmark_Ptr_BaseType1_Reflect_Pcopy(b *testing.B) { 60 | for i := 0; i < b.N; i++ { 61 | var dst test_BaseType_ptr_Dst 62 | err := Copy(&dst, &local_test_BaseType_ptr_Dst) 63 | if err != nil { 64 | b.Fatal(err) 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /pcopy_benchmark_ptr_baseslice_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import "testing" 5 | 6 | func Benchmark_Ptr_BaseSlice_Unsafe_Pcopy(b *testing.B) { 7 | err := Preheat(&test_BaseSliceType_ptr_Dst{}, &test_BaseSliceType_ptr_Src{}) 8 | if err != nil { 9 | b.Fatal(err) 10 | } 11 | 12 | b.ResetTimer() 13 | for i := 0; i < b.N; i++ { 14 | var dst test_BaseSliceType_ptr_Dst 15 | err := Copy(&dst, &local_test_BaseSliceType_ptr_Src, WithUsePreheat()) 16 | if err != nil { 17 | b.Fatal(err) 18 | } 19 | 20 | } 21 | } 22 | 23 | func Benchmark_Ptr_BaseSlice_RawCopy(b *testing.B) { 24 | for i := 0; i < b.N; i++ { 25 | var dst test_BaseSliceType_ptr_Dst 26 | dst.A = newDef(append([]int{}, local_test_BaseSliceType_ptr_Src.A...)) 27 | dst.B = newDef(append([]int8{}, local_test_BaseSliceType_ptr_Src.B...)) 28 | dst.C = newDef(append([]int16{}, local_test_BaseSliceType_ptr_Src.C...)) 29 | dst.D = newDef(append([]int32{}, local_test_BaseSliceType_ptr_Src.D...)) 30 | dst.E = newDef(append([]int64{}, local_test_BaseSliceType_ptr_Src.E...)) 31 | dst.F = newDef(append([]uint{}, local_test_BaseSliceType_ptr_Src.F...)) 32 | dst.G = newDef(append([]uint8{}, local_test_BaseSliceType_ptr_Src.G...)) 33 | dst.H = newDef(append([]uint16{}, local_test_BaseSliceType_ptr_Src.H...)) 34 | dst.I = newDef(append([]uint32{}, local_test_BaseSliceType_ptr_Src.I...)) 35 | dst.J = newDef(append([]uint64{}, local_test_BaseSliceType_ptr_Src.J...)) 36 | dst.K = newDef(append([]uintptr{}, local_test_BaseSliceType_ptr_Src.K...)) 37 | dst.L = newDef(append([]float32{}, local_test_BaseSliceType_ptr_Src.L...)) 38 | dst.M = newDef(append([]float64{}, local_test_BaseSliceType_ptr_Src.M...)) 39 | dst.N = newDef(append([]complex64{}, local_test_BaseSliceType_ptr_Src.N...)) 40 | dst.O = newDef(append([]complex128{}, local_test_BaseSliceType_ptr_Src.O...)) 41 | dst.P = newDef(append([]bool{}, local_test_BaseSliceType_ptr_Src.P...)) 42 | dst.Q = newDef(append([]string{}, local_test_BaseSliceType_ptr_Src.Q...)) 43 | } 44 | } 45 | 46 | func Benchmark_Ptr_BaseSlice_miniCopy(b *testing.B) { 47 | for i := 0; i < b.N; i++ { 48 | var dst test_BaseSliceType_ptr_Dst 49 | err := miniCopy(&dst, &local_test_BaseSliceType_ptr_Src) 50 | if err != nil { 51 | b.Fatal(err) 52 | } 53 | } 54 | } 55 | 56 | func Benchmark_Ptr_BaseSlice_Reflect_Pcopy(b *testing.B) { 57 | for i := 0; i < b.N; i++ { 58 | var dst test_BaseSliceType_ptr_Dst 59 | err := Copy(&dst, &local_test_BaseSliceType_ptr_Src) 60 | if err != nil { 61 | b.Fatal(err) 62 | } 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /pcopy_benchmark_slice_with_struct_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import "testing" 5 | 6 | func Benchmark_SliceWithStruct_Unsafe_Pcopy(b *testing.B) { 7 | var dst test_SliceWithStruct_Src 8 | 9 | err := Preheat(&dst, &test_SliceWithStruct_Src{}) 10 | if err != nil { 11 | b.Fatal(err) 12 | } 13 | 14 | b.ResetTimer() 15 | for i := 0; i < b.N; i++ { 16 | var dst test_SliceWithStruct_Src 17 | // err := Copy(&dst, &local_SliceWithStruct_Src) 18 | err := Copy(&dst, &local_SliceWithStruct_Src, WithUsePreheat()) 19 | if err != nil { 20 | b.Fatal(err) 21 | } 22 | } 23 | } 24 | 25 | func Benchmark_SliceWithStruct_RawCopy(b *testing.B) { 26 | for i := 0; i < b.N; i++ { 27 | var dst test_SliceWithStruct_Dst 28 | dst.A = make([]test_SliceWithStruct_Dst_Item1, len(local_SliceWithStruct_Src.A)) 29 | copy(dst.A, local_SliceWithStruct_Src.A) 30 | dst.B = append(dst.B, local_SliceWithStruct_Src.B...) 31 | } 32 | } 33 | 34 | func Benchmark_SliceWithStruct_miniCopy(b *testing.B) { 35 | for i := 0; i < b.N; i++ { 36 | var dst test_SliceWithStruct_Src 37 | err := miniCopy(&dst, &local_SliceWithStruct_Src) 38 | if err != nil { 39 | b.Fatal(err) 40 | } 41 | 42 | } 43 | } 44 | 45 | func Benchmark_SliceWithStruct_Reflect_Pcopy(b *testing.B) { 46 | for i := 0; i < b.N; i++ { 47 | var dst test_SliceWithStruct_Src 48 | err := Copy(&dst, &local_SliceWithStruct_Src) 49 | if err != nil { 50 | b.Fatal(err) 51 | } 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /pcopy_better_to_use_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type testBetterToUse struct { 11 | Str string 12 | ID int 13 | } 14 | 15 | // src是指针类型的结构体 16 | // dst是普通结构体 17 | func Test_srcPtr_DstBaseType(t *testing.T) { 18 | t1 := testBetterToUse{Str: "hello", ID: 1} 19 | t2 := testBetterToUse{} 20 | err := Copy(&t2, &t1) 21 | assert.NoError(t, err) 22 | assert.Equal(t, t1, t2) 23 | } 24 | 25 | func Test_srcPtr_DstBaseType_NotPanics(t *testing.T) { 26 | t1 := testBetterToUse{Str: "hello", ID: 1} 27 | assert.NotPanics(t, func() { 28 | Copy((*testBetterToUse)(nil), &t1) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /pcopy_bug_test.go: -------------------------------------------------------------------------------- 1 | package pcopy 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type LetterInbox struct { 11 | ID int32 12 | FromRid int64 13 | ToRid int64 14 | Msg string 15 | } 16 | 17 | var resp = []LetterInbox{ 18 | { 19 | ID: 1, 20 | FromRid: 3, 21 | ToRid: 4, 22 | Msg: "ww,", 23 | }, 24 | } 25 | 26 | type ReadLetterResponseItem struct { 27 | Msg string 28 | } 29 | 30 | type ReadLetterResponse struct { 31 | List []*ReadLetterResponseItem `protobuf:"bytes,1,rep,name=list,proto3" json:"list,omitempty"` 32 | } 33 | 34 | func Test_ReadLetter(t *testing.T) { 35 | var rv ReadLetterResponse 36 | err := Preheat(&rv.List, &resp) 37 | assert.NoError(t, err) 38 | err = Copy(&rv.List, &resp, WithUsePreheat()) 39 | assert.NoError(t, err) 40 | 41 | fmt.Printf("%#v\n", rv.List) 42 | fmt.Printf("%#v\n", rv.List[0]) 43 | // fmt.Printf("%#v\n", rv.List[1]) 44 | } 45 | 46 | func Test_ReadLetter2(t *testing.T) { 47 | var rv ReadLetterResponse 48 | err := Preheat(&rv.List, &LetterInbox{}) 49 | assert.NoError(t, err) 50 | 51 | resp2 := []LetterInbox{ 52 | { 53 | ID: 1, 54 | FromRid: 3, 55 | ToRid: 4, 56 | Msg: "hello", 57 | }, 58 | } 59 | err = Copy(&rv.List, &resp2, WithUsePreheat()) 60 | assert.NoError(t, err) 61 | 62 | assert.Equal(t, rv.List[0].Msg, "hello") 63 | } 64 | 65 | func Test_ReadLetter3(t *testing.T) { 66 | var rv ReadLetterResponse 67 | 68 | resp2 := []LetterInbox{ 69 | { 70 | ID: 1, 71 | FromRid: 3, 72 | ToRid: 4, 73 | Msg: "hello", 74 | }, 75 | } 76 | err := Copy(&rv.List, &resp2) 77 | assert.NoError(t, err) 78 | 79 | assert.Equal(t, rv.List[0].Msg, "hello") 80 | } 81 | -------------------------------------------------------------------------------- /pcopy_dst_ptr_struct_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type TestDstStructPtr_Dst struct { 11 | TestDstPtr *struct { 12 | X int 13 | Y int 14 | } 15 | } 16 | 17 | type TestDstPtr2 struct { 18 | X int 19 | Y int 20 | } 21 | 22 | type TestDstStructPtr_Src struct { 23 | TestDstPtr TestDstPtr2 24 | } 25 | 26 | func Test_DstPtr_Struct(t *testing.T) { 27 | var dst TestDstStructPtr_Dst 28 | src := TestDstStructPtr_Src{ 29 | TestDstPtr: TestDstPtr2{X: 3, Y: 4}, 30 | } 31 | 32 | Copy(&dst, &src) 33 | assert.NotNil(t, dst.TestDstPtr) 34 | assert.Equal(t, dst.TestDstPtr.X, 3) 35 | assert.Equal(t, dst.TestDstPtr.Y, 4) 36 | } 37 | -------------------------------------------------------------------------------- /pcopy_enum_fix2_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type DiaryResourceTypeDst int32 11 | 12 | const ( 13 | // 文本 14 | DiaryResourceType_DiaryResourceTypeTextDst DiaryResourceTypeDst = 0 15 | // 图片 16 | DiaryResourceType_DiaryResourceTypeImageDst DiaryResourceTypeDst = 1 17 | // 视频 18 | DiaryResourceType_DiaryResourceTypeVideoDst DiaryResourceTypeDst = 2 19 | // Ours类型 20 | DiaryResourceType_DiaryResourceTypeOursDst DiaryResourceTypeDst = 3 21 | ) 22 | 23 | type SquareDiaryItemDst struct { 24 | // 资源类型 25 | ResourceType DiaryResourceTypeDst `protobuf:"varint,7,opt,name=ResourceType,proto3,enum=topic.v1.DiaryResourceType" json:"ResourceType,omitempty"` 26 | // 资源链接数组 27 | } 28 | 29 | type GetRecommendedListResp_GetRecommendedListRespData struct { 30 | // 列表 31 | List []*SquareDiaryItemDst `protobuf:"bytes,1,rep,name=List,proto3" json:"List,omitempty"` 32 | // 是否有更多记录 33 | HasMore bool `protobuf:"varint,2,opt,name=HasMore,proto3" json:"HasMore,omitempty"` 34 | // 记录位置 35 | Pos int32 `protobuf:"varint,3,opt,name=Pos,proto3" json:"Pos,omitempty"` 36 | // 相同记录的偏移量 37 | Offset int32 `protobuf:"varint,4,opt,name=Offset,proto3" json:"Offset,omitempty"` 38 | } 39 | 40 | type DiaryResourceTypeSrc int32 41 | 42 | const ( 43 | // 文本 44 | DiaryResourceType_DiaryResourceTypeText DiaryResourceTypeSrc = 0 45 | // 图片 46 | DiaryResourceType_DiaryResourceTypeImage DiaryResourceTypeSrc = 1 47 | // 视频 48 | DiaryResourceType_DiaryResourceTypeVideo DiaryResourceTypeSrc = 2 49 | // Ours类型 50 | DiaryResourceType_DiaryResourceTypeOurs DiaryResourceTypeSrc = 3 51 | ) 52 | 53 | type SquareDiaryItemSrc struct { 54 | // 资源类型 55 | ResourceType DiaryResourceTypeSrc `protobuf:"varint,7,opt,name=ResourceType,proto3,enum=topic.v1.DiaryResourceType" json:"ResourceType,omitempty"` 56 | // 资源链接数组 57 | } 58 | 59 | type GetRecommendedListResp struct { 60 | // 列表 61 | List []*SquareDiaryItemSrc `protobuf:"bytes,1,rep,name=List,proto3" json:"List,omitempty"` 62 | // 是否有更多记录 63 | HasMore bool `protobuf:"varint,2,opt,name=HasMore,proto3" json:"HasMore,omitempty"` 64 | // 记录位置 65 | Pos int32 `protobuf:"varint,3,opt,name=Pos,proto3" json:"Pos,omitempty"` 66 | // 相同记录的偏移量 67 | Offset int32 `protobuf:"varint,4,opt,name=Offset,proto3" json:"Offset,omitempty"` 68 | } 69 | 70 | func Test_Fix2(t *testing.T) { 71 | var r *GetRecommendedListResp_GetRecommendedListRespData 72 | resp := GetRecommendedListResp{ 73 | // 帮我优化这个代码 74 | List: []*SquareDiaryItemSrc{{ResourceType: DiaryResourceType_DiaryResourceTypeVideo}}, 75 | HasMore: true, 76 | Pos: 10, 77 | Offset: 11, 78 | } 79 | err := Copy(&r, &resp) 80 | assert.NoError(t, err) 81 | assert.NotEqual(t, r, nil) 82 | assert.NotEqual(t, len(r.List), 0) 83 | } 84 | -------------------------------------------------------------------------------- /pcopy_fix_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | // src 11 | type TopicItemSrc struct { 12 | ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` 13 | Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` 14 | CreateTime string `protobuf:"bytes,3,opt,name=CreateTime,proto3" json:"CreateTime,omitempty"` 15 | } 16 | 17 | // src 18 | type AllTopicResp struct { 19 | TopicItem []*TopicItemSrc `protobuf:"bytes,1,rep,name=TopicItem,proto3" json:"TopicItem,omitempty"` 20 | Total int32 `protobuf:"varint,2,opt,name=Total,proto3" json:"Total,omitempty"` // 总条数 21 | } 22 | 23 | // desc 24 | type AllTopicResp_AllTopicRespData struct { 25 | TopicItem []*TopicItemDst `protobuf:"bytes,1,rep,name=TopicItem,proto3" json:"TopicItem,omitempty"` 26 | Total int32 `protobuf:"varint,2,opt,name=Total,proto3" json:"Total,omitempty"` // 总条数 27 | } 28 | 29 | type TopicItemDst struct { 30 | ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` 31 | Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` 32 | CreateTime string `protobuf:"bytes,3,opt,name=CreateTime,proto3" json:"CreateTime,omitempty"` 33 | } 34 | 35 | type AllTopicResp2 struct { 36 | Code int32 `protobuf:"varint,1,opt,name=Code,proto3" json:"Code,omitempty"` 37 | Message string `protobuf:"bytes,2,opt,name=Message,proto3" json:"Message,omitempty"` 38 | Data *AllTopicResp_AllTopicRespData `protobuf:"bytes,3,opt,name=Data,proto3" json:"Data,omitempty"` 39 | } 40 | 41 | func TestCopy(t *testing.T) { 42 | resp := AllTopicResp{Total: 100, TopicItem: []*TopicItemSrc{ 43 | {ID: "111", Name: "111"}, 44 | {ID: "111", Name: "222"}, 45 | }} 46 | 47 | rsp := AllTopicResp2{Data: &AllTopicResp_AllTopicRespData{}} 48 | Copy(rsp.Data, &resp) 49 | 50 | // fmt.Printf("%#v\n", resp) 51 | // fmt.Printf("%#v\n", rsp.Data) 52 | assert.NotNil(t, rsp.Data) 53 | assert.Equal(t, rsp.Data.Total, resp.Total) 54 | assert.Equal(t, len(rsp.Data.TopicItem), len(resp.TopicItem)) 55 | assert.Greater(t, len(rsp.Data.TopicItem), 0) 56 | assert.Greater(t, len(resp.TopicItem), 0) 57 | for i, v := range resp.TopicItem { 58 | assert.Equal(t, v.ID, rsp.Data.TopicItem[i].ID) 59 | assert.Equal(t, v.Name, rsp.Data.TopicItem[i].Name) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pcopy_func_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "testing" 7 | "unsafe" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func Test_FuncToFunc(t *testing.T) { 13 | type dst struct { 14 | A func() 15 | } 16 | 17 | type src struct { 18 | A func() 19 | } 20 | 21 | d := dst{} 22 | s := src{ 23 | A: func() { fmt.Printf("hello") }, 24 | } 25 | 26 | Copy(&d, &s) 27 | 28 | // 如果指向的是同一个地址的函数,注释的方法是不行的 29 | // assert.Equal(t, d.A, s.A) 30 | assert.Equal(t, *(*uintptr)(unsafe.Pointer(&d.A)), *(*uintptr)(unsafe.Pointer(&s.A))) 31 | } 32 | 33 | // 测试特殊情况 34 | func Test_Func_Special(t *testing.T) { 35 | type fn struct { 36 | Add func() 37 | } 38 | 39 | for _, tc := range []testCase{ 40 | // dst 里面没有Add成员变量 41 | func() testCase { 42 | Copy(new(int), &fn{Add: func() {}}) 43 | return testCase{true, true} 44 | }(), 45 | func() testCase { 46 | // dst里面成员变量 与 src不一致 47 | type dstFn struct { 48 | Add int 49 | } 50 | 51 | Copy(&dstFn{}, &fn{Add: func() {}}) 52 | return testCase{true, true} 53 | }(), 54 | } { 55 | assert.Equal(t, tc.need, tc.got) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pcopy_getlikefavorited_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | type CommentModule int32 12 | 13 | const ( 14 | CommentModule_CommentModuleUndefined CommentModule = 0 15 | CommentModule_CommentModuleDiary CommentModule = 1 16 | CommentModule_CommentModuleHotPot CommentModule = 2 17 | ) 18 | 19 | type LikeType int32 20 | 21 | const ( 22 | LikeType_LIKE LikeType = 0 23 | LikeType_COLLECT LikeType = 1 24 | ) 25 | 26 | type GetMessageLikeFavoritedResp struct { 27 | Code int32 28 | Message string 29 | Data *GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData 30 | } 31 | 32 | type MessageWhoAndTime struct { 33 | HeadPic string 34 | Nickname string 35 | Time string 36 | DID uint64 37 | Seq int64 38 | } 39 | 40 | type CommentType int32 41 | 42 | const ( 43 | CommentType_DIARY CommentType = 0 44 | 45 | CommentType_COMMENT CommentType = 1 46 | ) 47 | 48 | type MessageResourceType int32 49 | 50 | const ( 51 | MessageResourceType_MessageResourceTypeLike MessageResourceType = 2 52 | 53 | MessageResourceType_MessageResourceTypeFavorited MessageResourceType = 3 54 | 55 | MessageResourceType_MessageesourceTypeText MessageResourceType = 4 56 | 57 | MessageResourceType_MessageResourceTypeImage MessageResourceType = 5 58 | 59 | MessageResourceType_MessageResourceTypeVideo MessageResourceType = 6 60 | ) 61 | 62 | type GetMessageLikeFavoritedRespItem struct { 63 | Info *MessageWhoAndTime 64 | 65 | LikeType LikeType 66 | 67 | CommentType CommentType 68 | ResourceType MessageResourceType 69 | 70 | PostID uint64 71 | 72 | RefMessage string 73 | RefMessageID uint64 74 | ParentCid uint64 75 | CommentModule CommentModule 76 | ArticleThumbnail string 77 | } 78 | 79 | type GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData struct { 80 | Items []*GetMessageLikeFavoritedRespItem 81 | } 82 | 83 | var local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData = GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData{ 84 | Items: []*GetMessageLikeFavoritedRespItem{ 85 | { 86 | Info: &MessageWhoAndTime{ 87 | HeadPic: "headpic", 88 | Nickname: "nickname", 89 | Time: "time", 90 | DID: 1, 91 | Seq: 1, 92 | }, 93 | LikeType: LikeType_COLLECT, 94 | CommentType: CommentType_COMMENT, 95 | ResourceType: MessageResourceType_MessageResourceTypeLike, 96 | PostID: 1, 97 | RefMessage: "refmessage", 98 | RefMessageID: 1, 99 | ParentCid: 1, 100 | CommentModule: CommentModule_CommentModuleDiary, 101 | ArticleThumbnail: "articlethumbnail", 102 | }, 103 | { 104 | Info: &MessageWhoAndTime{ 105 | HeadPic: "headpic2", 106 | Nickname: "nickname2", 107 | Time: "time2", 108 | DID: 2, 109 | Seq: 2, 110 | }, 111 | LikeType: LikeType_COLLECT, 112 | CommentType: CommentType_COMMENT, 113 | ResourceType: MessageResourceType_MessageResourceTypeLike, 114 | PostID: 2, 115 | RefMessage: "refmessage", 116 | RefMessageID: 2, 117 | ParentCid: 2, 118 | CommentModule: CommentModule_CommentModuleDiary, 119 | ArticleThumbnail: "articlethumbnail", 120 | }, 121 | { 122 | Info: &MessageWhoAndTime{ 123 | HeadPic: "headpic2", 124 | Nickname: "nickname2", 125 | Time: "time2", 126 | DID: 2, 127 | Seq: 2, 128 | }, 129 | LikeType: LikeType_COLLECT, 130 | CommentType: CommentType_COMMENT, 131 | ResourceType: MessageResourceType_MessageResourceTypeLike, 132 | PostID: 2, 133 | RefMessage: "refmessage", 134 | RefMessageID: 2, 135 | ParentCid: 2, 136 | CommentModule: CommentModule_CommentModuleDiary, 137 | ArticleThumbnail: "articlethumbnail", 138 | }, 139 | { 140 | Info: &MessageWhoAndTime{ 141 | HeadPic: "headpic2", 142 | Nickname: "nickname2", 143 | Time: "time2", 144 | DID: 2, 145 | Seq: 2, 146 | }, 147 | LikeType: LikeType_COLLECT, 148 | CommentType: CommentType_COMMENT, 149 | ResourceType: MessageResourceType_MessageResourceTypeLike, 150 | PostID: 2, 151 | RefMessage: "refmessage", 152 | RefMessageID: 2, 153 | ParentCid: 2, 154 | CommentModule: CommentModule_CommentModuleDiary, 155 | ArticleThumbnail: "articlethumbnail", 156 | }, 157 | { 158 | Info: &MessageWhoAndTime{ 159 | HeadPic: "headpic2", 160 | Nickname: "nickname2", 161 | Time: "time2", 162 | DID: 2, 163 | Seq: 2, 164 | }, 165 | LikeType: LikeType_COLLECT, 166 | CommentType: CommentType_COMMENT, 167 | ResourceType: MessageResourceType_MessageResourceTypeLike, 168 | PostID: 2, 169 | RefMessage: "refmessage", 170 | RefMessageID: 2, 171 | ParentCid: 2, 172 | CommentModule: CommentModule_CommentModuleDiary, 173 | ArticleThumbnail: "articlethumbnail", 174 | }, 175 | }, 176 | } 177 | 178 | func Test_GetMessageLikeFavorited_1(t *testing.T) { 179 | err := Preheat(&GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData{}, &GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData{}) 180 | assert.NoError(t, err) 181 | 182 | var dst GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData 183 | err = Copy(&dst, &local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData, WithUsePreheat()) 184 | assert.NoError(t, err) 185 | fmt.Printf("%d\n", len(dst.Items)) 186 | assert.Equal(t, local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData, dst) 187 | } 188 | 189 | // 左边传参数是个二级指针 190 | func Test_GetMessageLikeFavorited_2(t *testing.T) { 191 | err := Preheat(&GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData{}, &GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData{}) 192 | assert.NoError(t, err) 193 | var resp GetMessageLikeFavoritedResp 194 | // var dst GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData 195 | err = Copy(&resp.Data, &local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData, WithUsePreheat()) 196 | assert.NoError(t, err) 197 | assert.Equal(t, local_GetMessageLikeFavoritedResp_GetMessageLikeFavoritedRespData, *resp.Data) 198 | } 199 | -------------------------------------------------------------------------------- /pcopy_getredpoint_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type GetRedPointResp_GetRedPointRespData struct { 11 | Point map[uint32]uint32 `protobuf:"bytes,1,rep,name=point,proto3" json:"point,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` 12 | } 13 | 14 | type GetRedPointResp struct { 15 | Code int32 `protobuf:"varint,1,opt,name=Code,proto3" json:"Code,omitempty"` 16 | Message string `protobuf:"bytes,2,opt,name=Message,proto3" json:"Message,omitempty"` 17 | Data *GetRedPointResp_GetRedPointRespData `protobuf:"bytes,3,opt,name=Data,proto3" json:"Data,omitempty"` 18 | } 19 | 20 | type GetRedPointRespData struct { 21 | Point map[uint32]uint32 `protobuf:"bytes,1,rep,name=point,proto3" json:"point,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` 22 | } 23 | 24 | var local_GetRedPointRespData = GetRedPointRespData{ 25 | Point: map[uint32]uint32{ 26 | 1: 1, 27 | 2: 2, 28 | 3: 3, 29 | 4: 4, 30 | 5: 5, 31 | }, 32 | } 33 | 34 | func Test_GetRedPoint(t *testing.T) { 35 | Preheat(&GetRedPointResp{}, &GetRedPointRespData{}) 36 | rv := GetRedPointResp{} 37 | 38 | err := Copy(&rv.Data, &local_GetRedPointRespData, WithUsePreheat()) 39 | assert.NoError(t, err) 40 | assert.Equal(t, rv.Data.Point, local_GetRedPointRespData.Point) 41 | } 42 | -------------------------------------------------------------------------------- /pcopy_have_value_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type testHaveValue struct { 11 | Int int 12 | Float32 float32 13 | } 14 | 15 | func Test_have_value(t *testing.T) { 16 | t1 := testHaveValue{Int: 3, Float32: 3.14} 17 | t2 := testHaveValue{} 18 | Copy(&t1, &t2) 19 | assert.Equal(t, t1, testHaveValue{3, 3.14}) 20 | } 21 | -------------------------------------------------------------------------------- /pcopy_interface_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | // 测试interface{} 12 | func Test_Inteface(t *testing.T) { 13 | type interfaceTest struct { 14 | I interface{} 15 | S interface{} 16 | } 17 | 18 | for _, tc := range []testCase{ 19 | func() testCase { 20 | d := interfaceTest{} 21 | src := interfaceTest{ 22 | I: 5, 23 | S: "hello", 24 | } 25 | 26 | Copy(&d, &src) 27 | return testCase{got: d, need: src} 28 | }(), 29 | } { 30 | assert.Equal(t, tc.need, tc.got) 31 | } 32 | } 33 | 34 | // 基础类型 35 | func Test_Interface_Pcopy2(t *testing.T) { 36 | err := Preheat(&interfaceDstTest{}, &interfaceBaseSrcTest{}) 37 | // err := Preheat(&interfaceDstTest{}, &local) 38 | assert.NoError(t, err) 39 | for _, tc := range []interfaceBaseSrcTest{ 40 | { 41 | A: uint(1), 42 | B: uint8(2), 43 | C: uint16(3), 44 | D: uint32(4), 45 | E: uint64(5), 46 | F: int(6), 47 | G: int8(7), 48 | H: int16(8), 49 | I: int32(9), 50 | J: int64(10), 51 | K: float32(11), 52 | L: float64(12), 53 | // M: "13", 54 | // N: true, 55 | }, 56 | } { 57 | 58 | var data interfaceDstTest 59 | 60 | err = Copy(&data, &tc, WithUsePreheat()) 61 | // (&data).printAddress() 62 | // fmt.Printf("data: %x: data.A: %T, data.A: %d\n", data, data.A, data.A) 63 | assert.NoError(t, err) 64 | assert.Equal(t, interfaceDstTest(tc), data) 65 | } 66 | } 67 | 68 | func Test_Interface_Pcopy3(t *testing.T) { 69 | err := Preheat(&interfaceSliceDstTest{}, &interfaceBaseSliceSrcTest{}) 70 | assert.NoError(t, err) 71 | for _, tc := range []interfaceBaseSliceSrcTest{ 72 | { 73 | A: []uint{1, 2, 3, 4}, 74 | B: []uint8{2, 3, 4, 5}, 75 | C: []uint16{3, 4, 5, 6}, 76 | D: []uint32{4, 5, 6, 7}, 77 | E: []uint64{5, 6, 7, 8}, 78 | F: []int{6, 7, 8, 9}, 79 | G: []int8{7, 8, 9, 10}, 80 | H: []int16{8, 9, 10, 11}, 81 | I: []int32{9, 10, 11, 12}, 82 | J: []int64{10, 11, 12, 13}, 83 | K: []float32{11, 12, 13, 14}, 84 | L: []float64{12, 13, 14, 15}, 85 | M: []string{"13", "14", "15", "16"}, 86 | N: []bool{true, false, true, false}, 87 | }, 88 | } { 89 | 90 | var data interfaceSliceDstTest 91 | 92 | err = Copy(&data, &tc, WithUsePreheat()) 93 | // (&data).printAddress() 94 | // fmt.Printf("data: %x: data.A: %T, data.A: %d\n", data, data.A, data.A) 95 | assert.NoError(t, err) 96 | assert.Equal(t, interfaceSliceDstTest(tc), data) 97 | } 98 | } 99 | 100 | // TODO 打开 101 | func Test_Interface_Pcopy(t *testing.T) { 102 | type interfaceTest struct { 103 | I interface{} 104 | S interface{} 105 | } 106 | 107 | err := Preheat(&interfaceTest{}, &interfaceTest{}) 108 | assert.NoError(t, err) 109 | for _, tc := range []interfaceTest{ 110 | { 111 | I: 5, 112 | S: "hello", 113 | }, 114 | { 115 | I: "world", 116 | S: 5, 117 | }, 118 | } { 119 | 120 | var data interfaceTest 121 | 122 | err = Copy(&data, &tc, WithUsePreheat()) 123 | assert.NoError(t, err) 124 | fmt.Printf("%v, %v\n", data, tc) 125 | assert.Equal(t, tc, data) 126 | } 127 | } 128 | 129 | // 测试interface{}特殊情况 130 | // 只要不崩溃就是对的 131 | func Test_Interface_Special(t *testing.T) { 132 | for _, tc := range []testCase{ 133 | // src, dst里面同名成员变量类型不一样 134 | func() testCase { 135 | type dst struct { 136 | I int 137 | } 138 | 139 | type src struct { 140 | I interface{} 141 | } 142 | 143 | Copy(&dst{}, &src{I: "hello"}) 144 | return testCase{} 145 | }(), 146 | 147 | // src有nil成员变量 148 | func() testCase { 149 | type dst struct { 150 | I int 151 | } 152 | 153 | type src struct { 154 | I interface{} 155 | } 156 | 157 | Copy((*int)(nil), (*int)(nil)) 158 | return testCase{} 159 | }(), 160 | } { 161 | assert.Equal(t, tc.need, tc.got) 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /pcopy_map_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type test_MapWithMap_Item struct { 11 | A string 12 | B string 13 | } 14 | 15 | type test_MapWithMap_Dst struct { 16 | A map[string]string 17 | B map[string]map[string]string 18 | C map[string]test_MapWithMap_Item 19 | } 20 | 21 | type test_MapWithMap_Src test_MapWithMap_Dst 22 | 23 | var local_MapWithMap_Src = test_MapWithMap_Src{ 24 | A: map[string]string{ 25 | "1": "1", 26 | "2": "2", 27 | }, 28 | B: map[string]map[string]string{ 29 | "1": { 30 | "1": "1", 31 | "2": "2", 32 | }, 33 | "2": { 34 | "1": "1", 35 | "2": "2", 36 | }, 37 | }, 38 | C: map[string]test_MapWithMap_Item{ 39 | "1": { 40 | A: "1", 41 | B: "2", 42 | }, 43 | "2": { 44 | A: "1", 45 | B: "2", 46 | }, 47 | }, 48 | } 49 | 50 | func Test_MapWithMap(t *testing.T) { 51 | err := Preheat(&test_MapWithMap_Dst{}, &test_MapWithMap_Src{}) 52 | assert.NoError(t, err) 53 | 54 | d := test_MapWithMap_Dst{} 55 | err = Copy(&d, &local_MapWithMap_Src, WithUsePreheat()) 56 | assert.NoError(t, err) 57 | // Copy(&d, &local_MapWithMap_Src) 58 | assert.Equal(t, d, test_MapWithMap_Dst(local_MapWithMap_Src)) 59 | } 60 | 61 | func Test_MapToMap2(t *testing.T) { 62 | type dst struct { 63 | A map[int]int 64 | B map[string]string 65 | } 66 | 67 | type src struct { 68 | B map[string]string 69 | A map[int]int 70 | } 71 | 72 | var d dst 73 | b := map[string]string{ 74 | "testA": "testA", 75 | "testB": "testB", 76 | } 77 | 78 | a := map[int]int{ 79 | 1: 1, 80 | 2: 2, 81 | } 82 | 83 | s := src{ 84 | B: b, 85 | A: a, 86 | } 87 | 88 | err := Preheat(&dst{}, &src{}) 89 | assert.NoError(t, err) 90 | Copy(&d, &s, WithUsePreheat()) 91 | assert.Equal(t, d, dst{A: a, B: b}) 92 | } 93 | 94 | func Test_MapToMap(t *testing.T) { 95 | type dst struct { 96 | A map[int]int 97 | B map[string]string 98 | } 99 | 100 | type src struct { 101 | B map[string]string 102 | A map[int]int 103 | } 104 | 105 | var d dst 106 | b := map[string]string{ 107 | "testA": "testA", 108 | "testB": "testB", 109 | } 110 | 111 | a := map[int]int{ 112 | 1: 1, 113 | 2: 2, 114 | } 115 | 116 | s := src{ 117 | B: b, 118 | A: a, 119 | } 120 | 121 | Copy(&d, &s) 122 | assert.Equal(t, d, dst{A: a, B: b}) 123 | } 124 | 125 | func Test_Map_Special(t *testing.T) { 126 | type mVal struct { 127 | ID int 128 | Name string 129 | } 130 | 131 | for _, tc := range []testCase{ 132 | // map里面的值是结构体 133 | func() testCase { 134 | src := map[string]mVal{ 135 | "1": {ID: 1, Name: "name:1"}, 136 | "2": {ID: 2, Name: "name:2"}, 137 | "3": {ID: 3, Name: "name:3"}, 138 | } 139 | 140 | var dst map[string]mVal 141 | Copy(&dst, &src) 142 | return testCase{got: dst, need: src} 143 | }(), 144 | // dst的地址不是指针,没有发生panic 145 | func() testCase { 146 | src := map[string]mVal{ 147 | "1": {ID: 1, Name: "name:1"}, 148 | } 149 | 150 | var dst map[string]mVal 151 | Copy(&dst, &src) 152 | return testCase{} 153 | }(), 154 | func() testCase { 155 | src := map[string]mVal{ 156 | "1": {ID: 1, Name: "name:1"}, 157 | } 158 | 159 | Copy(new(int), &src) 160 | return testCase{} 161 | }(), 162 | // key相同,value不同 163 | func() testCase { 164 | dst := map[int]string{ 165 | 1: "hello", 166 | } 167 | src := map[int]int{ 168 | 1: 1, 169 | } 170 | 171 | Copy(&dst, &src) 172 | return testCase{} 173 | }(), 174 | // key不同,value不同 175 | func() testCase { 176 | dst := map[string]int64{ 177 | "hello": 3, 178 | } 179 | src := map[int]int64{ 180 | 1: 64, 181 | } 182 | 183 | Copy(&dst, &src) 184 | return testCase{} 185 | }(), 186 | } { 187 | assert.Equal(t, tc.need, tc.got) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /pcopy_ptr_basemap_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "testing" 7 | "unsafe" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | // 测试基础类型的指针 13 | type test_Ptr_BaseMapType_Dst struct { 14 | A *map[int]int 15 | B *map[int8]int8 16 | C *map[int16]int16 17 | D *map[int32]int32 18 | E *map[int64]int64 19 | F *map[uint]uint 20 | G *map[uint8]uint8 21 | H *map[uint16]uint16 22 | I *map[uint32]uint32 23 | J *map[uint64]uint64 24 | K *map[uintptr]uintptr 25 | L *map[float32]float32 26 | M *map[float64]float64 27 | N *map[complex64]complex64 28 | O *map[complex128]complex128 29 | P *map[bool]bool 30 | Q *map[string]string 31 | // R *interface{} TODO 32 | } 33 | 34 | type test_Ptr_BaseMapType_Src struct { 35 | A map[int]int 36 | B map[int8]int8 37 | C map[int16]int16 38 | D map[int32]int32 39 | E map[int64]int64 40 | F map[uint]uint 41 | G map[uint8]uint8 42 | H map[uint16]uint16 43 | I map[uint32]uint32 44 | J map[uint64]uint64 45 | K map[uintptr]uintptr 46 | L map[float32]float32 47 | M map[float64]float64 48 | N map[complex64]complex64 49 | O map[complex128]complex128 50 | P map[bool]bool 51 | Q map[string]string 52 | } 53 | 54 | var local_test_Ptr_BaseMapType_Dst = test_Ptr_BaseMapType_Dst{ 55 | A: newDef(map[int]int{1: 1, 2: 2}), 56 | B: newDef(map[int8]int8{3: 3, 4: 4}), 57 | C: newDef(map[int16]int16{5: 5, 6: 6}), 58 | D: newDef(map[int32]int32{7: 7, 8: 8}), 59 | E: newDef(map[int64]int64{9: 9, 10: 10}), 60 | F: newDef(map[uint]uint{11: 11, 12: 12}), 61 | G: newDef(map[uint8]uint8{13: 13, 14: 14}), 62 | H: newDef(map[uint16]uint16{15: 15, 16: 16}), 63 | I: newDef(map[uint32]uint32{17: 17, 18: 18}), 64 | J: newDef(map[uint64]uint64{19: 19, 20: 20}), 65 | K: newDef(map[uintptr]uintptr{21: 21, 22: 22}), 66 | L: newDef(map[float32]float32{23: 23, 24: 24}), 67 | M: newDef(map[float64]float64{25: 25, 26: 26}), 68 | N: newDef(map[complex64]complex64{27: 27, 28: 28}), 69 | O: newDef(map[complex128]complex128{29: 29, 30: 30}), 70 | P: newDef(map[bool]bool{true: true, false: false}), 71 | Q: newDef(map[string]string{"a": "a", "b": "b"}), 72 | } 73 | 74 | var local_test_Ptr_BaseMapType_Src = test_Ptr_BaseMapType_Src{ 75 | A: map[int]int{1: 1, 2: 2}, 76 | B: map[int8]int8{3: 3, 4: 4}, 77 | C: map[int16]int16{5: 5, 6: 6}, 78 | D: map[int32]int32{7: 7, 8: 8}, 79 | E: map[int64]int64{9: 9, 10: 10}, 80 | F: map[uint]uint{11: 11, 12: 12}, 81 | G: map[uint8]uint8{13: 13, 14: 14}, 82 | H: map[uint16]uint16{15: 15, 16: 16}, 83 | I: map[uint32]uint32{17: 17, 18: 18}, 84 | J: map[uint64]uint64{19: 19, 20: 20}, 85 | K: map[uintptr]uintptr{21: 21, 22: 22}, 86 | L: map[float32]float32{23: 23, 24: 24}, 87 | M: map[float64]float64{25: 25, 26: 26}, 88 | N: map[complex64]complex64{27: 27, 28: 28}, 89 | O: map[complex128]complex128{29: 29, 30: 30}, 90 | P: map[bool]bool{true: true, false: false}, 91 | Q: map[string]string{"a": "a", "b": "b"}, 92 | } 93 | 94 | func Test_Ptr_BaseMapType1_1(t *testing.T) { 95 | err := Preheat(&test_Ptr_BaseMapType_Dst{}, &local_test_Ptr_BaseMapType_Dst) 96 | assert.NoError(t, err, "Preheat(&test_BaseMapType_ptr_Dst{}, &local_test_BaseMapType_ptr_Dst)") 97 | 98 | var opts []Option 99 | for _, b := range []bool{true, false} { 100 | var dst test_Ptr_BaseMapType_Dst 101 | if b { 102 | opts = append(opts, WithUsePreheat()) 103 | } else { 104 | opts = []Option{} 105 | } 106 | err = Copy(&dst, &local_test_Ptr_BaseMapType_Dst, opts...) 107 | assert.NoError(t, err, "Copy(&dst, &local_test_BaseMapType_ptr_Dst, WithUsePreheat()") 108 | assert.Equal(t, local_test_Ptr_BaseMapType_Dst, dst) 109 | assert.NotEqual(t, unsafe.Pointer(local_test_Ptr_BaseMapType_Dst.A), unsafe.Pointer(dst.A), fmt.Sprintf("local_test_BaseMapType_ptr_Dst.A: %p, dst.A: %p", local_test_Ptr_BaseMapType_Dst.A, dst.A)) 110 | assert.Equal(t, *local_test_Ptr_BaseMapType_Dst.A, *dst.A) 111 | } 112 | } 113 | 114 | func Test_Ptr_BaseMapType1_2(t *testing.T) { 115 | err := Preheat(&test_Ptr_BaseMapType_Dst{}, &test_Ptr_BaseMapType_Dst{}) 116 | assert.NoError(t, err) 117 | 118 | var opts []Option 119 | for _, b := range []bool{true, false} { 120 | var dst test_Ptr_BaseMapType_Dst 121 | if b { 122 | opts = append(opts, WithUsePreheat()) 123 | } else { 124 | opts = []Option{} 125 | } 126 | err = Copy(&dst, &local_test_Ptr_BaseMapType_Dst, opts...) 127 | assert.NoError(t, err) 128 | assert.Equal(t, local_test_Ptr_BaseMapType_Dst, dst) 129 | assert.NotEqual(t, unsafe.Pointer(local_test_Ptr_BaseMapType_Dst.A), unsafe.Pointer(dst.A)) 130 | assert.Equal(t, *local_test_Ptr_BaseMapType_Dst.A, *dst.A) 131 | } 132 | } 133 | 134 | func Test_Ptr_BaseMapType2(t *testing.T) { 135 | err := Preheat(&test_Ptr_BaseMapType_Src{}, &test_Ptr_BaseMapType_Dst{}) 136 | assert.NoError(t, err) 137 | 138 | var opts []Option 139 | for _, b := range []bool{true, false} { 140 | var dst test_Ptr_BaseMapType_Src 141 | if b { 142 | opts = append(opts, WithUsePreheat()) 143 | } else { 144 | opts = []Option{} 145 | } 146 | err = Copy(&dst, &local_test_Ptr_BaseMapType_Dst, opts...) 147 | assert.NoError(t, err) 148 | assert.Equal(t, local_test_Ptr_BaseMapType_Src, dst) 149 | } 150 | } 151 | 152 | func Test_Ptr_BaseMapType3(t *testing.T) { 153 | err := Preheat(&test_Ptr_BaseMapType_Dst{}, &test_Ptr_BaseMapType_Src{}) 154 | assert.NoError(t, err) 155 | 156 | var opts []Option 157 | for _, b := range []bool{true, false} { 158 | if b { 159 | opts = append(opts, WithUsePreheat()) 160 | } else { 161 | opts = []Option{} 162 | } 163 | var dst test_Ptr_BaseMapType_Dst 164 | err = Copy(&dst, &local_test_Ptr_BaseMapType_Src, opts...) 165 | assert.NoError(t, err) 166 | fmt.Printf("dst: %+v\n", dst) 167 | fmt.Printf(".A %p\n", dst.A) 168 | assert.Equal(t, local_test_Ptr_BaseMapType_Dst, dst) 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /pcopy_ptr_baseslice_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "testing" 7 | "unsafe" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | // 测试基础类型的指针 13 | type test_BaseSliceType_ptr_Dst struct { 14 | A *[]int 15 | B *[]int8 16 | C *[]int16 17 | D *[]int32 18 | E *[]int64 19 | F *[]uint 20 | G *[]uint8 21 | H *[]uint16 22 | I *[]uint32 23 | J *[]uint64 24 | K *[]uintptr 25 | L *[]float32 26 | M *[]float64 27 | N *[]complex64 28 | O *[]complex128 29 | P *[]bool 30 | Q *[]string 31 | // R *interface{} TODO 32 | } 33 | 34 | type test_BaseSliceType_ptr_Src struct { 35 | A []int 36 | B []int8 37 | C []int16 38 | D []int32 39 | E []int64 40 | F []uint 41 | G []uint8 42 | H []uint16 43 | I []uint32 44 | J []uint64 45 | K []uintptr 46 | L []float32 47 | M []float64 48 | N []complex64 49 | O []complex128 50 | P []bool 51 | Q []string 52 | // R interface{} 53 | } 54 | 55 | var local_test_BaseSliceType_ptr_Dst = test_BaseSliceType_ptr_Dst{ 56 | A: newDef([]int{1, 2}), 57 | B: newDef([]int8{3, 4}), 58 | C: newDef([]int16{5, 6}), 59 | D: newDef([]int32{7, 8}), 60 | E: newDef([]int64{9, 10}), 61 | F: newDef([]uint{11, 12}), 62 | G: newDef([]uint8{13, 14}), 63 | H: newDef([]uint16{15, 16}), 64 | I: newDef([]uint32{17, 18}), 65 | J: newDef([]uint64{19, 20}), 66 | K: newDef([]uintptr{21, 22}), 67 | L: newDef([]float32{23.23, 24.24}), 68 | M: newDef([]float64{25.25, 26.26}), 69 | N: newDef([]complex64{27.27, 28.28}), 70 | O: newDef([]complex128{29.29, 30.30}), 71 | P: newDef([]bool{true, false}), 72 | Q: newDef([]string{"hello", "world"}), 73 | } 74 | 75 | var local_test_BaseSliceType_ptr_Src = test_BaseSliceType_ptr_Src{ 76 | A: []int{1, 2}, 77 | B: []int8{3, 4}, 78 | C: []int16{5, 6}, 79 | D: []int32{7, 8}, 80 | E: []int64{9, 10}, 81 | F: []uint{11, 12}, 82 | G: []uint8{13, 14}, 83 | H: []uint16{15, 16}, 84 | I: []uint32{17, 18}, 85 | J: []uint64{19, 20}, 86 | K: []uintptr{21, 22}, 87 | L: []float32{23.23, 24.24}, 88 | M: []float64{25.25, 26.26}, 89 | N: []complex64{27.27, 28.28}, 90 | O: []complex128{29.29, 30.30}, 91 | P: []bool{true, false}, 92 | Q: []string{"hello", "world"}, 93 | } 94 | 95 | func Test_Ptr_BaseSliceType1_1(t *testing.T) { 96 | err := Preheat(&test_BaseSliceType_ptr_Dst{}, &local_test_BaseSliceType_ptr_Dst) 97 | assert.NoError(t, err, "Preheat(&test_BaseSliceType_ptr_Dst{}, &local_test_BaseSliceType_ptr_Dst)") 98 | 99 | var opts []Option 100 | for _, b := range []bool{true, false} { 101 | if b { 102 | opts = append(opts, WithUsePreheat()) 103 | } else { 104 | opts = []Option{} 105 | } 106 | var dst test_BaseSliceType_ptr_Dst 107 | err = Copy(&dst, &local_test_BaseSliceType_ptr_Dst, opts...) 108 | assert.NoError(t, err, "Copy(&dst, &local_test_BaseSliceType_ptr_Dst, WithUsePreheat()") 109 | assert.Equal(t, local_test_BaseSliceType_ptr_Dst, dst) 110 | assert.NotEqual(t, unsafe.Pointer(local_test_BaseSliceType_ptr_Dst.A), unsafe.Pointer(dst.A), fmt.Sprintf("local_test_BaseSliceType_ptr_Dst.A: %p, dst.A: %p", local_test_BaseSliceType_ptr_Dst.A, dst.A)) 111 | assert.Equal(t, *local_test_BaseSliceType_ptr_Dst.A, *dst.A) 112 | } 113 | } 114 | 115 | func Test_Ptr_BaseSliceType1_2(t *testing.T) { 116 | err := Preheat(&test_BaseSliceType_ptr_Dst{}, &test_BaseSliceType_ptr_Dst{}) 117 | assert.NoError(t, err) 118 | 119 | var opts []Option 120 | for _, b := range []bool{true, false} { 121 | if b { 122 | opts = append(opts, WithUsePreheat()) 123 | } else { 124 | opts = []Option{} 125 | } 126 | var dst test_BaseSliceType_ptr_Dst 127 | err = Copy(&dst, &local_test_BaseSliceType_ptr_Dst, opts...) 128 | assert.NoError(t, err) 129 | assert.Equal(t, local_test_BaseSliceType_ptr_Dst, dst) 130 | assert.NotEqual(t, unsafe.Pointer(local_test_BaseSliceType_ptr_Dst.A), unsafe.Pointer(dst.A)) 131 | assert.Equal(t, *local_test_BaseSliceType_ptr_Dst.A, *dst.A) 132 | } 133 | } 134 | 135 | func Test_Ptr_BaseSliceType2(t *testing.T) { 136 | err := Preheat(&test_BaseSliceType_ptr_Src{}, &test_BaseSliceType_ptr_Dst{}) 137 | assert.NoError(t, err) 138 | // Preheat(&test_BaseSliceType_ptr_Dst{}, &test_BaseSliceType_ptr_Src{}) 139 | 140 | var opts []Option 141 | for _, b := range []bool{true, false} { 142 | if b { 143 | opts = append(opts, WithUsePreheat()) 144 | } else { 145 | opts = []Option{} 146 | } 147 | var dst test_BaseSliceType_ptr_Src 148 | err = Copy(&dst, &local_test_BaseSliceType_ptr_Dst, opts...) 149 | assert.NoError(t, err) 150 | assert.Equal(t, local_test_BaseSliceType_ptr_Src, dst) 151 | } 152 | } 153 | 154 | func Test_Ptr_BaseSliceType3(t *testing.T) { 155 | err := Preheat(&test_BaseSliceType_ptr_Dst{}, &test_BaseSliceType_ptr_Src{}) 156 | assert.NoError(t, err) 157 | 158 | var opts []Option 159 | for _, b := range []bool{true, false} { 160 | if b { 161 | opts = append(opts, WithUsePreheat()) 162 | } else { 163 | opts = []Option{} 164 | } 165 | 166 | var dst test_BaseSliceType_ptr_Dst 167 | err = Copy(&dst, &local_test_BaseSliceType_ptr_Src, opts...) 168 | assert.NoError(t, err) 169 | fmt.Printf("dst: %+v\n", dst) 170 | fmt.Printf(".A %p\n", dst.A) 171 | assert.Equal(t, local_test_BaseSliceType_ptr_Dst, dst) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /pcopy_ptr_basetype_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "testing" 7 | "unsafe" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | // 测试基础类型的指针 13 | type test_BaseType_ptr_Dst struct { 14 | A *int 15 | B *int8 16 | C *int16 17 | D *int32 18 | E *int64 19 | F *uint 20 | G *uint8 21 | H *uint16 22 | I *uint32 23 | J *uint64 24 | K *uintptr 25 | L *float32 26 | M *float64 27 | N *complex64 28 | O *complex128 29 | P *bool 30 | Q *string 31 | // R *interface{} TODO 32 | } 33 | 34 | type test_BaseType_ptr_Src struct { 35 | A int 36 | B int8 37 | C int16 38 | D int32 39 | E int64 40 | F uint 41 | G uint8 42 | H uint16 43 | I uint32 44 | J uint64 45 | K uintptr 46 | L float32 47 | M float64 48 | N complex64 49 | O complex128 50 | P bool 51 | Q string 52 | // R interface{} 53 | } 54 | 55 | func newDef[T any](t T) (rv *T) { 56 | return &t 57 | } 58 | 59 | var local_test_BaseType_ptr_Dst = test_BaseType_ptr_Dst{ 60 | A: newDef(1), 61 | B: newDef(int8(2)), 62 | C: newDef(int16(3)), 63 | D: newDef(int32(4)), 64 | E: newDef(int64(5)), 65 | F: newDef(uint(6)), 66 | G: newDef(uint8(7)), 67 | H: newDef(uint16(8)), 68 | I: newDef(uint32(9)), 69 | J: newDef(uint64(10)), 70 | K: newDef(uintptr(11)), 71 | L: newDef(float32(12.12)), 72 | M: newDef(float64(13.13)), 73 | N: newDef(complex64(14.14)), 74 | O: newDef(complex128(15.15)), 75 | P: newDef(true), 76 | Q: newDef("hello"), 77 | // R: newDef("world"), 78 | } 79 | 80 | var local_test_BaseType_ptr_Src = test_BaseType_ptr_Src{ 81 | A: 1, 82 | B: int8(2), 83 | C: int16(3), 84 | D: int32(4), 85 | E: int64(5), 86 | F: uint(6), 87 | G: uint8(7), 88 | H: uint16(8), 89 | I: uint32(9), 90 | J: uint64(10), 91 | K: uintptr(11), 92 | L: float32(12.12), 93 | M: float64(13.13), 94 | N: complex64(14.14), 95 | O: complex128(15.15), 96 | P: true, 97 | Q: "hello", 98 | // R: "world", 99 | } 100 | 101 | func Test_Ptr_BaseType1_1(t *testing.T) { 102 | err := Preheat(&test_BaseType_ptr_Dst{}, &local_test_BaseType_ptr_Dst) 103 | assert.NoError(t, err, "Preheat(&test_BaseType_ptr_Dst{}, &local_test_BaseType_ptr_Dst)") 104 | 105 | var opts []Option 106 | for _, b := range []bool{true, false} { 107 | if b { 108 | opts = append(opts, WithUsePreheat()) 109 | } else { 110 | opts = []Option{} 111 | } 112 | var dst test_BaseType_ptr_Dst 113 | err = Copy(&dst, &local_test_BaseType_ptr_Dst, opts...) 114 | assert.NoError(t, err, "Copy(&dst, &local_test_BaseType_ptr_Dst, WithUsePreheat()") 115 | assert.Equal(t, local_test_BaseType_ptr_Dst, dst) 116 | assert.NotEqual(t, unsafe.Pointer(local_test_BaseType_ptr_Dst.A), unsafe.Pointer(dst.A), fmt.Sprintf("local_test_BaseType_ptr_Dst.A: %p, dst.A: %p", local_test_BaseType_ptr_Dst.A, dst.A)) 117 | assert.Equal(t, *local_test_BaseType_ptr_Dst.A, *dst.A) 118 | } 119 | } 120 | 121 | func Test_Ptr_BaseType1_2(t *testing.T) { 122 | err := Preheat(&test_BaseType_ptr_Dst{}, &test_BaseType_ptr_Dst{}) 123 | assert.NoError(t, err) 124 | 125 | var opts []Option 126 | for _, b := range []bool{true, false} { 127 | if b { 128 | opts = append(opts, WithUsePreheat()) 129 | } else { 130 | opts = []Option{} 131 | } 132 | var dst test_BaseType_ptr_Dst 133 | err = Copy(&dst, &local_test_BaseType_ptr_Dst, opts...) 134 | assert.NoError(t, err) 135 | assert.Equal(t, local_test_BaseType_ptr_Dst, dst) 136 | assert.NotEqual(t, unsafe.Pointer(local_test_BaseType_ptr_Dst.A), unsafe.Pointer(dst.A)) 137 | assert.Equal(t, *local_test_BaseType_ptr_Dst.A, *dst.A) 138 | } 139 | } 140 | 141 | func Test_Ptr_BaseType2(t *testing.T) { 142 | err := Preheat(&test_BaseType_ptr_Src{}, &test_BaseType_ptr_Dst{}) 143 | assert.NoError(t, err) 144 | // Preheat(&test_BaseType_ptr_Dst{}, &test_BaseType_ptr_Src{}) 145 | 146 | var opts []Option 147 | for _, b := range []bool{true, false} { 148 | if b { 149 | opts = append(opts, WithUsePreheat()) 150 | } else { 151 | opts = []Option{} 152 | } 153 | var dst test_BaseType_ptr_Src 154 | err = Copy(&dst, &local_test_BaseType_ptr_Dst, opts...) 155 | assert.NoError(t, err) 156 | assert.Equal(t, local_test_BaseType_ptr_Src, dst) 157 | } 158 | } 159 | 160 | func Test_Ptr_BaseType3(t *testing.T) { 161 | err := Preheat(&test_BaseType_ptr_Dst{}, &test_BaseType_ptr_Src{}) 162 | assert.NoError(t, err) 163 | 164 | var opts []Option 165 | for _, b := range []bool{true, false} { 166 | if b { 167 | opts = append(opts, WithUsePreheat()) 168 | } else { 169 | opts = []Option{} 170 | } 171 | var dst test_BaseType_ptr_Dst 172 | err = Copy(&dst, &local_test_BaseType_ptr_Src, opts...) 173 | assert.NoError(t, err) 174 | fmt.Printf("dst: %+v\n", dst) 175 | fmt.Printf(".A %p\n", dst.A) 176 | assert.Equal(t, local_test_BaseType_ptr_Dst, dst) 177 | } 178 | } 179 | 180 | // 测试指针 181 | func Test_Ptr_OK(t *testing.T) { 182 | type interfaceTest struct { 183 | Iptr *int 184 | Fptr *float64 185 | } 186 | 187 | for _, tc := range []testCase{ 188 | func() testCase { 189 | d := interfaceTest{} 190 | src := interfaceTest{ 191 | Iptr: new(int), 192 | Fptr: new(float64), 193 | } 194 | 195 | *src.Iptr = 3 196 | *src.Fptr = 3.3 197 | Copy(&d, &src) 198 | return testCase{got: d, need: src} 199 | }(), 200 | } { 201 | assert.Equal(t, tc.need, tc.got) 202 | } 203 | } 204 | 205 | func Test_Ptr_OKCopyEx(t *testing.T) { 206 | type interfaceTest struct { 207 | Iptr *int 208 | Fptr *float64 209 | } 210 | 211 | for _, tc := range []testCase{ 212 | func() testCase { 213 | d := interfaceTest{} 214 | src := interfaceTest{ 215 | Iptr: new(int), 216 | Fptr: new(float64), 217 | } 218 | 219 | *src.Iptr = 3 220 | *src.Fptr = 3.3 221 | err := Copy(&d, &src) 222 | assert.NoError(t, err) 223 | return testCase{got: d, need: src} 224 | }(), 225 | } { 226 | assert.Equal(t, tc.need, tc.got) 227 | } 228 | } 229 | 230 | // 测试指针特殊情况 231 | // 只要不崩溃就是对的 232 | func Test_Ptr_Special(t *testing.T) { 233 | for _, tc := range []testCase{ 234 | // dst 是空指针 235 | func() testCase { 236 | Copy((*int)(nil), new(int)) 237 | return testCase{} 238 | }(), 239 | 240 | // dst, src是不同类型 241 | func() testCase { 242 | s := "hello" 243 | Copy(&s, new(int)) 244 | return testCase{} 245 | }(), 246 | // dst 是双指针 247 | func() testCase { 248 | n := 3 249 | dst := 0 250 | dstPtr := &dst 251 | dstPtrPtr := &dstPtr 252 | err := Copy(dstPtrPtr, &n) 253 | assert.NoError(t, err) 254 | return testCase{} 255 | }(), 256 | } { 257 | assert.Equal(t, tc.need, tc.got) 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /pcopy_ptr_double_basetype_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "testing" 7 | "unsafe" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | // 测试基础类型的指针 13 | type test_BaseType_DoublePtr_Dst struct { 14 | A **int 15 | B **int8 16 | C **int16 17 | D **int32 18 | E **int64 19 | F **uint 20 | G **uint8 21 | H **uint16 22 | I **uint32 23 | J **uint64 24 | K **uintptr 25 | L **float32 26 | M **float64 27 | N **complex64 28 | O **complex128 29 | P **bool 30 | Q **string 31 | // R *interface{} TODO 32 | } 33 | 34 | type test_BaseType_DoublePtr_Src struct { 35 | A int 36 | B int8 37 | C int16 38 | D int32 39 | E int64 40 | F uint 41 | G uint8 42 | H uint16 43 | I uint32 44 | J uint64 45 | K uintptr 46 | L float32 47 | M float64 48 | N complex64 49 | O complex128 50 | P bool 51 | Q string 52 | // R interface{} 53 | } 54 | 55 | func newDoubleDef[T any](t T) (rv **T) { 56 | p := &t 57 | return &p 58 | } 59 | 60 | var local_test_BaseType_DoublePtr_Dst = test_BaseType_DoublePtr_Dst{ 61 | A: newDoubleDef(1), 62 | B: newDoubleDef(int8(2)), 63 | C: newDoubleDef(int16(3)), 64 | D: newDoubleDef(int32(4)), 65 | E: newDoubleDef(int64(5)), 66 | F: newDoubleDef(uint(6)), 67 | G: newDoubleDef(uint8(7)), 68 | H: newDoubleDef(uint16(8)), 69 | I: newDoubleDef(uint32(9)), 70 | J: newDoubleDef(uint64(10)), 71 | K: newDoubleDef(uintptr(11)), 72 | L: newDoubleDef(float32(12.12)), 73 | M: newDoubleDef(float64(13.13)), 74 | N: newDoubleDef(complex64(14.14)), 75 | O: newDoubleDef(complex128(15.15)), 76 | P: newDoubleDef(true), 77 | Q: newDoubleDef("hello"), 78 | // R: newDef("world"), 79 | } 80 | 81 | var local_test_BaseType_DoublePtr_Src = test_BaseType_DoublePtr_Src{ 82 | A: 1, 83 | B: int8(2), 84 | C: int16(3), 85 | D: int32(4), 86 | E: int64(5), 87 | F: uint(6), 88 | G: uint8(7), 89 | H: uint16(8), 90 | I: uint32(9), 91 | J: uint64(10), 92 | K: uintptr(11), 93 | L: float32(12.12), 94 | M: float64(13.13), 95 | N: complex64(14.14), 96 | O: complex128(15.15), 97 | P: true, 98 | Q: "hello", 99 | // R: "world", 100 | } 101 | 102 | func Test_DoublePtr_BaseType1_1(t *testing.T) { 103 | err := Preheat(&test_BaseType_DoublePtr_Dst{}, &local_test_BaseType_DoublePtr_Dst) 104 | assert.NoError(t, err, "Preheat(&test_BaseType_DoublePtr_Dst{}, &local_test_BaseType_DoublePtr_Dst)") 105 | 106 | var opts []Option 107 | for _, b := range []bool{true, false} { 108 | if b { 109 | opts = append(opts, WithUsePreheat()) 110 | } else { 111 | opts = []Option{} 112 | } 113 | var dst test_BaseType_DoublePtr_Dst 114 | err = Copy(&dst, &local_test_BaseType_DoublePtr_Dst, opts...) 115 | assert.NoError(t, err, "Copy(&dst, &local_test_BaseType_DoublePtr_Dst, WithUsePreheat()") 116 | assert.Equal(t, local_test_BaseType_DoublePtr_Dst, dst) 117 | assert.NotEqual(t, unsafe.Pointer(local_test_BaseType_DoublePtr_Dst.A), unsafe.Pointer(dst.A), fmt.Sprintf("local_test_BaseType_DoublePtr_Dst.A: %p, dst.A: %p", local_test_BaseType_DoublePtr_Dst.A, dst.A)) 118 | assert.Equal(t, *local_test_BaseType_DoublePtr_Dst.A, *dst.A) 119 | } 120 | } 121 | 122 | func Test_DoublePtr_BaseType1_2(t *testing.T) { 123 | err := Preheat(&test_BaseType_DoublePtr_Dst{}, &test_BaseType_DoublePtr_Dst{}) 124 | assert.NoError(t, err) 125 | 126 | var opts []Option 127 | for _, b := range []bool{true, false} { 128 | if b { 129 | opts = append(opts, WithUsePreheat()) 130 | } else { 131 | opts = []Option{} 132 | } 133 | var dst test_BaseType_DoublePtr_Dst 134 | err = Copy(&dst, &local_test_BaseType_DoublePtr_Dst, opts...) 135 | assert.NoError(t, err) 136 | assert.Equal(t, local_test_BaseType_DoublePtr_Dst, dst) 137 | assert.NotEqual(t, unsafe.Pointer(local_test_BaseType_DoublePtr_Dst.A), unsafe.Pointer(dst.A)) 138 | assert.Equal(t, *local_test_BaseType_DoublePtr_Dst.A, *dst.A) 139 | } 140 | } 141 | 142 | func Test_DoublePtr_BaseType2(t *testing.T) { 143 | err := Preheat(&test_BaseType_DoublePtr_Src{}, &test_BaseType_DoublePtr_Dst{}) 144 | assert.NoError(t, err) 145 | // Preheat(&test_BaseType_DoublePtr_Dst{}, &test_BaseType_DoublePtr_Src{}) 146 | 147 | var opts []Option 148 | for _, b := range []bool{true, false} { 149 | if b { 150 | opts = append(opts, WithUsePreheat()) 151 | } else { 152 | opts = []Option{} 153 | } 154 | var dst test_BaseType_DoublePtr_Src 155 | err = Copy(&dst, &local_test_BaseType_DoublePtr_Dst, opts...) 156 | assert.NoError(t, err) 157 | assert.Equal(t, local_test_BaseType_DoublePtr_Src, dst) 158 | } 159 | } 160 | 161 | func Test_DoublePtr_BaseType3(t *testing.T) { 162 | err := Preheat(&test_BaseType_DoublePtr_Dst{}, &test_BaseType_DoublePtr_Src{}) 163 | assert.NoError(t, err) 164 | 165 | var opts []Option 166 | for _, b := range []bool{true, false} { 167 | if b { 168 | opts = append(opts, WithUsePreheat()) 169 | } else { 170 | opts = []Option{} 171 | } 172 | var dst test_BaseType_DoublePtr_Dst 173 | err = Copy(&dst, &local_test_BaseType_DoublePtr_Src, opts...) 174 | assert.NoError(t, err) 175 | fmt.Printf("dst: %+v\n", dst) 176 | fmt.Printf(".A %p\n", dst.A) 177 | assert.Equal(t, local_test_BaseType_DoublePtr_Dst, dst) 178 | } 179 | } 180 | 181 | type Msg struct { 182 | FromUid int32 `protobuf:"varint,1,opt,name=from_uid,json=fromUid,proto3" json:"from_uid,omitempty"` 183 | ToUid int32 `protobuf:"varint,2,opt,name=to_uid,json=toUid,proto3" json:"to_uid,omitempty"` 184 | Msg string `protobuf:"bytes,3,opt,name=msg,proto3" json:"msg,omitempty"` 185 | } 186 | 187 | // 测试src是双指针的情况 188 | // 为了提升Pcopy库的兼容性, src是双指针, dst是单指针, 也可以正常拷贝 189 | func Test_Src_DoublePtr_Test(t *testing.T) { 190 | m1 := Msg{FromUid: 1, ToUid: 2, Msg: "1to2"} 191 | 192 | var opts []Option 193 | for _, b := range []bool{true, false} { 194 | if b { 195 | opts = append(opts, WithUsePreheat()) 196 | } else { 197 | opts = []Option{} 198 | } 199 | // 单指针 200 | m1Ptr := &m1 201 | 202 | var m2 Msg 203 | // dst(m2) 是单指针 204 | // src(m1Ptr) 是双指针 205 | err := Copy(&m2, &m1Ptr, opts...) 206 | 207 | assert.NoError(t, err) 208 | assert.Equal(t, m1, m2) 209 | } 210 | } 211 | 212 | func Test_Src_DoublePtr_Test_CopyEx(t *testing.T) { 213 | m1 := Msg{FromUid: 1, ToUid: 2, Msg: "1to2"} 214 | 215 | // 单指针 216 | m1Ptr := &m1 217 | 218 | var opts []Option 219 | for _, b := range []bool{true, false} { 220 | if b { 221 | opts = append(opts, WithUsePreheat()) 222 | } else { 223 | opts = []Option{} 224 | } 225 | var m2 Msg 226 | // dst(m2) 是单指针 227 | // src(m1Ptr) 是双指针 228 | err := Copy(&m2, &m1Ptr, opts...) 229 | 230 | assert.NoError(t, err) 231 | assert.Equal(t, m1, m2) 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /pcopy_ptr_struct_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "testing" 7 | "unsafe" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | type test_BaseStructType_ptr_Dst_A struct { 13 | A int 14 | B int8 15 | C int16 16 | D int32 17 | } 18 | 19 | type test_BaseStructType_ptr_Dst_B struct { 20 | A int 21 | B int8 22 | C int16 23 | D int32 24 | } 25 | 26 | // 测试基础类型的指针 27 | type test_BaseStructType_ptr_Dst struct { 28 | A *test_BaseStructType_ptr_Dst_A 29 | B *test_BaseStructType_ptr_Dst_B 30 | } 31 | 32 | type test_BaseStructType_ptr_Src struct { 33 | A test_BaseStructType_ptr_Dst_A 34 | B test_BaseStructType_ptr_Dst_B 35 | } 36 | 37 | var local_test_BaseStructType_ptr_Dst = test_BaseStructType_ptr_Dst{ 38 | A: &test_BaseStructType_ptr_Dst_A{ 39 | A: 1, 40 | B: 2, 41 | C: 3, 42 | D: 4, 43 | }, 44 | B: &test_BaseStructType_ptr_Dst_B{ 45 | A: 5, 46 | B: 6, 47 | C: 7, 48 | D: 8, 49 | }, 50 | } 51 | 52 | var local_test_BaseStructType_ptr_Src = test_BaseStructType_ptr_Src{ 53 | A: test_BaseStructType_ptr_Dst_A{ 54 | A: 1, 55 | B: 2, 56 | C: 3, 57 | D: 4, 58 | }, 59 | B: test_BaseStructType_ptr_Dst_B{ 60 | A: 5, 61 | B: 6, 62 | C: 7, 63 | D: 8, 64 | }, 65 | } 66 | 67 | func Test_Ptr_BaseStructType1_1(t *testing.T) { 68 | err := Preheat(&test_BaseStructType_ptr_Dst{}, &local_test_BaseStructType_ptr_Dst) 69 | assert.NoError(t, err, "Preheat(&test_BaseStructType_ptr_Dst{}, &local_test_BaseStructType_ptr_Dst)") 70 | 71 | var opts []Option 72 | for _, b := range []bool{true, false} { 73 | if b { 74 | opts = append(opts, WithUsePreheat()) 75 | } else { 76 | opts = []Option{} 77 | } 78 | var dst test_BaseStructType_ptr_Dst 79 | err = Copy(&dst, &local_test_BaseStructType_ptr_Dst, opts...) 80 | assert.NoError(t, err, "Copy(&dst, &local_test_BaseStructType_ptr_Dst, WithUsePreheat()") 81 | assert.Equal(t, local_test_BaseStructType_ptr_Dst, dst) 82 | assert.NotEqual(t, unsafe.Pointer(local_test_BaseStructType_ptr_Dst.A), unsafe.Pointer(dst.A), fmt.Sprintf("local_test_BaseStructType_ptr_Dst.A: %p, dst.A: %p", local_test_BaseStructType_ptr_Dst.A, dst.A)) 83 | assert.Equal(t, *local_test_BaseStructType_ptr_Dst.A, *dst.A) 84 | } 85 | } 86 | 87 | func Test_Ptr_BaseStructType1_2(t *testing.T) { 88 | err := Preheat(&test_BaseStructType_ptr_Dst{}, &test_BaseStructType_ptr_Dst{}) 89 | assert.NoError(t, err) 90 | 91 | var opts []Option 92 | for _, b := range []bool{true, false} { 93 | if b { 94 | opts = append(opts, WithUsePreheat()) 95 | } else { 96 | opts = []Option{} 97 | } 98 | var dst test_BaseStructType_ptr_Dst 99 | err = Copy(&dst, &local_test_BaseStructType_ptr_Dst, opts...) 100 | assert.NoError(t, err) 101 | assert.Equal(t, local_test_BaseStructType_ptr_Dst, dst) 102 | assert.NotEqual(t, unsafe.Pointer(local_test_BaseStructType_ptr_Dst.A), unsafe.Pointer(dst.A)) 103 | assert.Equal(t, *local_test_BaseStructType_ptr_Dst.A, *dst.A) 104 | } 105 | } 106 | 107 | func Test_Ptr_BaseStructType2(t *testing.T) { 108 | err := Preheat(&test_BaseStructType_ptr_Src{}, &test_BaseStructType_ptr_Dst{}) 109 | assert.NoError(t, err) 110 | // Preheat(&test_BaseStructType_ptr_Dst{}, &test_BaseStructType_ptr_Src{}) 111 | 112 | var opts []Option 113 | for _, b := range []bool{true, false} { 114 | if b { 115 | opts = append(opts, WithUsePreheat()) 116 | } else { 117 | opts = []Option{} 118 | } 119 | var dst test_BaseStructType_ptr_Src 120 | err = Copy(&dst, &local_test_BaseStructType_ptr_Dst, opts...) 121 | assert.NoError(t, err) 122 | assert.Equal(t, local_test_BaseStructType_ptr_Src, dst) 123 | } 124 | } 125 | 126 | func Test_Ptr_BaseStructType3(t *testing.T) { 127 | err := Preheat(&test_BaseStructType_ptr_Dst{}, &test_BaseStructType_ptr_Src{}) 128 | assert.NoError(t, err) 129 | 130 | var opts []Option 131 | for _, b := range []bool{true, false} { 132 | if b { 133 | opts = append(opts, WithUsePreheat()) 134 | } else { 135 | opts = []Option{} 136 | } 137 | var dst test_BaseStructType_ptr_Dst 138 | err = Copy(&dst, &local_test_BaseStructType_ptr_Src, opts...) 139 | assert.NoError(t, err) 140 | fmt.Printf("dst: %+v\n", dst) 141 | fmt.Printf(".A %p\n", dst.A) 142 | assert.Equal(t, local_test_BaseStructType_ptr_Dst, dst) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /pcopy_readme_example_test.go: -------------------------------------------------------------------------------- 1 | package pcopy 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type dst struct { 11 | ID int 12 | Result string 13 | } 14 | 15 | type src struct { 16 | ID int 17 | Text string 18 | } 19 | 20 | func Test_ReadME_Struct_Example(t *testing.T) { 21 | d, s := dst{}, src{ID: 3} 22 | err := Preheat(&dst{}, &src{}) // 一对类型只要预热一次 23 | assert.Nil(t, err) 24 | err = Copy(&d, &s, WithUsePreheat()) 25 | assert.Nil(t, err) 26 | assert.Equal(t, d.ID, s.ID) 27 | } 28 | 29 | func Test_ReadME_Slice_Example2(t *testing.T) { 30 | i := []int{1, 2, 3, 4, 5, 6} 31 | var o []int 32 | 33 | err := Preheat(&o, &i) 34 | assert.NoError(t, err) 35 | err = Copy(&o, &i, WithUsePreheat()) 36 | assert.NoError(t, err) 37 | 38 | assert.Equal(t, i, o) 39 | fmt.Printf("%#v\n", o) 40 | } 41 | 42 | func Test_ReadMe_Map_Example3(t *testing.T) { 43 | i := map[string]int{ 44 | "cat": 100, 45 | "head": 10, 46 | "tr": 3, 47 | "tail": 44, 48 | } 49 | 50 | var o map[string]int 51 | err := Preheat(&o, &i) 52 | assert.NoError(t, err) 53 | err = Copy(&o, &i, WithUsePreheat()) 54 | assert.NoError(t, err) 55 | assert.Equal(t, i, o) 56 | fmt.Printf("%#v\n", o) 57 | } 58 | -------------------------------------------------------------------------------- /pcopy_slice_and_array_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "testing" 7 | "unsafe" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | type test_SliceWithStruct_Dst_Item1 struct { 13 | A int 14 | B int8 15 | C int16 16 | D int32 17 | E int64 18 | F uint 19 | G uint8 20 | H uint16 21 | I uint32 22 | J uint64 23 | K float32 24 | L float64 25 | M string 26 | N bool 27 | } 28 | 29 | type test_SliceWithStruct_Dst struct { 30 | A []test_SliceWithStruct_Dst_Item1 31 | B []int 32 | } 33 | 34 | type test_SliceWithStruct_Src test_SliceWithStruct_Dst 35 | 36 | var local_SliceWithStruct_Src = test_SliceWithStruct_Src{ 37 | A: []test_SliceWithStruct_Dst_Item1{ 38 | {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11.1, 12.2, "13", true}, 39 | {14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24.2, 25.3, "26", false}, 40 | {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11.1, 12.2, "12", true}, 41 | }, 42 | B: []int{1, 2, 3, 4, 5}, 43 | } 44 | 45 | func Test_SliceWithStruct(t *testing.T) { 46 | fmt.Printf("%d\n", unsafe.Sizeof(test_SliceWithStruct_Dst_Item1{})) 47 | for _, tc := range []test_SliceWithStruct_Src{ 48 | { 49 | A: []test_SliceWithStruct_Dst_Item1{ 50 | {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11.1, 12.2, "13", true}, 51 | {14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24.2, 25.3, "26", false}, 52 | }, 53 | B: []int{1, 2, 3, 4, 5}, 54 | }, 55 | } { 56 | var d test_SliceWithStruct_Dst 57 | Preheat(&d, &tc) 58 | d = test_SliceWithStruct_Dst{} 59 | fmt.Printf("Test_SliceWithStruct:d. %p tc.base %p \n", &d, &tc) 60 | fmt.Printf("Test_SliceWithStruct:d.A %p tc.A %p \n", &d.A, &tc.A) 61 | fmt.Printf("Test_SliceWithStruct:d.B %p tc.B %p \n", &d.B, &tc.B) 62 | Copy(&d, &tc, WithUsePreheat()) 63 | assert.Equal(t, tc, test_SliceWithStruct_Src(d)) 64 | } 65 | } 66 | 67 | // 测试slice拷贝到array 68 | func Test_SliceToArray(t *testing.T) { 69 | for _, tc := range []testCase{ 70 | func() testCase { 71 | type dst struct { 72 | A [3]int 73 | } 74 | 75 | type src struct { 76 | A []int 77 | } 78 | 79 | var d dst 80 | s := src{ 81 | A: []int{1, 2, 3, 4, 5}, 82 | } 83 | 84 | var need dst 85 | need.A = [3]int{1, 2, 3} 86 | 87 | Copy(&d, &s) 88 | return testCase{got: d, need: need} 89 | }(), 90 | } { 91 | assert.Equal(t, tc.need, tc.got) 92 | } 93 | } 94 | 95 | func Test_SliceToArrayEx(t *testing.T) { 96 | for _, tc := range []testCase{ 97 | func() testCase { 98 | type dst struct { 99 | A [3]int 100 | } 101 | 102 | type src struct { 103 | A []int 104 | } 105 | 106 | var d dst 107 | s := src{ 108 | A: []int{1, 2, 3, 4, 5}, 109 | } 110 | 111 | var need dst 112 | need.A = [3]int{1, 2, 3} 113 | 114 | err := Copy(&d, &s) 115 | assert.NoError(t, err) 116 | return testCase{got: d, need: need} 117 | }(), 118 | } { 119 | assert.Equal(t, tc.need, tc.got) 120 | } 121 | } 122 | 123 | // 测试array拷贝到slice 124 | func Test_ArrayToSlice(t *testing.T) { 125 | for _, tc := range []testCase{ 126 | func() testCase { 127 | type dst struct { 128 | A []int 129 | } 130 | 131 | type src struct { 132 | A [3]int 133 | } 134 | 135 | var d dst 136 | s := src{ 137 | A: [3]int{1, 2, 3}, 138 | } 139 | 140 | var need dst 141 | need.A = s.A[:] 142 | 143 | Copy(&d, &s) 144 | return testCase{got: d, need: need} 145 | }(), 146 | } { 147 | assert.Equal(t, tc.need, tc.got) 148 | } 149 | } 150 | 151 | // 测试特殊情况 152 | func Test_ArraySlice_Special(t *testing.T) { 153 | // 不崩溃就是对的 154 | for _, tc := range []testCase{ 155 | func() testCase { 156 | type dst struct { 157 | A int 158 | } 159 | 160 | type src struct { 161 | A [3]int 162 | } 163 | 164 | var d dst 165 | s := src{ 166 | A: [3]int{1, 2, 3}, 167 | } 168 | Copy(&d, &s) 169 | return testCase{} 170 | }(), 171 | 172 | func() testCase { 173 | a1 := []int{1, 2, 3, 4, 5, 6} 174 | a2 := []string{} 175 | Copy(&a2, &a1) 176 | return testCase{} 177 | }(), 178 | } { 179 | assert.Equal(t, tc.need, tc.got) 180 | } 181 | } 182 | 183 | // 测试 184 | func Test_ArraySlice_SpecialCopyEx(t *testing.T) { 185 | // 不崩溃就是对的 186 | for _, tc := range []testCase{ 187 | func() testCase { 188 | type dst struct { 189 | A int 190 | } 191 | 192 | type src struct { 193 | A [3]int 194 | } 195 | 196 | var d dst 197 | s := src{ 198 | A: [3]int{1, 2, 3}, 199 | } 200 | Copy(&d, &s) 201 | return testCase{} 202 | }(), 203 | 204 | func() testCase { 205 | a1 := []int{1, 2, 3, 4, 5, 6} 206 | a2 := []string{} 207 | Copy(&a2, &a1) 208 | return testCase{} 209 | }(), 210 | } { 211 | assert.Equal(t, tc.need, tc.got) 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /pcopy_slice_basetype_ptr_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "testing" 7 | "unsafe" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | type slicePtrTestSrc struct { 13 | A []*int 14 | B []*int8 15 | C []*int16 16 | D []*int32 17 | E []*int64 18 | F []*uint 19 | G []*uint8 20 | H []*uint16 21 | I []*uint32 22 | J []*uint64 23 | K []*uintptr 24 | L []*float32 25 | M []*float64 26 | N []*complex64 27 | O []*complex128 28 | P []*bool 29 | Q []*string 30 | // R []*interface{} 31 | S []*byte 32 | T []*rune 33 | } 34 | 35 | type slicePtrTestDst struct { 36 | A []int 37 | B []int8 38 | C []int16 39 | D []int32 40 | E []int64 41 | F []uint 42 | G []uint8 43 | H []uint16 44 | I []uint32 45 | J []uint64 46 | K []uintptr 47 | L []float32 48 | M []float64 49 | N []complex64 50 | O []complex128 51 | P []bool 52 | Q []string 53 | // R []interface{} 54 | S []byte 55 | T []rune 56 | } 57 | 58 | var ( 59 | local_int_1 = 1 60 | local_int_2 = 2 61 | local_int_3 = 3 62 | ) 63 | 64 | var ( 65 | local_string_1 = "hello" 66 | local_string_2 = "world" 67 | local_string_3 = "!" 68 | ) 69 | 70 | var ( 71 | local_Complex64_1 = complex64(1) 72 | local_Complex64_2 = complex64(2) 73 | local_Complex64_3 = complex64(3) 74 | local_Complex128_1 = complex128(1) 75 | local_Complex128_2 = complex128(2) 76 | local_Complex128_3 = complex128(3) 77 | ) 78 | 79 | var ( 80 | local_bool_1 = true 81 | local_bool_2 = false 82 | local_bool_3 = true 83 | ) 84 | 85 | var local_SlicePtrTestSrc = slicePtrTestSrc{ 86 | A: []*int{&local_int_1, &local_int_2, &local_int_3}, 87 | B: []*int8{(*int8)(unsafe.Pointer(&local_int_1)), (*int8)(unsafe.Pointer(&local_int_2)), (*int8)(unsafe.Pointer(&local_int_3))}, 88 | C: []*int16{(*int16)(unsafe.Pointer(&local_int_1)), (*int16)(unsafe.Pointer(&local_int_2)), (*int16)(unsafe.Pointer(&local_int_3))}, 89 | D: []*int32{(*int32)(unsafe.Pointer(&local_int_1)), (*int32)(unsafe.Pointer(&local_int_2)), (*int32)(unsafe.Pointer(&local_int_3))}, 90 | E: []*int64{(*int64)(unsafe.Pointer(&local_int_1)), (*int64)(unsafe.Pointer(&local_int_2)), (*int64)(unsafe.Pointer(&local_int_3))}, 91 | F: []*uint{(*uint)(unsafe.Pointer(&local_int_1)), (*uint)(unsafe.Pointer(&local_int_2)), (*uint)(unsafe.Pointer(&local_int_3))}, 92 | G: []*uint8{(*uint8)(unsafe.Pointer(&local_int_1)), (*uint8)(unsafe.Pointer(&local_int_2)), (*uint8)(unsafe.Pointer(&local_int_3))}, 93 | H: []*uint16{(*uint16)(unsafe.Pointer(&local_int_1)), (*uint16)(unsafe.Pointer(&local_int_2)), (*uint16)(unsafe.Pointer(&local_int_3))}, 94 | I: []*uint32{(*uint32)(unsafe.Pointer(&local_int_1)), (*uint32)(unsafe.Pointer(&local_int_2)), (*uint32)(unsafe.Pointer(&local_int_3))}, 95 | J: []*uint64{(*uint64)(unsafe.Pointer(&local_int_1)), (*uint64)(unsafe.Pointer(&local_int_2)), (*uint64)(unsafe.Pointer(&local_int_3))}, 96 | K: []*uintptr{(*uintptr)(unsafe.Pointer(&local_int_1)), (*uintptr)(unsafe.Pointer(&local_int_2)), (*uintptr)(unsafe.Pointer(&local_int_3))}, 97 | L: []*float32{(*float32)(unsafe.Pointer(&local_int_1)), (*float32)(unsafe.Pointer(&local_int_2)), (*float32)(unsafe.Pointer(&local_int_3))}, 98 | M: []*float64{(*float64)(unsafe.Pointer(&local_int_1)), (*float64)(unsafe.Pointer(&local_int_2)), (*float64)(unsafe.Pointer(&local_int_3))}, 99 | N: []*complex64{(*complex64)(unsafe.Pointer(&local_int_1)), (*complex64)(unsafe.Pointer(&local_int_2)), (*complex64)(unsafe.Pointer(&local_int_3))}, 100 | O: []*complex128{(*complex128)(unsafe.Pointer(&local_int_1)), (*complex128)(unsafe.Pointer(&local_int_2)), (*complex128)(unsafe.Pointer(&local_int_3))}, 101 | P: []*bool{(*bool)(unsafe.Pointer(&local_bool_1)), (*bool)(unsafe.Pointer(&local_bool_2)), (*bool)(unsafe.Pointer(&local_bool_3))}, 102 | Q: []*string{(*string)(unsafe.Pointer(&local_string_1)), (*string)(unsafe.Pointer(&local_string_2)), (*string)(unsafe.Pointer(&local_string_3))}, 103 | // R: []*interface{}{(*interface{})(unsafe.Pointer(&local_int_1)), (*interface{})(unsafe.Pointer(&local_int_2)), (*interface{})(unsafe.Pointer(&local_int_3))}, 104 | S: []*byte{(*byte)(unsafe.Pointer(&local_int_1)), (*byte)(unsafe.Pointer(&local_int_2)), (*byte)(unsafe.Pointer(&local_int_3))}, 105 | T: []*rune{(*rune)(unsafe.Pointer(&local_int_1)), (*rune)(unsafe.Pointer(&local_int_2)), (*rune)(unsafe.Pointer(&local_int_3))}, 106 | } 107 | 108 | var local_SlicePtrTestDst = slicePtrTestDst{ 109 | A: []int{local_int_1, local_int_2, local_int_3}, 110 | B: []int8{int8(local_int_1), int8(local_int_2), int8(local_int_3)}, 111 | C: []int16{int16(local_int_1), int16(local_int_2), int16(local_int_3)}, 112 | D: []int32{int32(local_int_1), int32(local_int_2), int32(local_int_3)}, 113 | E: []int64{int64(local_int_1), int64(local_int_2), int64(local_int_3)}, 114 | F: []uint{uint(local_int_1), uint(local_int_2), uint(local_int_3)}, 115 | G: []uint8{uint8(local_int_1), uint8(local_int_2), uint8(local_int_3)}, 116 | H: []uint16{uint16(local_int_1), uint16(local_int_2), uint16(local_int_3)}, 117 | I: []uint32{uint32(local_int_1), uint32(local_int_2), uint32(local_int_3)}, 118 | J: []uint64{uint64(local_int_1), uint64(local_int_2), uint64(local_int_3)}, 119 | K: []uintptr{uintptr(local_int_1), uintptr(local_int_2), uintptr(local_int_3)}, 120 | L: []float32{float32(local_int_1), float32(local_int_2), float32(local_int_3)}, 121 | M: []float64{float64(local_int_1), float64(local_int_2), float64(local_int_3)}, 122 | N: []complex64{local_Complex64_1, local_Complex64_2, local_Complex64_3}, 123 | O: []complex128{local_Complex128_1, local_Complex128_2, local_Complex128_3}, 124 | P: []bool{local_int_1 != 0, local_int_2 != 0, local_int_3 != 0}, 125 | Q: []string{local_string_1, local_string_2, local_string_3}, 126 | // R: []interface{}{local_int_1, local_int_2, local_int_3}, 127 | S: []byte{byte(local_int_1), byte(local_int_2), byte(local_int_3)}, 128 | T: []rune{rune(local_int_1), rune(local_int_2), rune(local_int_3)}, 129 | } 130 | 131 | func Test_Slice_BaseTypePtr_DstPtr_SrcPtr(t *testing.T) { 132 | err := Preheat(&slicePtrTestSrc{}, &slicePtrTestSrc{}) 133 | assert.NoError(t, err) 134 | 135 | var opts []Option 136 | for _, b := range []bool{true, false} { 137 | if b { 138 | opts = append(opts, WithUsePreheat()) 139 | } else { 140 | opts = []Option{} 141 | } 142 | 143 | var dst slicePtrTestSrc 144 | err = Copy(&dst, &local_SlicePtrTestSrc, opts...) 145 | assert.NoError(t, err) 146 | fmt.Printf("dst: %+v\n", dst) 147 | fmt.Printf(".A %#v\n", dst.A) 148 | assert.Equal(t, dst.A, local_SlicePtrTestSrc.A) 149 | assert.Equal(t, dst.A[0], local_SlicePtrTestSrc.A[0]) 150 | assert.Equal(t, dst.B, local_SlicePtrTestSrc.B) 151 | assert.Equal(t, dst.C, local_SlicePtrTestSrc.C) 152 | assert.Equal(t, dst.D, local_SlicePtrTestSrc.D) 153 | assert.Equal(t, dst.E, local_SlicePtrTestSrc.E) 154 | assert.Equal(t, dst.F, local_SlicePtrTestSrc.F) 155 | assert.Equal(t, dst.G, local_SlicePtrTestSrc.G) 156 | assert.Equal(t, dst.H, local_SlicePtrTestSrc.H) 157 | assert.Equal(t, dst.I, local_SlicePtrTestSrc.I) 158 | assert.Equal(t, dst.J, local_SlicePtrTestSrc.J) 159 | assert.Equal(t, dst.K, local_SlicePtrTestSrc.K) 160 | assert.Equal(t, dst.L, local_SlicePtrTestSrc.L) 161 | assert.Equal(t, dst.M, local_SlicePtrTestSrc.M) 162 | assert.Equal(t, dst.N, local_SlicePtrTestSrc.N) 163 | assert.Equal(t, dst.O, local_SlicePtrTestSrc.O) 164 | assert.Equal(t, dst.P, local_SlicePtrTestSrc.P) 165 | assert.Equal(t, dst.Q, local_SlicePtrTestSrc.Q) 166 | // assert.Equal(t, dst.R, local_SlicePtrTestSrc.R) 167 | assert.Equal(t, dst.S, local_SlicePtrTestSrc.S) 168 | assert.Equal(t, dst.T, local_SlicePtrTestSrc.T) 169 | 170 | } 171 | } 172 | 173 | func Test_Slice_BaseTypePtr_Dst_SrcPtr(t *testing.T) { 174 | err := Preheat(&slicePtrTestDst{}, &slicePtrTestSrc{}) 175 | fmt.Printf("Preheat ok\n") 176 | assert.NoError(t, err) 177 | 178 | var opts []Option 179 | for _, b := range []bool{true, false} { 180 | if b { 181 | opts = append(opts, WithUsePreheat()) 182 | } else { 183 | opts = []Option{} 184 | } 185 | 186 | var dst slicePtrTestDst 187 | err = Copy(&dst, &local_SlicePtrTestSrc, opts...) 188 | assert.NoError(t, err) 189 | fmt.Printf("dst: %+v\n", dst) 190 | fmt.Printf(".A %#v\n", dst.A) 191 | for k := range dst.A { 192 | assert.Equal(t, dst.A[k], *local_SlicePtrTestSrc.A[k]) 193 | } 194 | 195 | for k := range dst.B { 196 | assert.Equal(t, dst.B[k], *local_SlicePtrTestSrc.B[k]) 197 | } 198 | 199 | for k := range dst.C { 200 | assert.Equal(t, dst.C[k], *local_SlicePtrTestSrc.C[k]) 201 | } 202 | 203 | for k := range dst.D { 204 | assert.Equal(t, dst.D[k], *local_SlicePtrTestSrc.D[k]) 205 | } 206 | 207 | for k := range dst.E { 208 | assert.Equal(t, dst.E[k], *local_SlicePtrTestSrc.E[k]) 209 | } 210 | 211 | for k := range dst.F { 212 | assert.Equal(t, dst.F[k], *local_SlicePtrTestSrc.F[k]) 213 | } 214 | 215 | for k := range dst.G { 216 | assert.Equal(t, dst.G[k], *local_SlicePtrTestSrc.G[k]) 217 | } 218 | 219 | for k := range dst.H { 220 | assert.Equal(t, dst.H[k], *local_SlicePtrTestSrc.H[k]) 221 | } 222 | 223 | for k := range dst.I { 224 | assert.Equal(t, dst.I[k], *local_SlicePtrTestSrc.I[k]) 225 | } 226 | 227 | for k := range dst.J { 228 | assert.Equal(t, dst.J[k], *local_SlicePtrTestSrc.J[k]) 229 | } 230 | 231 | for k := range dst.K { 232 | assert.Equal(t, dst.K[k], *local_SlicePtrTestSrc.K[k]) 233 | } 234 | 235 | for k := range dst.L { 236 | assert.Equal(t, dst.L[k], *local_SlicePtrTestSrc.L[k]) 237 | } 238 | 239 | for k := range dst.M { 240 | assert.Equal(t, dst.M[k], *local_SlicePtrTestSrc.M[k]) 241 | } 242 | 243 | for k := range dst.N { 244 | assert.Equal(t, dst.N[k], *local_SlicePtrTestSrc.N[k]) 245 | } 246 | 247 | for k := range dst.O { 248 | assert.Equal(t, dst.O[k], *local_SlicePtrTestSrc.O[k]) 249 | } 250 | 251 | for k := range dst.P { 252 | assert.Equal(t, dst.P[k], *local_SlicePtrTestSrc.P[k]) 253 | } 254 | 255 | for k := range dst.Q { 256 | assert.Equal(t, dst.Q[k], *local_SlicePtrTestSrc.Q[k]) 257 | } 258 | 259 | // for k := range dst.R { 260 | // assert.Equal(t, dst.R[k], *local_SlicePtrTestSrc.R[k]) 261 | // } 262 | 263 | for k := range dst.S { 264 | assert.Equal(t, dst.S[k], *local_SlicePtrTestSrc.S[k]) 265 | } 266 | 267 | for k := range dst.T { 268 | assert.Equal(t, dst.T[k], *local_SlicePtrTestSrc.T[k]) 269 | } 270 | 271 | } 272 | } 273 | 274 | func Test_Slice_BaseTypePtr_DstPtr_Src(t *testing.T) { 275 | err := Preheat(&slicePtrTestSrc{}, &slicePtrTestDst{}) 276 | assert.NoError(t, err) 277 | 278 | var opts []Option 279 | for _, b := range []bool{true, false} { 280 | if b { 281 | opts = append(opts, WithUsePreheat()) 282 | } else { 283 | opts = []Option{} 284 | } 285 | 286 | var dst slicePtrTestSrc 287 | err = Copy(&dst, &local_SlicePtrTestDst, opts...) 288 | assert.NoError(t, err) 289 | fmt.Printf("dst: %+v\n", dst) 290 | fmt.Printf(".A %#v\n", dst.A) 291 | for k := range local_SlicePtrTestDst.A { 292 | assert.Equal(t, *dst.A[k], local_SlicePtrTestDst.A[k]) 293 | } 294 | 295 | for k := range dst.B { 296 | assert.Equal(t, *dst.B[k], local_SlicePtrTestDst.B[k]) 297 | } 298 | 299 | for k := range dst.C { 300 | assert.Equal(t, *dst.C[k], local_SlicePtrTestDst.C[k]) 301 | } 302 | 303 | for k := range dst.D { 304 | assert.Equal(t, *dst.D[k], local_SlicePtrTestDst.D[k]) 305 | } 306 | 307 | for k := range dst.E { 308 | assert.Equal(t, *dst.E[k], local_SlicePtrTestDst.E[k]) 309 | } 310 | 311 | for k := range dst.F { 312 | assert.Equal(t, *dst.F[k], local_SlicePtrTestDst.F[k]) 313 | } 314 | 315 | for k := range dst.G { 316 | assert.Equal(t, *dst.G[k], local_SlicePtrTestDst.G[k]) 317 | } 318 | 319 | for k := range dst.H { 320 | assert.Equal(t, *dst.H[k], local_SlicePtrTestDst.H[k]) 321 | } 322 | 323 | for k := range dst.I { 324 | assert.Equal(t, *dst.I[k], local_SlicePtrTestDst.I[k]) 325 | } 326 | 327 | for k := range dst.J { 328 | assert.Equal(t, *dst.J[k], local_SlicePtrTestDst.J[k]) 329 | } 330 | 331 | for k := range dst.K { 332 | assert.Equal(t, *dst.K[k], local_SlicePtrTestDst.K[k]) 333 | } 334 | 335 | for k := range dst.L { 336 | assert.Equal(t, *dst.L[k], local_SlicePtrTestDst.L[k]) 337 | } 338 | 339 | for k := range dst.M { 340 | assert.Equal(t, *dst.M[k], local_SlicePtrTestDst.M[k]) 341 | } 342 | 343 | for k := range dst.N { 344 | assert.Equal(t, *dst.N[k], local_SlicePtrTestDst.N[k]) 345 | } 346 | 347 | for k := range dst.O { 348 | assert.Equal(t, *dst.O[k], local_SlicePtrTestDst.O[k]) 349 | } 350 | 351 | for k := range dst.P { 352 | assert.Equal(t, *dst.P[k], local_SlicePtrTestDst.P[k]) 353 | } 354 | 355 | for k := range dst.Q { 356 | assert.Equal(t, *dst.Q[k], local_SlicePtrTestDst.Q[k]) 357 | } 358 | 359 | // for k := range dst.R { 360 | // assert.Equal(t, *dst.R[k], local_SlicePtrTestDst.R[k]) 361 | // } 362 | 363 | for k := range dst.S { 364 | assert.Equal(t, *dst.S[k], local_SlicePtrTestDst.S[k]) 365 | } 366 | 367 | for k := range dst.T { 368 | assert.Equal(t, *dst.T[k], local_SlicePtrTestDst.T[k]) 369 | } 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /pcopy_slice_struct_ptr_test.go: -------------------------------------------------------------------------------- 1 | package pcopy 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type SliceStructPtrElemA struct { 11 | Info *MessageWhoAndTime 12 | LikeType LikeType 13 | Name string 14 | Age int 15 | } 16 | 17 | type SliceStructPtrElemB struct { 18 | Info *MessageWhoAndTime 19 | LikeType LikeType 20 | Name string 21 | Age int 22 | } 23 | 24 | type SliceStructPtrElemC struct { 25 | Info *MessageWhoAndTime 26 | LikeType LikeType 27 | Name string 28 | Age int 29 | } 30 | 31 | type SliceStructPtrElemD struct { 32 | Info *MessageWhoAndTime 33 | LikeType LikeType 34 | Name string 35 | Age int 36 | } 37 | 38 | type SliceStructPtrTest_Src struct { 39 | A []*SliceStructPtrElemA 40 | B []*SliceStructPtrElemB 41 | } 42 | 43 | type SliceStructPtrTest_Dst struct { 44 | A []SliceStructPtrElemA 45 | B []SliceStructPtrElemB 46 | } 47 | 48 | var local_SliceStructPtrTest_Src = SliceStructPtrTest_Src{ 49 | A: []*SliceStructPtrElemA{ 50 | { 51 | Info: &MessageWhoAndTime{ 52 | HeadPic: "A1", 53 | Nickname: "A1", 54 | Time: "A1", 55 | DID: 1, 56 | Seq: 1, 57 | }, 58 | LikeType: LikeType_COLLECT, 59 | Name: "A1", 60 | Age: 1, 61 | }, 62 | { 63 | Info: &MessageWhoAndTime{ 64 | HeadPic: "A1", 65 | Nickname: "A1", 66 | Time: "A1", 67 | DID: 1, 68 | Seq: 1, 69 | }, 70 | LikeType: LikeType_COLLECT, 71 | Name: "A2", 72 | Age: 2, 73 | }, 74 | { 75 | Info: &MessageWhoAndTime{ 76 | HeadPic: "A1", 77 | Nickname: "A1", 78 | Time: "A1", 79 | DID: 1, 80 | Seq: 1, 81 | }, 82 | LikeType: LikeType_COLLECT, 83 | Name: "A3", 84 | Age: 3, 85 | }, 86 | }, 87 | B: []*SliceStructPtrElemB{ 88 | { 89 | Info: &MessageWhoAndTime{ 90 | HeadPic: "A1", 91 | Nickname: "A1", 92 | Time: "A1", 93 | DID: 1, 94 | Seq: 1, 95 | }, 96 | LikeType: LikeType_COLLECT, 97 | Name: "B1", 98 | Age: 1, 99 | }, 100 | { 101 | Info: &MessageWhoAndTime{ 102 | HeadPic: "A1", 103 | Nickname: "A1", 104 | Time: "A1", 105 | DID: 1, 106 | Seq: 1, 107 | }, 108 | LikeType: LikeType_COLLECT, 109 | Name: "B2", 110 | Age: 2, 111 | }, 112 | { 113 | Info: &MessageWhoAndTime{ 114 | HeadPic: "A1", 115 | Nickname: "A1", 116 | Time: "A1", 117 | DID: 1, 118 | Seq: 1, 119 | }, 120 | LikeType: LikeType_COLLECT, 121 | Name: "B3", 122 | Age: 3, 123 | }, 124 | }, 125 | } 126 | 127 | var local_SliceStructPtrTest_Dst = SliceStructPtrTest_Dst{ 128 | A: []SliceStructPtrElemA{ 129 | { 130 | Name: "A1", 131 | Age: 1, 132 | }, 133 | { 134 | Name: "A2", 135 | Age: 2, 136 | }, 137 | { 138 | Name: "A3", 139 | Age: 3, 140 | }, 141 | }, 142 | B: []SliceStructPtrElemB{ 143 | { 144 | Name: "B1", 145 | Age: 1, 146 | }, 147 | { 148 | Name: "B2", 149 | Age: 2, 150 | }, 151 | { 152 | Name: "B3", 153 | Age: 3, 154 | }, 155 | }, 156 | } 157 | 158 | func Test_SliceStructPtr_Src(t *testing.T) { 159 | err := Preheat(&SliceStructPtrTest_Src{}, &SliceStructPtrTest_Src{}) 160 | assert.NoError(t, err, fmt.Sprintf("Test_SliceStructPtr_Src %+v", err)) 161 | 162 | var opts []Option 163 | for _, b := range []bool{true, false} { 164 | if b { 165 | opts = append(opts, WithUsePreheat()) 166 | } else { 167 | opts = []Option{} 168 | } 169 | var dst SliceStructPtrTest_Src 170 | err := Copy(&dst, &local_SliceStructPtrTest_Src, opts...) 171 | assert.Equal(t, local_SliceStructPtrTest_Src, dst) 172 | assert.NoError(t, err) 173 | } 174 | } 175 | 176 | func Test_SliceStructPtr_Dst(t *testing.T) { 177 | err := Preheat(&SliceStructPtrTest_Dst{}, &SliceStructPtrTest_Dst{}) 178 | assert.NoError(t, err, fmt.Sprintf("Test_SliceStructPtr_Src %+v", err)) 179 | 180 | var opts []Option 181 | for _, b := range []bool{true, false} { 182 | if b { 183 | opts = append(opts, WithUsePreheat()) 184 | } else { 185 | opts = []Option{} 186 | } 187 | var dst SliceStructPtrTest_Dst 188 | err := Copy(&dst, &local_SliceStructPtrTest_Dst, opts...) 189 | assert.NoError(t, err) 190 | assert.Equal(t, local_SliceStructPtrTest_Dst, dst) 191 | } 192 | } 193 | 194 | func Test_SliceStructPtr_Dst_Src(t *testing.T) { 195 | err := Preheat(&SliceStructPtrTest_Dst{}, &SliceStructPtrTest_Src{}) 196 | assert.NoError(t, err, fmt.Sprintf("Test_SliceStructPtr_Src %+v", err)) 197 | 198 | var opts []Option 199 | for _, b := range []bool{true, false} { 200 | if b { 201 | opts = append(opts, WithUsePreheat()) 202 | } else { 203 | opts = []Option{} 204 | } 205 | var dst SliceStructPtrTest_Dst 206 | err := Copy(&dst, &local_SliceStructPtrTest_Src, opts...) 207 | assert.NoError(t, err) 208 | 209 | for i, v := range local_SliceStructPtrTest_Src.A { 210 | assert.Equal(t, dst.A[i], *v) 211 | } 212 | 213 | for i, v := range local_SliceStructPtrTest_Src.B { 214 | assert.Equal(t, dst.B[i], *v) 215 | } 216 | } 217 | } 218 | 219 | type SliceStructPtrElemA2 struct { 220 | Name string 221 | } 222 | 223 | type SliceStructPtrTest_Src2 struct { 224 | A []*SliceStructPtrElemA2 225 | } 226 | 227 | type SliceStructPtrTest_Src_Resp struct { 228 | Data *SliceStructPtrTest_Src2 229 | } 230 | 231 | var local_SliceStructPtrTest_Src2 = SliceStructPtrTest_Src2{ 232 | A: []*SliceStructPtrElemA2{ 233 | { 234 | Name: "A1", 235 | }, 236 | }, 237 | } 238 | 239 | func Test_SliceStructPtr_Dst_Src2(t *testing.T) { 240 | err := Preheat(&SliceStructPtrTest_Src2{}, &SliceStructPtrTest_Src2{}) 241 | assert.NoError(t, err, fmt.Sprintf("Test_SliceStructPtr_Src %+v", err)) 242 | 243 | var opts []Option 244 | for _, b := range []bool{true, false} { 245 | if b { 246 | opts = append(opts, WithUsePreheat()) 247 | } else { 248 | opts = []Option{} 249 | } 250 | var dst SliceStructPtrTest_Src_Resp 251 | err := Copy(&dst.Data, &local_SliceStructPtrTest_Src2, opts...) 252 | assert.NoError(t, err) 253 | 254 | assert.NotNil(t, dst.Data) 255 | assert.NotEqual(t, len(dst.Data.A), 0) 256 | for i, v := range local_SliceStructPtrTest_Src2.A { 257 | assert.Equal(t, *dst.Data.A[i], *v) 258 | } 259 | 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /pcopy_struct_basetype_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "reflect" 6 | "testing" 7 | "unsafe" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | type PCopyDst struct { 13 | // 所有基础类型 14 | Bool bool 15 | Int int 16 | Int8 int8 17 | Int16 int16 18 | Int32 int32 19 | Int64 int64 20 | Uint uint 21 | Uint8 uint8 22 | Uint16 uint16 23 | Uint32 uint32 24 | Uint64 uint64 25 | String string 26 | Float32 float32 27 | Float64 float64 28 | // Complex64 complex64 29 | // Complex128 complex128 30 | } 31 | 32 | type PCopySrc PCopyDst 33 | 34 | var testSrc = PCopySrc{ 35 | Bool: true, 36 | Int: 1, 37 | Int8: 2, 38 | Int16: 3, 39 | Int32: 4, 40 | Int64: 5, 41 | Uint: 6, 42 | Uint8: 7, 43 | Uint16: 8, 44 | Uint32: 9, 45 | Uint64: 10, 46 | String: "11", 47 | Float32: 12.0, 48 | Float64: 13.0, 49 | // Complex64: 14.0, 50 | // Complex128: 15.0, 51 | } 52 | 53 | // 基础类型 54 | func Test_PcopyBase(t *testing.T) { 55 | var dst PCopyDst 56 | 57 | err := Preheat(&dst, &testSrc) 58 | assert.NoError(t, err) 59 | dst = PCopyDst{} 60 | 61 | err = Copy(&dst, &testSrc, WithUsePreheat()) 62 | 63 | var dst2 PCopyDst 64 | dst2.Bool = true 65 | dst2.Int = 1 66 | dst2.Int8 = 2 67 | dst2.Int16 = 3 68 | dst2.Int32 = 4 69 | dst2.Int64 = 5 70 | dst2.Uint = 6 71 | dst2.Uint8 = 7 72 | dst2.Uint16 = 8 73 | dst2.Uint32 = 9 74 | dst2.Uint64 = 10 75 | dst2.String = "11" 76 | dst2.Float32 = 12.0 77 | dst2.Float64 = 13.0 78 | // dst2.Complex64 = 14.0 79 | // dst2.Complex128 = 15.0 80 | assert.NoError(t, err) 81 | assert.Equal(t, dst, dst2) 82 | } 83 | 84 | // 基础指针类型 85 | // TODO 86 | type DopyDst_BasePtr struct { 87 | // 所有基础类型 88 | Bool *bool 89 | Int *int 90 | Int8 *int8 91 | Int16 *int16 92 | Int32 *int32 93 | Int64 *int64 94 | Uint *uint 95 | Uint8 *uint8 96 | Uint16 *uint16 97 | Uint32 *uint32 98 | Uint64 *uint64 99 | String *string 100 | Float32 *float32 101 | Float64 *float64 102 | // Complex64 complex64 103 | // Complex128 complex128 104 | } 105 | 106 | // func TestFastCopyBasePtr(t *testing.T) { 107 | // var dst FastCopyDst_BasePtr 108 | 109 | // err := Preheat(&dst, &testSrc) 110 | // assert.NoError(t, err) 111 | 112 | // printCacheAllFunc() 113 | // err = CopyEx(&dst, &testSrc, WithPreheat()) 114 | 115 | // var dst2 FastCopyDst_BasePtr 116 | // dst2.Bool = new(bool) 117 | // *dst2.Bool = true 118 | 119 | // dst2.Int = new(int) 120 | // *dst2.Int = 1 121 | 122 | // dst2.Int8 = new(int8) 123 | // *dst2.Int8 = 2 124 | 125 | // dst2.Int16 = new(int16) 126 | // *dst2.Int16 = 3 127 | 128 | // dst2.Int32 = new(int32) 129 | // *dst2.Int32 = 4 130 | 131 | // dst2.Int64 = new(int64) 132 | // *dst2.Int64 = 5 133 | 134 | // dst2.Uint = new(uint) 135 | // *dst2.Uint = 6 136 | 137 | // dst2.Uint8 = new(uint8) 138 | // *dst2.Uint8 = 7 139 | 140 | // dst2.Uint16 = new(uint16) 141 | // *dst2.Uint16 = 8 142 | 143 | // dst2.Uint32 = new(uint32) 144 | // *dst2.Uint32 = 9 145 | 146 | // dst2.Uint64 = new(uint64) 147 | // *dst2.Uint64 = 10 148 | 149 | // // dst2.Complex64 = 14.0 150 | // // dst2.Complex128 = 15.0 151 | // assert.NoError(t, err) 152 | // assert.Equal(t, dst, dst2) 153 | // } 154 | 155 | // 基础类型带slice 156 | 157 | func getSliceHeaderPtr(s unsafe.Pointer) *reflect.SliceHeader { 158 | return (*reflect.SliceHeader)(s) 159 | } 160 | 161 | type _PcopySrc_BaseStruct _PcopyDst_BaseStruct 162 | 163 | // 基础下struct 套struct的情况 164 | type _PcopyDst_BaseStruct struct { 165 | Bool bool 166 | Int int 167 | Int8 int8 168 | Int16 int16 169 | Int32 int32 170 | Int64 int64 171 | Uint uint 172 | Uint8 uint8 173 | Uint16 uint16 174 | Uint32 uint32 175 | Uint64 uint64 176 | String string 177 | Float32 float32 178 | Float64 float64 179 | // Complex64 complex64 180 | // Complex128 complex128 181 | SliceBool []bool 182 | SliceInt []int 183 | SliceInt8 []int8 184 | SliceInt16 []int16 185 | SliceInt32 []int32 186 | SliceInt64 []int64 187 | SliceUint []uint 188 | SliceUint8 []uint8 189 | SliceUint16 []uint16 190 | SliceUint32 []uint32 191 | SliceUint64 []uint64 192 | SliceString []string 193 | SliceFloat32 []float32 194 | SliceFloat64 []float64 195 | 196 | Sub2 _PcopyDst_BaseStruct_Sub2 197 | } 198 | 199 | type _PcopyDst_BaseStruct_Sub2 struct { 200 | Bool bool 201 | Int int 202 | Int8 int8 203 | Int16 int16 204 | Int32 int32 205 | Int64 int64 206 | Uint uint 207 | Uint8 uint8 208 | Uint16 uint16 209 | Uint32 uint32 210 | Uint64 uint64 211 | String string 212 | Float32 float32 213 | Float64 float64 214 | // Complex64 complex64 215 | // Complex128 complex128 216 | SliceBool []bool 217 | SliceInt []int 218 | SliceInt8 []int8 219 | SliceInt16 []int16 220 | SliceInt32 []int32 221 | SliceInt64 []int64 222 | SliceUint []uint 223 | SliceUint8 []uint8 224 | SliceUint16 []uint16 225 | SliceUint32 []uint32 226 | SliceUint64 []uint64 227 | SliceString []string 228 | SliceFloat32 []float32 229 | SliceFloat64 []float64 230 | 231 | Sub3 _PcopyDst_BaseStruct_Sub3 232 | } 233 | 234 | type _PcopyDst_BaseStruct_Sub3 struct { 235 | Bool bool 236 | Int int 237 | Int8 int8 238 | Int16 int16 239 | Int32 int32 240 | Int64 int64 241 | Uint uint 242 | Uint8 uint8 243 | Uint16 uint16 244 | Uint32 uint32 245 | Uint64 uint64 246 | String string 247 | Float32 float32 248 | Float64 float64 249 | // Complex64 complex64 250 | // Complex128 complex128 251 | SliceBool []bool 252 | SliceInt []int 253 | SliceInt8 []int8 254 | SliceInt16 []int16 255 | SliceInt32 []int32 256 | SliceInt64 []int64 257 | SliceUint []uint 258 | SliceUint8 []uint8 259 | SliceUint16 []uint16 260 | SliceUint32 []uint32 261 | SliceUint64 []uint64 262 | SliceString []string 263 | SliceFloat32 []float32 264 | SliceFloat64 []float64 265 | } 266 | 267 | var testBaseStructSrc _PcopySrc_BaseStruct = _PcopySrc_BaseStruct{ 268 | Bool: true, 269 | Int: 1, 270 | Int8: 2, 271 | Int16: 3, 272 | Int32: 4, 273 | Int64: 5, 274 | Uint: 6, 275 | Uint8: 7, 276 | Uint16: 8, 277 | Uint32: 9, 278 | Uint64: 10, 279 | String: "11", 280 | Float32: 12, 281 | Float64: 13, 282 | // Complex64: 14, 283 | // Complex128: 15, 284 | SliceBool: []bool{true, false}, 285 | SliceInt: []int{1, 2}, 286 | SliceInt8: []int8{3, 4}, 287 | SliceInt16: []int16{5, 6}, 288 | SliceInt32: []int32{7, 8}, 289 | SliceInt64: []int64{9, 10}, 290 | SliceUint: []uint{11, 12}, 291 | SliceUint8: []uint8{13, 14}, 292 | SliceUint16: []uint16{15, 16}, 293 | SliceUint32: []uint32{17, 18}, 294 | SliceUint64: []uint64{19, 20}, 295 | SliceString: []string{"21", "22"}, 296 | SliceFloat32: []float32{23, 24}, 297 | SliceFloat64: []float64{25, 26}, 298 | Sub2: _PcopyDst_BaseStruct_Sub2{ 299 | Bool: true, 300 | Int: 1, 301 | Int8: 2, 302 | Int16: 3, 303 | Int32: 4, 304 | Int64: 5, 305 | Uint: 6, 306 | Uint8: 7, 307 | Uint16: 8, 308 | Uint32: 9, 309 | Uint64: 10, 310 | String: "11", 311 | Float32: 12, 312 | Float64: 13, 313 | // Complex64: 14, 314 | // Complex128: 15, 315 | SliceBool: []bool{true, false}, 316 | SliceInt: []int{1, 2}, 317 | SliceInt8: []int8{3, 4}, 318 | SliceInt16: []int16{5, 6}, 319 | SliceInt32: []int32{7, 8}, 320 | SliceInt64: []int64{9, 10}, 321 | SliceUint: []uint{11, 12}, 322 | SliceUint8: []uint8{13, 14}, 323 | SliceUint16: []uint16{15, 16}, 324 | SliceUint32: []uint32{17, 18}, 325 | SliceUint64: []uint64{19, 20}, 326 | SliceString: []string{"21", "22"}, 327 | SliceFloat32: []float32{23, 24}, 328 | SliceFloat64: []float64{25, 26}, 329 | Sub3: _PcopyDst_BaseStruct_Sub3{ 330 | Bool: true, 331 | Int: 1, 332 | Int8: 2, 333 | Int16: 3, 334 | Int32: 4, 335 | Int64: 5, 336 | Uint: 6, 337 | Uint8: 7, 338 | Uint16: 8, 339 | Uint32: 9, 340 | Uint64: 10, 341 | String: "11", 342 | Float32: 12, 343 | Float64: 13, 344 | // Complex64: 14, 345 | // Complex128: 15, 346 | SliceBool: []bool{true, false}, 347 | SliceInt: []int{1, 2}, 348 | SliceInt8: []int8{3, 4}, 349 | SliceInt16: []int16{5, 6}, 350 | SliceInt32: []int32{7, 8}, 351 | SliceInt64: []int64{9, 10}, 352 | SliceUint: []uint{11, 12}, 353 | SliceUint8: []uint8{13, 14}, 354 | SliceUint16: []uint16{15, 16}, 355 | SliceUint32: []uint32{17, 18}, 356 | SliceUint64: []uint64{19, 20}, 357 | SliceString: []string{"21", "22"}, 358 | SliceFloat32: []float32{23, 24}, 359 | SliceFloat64: []float64{25, 26}, 360 | }, 361 | }, 362 | } 363 | 364 | func Test_StructWithStruct(t *testing.T) { 365 | var dst _PcopyDst_BaseStruct 366 | 367 | err := Preheat(&dst, &testBaseStructSrc) 368 | // err := Preheat(&dst, &testSrc) 369 | assert.NoError(t, err) 370 | dst = _PcopyDst_BaseStruct{} 371 | 372 | // fmt.Printf("%v\n", cacheAllFunc) 373 | err = Copy(&dst, &testBaseStructSrc, WithUsePreheat()) 374 | 375 | // 直接赋值对于slice是浅拷贝,这里只是为了测试 376 | dst2 := testBaseStructSrc 377 | assert.NoError(t, err) 378 | assert.Equal(t, dst, _PcopyDst_BaseStruct(dst2)) 379 | } 380 | -------------------------------------------------------------------------------- /pcopy_struct_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | // 测试结构体内嵌的情况 11 | func Test_Struct_Embed(t *testing.T) { 12 | type core struct { 13 | ID int 14 | Name string 15 | } 16 | 17 | type dst struct { 18 | id int 19 | name string 20 | core 21 | } 22 | 23 | type src dst 24 | 25 | var d dst 26 | s := src{core: core{ID: 3, Name: "name"}} 27 | err := Copy(&d, &s) 28 | assert.NoError(t, err) 29 | assert.Equal(t, d, dst(s)) 30 | } 31 | 32 | // 测试结构体特殊情况 33 | func Test_Struct_Special(t *testing.T) { 34 | for _, tc := range []testCase{ 35 | // 测试结构体里面有小写成员变量 36 | func() testCase { 37 | type dst struct { 38 | id int 39 | name string 40 | } 41 | 42 | type src dst 43 | 44 | Copy(&dst{}, &src{id: 3}) 45 | return testCase{} 46 | }(), 47 | // 测试内嵌结构体的情况 48 | } { 49 | assert.Equal(t, tc.need, tc.got) 50 | } 51 | } 52 | 53 | func Test_Struct_SpecialCopyEx(t *testing.T) { 54 | for _, tc := range []testCase{ 55 | // 测试结构体里面有小写成员变量 56 | func() testCase { 57 | type dst struct { 58 | id int 59 | name string 60 | } 61 | 62 | type src dst 63 | 64 | Copy(&dst{}, &src{id: 3}) 65 | return testCase{} 66 | }(), 67 | // 测试内嵌结构体的情况 68 | func() testCase { 69 | type core struct { 70 | ID int 71 | Name string 72 | } 73 | 74 | type dst struct { 75 | id int 76 | name string 77 | core 78 | } 79 | 80 | type src dst 81 | 82 | var d dst 83 | s := src{core: core{ID: 3, Name: "name"}} 84 | err := Copy(&d, &s) 85 | assert.NoError(t, err) 86 | return testCase{got: d, need: (dst)(s)} 87 | }(), 88 | } { 89 | assert.Equal(t, tc.need, tc.got) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /pcopy_struct_with_struct_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | -------------------------------------------------------------------------------- /pcopy_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type testCase struct { 11 | need interface{} 12 | got interface{} 13 | } 14 | 15 | // 最大深度 16 | // func Test_MaxDepth(t *testing.T) { 17 | // type depth struct { 18 | // First string 19 | // Data struct { 20 | // Result string 21 | // } 22 | // Err struct { 23 | // ErrMsg struct { 24 | // Message string 25 | // } 26 | // } 27 | // } 28 | 29 | // src := depth{} 30 | // src.First = "first" 31 | // src.Data.Result = "test" 32 | // src.Err.ErrMsg.Message = "good" 33 | 34 | // for _, tc := range []testCase{ 35 | // func() testCase { 36 | // d := depth{} 37 | // err := Copy(&d, &src, WithMaxDepth(2)) 38 | // assert.NoError(t, err) 39 | // if err != nil { 40 | // return testCase{} 41 | // } 42 | // need := depth{} 43 | // need.First = "first" 44 | // need.Data.Result = "test" 45 | // return testCase{got: d, need: need} 46 | // }(), 47 | // func() testCase { 48 | // d := depth{} 49 | // Copy(&d, &src, WithMaxDepth(1)) 50 | // need := depth{} 51 | // need.First = "first" 52 | // return testCase{got: d, need: need} 53 | // }(), 54 | // func() testCase { 55 | // d := depth{} 56 | // Copy(&d, &src, WithMaxDepth(3)) 57 | // need := depth{} 58 | // need.First = "first" 59 | // need.Data.Result = "test" 60 | // need.Err.ErrMsg.Message = "good" 61 | // return testCase{got: d, need: need} 62 | // }(), 63 | // } { 64 | // assert.Equal(t, tc.need, tc.got) 65 | // } 66 | // } 67 | 68 | // 测试设置tag的情况 69 | // func Test_TagName(t *testing.T) { 70 | // type tagName struct { 71 | // First string `copy:"first"` 72 | // Data struct { 73 | // Result string 74 | // } 75 | // } 76 | 77 | // src := tagName{} 78 | // src.First = "first" 79 | // src.Data.Result = "test" 80 | 81 | // for _, tc := range []testCase{ 82 | // func() testCase { 83 | // d := tagName{} 84 | // Copy(&d, &src, WithTagName("copy")) 85 | // need := tagName{} 86 | // need.First = "first" 87 | // return testCase{got: d, need: need} 88 | // }(), 89 | // } { 90 | // assert.Equal(t, tc.need, tc.got) 91 | // } 92 | // } 93 | 94 | // 下面的test case 确保不panic 95 | func Test_Special(t *testing.T) { 96 | for _, tc := range []testCase{ 97 | func() testCase { 98 | // src有的字段, dst里面没有 99 | type src struct { 100 | Sex string 101 | } 102 | 103 | type dst struct { 104 | ID string 105 | } 106 | 107 | d := dst{} 108 | s := src{Sex: "m"} 109 | Copy(&d, &s) 110 | return testCase{got: d, need: d} 111 | }(), 112 | func() testCase { 113 | // 同样的字段不同数据类型,不拷贝 114 | type src struct { 115 | Sex string 116 | } 117 | 118 | type dst struct { 119 | Sex int 120 | } 121 | 122 | d := dst{} 123 | s := src{Sex: "m"} 124 | Copy(&d, &s) 125 | return testCase{got: d, need: d} 126 | }(), 127 | func() testCase { 128 | var s *int 129 | Copy(new(int), s) 130 | return testCase{got: true, need: true} 131 | }(), 132 | } { 133 | assert.Equal(t, tc.need, tc.got) 134 | } 135 | } 136 | 137 | // 测试循环引用 138 | /* 139 | func Test_Cycle(t *testing.T) { 140 | for _, e := range []error{ 141 | func() error { 142 | type src2 struct { 143 | P1 *src2 144 | ID string 145 | } 146 | 147 | // p1指向自己,构造一个环 148 | s := src2{} 149 | s.P1 = &s 150 | 151 | d := src2{} 152 | return Copy(&d, &s) 153 | }(), 154 | } { 155 | assert.Error(t, e) 156 | } 157 | } 158 | */ 159 | -------------------------------------------------------------------------------- /pcopy_time_Time_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func Test_timeTime_BaseType(t *testing.T) { 12 | err := Preheat(&time.Time{}, &time.Time{}) 13 | assert.NoError(t, err) 14 | 15 | src := time.Now() 16 | var dst time.Time 17 | err = Copy(&dst, &src, WithUsePreheat()) 18 | assert.NoError(t, err) 19 | assert.Equal(t, src, dst) 20 | } 21 | 22 | func Test_timeTime_Slice(t *testing.T) { 23 | err := Preheat(&[]time.Time{}, &[]time.Time{}) 24 | assert.NoError(t, err) 25 | 26 | src := []time.Time{time.Now(), time.Now(), time.Now()} 27 | var dst []time.Time 28 | err = Copy(&dst, &src, WithUsePreheat()) 29 | assert.NoError(t, err) 30 | assert.Equal(t, src, dst) 31 | } 32 | 33 | type testCaseWithTime1 struct { 34 | T1 time.Time 35 | I int 36 | } 37 | 38 | type testCaseWithTime2 struct { 39 | T1 time.Time 40 | I int 41 | } 42 | 43 | func Test_Time(t *testing.T) { 44 | var t1 testCaseWithTime1 45 | t2 := testCaseWithTime2{T1: time.Now(), I: 3} 46 | 47 | err := Copy(&t1, &t2) 48 | assert.NoError(t, err) 49 | assert.Equal(t, t1.T1, t2.T1) 50 | assert.Equal(t, t1.I, t2.I) 51 | } 52 | 53 | func Test_TimeCopyEx(t *testing.T) { 54 | var t1 testCaseWithTime1 55 | t2 := testCaseWithTime2{T1: time.Now(), I: 3} 56 | 57 | err := Copy(&t1, &t2) 58 | assert.NoError(t, err) 59 | assert.Equal(t, t1.T1, t2.T1) 60 | assert.Equal(t, t1.I, t2.I) 61 | } 62 | -------------------------------------------------------------------------------- /pcopy_type_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | // 测试所有类型(除了指针和inteface{}) 11 | func Test_TypeCase(t *testing.T) { 12 | type allType struct { 13 | Bool bool 14 | Int8 int8 15 | Int16 int16 16 | Int32 int32 17 | Int64 int64 18 | Uint8 uint8 19 | Uint16 uint16 20 | Uint32 uint32 21 | Uint64 uint64 22 | Uintptr uintptr 23 | Float32 float32 24 | Float64 float64 25 | Complex64 complex64 26 | Complex128 complex128 27 | ArrayInt [3]int 28 | // TODO 展开所有类型array 29 | SliceInt []int 30 | C chan int 31 | 32 | // CB func() error 33 | S string 34 | } 35 | 36 | for _, tc := range []testCase{ 37 | func() testCase { 38 | d := allType{} 39 | src := allType{ 40 | Bool: true, 41 | Int8: 1, 42 | Int16: 2, 43 | Int32: 3, 44 | Int64: 4, 45 | Uint8: 1, 46 | Uint16: 2, 47 | Uint32: 3, 48 | Uint64: 4, 49 | Uintptr: 0x123456, 50 | Float32: 3.1, 51 | Float64: 3.2, 52 | Complex64: 1.1, 53 | Complex128: 2.2, 54 | ArrayInt: [3]int{1, 2, 3}, 55 | SliceInt: []int{4, 5, 6}, 56 | C: make(chan int), 57 | // CB: func() error { return nil }, 58 | S: "test all", 59 | } 60 | 61 | Copy(&d, &src) 62 | return testCase{got: d, need: src} 63 | }(), 64 | } { 65 | assert.Equal(t, tc.need, tc.got) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /preheat.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | func Preheat[T any, U any](dst *T, src *U) error { 5 | return Copy(dst, src, withPreheat()) 6 | } 7 | -------------------------------------------------------------------------------- /set_basemap_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | // 生成set_basemap_func.go文件 11 | // func Test_Gen_setBasemap(t *testing.T) { 12 | // err := saveBaseMapFuncToFile("./set_basemap_func.go") 13 | // assert.NoError(t, err) 14 | // } 15 | 16 | type PCopyDst_BaseMap struct { 17 | A map[string]string 18 | B map[string]int 19 | C map[string]float64 20 | D map[string]uint 21 | E map[string]uint8 22 | F map[string]uint16 23 | G map[string]uint32 24 | H map[string]uint64 25 | I map[string]bool 26 | J map[string]int8 27 | K map[string]int16 28 | L map[string]int32 29 | M map[string]int64 30 | // N map[string]struct{} 31 | O map[string]float32 32 | // P map[string]complex64 33 | BA int 34 | BB int8 35 | BC int16 36 | BD int32 37 | BE int64 38 | BF uint 39 | BG uint8 40 | BH uint16 41 | BI uint32 42 | BJ uint64 43 | BK bool 44 | BL float32 45 | BM float64 46 | // BN complex64 47 | // BO complex128 48 | 49 | CA []int 50 | CB []int8 51 | CC []int16 52 | CD []int32 53 | CE []int64 54 | CF []uint 55 | CG []uint8 56 | CH []uint16 57 | CI []uint32 58 | CJ []uint64 59 | CK []bool 60 | CL []float32 61 | CM []float64 62 | // CN []complex64 63 | // CO []complex128 64 | } 65 | 66 | type PCopySrc_BaseMap PCopyDst_BaseMap 67 | 68 | var testSrc_BaseMap = PCopySrc_BaseMap{ 69 | A: map[string]string{ 70 | "1": "1", 71 | "2": "2", 72 | }, 73 | B: map[string]int{ 74 | "1": 1, 75 | "2": 2, 76 | }, 77 | C: map[string]float64{ 78 | "1": 1, 79 | "2": 2, 80 | }, 81 | D: map[string]uint{ 82 | "1": 1, 83 | "2": 2, 84 | }, 85 | E: map[string]uint8{ 86 | "1": 1, 87 | "2": 2, 88 | }, 89 | F: map[string]uint16{ 90 | "1": 1, 91 | "2": 2, 92 | }, 93 | G: map[string]uint32{ 94 | "1": 1, 95 | "2": 2, 96 | }, 97 | H: map[string]uint64{ 98 | "1": 1, 99 | "2": 2, 100 | }, 101 | I: map[string]bool{ 102 | "1": true, 103 | "2": false, 104 | }, 105 | J: map[string]int8{ 106 | "1": 1, 107 | "2": 2, 108 | }, 109 | K: map[string]int16{ 110 | "1": 1, 111 | "2": 2, 112 | }, 113 | L: map[string]int32{ 114 | "1": 1, 115 | "2": 2, 116 | }, 117 | M: map[string]int64{ 118 | "1": 1, 119 | "2": 2, 120 | }, 121 | // N: map[string]struct{}{ 122 | // "1": struct{}{}, 123 | // "2": struct{}{}, 124 | // }, 125 | O: map[string]float32{ 126 | "1": 1, 127 | "2": 2, 128 | }, 129 | // P: map[string]complex64{ 130 | // "1": 1, 131 | // "2": 2, 132 | // }, 133 | BA: 1, 134 | BB: 1, 135 | BC: 1, 136 | BD: 1, 137 | BE: 1, 138 | BF: 1, 139 | BG: 1, 140 | BH: 1, 141 | BI: 1, 142 | BJ: 1, 143 | BK: true, 144 | BL: 1, 145 | BM: 1, 146 | // BN: 1, 147 | // BO: 1, 148 | 149 | CA: []int{1, 2}, 150 | CB: []int8{1, 2}, 151 | CC: []int16{1, 2}, 152 | CD: []int32{1, 2}, 153 | CE: []int64{1, 2}, 154 | CF: []uint{1, 2}, 155 | CG: []uint8{1, 2}, 156 | CH: []uint16{1, 2}, 157 | CI: []uint32{1, 2}, 158 | CJ: []uint64{1, 2}, 159 | CK: []bool{true, false}, 160 | CL: []float32{1, 2}, 161 | CM: []float64{1, 2}, 162 | // CN: []complex64{1, 2}, 163 | // CO: []complex128{1, 2}, 164 | 165 | } 166 | 167 | func Test_SetBaseMap(t *testing.T) { 168 | var dst PCopyDst_BaseMap 169 | 170 | err := Preheat(&dst, &testSrc_BaseMap) 171 | dst = PCopyDst_BaseMap{} 172 | assert.NoError(t, err) 173 | 174 | err = Copy(&dst, &testSrc_BaseMap, WithUsePreheat()) 175 | assert.NoError(t, err) 176 | 177 | var dst2 PCopyDst_BaseMap = PCopyDst_BaseMap(testSrc_BaseMap) 178 | assert.Equal(t, dst, dst2) 179 | } 180 | -------------------------------------------------------------------------------- /set_basemap_tmpl.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "bytes" 6 | "fmt" 7 | "go/format" 8 | "io/ioutil" 9 | "text/template" 10 | 11 | "github.com/Masterminds/sprig/v3" 12 | ) 13 | 14 | var baseMapFuncTmpl = ` 15 | // Copyright [2020-2024] [guonaihong] 16 | package pcopy 17 | 18 | import ( 19 | "fmt" 20 | "reflect" 21 | "unsafe" 22 | ) 23 | // 自动生成的代码, 不要修改 24 | // 生成的代码位于,如下位置 25 | // set_basemap_tmpl.go 26 | // 生成命令位于 set_basemap_test.go 27 | {{$TypeName := .TypeName}} 28 | {{range $_, $TypeKey := .TypeName}} 29 | {{range $_, $TypeVal := $TypeName}} 30 | func setBaseMap{{$TypeKey|title}}{{$TypeVal|title}}(dstAddr, srcAddr unsafe.Pointer) { 31 | src := (*map[{{$TypeKey}}]{{$TypeVal}})(srcAddr) 32 | if len(*src) == 0 { 33 | return 34 | } 35 | dst := (*map[{{$TypeKey}}]{{$TypeVal}})(dstAddr) 36 | if dst == nil || len(*dst) == 0 { 37 | *dst = make(map[{{$TypeKey}}]{{$TypeVal}}, len(*src)) 38 | } 39 | 40 | for k, v := range *src { 41 | (*dst)[k] = v 42 | } 43 | } 44 | {{end}} 45 | {{end}} 46 | ` 47 | 48 | var baseMapTable = ` 49 | {{$TypeName := .TypeName}} 50 | var baseMapTab = setBaseMapFuncTab{ 51 | 52 | {{range $_, $TypeKey := .TypeName}} 53 | {{range $_, $TypeVal := $TypeName}} 54 | 55 | {{$val := $TypeVal}} 56 | {{if eq $val "int"}} 57 | {{$val = "reflect.Int"}} 58 | {{else if eq $val "int8"}} 59 | {{$val = "reflect.Int8"}} 60 | {{else if eq $val "int16"}} 61 | {{$val = "reflect.Int16"}} 62 | {{else if eq $val "int32"}} 63 | {{$val = "reflect.Int32"}} 64 | {{else if eq $val "int64"}} 65 | {{$val = "reflect.Int64"}} 66 | {{else if eq $val "uint"}} 67 | {{$val = "reflect.Uint"}} 68 | {{else if eq $val "uint8"}} 69 | {{$val = "reflect.Uint8"}} 70 | {{else if eq $val "uint16"}} 71 | {{$val = "reflect.Uint16"}} 72 | {{else if eq $val "uint32"}} 73 | {{$val = "reflect.Uint32"}} 74 | {{else if eq $val "uint64"}} 75 | {{$val = "reflect.Uint64"}} 76 | {{else if eq $val "float32"}} 77 | {{$val = "reflect.Float32"}} 78 | {{else if eq $val "float64"}} 79 | {{$val = "reflect.Float64"}} 80 | {{else if eq $val "string"}} 81 | {{$val = "reflect.String"}} 82 | {{else if eq $val "bool"}} 83 | {{$val = "reflect.Bool"}} 84 | {{end}} 85 | 86 | {{$key := $TypeKey}} 87 | {{if eq $key "int"}} 88 | {{$key = "reflect.Int"}} 89 | {{else if eq $key "int8"}} 90 | {{$key = "reflect.Int8"}} 91 | {{else if eq $key "int16"}} 92 | {{$key = "reflect.Int16"}} 93 | {{else if eq $key "int32"}} 94 | {{$key = "reflect.Int32"}} 95 | {{else if eq $key "int64"}} 96 | {{$key = "reflect.Int64"}} 97 | {{else if eq $key "uint"}} 98 | {{$key = "reflect.Uint"}} 99 | {{else if eq $key "uint8"}} 100 | {{$key = "reflect.Uint8"}} 101 | {{else if eq $key "uint16"}} 102 | {{$key = "reflect.Uint16"}} 103 | {{else if eq $key "uint32"}} 104 | {{$key = "reflect.Uint32"}} 105 | {{else if eq $key "uint64"}} 106 | {{$key = "reflect.Uint64"}} 107 | {{else if eq $key "float32"}} 108 | {{$key = "reflect.Float32"}} 109 | {{else if eq $key "float64"}} 110 | {{$key = "reflect.Float64"}} 111 | {{else if eq $key "string"}} 112 | {{$key = "reflect.String"}} 113 | {{else if eq $key "bool"}} 114 | {{$key = "reflect.Bool"}} 115 | {{end}} 116 | 117 | baseMapKind{key:{{$key}}, val:{{$val}}}: setBaseMap{{$TypeKey|title}}{{$TypeVal|title}}, 118 | {{end}} 119 | {{end}} 120 | } 121 | 122 | func getSetBaseMapFunc(key reflect.Kind, val reflect.Kind, p bool) setUnsafeFunc { 123 | k := baseMapKind{key: key, val: val} 124 | f, ok := baseMapTab[k] 125 | if p && !ok { 126 | panic(fmt.Sprintf("not support type:key %v val: %v", key, val)) 127 | } 128 | return f 129 | } 130 | ` 131 | 132 | // 基础类型表 133 | var baseTypeTable = []string{ 134 | "int", 135 | "int8", 136 | "int16", 137 | "int32", 138 | "int64", 139 | "uint", 140 | "uint8", 141 | "uint16", 142 | "uint32", 143 | "uint64", 144 | "float32", 145 | "float64", 146 | "string", 147 | "bool", 148 | } 149 | 150 | func saveBaseMapFuncToFile(fileName string) error { 151 | var out bytes.Buffer 152 | tmpl, err := template.New("setBaseMapFunc").Funcs(sprig.TxtFuncMap()).Parse(baseMapFuncTmpl) 153 | if err != nil { 154 | return fmt.Errorf("build setBaseMapFunc fail:%w", err) 155 | } 156 | 157 | err = tmpl.Execute(&out, baseTypeTmpl{TypeName: baseTypeTable}) 158 | if err != nil { 159 | return err 160 | } 161 | tmpl, err = template.New("setBaseMapTable").Funcs(sprig.TxtFuncMap()).Parse(baseMapTable) 162 | if err != nil { 163 | return fmt.Errorf("build setBaseMapTable fail:%w", err) 164 | } 165 | err = tmpl.Execute(&out, baseTypeTmpl{TypeName: baseTypeTable}) 166 | if err != nil { 167 | return err 168 | } 169 | 170 | // 格式化代码 171 | src, err := format.Source(out.Bytes()) 172 | if err != nil { 173 | return fmt.Errorf("format code fail:%w", err) 174 | } 175 | 176 | return ioutil.WriteFile(fileName, src, 0o666) 177 | } 178 | -------------------------------------------------------------------------------- /set_baseslice.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "reflect" 7 | "unsafe" 8 | ) 9 | 10 | var copyBaseSliceTab = setUnsafeFuncTab{ 11 | reflect.Bool: setBaseSliceBool, 12 | reflect.Int: setBaseSliceInt, 13 | reflect.Int8: setBaseSliceInt8, 14 | reflect.Int16: setBaseSliceInt16, 15 | reflect.Int32: setBaseSliceInt32, 16 | reflect.Int64: setBaseSliceInt64, 17 | reflect.Uint: setBaseSliceUint, 18 | reflect.Uint8: setBaseSliceUint8, 19 | reflect.Uint16: setBaseSliceUint16, 20 | reflect.Uint32: setBaseSliceUint32, 21 | reflect.Uint64: setBaseSliceUint64, 22 | reflect.String: setBaseSliceString, 23 | reflect.Float32: setBaseSliceFloat32, 24 | reflect.Float64: setBaseSliceFloat64, 25 | reflect.Complex64: setBaseSliceComplex64, 26 | reflect.Complex128: setBaseSliceComplex128, 27 | reflect.Uintptr: setBaseSliceUintptr, 28 | } 29 | 30 | func getSetBaseSliceFunc(t reflect.Kind) setUnsafeFunc { 31 | f, ok := copyBaseSliceTab[t] 32 | if !ok { 33 | panic(fmt.Sprintf("not support type:%v", t)) 34 | } 35 | return f 36 | } 37 | 38 | func setBaseSliceBool(dstAddr, srcAddr unsafe.Pointer) { 39 | dstSlice := *(*[]bool)(dstAddr) 40 | srcSlice := *(*[]bool)(srcAddr) 41 | if cap(dstSlice) < len(srcSlice) { 42 | *(*[]bool)(dstAddr) = make([]bool, len(srcSlice)) 43 | dstSlice = *(*[]bool)(dstAddr) 44 | } 45 | copy(dstSlice, srcSlice) 46 | } 47 | 48 | func setBaseSliceInt(dstAddr, srcAddr unsafe.Pointer) { 49 | dstSlice := *(*[]int)(dstAddr) 50 | srcSlice := *(*[]int)(srcAddr) 51 | if cap(dstSlice) < len(srcSlice) { 52 | *(*[]int)(dstAddr) = make([]int, len(srcSlice)) 53 | dstSlice = *(*[]int)(dstAddr) 54 | } 55 | copy(dstSlice, srcSlice) 56 | } 57 | 58 | func setBaseSliceInt8(dstAddr, srcAddr unsafe.Pointer) { 59 | dstSlice := *(*[]int8)(dstAddr) 60 | srcSlice := *(*[]int8)(srcAddr) 61 | if cap(dstSlice) < len(srcSlice) { 62 | *(*[]int8)(dstAddr) = make([]int8, len(srcSlice)) 63 | dstSlice = *(*[]int8)(dstAddr) 64 | } 65 | copy(dstSlice, srcSlice) 66 | } 67 | 68 | func setBaseSliceInt16(dstAddr, srcAddr unsafe.Pointer) { 69 | dstSlice := *(*[]int16)(dstAddr) 70 | srcSlice := *(*[]int16)(srcAddr) 71 | if cap(dstSlice) < len(srcSlice) { 72 | *(*[]int16)(dstAddr) = make([]int16, len(srcSlice)) 73 | dstSlice = *(*[]int16)(dstAddr) 74 | } 75 | copy(dstSlice, srcSlice) 76 | } 77 | 78 | func setBaseSliceInt32(dstAddr, srcAddr unsafe.Pointer) { 79 | dstSlice := *(*[]int32)(dstAddr) 80 | srcSlice := *(*[]int32)(srcAddr) 81 | if cap(dstSlice) < len(srcSlice) { 82 | *(*[]int32)(dstAddr) = make([]int32, len(srcSlice)) 83 | dstSlice = *(*[]int32)(dstAddr) 84 | } 85 | copy(dstSlice, srcSlice) 86 | } 87 | 88 | func setBaseSliceInt64(dstAddr, srcAddr unsafe.Pointer) { 89 | dstSlice := *(*[]int64)(dstAddr) 90 | srcSlice := *(*[]int64)(srcAddr) 91 | if cap(dstSlice) < len(srcSlice) { 92 | *(*[]int64)(dstAddr) = make([]int64, len(srcSlice)) 93 | dstSlice = *(*[]int64)(dstAddr) 94 | } 95 | copy(dstSlice, srcSlice) 96 | } 97 | 98 | func setBaseSliceUint(dstAddr, srcAddr unsafe.Pointer) { 99 | dstSlice := *(*[]uint)(dstAddr) 100 | srcSlice := *(*[]uint)(srcAddr) 101 | if cap(dstSlice) < len(srcSlice) { 102 | *(*[]uint)(dstAddr) = make([]uint, len(srcSlice)) 103 | dstSlice = *(*[]uint)(dstAddr) 104 | } 105 | copy(dstSlice, srcSlice) 106 | } 107 | 108 | func setBaseSliceUint8(dstAddr, srcAddr unsafe.Pointer) { 109 | dstSlice := *(*[]uint8)(dstAddr) 110 | srcSlice := *(*[]uint8)(srcAddr) 111 | if cap(dstSlice) < len(srcSlice) { 112 | *(*[]uint8)(dstAddr) = make([]uint8, len(srcSlice)) 113 | dstSlice = *(*[]uint8)(dstAddr) 114 | } 115 | copy(dstSlice, srcSlice) 116 | } 117 | 118 | func setBaseSliceUint16(dstAddr, srcAddr unsafe.Pointer) { 119 | dstSlice := *(*[]uint16)(dstAddr) 120 | srcSlice := *(*[]uint16)(srcAddr) 121 | if cap(dstSlice) < len(srcSlice) { 122 | *(*[]uint16)(dstAddr) = make([]uint16, len(srcSlice)) 123 | dstSlice = *(*[]uint16)(dstAddr) 124 | } 125 | copy(dstSlice, srcSlice) 126 | } 127 | 128 | func setBaseSliceUint32(dstAddr, srcAddr unsafe.Pointer) { 129 | dstSlice := *(*[]uint32)(dstAddr) 130 | srcSlice := *(*[]uint32)(srcAddr) 131 | if cap(dstSlice) < len(srcSlice) { 132 | *(*[]uint32)(dstAddr) = make([]uint32, len(srcSlice)) 133 | dstSlice = *(*[]uint32)(dstAddr) 134 | } 135 | copy(dstSlice, srcSlice) 136 | } 137 | 138 | func setBaseSliceUint64(dstAddr, srcAddr unsafe.Pointer) { 139 | dstSlice := *(*[]uint64)(dstAddr) 140 | srcSlice := *(*[]uint64)(srcAddr) 141 | if cap(dstSlice) < len(srcSlice) { 142 | *(*[]uint64)(dstAddr) = make([]uint64, len(srcSlice)) 143 | dstSlice = *(*[]uint64)(dstAddr) 144 | } 145 | copy(dstSlice, srcSlice) 146 | } 147 | 148 | func setBaseSliceString(dstAddr, srcAddr unsafe.Pointer) { 149 | dstSlice := *(*[]string)(dstAddr) 150 | srcSlice := *(*[]string)(srcAddr) 151 | if cap(dstSlice) < len(srcSlice) { 152 | *(*[]string)(dstAddr) = make([]string, len(srcSlice)) 153 | dstSlice = *(*[]string)(dstAddr) 154 | } 155 | copy(dstSlice, srcSlice) 156 | } 157 | 158 | func setBaseSliceFloat32(dstAddr, srcAddr unsafe.Pointer) { 159 | dstSlice := *(*[]float32)(dstAddr) 160 | srcSlice := *(*[]float32)(srcAddr) 161 | if cap(dstSlice) < len(srcSlice) { 162 | *(*[]float32)(dstAddr) = make([]float32, len(srcSlice)) 163 | dstSlice = *(*[]float32)(dstAddr) 164 | } 165 | copy(dstSlice, srcSlice) 166 | } 167 | 168 | func setBaseSliceFloat64(dstAddr, srcAddr unsafe.Pointer) { 169 | dstSlice := *(*[]float64)(dstAddr) 170 | srcSlice := *(*[]float64)(srcAddr) 171 | if cap(dstSlice) < len(srcSlice) { 172 | *(*[]float64)(dstAddr) = make([]float64, len(srcSlice)) 173 | dstSlice = *(*[]float64)(dstAddr) 174 | } 175 | copy(dstSlice, srcSlice) 176 | } 177 | 178 | func setBaseSliceComplex64(dstAddr, srcAddr unsafe.Pointer) { 179 | dstSlice := *(*[]complex64)(dstAddr) 180 | srcSlice := *(*[]complex64)(srcAddr) 181 | if cap(dstSlice) < len(srcSlice) { 182 | *(*[]complex64)(dstAddr) = make([]complex64, len(srcSlice)) 183 | dstSlice = *(*[]complex64)(dstAddr) 184 | } 185 | copy(dstSlice, srcSlice) 186 | } 187 | 188 | func setBaseSliceComplex128(dstAddr, srcAddr unsafe.Pointer) { 189 | dstSlice := *(*[]complex128)(dstAddr) 190 | srcSlice := *(*[]complex128)(srcAddr) 191 | if cap(dstSlice) < len(srcSlice) { 192 | *(*[]complex128)(dstAddr) = make([]complex128, len(srcSlice)) 193 | dstSlice = *(*[]complex128)(dstAddr) 194 | } 195 | copy(dstSlice, srcSlice) 196 | } 197 | 198 | func setBaseSliceUintptr(dstAddr, srcAddr unsafe.Pointer) { 199 | dstSlice := *(*[]uintptr)(dstAddr) 200 | srcSlice := *(*[]uintptr)(srcAddr) 201 | if cap(dstSlice) < len(srcSlice) { 202 | *(*[]uintptr)(dstAddr) = make([]uintptr, len(srcSlice)) 203 | dstSlice = *(*[]uintptr)(dstAddr) 204 | } 205 | copy(dstSlice, srcSlice) 206 | } 207 | -------------------------------------------------------------------------------- /set_baseslice_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type PCopyDst_BaseSlice struct { 11 | // 所有基础类型 12 | Bool bool 13 | Int int 14 | Int8 int8 15 | Int16 int16 16 | Int32 int32 17 | Int64 int64 18 | Uint uint 19 | Uint8 uint8 20 | Uint16 uint16 21 | Uint32 uint32 22 | Uint64 uint64 23 | String string 24 | Float32 float32 25 | Float64 float64 26 | // Complex64 complex64 27 | // Complex128 complex128 28 | SliceBool []bool 29 | SliceInt []int 30 | SliceInt8 []int8 31 | SliceInt16 []int16 32 | SliceInt32 []int32 33 | SliceInt64 []int64 34 | SliceUint []uint 35 | SliceUint8 []uint8 36 | SliceUint16 []uint16 37 | SliceUint32 []uint32 38 | SliceUint64 []uint64 39 | SliceString []string 40 | SliceFloat32 []float32 41 | SliceFloat64 []float64 42 | 43 | Bytes []byte 44 | // SliceComplex64 []complex64 45 | // SliceComplex128 []complex128 46 | } 47 | 48 | type PCopySrc_BaseSlice PCopyDst_BaseSlice 49 | 50 | var testSrc_BaseSlice = PCopySrc_BaseSlice{ 51 | Bool: true, 52 | Int: 1, 53 | Int8: 2, 54 | Int16: 3, 55 | Int32: 4, 56 | Int64: 5, 57 | Uint: 6, 58 | Uint8: 7, 59 | Uint16: 8, 60 | Uint32: 9, 61 | Uint64: 10, 62 | String: "11", 63 | Float32: 12.0, 64 | Float64: 13.0, 65 | // Complex64: 14.0, 66 | // Complex128: 15.0, 67 | SliceBool: []bool{true, false}, 68 | SliceInt: []int{1, 2}, 69 | SliceInt8: []int8{3, 4}, 70 | SliceInt16: []int16{5, 6}, 71 | SliceInt32: []int32{7, 8}, 72 | SliceInt64: []int64{9, 10}, 73 | SliceUint: []uint{11, 12}, 74 | SliceUint8: []uint8{13, 14}, 75 | SliceUint16: []uint16{15, 16}, 76 | SliceUint32: []uint32{17, 18}, 77 | SliceUint64: []uint64{19, 20}, 78 | SliceString: []string{"21", "22"}, 79 | SliceFloat32: []float32{23.0, 24.0}, 80 | SliceFloat64: []float64{25.0, 26.0}, 81 | 82 | Bytes: []byte{27, 28, 29, 30, 31, 32, 33, 34}, 83 | // SliceComplex64: []complex64{27.0, 28.0}, 84 | // SliceComplex128: []complex128{29.0, 30.0}, 85 | } 86 | 87 | // 基础类型slice测试 88 | func TestPcopy_BaseWithSlice(t *testing.T) { 89 | var dst PCopyDst_BaseSlice 90 | 91 | err := Preheat(&dst, &testSrc_BaseSlice) 92 | dst = PCopyDst_BaseSlice{} 93 | assert.NoError(t, err) 94 | 95 | err = Copy(&dst, &testSrc_BaseSlice, WithUsePreheat()) 96 | assert.NoError(t, err) 97 | 98 | var dst2 PCopyDst_BaseSlice = PCopyDst_BaseSlice(testSrc_BaseSlice) 99 | assert.Equal(t, dst, dst2) 100 | } 101 | -------------------------------------------------------------------------------- /set_basetype.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "reflect" 7 | "time" 8 | "unsafe" 9 | ) 10 | 11 | var copyTab = setUnsafeFuncTab{ 12 | reflect.Bool: setBool, 13 | reflect.Int: setInt, 14 | reflect.Int8: setInt8, 15 | reflect.Int16: setInt16, 16 | reflect.Int32: setInt32, 17 | reflect.Int64: setInt64, 18 | reflect.Uint: setUint, 19 | reflect.Uint8: setUint8, 20 | reflect.Uint16: setUint16, 21 | reflect.Uint32: setUint32, 22 | reflect.Uint64: setUint64, 23 | reflect.String: setString, 24 | reflect.Float32: setFloat32, 25 | reflect.Float64: setFloat64, 26 | reflect.Complex64: setComplex64, 27 | reflect.Complex128: setComplex128, 28 | reflect.Uintptr: setUintptr, 29 | } 30 | 31 | func getSetBaseFunc(t reflect.Kind) setUnsafeFunc { 32 | f, ok := copyTab[t] 33 | if !ok { 34 | panic(fmt.Sprintf("not support type:%T", t)) 35 | } 36 | return f 37 | } 38 | 39 | func setUintptr(dstAddr, srcAddr unsafe.Pointer) { 40 | *(*uintptr)(dstAddr) = *(*uintptr)(srcAddr) 41 | } 42 | 43 | func setBool(dstAddr, srcAddr unsafe.Pointer) { 44 | *(*bool)(dstAddr) = *(*bool)(srcAddr) 45 | } 46 | 47 | func setInt(dstAddr, srcAddr unsafe.Pointer) { 48 | *(*int)(dstAddr) = *(*int)(srcAddr) 49 | } 50 | 51 | func setInt8(dstAddr, srcAddr unsafe.Pointer) { 52 | *(*int8)(dstAddr) = *(*int8)(srcAddr) 53 | } 54 | 55 | func setInt16(dstAddr, srcAddr unsafe.Pointer) { 56 | *(*int16)(dstAddr) = *(*int16)(srcAddr) 57 | } 58 | 59 | func setInt32(dstAddr, srcAddr unsafe.Pointer) { 60 | *(*int32)(dstAddr) = *(*int32)(srcAddr) 61 | } 62 | 63 | func setInt64(dstAddr, srcAddr unsafe.Pointer) { 64 | *(*int64)(dstAddr) = *(*int64)(srcAddr) 65 | } 66 | 67 | func setUint(dstAddr, srcAddr unsafe.Pointer) { 68 | *(*uint)(dstAddr) = *(*uint)(srcAddr) 69 | } 70 | 71 | func setUint8(dstAddr, srcAddr unsafe.Pointer) { 72 | *(*uint8)(dstAddr) = *(*uint8)(srcAddr) 73 | } 74 | 75 | func setUint16(dstAddr, srcAddr unsafe.Pointer) { 76 | *(*uint16)(dstAddr) = *(*uint16)(srcAddr) 77 | } 78 | 79 | func setUint32(dstAddr, srcAddr unsafe.Pointer) { 80 | *(*uint32)(dstAddr) = *(*uint32)(srcAddr) 81 | } 82 | 83 | func setUint64(dstAddr, srcAddr unsafe.Pointer) { 84 | *(*uint64)(dstAddr) = *(*uint64)(srcAddr) 85 | } 86 | 87 | func setString(dstAddr, srcAddr unsafe.Pointer) { 88 | *(*string)(dstAddr) = *(*string)(srcAddr) 89 | } 90 | 91 | func setFloat32(dstAddr, srcAddr unsafe.Pointer) { 92 | *(*float32)(dstAddr) = *(*float32)(srcAddr) 93 | } 94 | 95 | func setFloat64(dstAddr, srcAddr unsafe.Pointer) { 96 | *(*float64)(dstAddr) = *(*float64)(srcAddr) 97 | } 98 | 99 | func setComplex64(dstAddr, srcAddr unsafe.Pointer) { 100 | *(*complex64)(dstAddr) = *(*complex64)(srcAddr) 101 | } 102 | 103 | func setComplex128(dstAddr, srcAddr unsafe.Pointer) { 104 | *(*complex128)(dstAddr) = *(*complex128)(srcAddr) 105 | } 106 | 107 | func setTime(dstAddr, srcAddr unsafe.Pointer) { 108 | *(*time.Time)(dstAddr) = *(*time.Time)(srcAddr) 109 | } 110 | -------------------------------------------------------------------------------- /set_composite_type.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "fmt" 6 | "reflect" 7 | "unsafe" 8 | ) 9 | 10 | var copyCompositeTab = map[reflect.Kind]setReflectFunc{} 11 | 12 | func init() { 13 | // 这是一个复合类型,最外层是slice 14 | copyCompositeTab[reflect.Slice] = setCompositeSlice 15 | // 这是一个复合类型,最外层是map 16 | copyCompositeTab[reflect.Map] = setCompositeMap 17 | // 这是一个复合类型,最外层是interface 18 | copyCompositeTab[reflect.Interface] = setCompositeInterface 19 | // 这是一个复全类型, 最外层是ptr 20 | copyCompositeTab[reflect.Ptr] = setCompositePtr 21 | } 22 | 23 | type emptyInterface struct { 24 | typ unsafe.Pointer 25 | word unsafe.Pointer 26 | } 27 | 28 | func setCompositePtr(dstType, srcType reflect.Type, dst, src unsafe.Pointer, opt options, of *offsetAndFunc) (err error) { 29 | // 1.基础类型的指针 30 | // 2.基础slice的指针 31 | // 3.基础map的指针 32 | dstVal := reflect.NewAt(dstType, dst) 33 | srcVal := reflect.NewAt(srcType, src) 34 | for { 35 | 36 | if srcVal.Kind() == reflect.Ptr && srcVal.IsNil() { 37 | return nil 38 | } 39 | 40 | if dstVal.Kind() == reflect.Ptr && dstVal.IsNil() { 41 | dstVal.Set(reflect.New(dstVal.Type().Elem())) 42 | } 43 | 44 | if srcVal.Kind() == reflect.Ptr { 45 | srcVal = srcVal.Elem() 46 | } 47 | 48 | if dstVal.Kind() == reflect.Ptr { 49 | dstVal = dstVal.Elem() 50 | } 51 | 52 | if srcVal.Type().Kind() != reflect.Ptr && dstVal.Type().Kind() != reflect.Ptr { 53 | break 54 | } 55 | } 56 | 57 | if of.unsafeSet != nil { 58 | of.unsafeSet(unsafe.Pointer(dstVal.UnsafeAddr()), unsafe.Pointer(srcVal.UnsafeAddr())) 59 | return nil 60 | } 61 | 62 | if dstVal.Kind() == reflect.Interface && srcVal.Kind() == reflect.Interface { 63 | return setCompositeInterface(dstVal.Type(), srcVal.Type(), unsafe.Pointer(dstVal.UnsafeAddr()), unsafe.Pointer(srcVal.UnsafeAddr()), opt, of) 64 | } else if dstVal.Kind() == reflect.Slice && srcVal.Kind() == reflect.Slice { 65 | return setCompositeSlice(dstVal.Type(), srcVal.Type(), unsafe.Pointer(dstVal.UnsafeAddr()), unsafe.Pointer(srcVal.UnsafeAddr()), opt, of) 66 | } else if dstVal.Kind() == reflect.Map && srcVal.Kind() == reflect.Map { 67 | return setCompositeMap(dstVal.Type(), srcVal.Type(), unsafe.Pointer(dstVal.UnsafeAddr()), unsafe.Pointer(srcVal.UnsafeAddr()), opt, of) 68 | } 69 | 70 | exits, err := getFromCacheSetAndRun(dstSrcType{dst: dstVal.Type(), src: srcVal.Type()}, unsafe.Pointer(dstVal.UnsafeAddr()), unsafe.Pointer(srcVal.UnsafeAddr()), opt) 71 | if err != nil || exits { 72 | return err 73 | } 74 | 75 | return pcopyInner(dstVal.Interface(), srcVal.Interface(), opt) 76 | } 77 | 78 | func setCompositeInterface(dstType, srcType reflect.Type, dst, src unsafe.Pointer, opt options, of *offsetAndFunc) error { 79 | if dstType.Kind() != reflect.Interface { 80 | panic("dstType is not interface") 81 | } 82 | // 暂不支持带方法的interface 83 | if srcType.NumMethod() > 0 { 84 | return nil 85 | } 86 | 87 | // 暂不支持带方法的interface 88 | srcUnsafe := (*emptyInterface)(src) 89 | if srcUnsafe.typ == nil || srcUnsafe.word == nil { 90 | return nil 91 | } 92 | 93 | srcInter := (*interface{})(src) 94 | srcType = reflect.TypeOf(*srcInter) 95 | srcTypeKind := srcType.Kind() 96 | 97 | if isBaseType(srcType.Kind()) { 98 | *(*interface{})(dst) = getNewBaseType(srcTypeKind, *(*interface{})(src)) 99 | return nil 100 | } 101 | 102 | if srcTypeKind == reflect.Slice { 103 | sh := (*reflect.SliceHeader)(srcUnsafe.word) 104 | if sh.Len == 0 { 105 | return nil 106 | } 107 | elemKind := srcType.Elem().Kind() 108 | if isBaseType(elemKind) { 109 | *(*interface{})(dst) = getNewBaseSliceType(elemKind, sh) 110 | return nil 111 | } 112 | } 113 | 114 | // TODO 这里也可以细化出map的情况 115 | // 以后再优化 116 | 117 | return pcopyInner((*interface{})(dst), (*interface{})(src), opt) 118 | } 119 | 120 | // map 拿不到UnsafeAddr指针,所以只能用reflect.Value来做 121 | func setCompositeMap(dstType, srcType reflect.Type, dst, src unsafe.Pointer, opt options, of *offsetAndFunc) (err error) { 122 | srcMap := reflect.NewAt(srcType, src).Elem() 123 | if srcMap.Len() == 0 { 124 | return nil 125 | } 126 | 127 | dstMap := reflect.NewAt(dstType, dst).Elem() 128 | if dstMap.IsNil() { 129 | dstMap.Set(reflect.MakeMapWithSize(dstType, srcMap.Len())) 130 | } 131 | 132 | mapKeyType := dstMap.Type().Key() 133 | mapValType := dstMap.Type().Elem() 134 | 135 | // 获取src map的值 136 | iter := srcMap.MapRange() 137 | 138 | isBaseKeyType := isBaseType(mapKeyType.Kind()) 139 | isBaseValType := isBaseType(mapValType.Kind()) 140 | 141 | // 对map进行反射得到的值不能取地址,所以这里要关闭preheat和usePreheat 142 | opt.preheat = false 143 | opt.usePreheat = false 144 | 145 | for iter.Next() { 146 | srcMapKey := iter.Key() 147 | srcMapVal := iter.Value() 148 | 149 | newDstVal := reflect.New(mapValType) 150 | newDstKey := reflect.New(mapKeyType) 151 | 152 | if isBaseKeyType { 153 | newKey2 := getNewBaseType(mapKeyType.Kind(), srcMapKey.Interface()) 154 | newDstKey.Elem().Set(reflect.ValueOf(newKey2)) 155 | } else { 156 | if err = pcopyInner(newDstKey.Interface(), srcMapKey.Interface(), opt); err != nil { 157 | return err 158 | } 159 | } 160 | 161 | if isBaseValType { 162 | newVal2 := getNewBaseType(mapValType.Kind(), srcMapVal.Interface()) 163 | newDstVal.Elem().Set(reflect.ValueOf(newVal2)) 164 | goto next 165 | } 166 | 167 | if err = pcopyInner(newDstVal.Interface(), srcMapVal.Interface(), opt); err != nil { 168 | return err 169 | } 170 | next: 171 | dstMap.SetMapIndex(newDstKey.Elem(), newDstVal.Elem()) 172 | } 173 | return err 174 | } 175 | 176 | func setCompositeSlice(dstType, srcType reflect.Type, dst, src unsafe.Pointer, opt options, of *offsetAndFunc) error { 177 | srcValPtr := reflect.NewAt(srcType, src) 178 | // src转成reflect.Value 179 | srcVal := srcValPtr.Elem() 180 | if srcVal.Len() == 0 { 181 | return nil 182 | } 183 | 184 | dstValPtr := reflect.NewAt(dstType, dst) 185 | // dst转成reflect.Value 186 | dstVal := dstValPtr.Elem() 187 | if dstVal.Len() < srcVal.Len() { 188 | dstVal.Set(reflect.MakeSlice(dstType, srcVal.Len(), srcVal.Len())) 189 | } 190 | 191 | dstOffset := dstVal.Type().Elem().Size() 192 | srcOffset := srcVal.Type().Elem().Size() 193 | subDstType := dstVal.Type().Elem() 194 | subSrcType := srcVal.Type().Elem() 195 | 196 | key := dstSrcType{dst: subDstType, src: subSrcType} 197 | 198 | dstSliceAddr := unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(dst)).Data) 199 | srcSliceAddr := unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(src)).Data) 200 | 201 | exits, err := getFromCacheSetAndRun(key, dstSliceAddr, srcSliceAddr, opt) 202 | if err != nil { 203 | return err 204 | } 205 | if exits { 206 | for i := 1; i < srcVal.Len(); i++ { 207 | exits, err = getFromCacheSetAndRun(key, addOffset(dstSliceAddr, dstOffset, i), addOffset(srcSliceAddr, srcOffset, i), opt) 208 | if err != nil { 209 | return err 210 | } 211 | if !exits { 212 | // 这里不可能出现exits为false的情况, 除非进程空间被unsafe.Pointer指针写坏了 213 | panic(fmt.Sprintf("not support type:subDstType(%v) subSrcType(%v)", key.dst, key.src)) 214 | } 215 | } 216 | return nil 217 | } 218 | 219 | if subDstType.Kind() == reflect.Ptr || subSrcType.Kind() == reflect.Ptr { 220 | 221 | for i := 0; i < srcVal.Len(); i++ { 222 | if err := setCompositePtr(subDstType, subSrcType, addOffset(dstSliceAddr, dstOffset, i), addOffset(srcSliceAddr, srcOffset, i), opt, of); err != nil { 223 | return err 224 | } 225 | } 226 | return nil 227 | } 228 | 229 | return pcopyInner(dstValPtr.Interface(), srcValPtr.Interface(), opt) 230 | } 231 | 232 | func getSetCompositeFunc(t reflect.Kind) setReflectFunc { 233 | f, ok := copyCompositeTab[t] 234 | if !ok { 235 | panic(fmt.Sprintf("not support type:%T", t)) 236 | } 237 | return f 238 | } 239 | -------------------------------------------------------------------------------- /set_composite_type_slice_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | -------------------------------------------------------------------------------- /set_slice_elem_is_ptr_test.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | // 生成set_slice_elem_is_ptr.go文件 5 | // func Test_Gen_setSliceElemIsPtr(t *testing.T) { 6 | // err := saveSliceElemIsPtrToFile("./set_slice_elem_is_ptr.go") 7 | // assert.NoError(t, err) 8 | // } 9 | -------------------------------------------------------------------------------- /set_slice_elem_is_ptr_tmpl.go: -------------------------------------------------------------------------------- 1 | // Copyright [2020-2023] [guonaihong] 2 | package pcopy 3 | 4 | import ( 5 | "bytes" 6 | "fmt" 7 | "go/format" 8 | "io/ioutil" 9 | "text/template" 10 | 11 | "github.com/Masterminds/sprig/v3" 12 | ) 13 | 14 | var sliceElemIsPtrFuncTmpl = ` 15 | // Copyright [2020-2024] [guonaihong] 16 | package pcopy 17 | 18 | import ( 19 | "fmt" 20 | "reflect" 21 | "unsafe" 22 | ) 23 | // 自动生成的代码, 不要修改 24 | // 生成的代码位于,如下位置 25 | // set_basemap_tmpl.go 26 | // 生成命令位于 set_basemap_test.go 27 | {{$TypeName := .TypeName}} 28 | {{range $_, $TypeVal := $TypeName}} 29 | {{$val := $TypeVal}} 30 | {{if eq $val "int"}} 31 | {{$val = "reflect.Int"}} 32 | {{else if eq $val "int8"}} 33 | {{$val = "reflect.Int8"}} 34 | {{else if eq $val "int16"}} 35 | {{$val = "reflect.Int16"}} 36 | {{else if eq $val "int32"}} 37 | {{$val = "reflect.Int32"}} 38 | {{else if eq $val "int64"}} 39 | {{$val = "reflect.Int64"}} 40 | {{else if eq $val "uint"}} 41 | {{$val = "reflect.Uint"}} 42 | {{else if eq $val "uint8"}} 43 | {{$val = "reflect.Uint8"}} 44 | {{else if eq $val "uint16"}} 45 | {{$val = "reflect.Uint16"}} 46 | {{else if eq $val "uint32"}} 47 | {{$val = "reflect.Uint32"}} 48 | {{else if eq $val "uint64"}} 49 | {{$val = "reflect.Uint64"}} 50 | {{else if eq $val "float32"}} 51 | {{$val = "reflect.Float32"}} 52 | {{else if eq $val "float64"}} 53 | {{$val = "reflect.Float64"}} 54 | {{else if eq $val "string"}} 55 | {{$val = "reflect.String"}} 56 | {{else if eq $val "bool"}} 57 | {{$val = "reflect.Bool"}} 58 | {{else if eq $val "complex64"}} 59 | {{$val = "reflect.Complex64"}} 60 | {{else if eq $val "complex128"}} 61 | {{$val = "reflect.Complex128"}} 62 | {{else if eq $val "uintptr"}} 63 | {{$val = "reflect.Uintptr"}} 64 | {{end}} 65 | func setSliceElemIsBaseTypeOrPtr{{$TypeVal|title}}(dstType, srcType reflect.Type, dst, src unsafe.Pointer, opt options, of *offsetAndFunc) (err error) { 66 | if srcType.Elem().Kind() == reflect.Ptr && dstType.Elem().Kind() == reflect.Ptr { 67 | srcSlice := *(*[]*{{$TypeVal}})(src) 68 | if len(srcSlice) == 0 { 69 | return nil 70 | } 71 | dstSlice := (*[]*{{$TypeVal}})(dst) 72 | if len(*dstSlice) < len(srcSlice) { 73 | *dstSlice = make([]*{{$TypeVal}}, 0, len(srcSlice)) 74 | } 75 | for _, v := range srcSlice { 76 | var dv {{$TypeVal}} 77 | if v != nil { 78 | dv = *v 79 | } 80 | *dstSlice = append(*dstSlice, &dv) 81 | } 82 | return nil 83 | } 84 | 85 | if srcType.Elem().Kind() == {{$val}} && dstType.Elem().Kind() == reflect.Ptr { 86 | srcSlice := *(*[]{{$TypeVal}})(src) 87 | if len(srcSlice) == 0 { 88 | return nil 89 | } 90 | dstSlice := (*[]*{{$TypeVal}})(dst) 91 | if len(*dstSlice) < len(srcSlice) { 92 | *dstSlice = make([]*{{$TypeVal}}, 0, len(srcSlice)) 93 | } 94 | for _, v := range srcSlice { 95 | dv := v 96 | *dstSlice = append(*dstSlice, &dv) 97 | } 98 | return nil 99 | } 100 | 101 | if srcType.Elem().Kind() == reflect.Ptr && dstType.Elem().Kind() == {{$val}} { 102 | srcSlice := *(*[]*{{$TypeVal}})(src) 103 | if len(srcSlice) == 0 { 104 | return nil 105 | } 106 | dstSlice := (*[]{{$TypeVal}})(dst) 107 | if len(*dstSlice) < len(srcSlice) { 108 | *dstSlice = make([]{{$TypeVal}}, 0, len(srcSlice)) 109 | } 110 | for _, v := range srcSlice { 111 | var dv {{$TypeVal}} 112 | if v != nil { 113 | dv = *v 114 | } 115 | *dstSlice = append(*dstSlice, dv) 116 | } 117 | return nil 118 | } 119 | return nil 120 | } 121 | {{end}} 122 | ` 123 | 124 | var sliceElemIsPtrTable = ` 125 | {{$TypeName := .TypeName}} 126 | var copySliceElemIsBaseTypeOrPtrTab = map[reflect.Kind]setReflectFunc{ 127 | 128 | {{range $_, $TypeVal := .TypeName}} 129 | {{$val := $TypeVal}} 130 | {{if eq $val "int"}} 131 | {{$val = "reflect.Int"}} 132 | {{else if eq $val "int8"}} 133 | {{$val = "reflect.Int8"}} 134 | {{else if eq $val "int16"}} 135 | {{$val = "reflect.Int16"}} 136 | {{else if eq $val "int32"}} 137 | {{$val = "reflect.Int32"}} 138 | {{else if eq $val "int64"}} 139 | {{$val = "reflect.Int64"}} 140 | {{else if eq $val "uint"}} 141 | {{$val = "reflect.Uint"}} 142 | {{else if eq $val "uint8"}} 143 | {{$val = "reflect.Uint8"}} 144 | {{else if eq $val "uint16"}} 145 | {{$val = "reflect.Uint16"}} 146 | {{else if eq $val "uint32"}} 147 | {{$val = "reflect.Uint32"}} 148 | {{else if eq $val "uint64"}} 149 | {{$val = "reflect.Uint64"}} 150 | {{else if eq $val "float32"}} 151 | {{$val = "reflect.Float32"}} 152 | {{else if eq $val "float64"}} 153 | {{$val = "reflect.Float64"}} 154 | {{else if eq $val "string"}} 155 | {{$val = "reflect.String"}} 156 | {{else if eq $val "bool"}} 157 | {{$val = "reflect.Bool"}} 158 | {{else if eq $val "uintptr"}} 159 | {{$val = "reflect.Uintptr"}} 160 | {{else if eq $val "complex64"}} 161 | {{$val = "reflect.Complex64"}} 162 | {{else if eq $val "complex128"}} 163 | {{$val = "reflect.Complex128"}} 164 | {{end}} 165 | 166 | {{$val}}: setSliceElemIsBaseTypeOrPtr{{$TypeVal|title}}, 167 | {{end}} 168 | } 169 | 170 | func getSetSliceElemIsBaseTypeOrPtrFunc(src reflect.Kind, p bool) setReflectFunc { 171 | f, ok := copySliceElemIsBaseTypeOrPtrTab[src] 172 | if p && !ok { 173 | panic(fmt.Sprintf("not support type:dst %v ", src)) 174 | } 175 | return f 176 | } 177 | ` 178 | 179 | // 基础类型表 180 | var _sliceElemIsPtrTable = []string{ 181 | "int", 182 | "int8", 183 | "int16", 184 | "int32", 185 | "int64", 186 | "uint", 187 | "uint8", 188 | "uint16", 189 | "uint32", 190 | "uint64", 191 | "float32", 192 | "float64", 193 | "string", 194 | "bool", 195 | "uintptr", 196 | "complex64", 197 | "complex128", 198 | } 199 | 200 | func saveSliceElemIsPtrToFile(fileName string) error { 201 | var out bytes.Buffer 202 | tmpl, err := template.New("sliceElmeIsPtrFuncTmpl").Funcs(sprig.TxtFuncMap()).Parse(sliceElemIsPtrFuncTmpl) 203 | if err != nil { 204 | return fmt.Errorf("build sliceElemIsPtrFuncTmpl fail:%w", err) 205 | } 206 | 207 | err = tmpl.Execute(&out, baseTypeTmpl{TypeName: _sliceElemIsPtrTable}) 208 | if err != nil { 209 | return err 210 | } 211 | tmpl, err = template.New("sliceElemIsPtrTable").Funcs(sprig.TxtFuncMap()).Parse(sliceElemIsPtrTable) 212 | if err != nil { 213 | return fmt.Errorf("build sliceElemIsPtrTableTmpl fail:%w", err) 214 | } 215 | err = tmpl.Execute(&out, baseTypeTmpl{TypeName: _sliceElemIsPtrTable}) 216 | if err != nil { 217 | return err 218 | } 219 | 220 | // 格式化代码 221 | src, err := format.Source(out.Bytes()) 222 | if err != nil { 223 | return fmt.Errorf("format code fail:%w", err) 224 | } 225 | 226 | return ioutil.WriteFile(fileName, src, 0o666) 227 | } 228 | --------------------------------------------------------------------------------