diff --git a/go.mod b/go.mod index 4e2f3ca..1f9dae0 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/cloudwego/frugal -go 1.17 +go 1.18 require ( github.com/cloudwego/gopkg v0.1.2 diff --git a/internal/reflect/append.go b/internal/reflect/append.go new file mode 100644 index 0000000..ca33c4d --- /dev/null +++ b/internal/reflect/append.go @@ -0,0 +1,105 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import "unsafe" + +func appendStruct(t *tType, b []byte, base unsafe.Pointer) ([]byte, error) { + sd := t.Sd + if base == nil { + return append(b, byte(tSTOP)), nil + } + var err error + for _, f := range sd.fields { + t := f.Type + p := unsafe.Add(base, f.Offset) + if f.CanSkipEncodeIfNil && *(*unsafe.Pointer)(p) == nil { + continue + } + if f.CanSkipIfDefault && t.Equal(f.Default, p) { + continue + } + + // field header + b = append(b, byte(t.WT), byte(f.ID>>8), byte(f.ID)) + + // field value + // the following code should be the same as func `appendAny` + // manually copy here for inlining: + + if t.IsPointer { + p = *(*unsafe.Pointer)(p) + } + if t.SimpleType { // fast path + switch t.T { + case tBYTE, tBOOL: + b = append(b, *(*byte)(p)) // for tBOOL, true -> 1, false -> 0 + case tI16: + b = appendUint16(b, *((*uint16)(p))) + case tI32: + b = appendUint32(b, *((*uint32)(p))) + case tENUM: + b = appendUint32(b, uint32(*((*int64)(p)))) + case tI64, tDOUBLE: + b = appendUint64(b, *((*uint64)(p))) + case tSTRING: + s := *((*string)(p)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + } + } else { + b, err = t.AppendFunc(t, b, p) + if err != nil { + return b, withFieldErr(err, sd, f) + } + } + } + if sd.hasUnknownFields { + xb := *(*[]byte)(unsafe.Add(base, sd.unknownFieldsOffset)) + if len(xb) > 0 { + b = append(b, xb...) + } + } + return append(b, byte(tSTOP)), nil +} + +func appendAny(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + if t.IsPointer { + p = *(*unsafe.Pointer)(p) + } + if t.SimpleType { + switch t.T { + case tBYTE, tBOOL: + b = append(b, *(*byte)(p)) // for tBOOL, true -> 1, false -> 0 + case tI16: + b = appendUint16(b, *((*uint16)(p))) + case tI32: + b = appendUint32(b, *((*uint32)(p))) + case tENUM: + b = appendUint32(b, uint32(*((*int64)(p)))) + case tI64, tDOUBLE: + b = appendUint64(b, *((*uint64)(p))) + case tSTRING: + s := *((*string)(p)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + } + return b, nil + } else { + return t.AppendFunc(t, b, p) + } +} diff --git a/internal/reflect/append_gen.sh b/internal/reflect/append_gen.sh new file mode 100755 index 0000000..0d82a26 --- /dev/null +++ b/internal/reflect/append_gen.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# +# Copyright 2024 CloudWeGo Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +export FRUGAL_GEN_APPEND_MAP_FILE="append_map_gen.go" +export FRUGAL_GEN_APPEND_LIST_FILE="append_list_gen.go" + +rm -f $FRUGAL_GEN_APPEND_MAP_FILE +rm -f $FRUGAL_GEN_APPEND_LIST_FILE + +exec go test -v -run=TestGenAppend diff --git a/internal/reflect/append_gen_test.go b/internal/reflect/append_gen_test.go new file mode 100644 index 0000000..a9e3fbe --- /dev/null +++ b/internal/reflect/append_gen_test.go @@ -0,0 +1,94 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import ( + "bytes" + "fmt" + "strings" +) + +var tOTHER = ttype(0xee) // must not in use, only for generating code + +func init() { + t2s[tOTHER] = "Other" // makes ttype2str work +} + +func ttype2FuncType(t ttype) string { + switch t { + case tSTRUCT, tMAP, tSET, tLIST: + t = tOTHER + case tDOUBLE: + t = tI64 + } + return ttype2str(t) +} + +var ( + defineErr = map[ttype]bool{tOTHER: true} + defineStr = map[ttype]bool{tSTRING: true} +) + +func getAppendCode(typ ttype, t, p string) string { + t2c := map[ttype]string{ + tBYTE: "b = append(b, *((*byte)({p})))", + tI16: "b = appendUint16(b, *((*uint16)({p})))", + tI32: "b = appendUint32(b, *((*uint32)({p})))", + tI64: "b = appendUint64(b, *((*uint64)({p})))", + tDOUBLE: "b = appendUint64(b, *((*uint64)({p})))", + tENUM: "b = appendUint32(b, uint32(*((*int64)({p}))))", + tSTRING: "s = *((*string)({p})); b = appendUint32(b, uint32(len(s))); b = append(b, s...)", + + // tSTRUCT, tMAP, tSET, tLIST -> tOTHER + tOTHER: `if {t}.IsPointer { + b, err = {t}.AppendFunc({t}, b, *(*unsafe.Pointer)({p})) + } else { + b, err = {t}.AppendFunc({t}, b, {p}) + } + if err != nil { + return b, err +}`, + } + s, ok := t2c[typ] + if !ok { + panic("type doesn't have code: " + ttype2str(typ)) + } + s = strings.ReplaceAll(s, "{t}", t) + s = strings.ReplaceAll(s, "{p}", p) + return s +} + +func codeWithLine(b []byte) string { + p := &strings.Builder{} + p.Grow(len(b) + 5*bytes.Count(b, []byte("\n"))) + + n := 1 + i := 0 + fmt.Fprintf(p, "%4d ", n) + for j := 0; j < len(b); j++ { + if b[j] == '\n' { + p.Write(b[i : j+1]) + i = j + 1 + n++ + fmt.Fprintf(p, "%4d ", n) + } + } + if i < len(b) { + p.Write(b[i:]) + } + return p.String() +} diff --git a/internal/reflect/append_list.go b/internal/reflect/append_list.go new file mode 100644 index 0000000..774e91b --- /dev/null +++ b/internal/reflect/append_list.go @@ -0,0 +1,67 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import "unsafe" + +var listAppendFuncs = map[ttype]appendFuncType{} + +func updateListAppendFunc(t *tType) { + if t.T != tLIST && t.T != tSET { + panic("[bug] type mismatch, got: " + ttype2str(t.T)) + } + f, ok := listAppendFuncs[t.V.T] + if ok { + t.AppendFunc = f + return + } + t.AppendFunc = appendListAny +} + +func registerListAppendFunc(t ttype, f appendFuncType) { + listAppendFuncs[t] = f +} + +func appendListHeader(t *tType, b []byte, p unsafe.Pointer) ([]byte, uint32, unsafe.Pointer) { + if *(*unsafe.Pointer)(p) == nil { + return append(b, byte(t.WT), 0, 0, 0, 0), 0, nil + } + h := (*sliceHeader)(p) + n := uint32(h.Len) + return append(b, byte(t.WT), + byte(n>>24), byte(n>>16), byte(n>>8), byte(n)), + n, h.UnsafePointer() +} + +func appendListAny(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + t = t.V + b, n, vp := appendListHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + for i := uint32(0); i < n; i++ { + if i != 0 { + vp = unsafe.Add(vp, t.Size) // move to next element + } + b, err = appendAny(t, b, vp) + if err != nil { + return b, err + } + } + return b, nil +} diff --git a/internal/reflect/append_list_gen.go b/internal/reflect/append_list_gen.go new file mode 100644 index 0000000..bfa1e40 --- /dev/null +++ b/internal/reflect/append_list_gen.go @@ -0,0 +1,152 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import "unsafe" + +// This File is generated by append_gen.sh. DO NOT EDIT. +// Template and code can be found in append_list_gen_test.go. + +func init() { + registerListAppendFunc(tBYTE, appendList_I08) + registerListAppendFunc(tI16, appendList_I16) + registerListAppendFunc(tI32, appendList_I32) + registerListAppendFunc(tI64, appendList_I64) + registerListAppendFunc(tDOUBLE, appendList_I64) + registerListAppendFunc(tENUM, appendList_ENUM) + registerListAppendFunc(tSTRING, appendList_STRING) + registerListAppendFunc(tSTRUCT, appendList_Other) + registerListAppendFunc(tMAP, appendList_Other) + registerListAppendFunc(tSET, appendList_Other) + registerListAppendFunc(tLIST, appendList_Other) +} + +func appendList_I08(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + t = t.V + b, n, vp := appendListHeader(t, b, p) + if n == 0 { + return b, nil + } + for i := uint32(0); i < n; i++ { + if i != 0 { + vp = unsafe.Add(vp, t.Size) + } + b = append(b, *((*byte)(vp))) + } + return b, nil +} + +func appendList_I16(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + t = t.V + b, n, vp := appendListHeader(t, b, p) + if n == 0 { + return b, nil + } + for i := uint32(0); i < n; i++ { + if i != 0 { + vp = unsafe.Add(vp, t.Size) + } + b = appendUint16(b, *((*uint16)(vp))) + } + return b, nil +} + +func appendList_I32(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + t = t.V + b, n, vp := appendListHeader(t, b, p) + if n == 0 { + return b, nil + } + for i := uint32(0); i < n; i++ { + if i != 0 { + vp = unsafe.Add(vp, t.Size) + } + b = appendUint32(b, *((*uint32)(vp))) + } + return b, nil +} + +func appendList_I64(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + t = t.V + b, n, vp := appendListHeader(t, b, p) + if n == 0 { + return b, nil + } + for i := uint32(0); i < n; i++ { + if i != 0 { + vp = unsafe.Add(vp, t.Size) + } + b = appendUint64(b, *((*uint64)(vp))) + } + return b, nil +} + +func appendList_ENUM(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + t = t.V + b, n, vp := appendListHeader(t, b, p) + if n == 0 { + return b, nil + } + for i := uint32(0); i < n; i++ { + if i != 0 { + vp = unsafe.Add(vp, t.Size) + } + b = appendUint32(b, uint32(*((*int64)(vp)))) + } + return b, nil +} + +func appendList_STRING(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + t = t.V + b, n, vp := appendListHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + for i := uint32(0); i < n; i++ { + if i != 0 { + vp = unsafe.Add(vp, t.Size) + } + s = *((*string)(vp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + } + return b, nil +} + +func appendList_Other(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + t = t.V + b, n, vp := appendListHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + for i := uint32(0); i < n; i++ { + if i != 0 { + vp = unsafe.Add(vp, t.Size) + } + if t.IsPointer { + b, err = t.AppendFunc(t, b, *(*unsafe.Pointer)(vp)) + } else { + b, err = t.AppendFunc(t, b, vp) + } + if err != nil { + return b, err + } + } + return b, nil +} diff --git a/internal/reflect/append_list_gen_test.go b/internal/reflect/append_list_gen_test.go new file mode 100644 index 0000000..c8f87bc --- /dev/null +++ b/internal/reflect/append_list_gen_test.go @@ -0,0 +1,180 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import ( + "bytes" + "fmt" + "go/format" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGenAppendListCode(t *testing.T) { + filename := os.Getenv("FRUGAL_GEN_APPEND_LIST_FILE") + if filename != "" { + genAppendListCode(t, filename) + return + } + + type EnumType int64 + + type Msg struct { + X int64 `frugal:"1,default,i64"` + Y int64 `frugal:"2,default,i64"` + } + + type TestStruct struct { + L1 []int8 `frugal:"1,optional,list"` + L2 []int16 `frugal:"2,optional,list"` + L3 []int32 `frugal:"3,optional,list"` + L4 []int64 `frugal:"4,optional,list"` + L5 []EnumType `frugal:"5,optional,list"` + L6 []string `frugal:"6,optional,list"` + L7 []*Msg `frugal:"7,optional,list"` + } + + var p0, p1 *TestStruct + var b []byte + var err error + + p0 = &TestStruct{ + L1: []int8{11, 12}, + L2: []int16{21, 22}, + L3: []int32{31, 32}, + L4: []int64{41, 42}, + L5: []EnumType{51, 52}, + L6: []string{"61", "62"}, + L7: []*Msg{{X: 71, Y: 72}, {X: 73, Y: 74}}, + } + b, err = Append(nil, p0) + require.NoError(t, err) + + p1 = &TestStruct{} + _, err = Decode(b, p1) + require.NoError(t, err) + require.Equal(t, p0, p1) + + // Empty list + p0 = &TestStruct{ + L1: []int8{}, + L2: []int16{}, + L3: []int32{}, + L4: []int64{}, + L5: []EnumType{}, + L6: []string{}, + L7: []*Msg{}, + } + b, err = Append(nil, p0) + require.NoError(t, err) + + p1 = &TestStruct{} + _, err = Decode(b, p1) + require.NoError(t, err) + require.Equal(t, p0, p1) + +} + +func genAppendListCode(t *testing.T, filename string) { + + defineErr := map[ttype]bool{tOTHER: true} + defineStr := map[ttype]bool{tSTRING: true} + + f := &bytes.Buffer{} + f.WriteString(appendListGenFileHeader) + + // func init + fmt.Fprintln(f, "func init() {") + supportTypes := []ttype{ + tBYTE, tI16, tI32, tI64, tDOUBLE, + tENUM, tSTRING, tSTRUCT, tMAP, tSET, tLIST, + } + t2var := map[ttype]string{ + tBYTE: "tBYTE", tI16: "tI16", tI32: "tI32", tI64: "tI64", tDOUBLE: "tDOUBLE", + tENUM: "tENUM", tSTRING: "tSTRING", + tSTRUCT: "tSTRUCT", tMAP: "tMAP", tSET: "tSET", tLIST: "tLIST", + } + for _, v := range supportTypes { + fmt.Fprintf(f, "registerListAppendFunc(%s, %s)\n", + t2var[v], appendListFuncName(v)) + } + fmt.Fprintln(f, "}") + fmt.Fprintln(f, "") + + // func appendList_XXX + for _, v := range []ttype{tBYTE, tI16, tI32, tI64, tENUM, tSTRING, tOTHER} { + fmt.Fprintf(f, "func %s(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) {\n", + appendListFuncName(v)) + fmt.Fprintln(f, "t = t.V") + fmt.Fprintln(f, "b, n, vp := appendListHeader(t, b, p)") + fmt.Fprintln(f, "if n == 0 { return b, nil }") + if defineErr[v] { + fmt.Fprintln(f, "var err error") + } else if defineStr[v] { + fmt.Fprintln(f, "var s string") + } + fmt.Fprintln(f, "for i := uint32(0); i < n; i++ {") + fmt.Fprintln(f, "if i != 0 { vp = unsafe.Add(vp, t.Size) }") + fmt.Fprintln(f, getAppendCode(v, "t", "vp")) + fmt.Fprintln(f, "}") + fmt.Fprintln(f, "return b, nil") + fmt.Fprintln(f, "}") + fmt.Fprintln(f, "") + } + + fileb, err := format.Source(f.Bytes()) + if err != nil { + t.Log(codeWithLine(f.Bytes())) + t.Fatal(err) + } + err = os.WriteFile(filename, fileb, 0o644) + if err != nil { + t.Fatal(err) + } + t.Logf("generated: %s", filename) +} + +func appendListFuncName(t ttype) string { + return fmt.Sprintf("appendList_%s", ttype2FuncType(t)) +} + +const appendListGenFileHeader = `/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import "unsafe" + +// This File is generated by append_gen.sh. DO NOT EDIT. +// Template and code can be found in append_list_gen_test.go. + +` diff --git a/internal/reflect/append_list_test.go b/internal/reflect/append_list_test.go new file mode 100644 index 0000000..4c720aa --- /dev/null +++ b/internal/reflect/append_list_test.go @@ -0,0 +1,46 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import ( + "testing" + "unsafe" + + "github.com/cloudwego/gopkg/protocol/thrift" + "github.com/stretchr/testify/require" +) + +func TestAppendListAny(t *testing.T) { + typ := &tType{T: tLIST} + typ.V = &tType{T: tI64, WT: tI64, Size: 8, SimpleType: true} + v := []int64{1, 2} + b, err := appendListAny(typ, nil, unsafe.Pointer(&v)) + require.NoError(t, err) + + x := thrift.BinaryProtocol{} + expectb := x.AppendListBegin(nil, thrift.I64, 2) + expectb = x.AppendI64(expectb, 1) + expectb = x.AppendI64(expectb, 2) + require.Equal(t, expectb, b) + + // empty case + v = nil + b, err = appendListAny(typ, nil, unsafe.Pointer(&v)) + require.NoError(t, err) + expectb = x.AppendListBegin(nil, thrift.I64, 0) + require.Equal(t, expectb, b) +} diff --git a/internal/reflect/append_map.go b/internal/reflect/append_map.go new file mode 100644 index 0000000..1895a62 --- /dev/null +++ b/internal/reflect/append_map.go @@ -0,0 +1,81 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import ( + "errors" + "unsafe" +) + +var mapAppendFuncs = map[struct{ k, v ttype }]appendFuncType{} + +func updateMapAppendFunc(t *tType) { + if t.T != tMAP { + panic("[bug] type mismatch, got: " + ttype2str(t.T)) + } + + f, ok := mapAppendFuncs[struct{ k, v ttype }{k: t.K.T, v: t.V.T}] + if ok { + t.AppendFunc = f + return + } + t.AppendFunc = appendMapAnyAny +} + +func registerMapAppendFunc(k, v ttype, f appendFuncType) { + mapAppendFuncs[struct{ k, v ttype }{k: k, v: v}] = f +} + +func checkMapN(n uint32) error { + if n == 0 { + return nil + } + return errors.New("map size changed during encoding") +} + +func appendMapHeader(t *tType, b []byte, p unsafe.Pointer) ([]byte, uint32) { + var n uint32 + if *(*unsafe.Pointer)(p) != nil { + n = uint32(maplen(*(*unsafe.Pointer)(p))) + } + return append(b, byte(t.K.WT), byte(t.V.WT), + byte(n>>24), byte(n>>16), byte(n>>8), byte(n)), n +} + +// this func will be replaced by funcs defined in append_map_gen.go +// see init() in append_map_gen.go for details. +func appendMapAnyAny(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b, err = appendAny(t.K, b, kp) + if err != nil { + return b, err + } + b, err = appendAny(t.V, b, vp) + if err != nil { + return b, err + } + } + return b, checkMapN(n) +} diff --git a/internal/reflect/append_map_gen.go b/internal/reflect/append_map_gen.go new file mode 100644 index 0000000..62540c6 --- /dev/null +++ b/internal/reflect/append_map_gen.go @@ -0,0 +1,984 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import "unsafe" + +// This File is generated by append_map_gen.sh. DO NOT EDIT. +// Template and code can be found in append_map_gen_test.go. + +func init() { + registerMapAppendFunc(tBYTE, tBYTE, appendMap_I08_I08) + registerMapAppendFunc(tBYTE, tI16, appendMap_I08_I16) + registerMapAppendFunc(tBYTE, tI32, appendMap_I08_I32) + registerMapAppendFunc(tBYTE, tI64, appendMap_I08_I64) + registerMapAppendFunc(tBYTE, tDOUBLE, appendMap_I08_I64) + registerMapAppendFunc(tBYTE, tENUM, appendMap_I08_ENUM) + registerMapAppendFunc(tBYTE, tSTRING, appendMap_I08_STRING) + registerMapAppendFunc(tBYTE, tSTRUCT, appendMap_I08_Other) + registerMapAppendFunc(tBYTE, tMAP, appendMap_I08_Other) + registerMapAppendFunc(tBYTE, tSET, appendMap_I08_Other) + registerMapAppendFunc(tBYTE, tLIST, appendMap_I08_Other) + registerMapAppendFunc(tI16, tBYTE, appendMap_I16_I08) + registerMapAppendFunc(tI16, tI16, appendMap_I16_I16) + registerMapAppendFunc(tI16, tI32, appendMap_I16_I32) + registerMapAppendFunc(tI16, tI64, appendMap_I16_I64) + registerMapAppendFunc(tI16, tDOUBLE, appendMap_I16_I64) + registerMapAppendFunc(tI16, tENUM, appendMap_I16_ENUM) + registerMapAppendFunc(tI16, tSTRING, appendMap_I16_STRING) + registerMapAppendFunc(tI16, tSTRUCT, appendMap_I16_Other) + registerMapAppendFunc(tI16, tMAP, appendMap_I16_Other) + registerMapAppendFunc(tI16, tSET, appendMap_I16_Other) + registerMapAppendFunc(tI16, tLIST, appendMap_I16_Other) + registerMapAppendFunc(tI32, tBYTE, appendMap_I32_I08) + registerMapAppendFunc(tI32, tI16, appendMap_I32_I16) + registerMapAppendFunc(tI32, tI32, appendMap_I32_I32) + registerMapAppendFunc(tI32, tI64, appendMap_I32_I64) + registerMapAppendFunc(tI32, tDOUBLE, appendMap_I32_I64) + registerMapAppendFunc(tI32, tENUM, appendMap_I32_ENUM) + registerMapAppendFunc(tI32, tSTRING, appendMap_I32_STRING) + registerMapAppendFunc(tI32, tSTRUCT, appendMap_I32_Other) + registerMapAppendFunc(tI32, tMAP, appendMap_I32_Other) + registerMapAppendFunc(tI32, tSET, appendMap_I32_Other) + registerMapAppendFunc(tI32, tLIST, appendMap_I32_Other) + registerMapAppendFunc(tI64, tBYTE, appendMap_I64_I08) + registerMapAppendFunc(tI64, tI16, appendMap_I64_I16) + registerMapAppendFunc(tI64, tI32, appendMap_I64_I32) + registerMapAppendFunc(tI64, tI64, appendMap_I64_I64) + registerMapAppendFunc(tI64, tDOUBLE, appendMap_I64_I64) + registerMapAppendFunc(tI64, tENUM, appendMap_I64_ENUM) + registerMapAppendFunc(tI64, tSTRING, appendMap_I64_STRING) + registerMapAppendFunc(tI64, tSTRUCT, appendMap_I64_Other) + registerMapAppendFunc(tI64, tMAP, appendMap_I64_Other) + registerMapAppendFunc(tI64, tSET, appendMap_I64_Other) + registerMapAppendFunc(tI64, tLIST, appendMap_I64_Other) + registerMapAppendFunc(tDOUBLE, tBYTE, appendMap_I64_I08) + registerMapAppendFunc(tDOUBLE, tI16, appendMap_I64_I16) + registerMapAppendFunc(tDOUBLE, tI32, appendMap_I64_I32) + registerMapAppendFunc(tDOUBLE, tI64, appendMap_I64_I64) + registerMapAppendFunc(tDOUBLE, tDOUBLE, appendMap_I64_I64) + registerMapAppendFunc(tDOUBLE, tENUM, appendMap_I64_ENUM) + registerMapAppendFunc(tDOUBLE, tSTRING, appendMap_I64_STRING) + registerMapAppendFunc(tDOUBLE, tSTRUCT, appendMap_I64_Other) + registerMapAppendFunc(tDOUBLE, tMAP, appendMap_I64_Other) + registerMapAppendFunc(tDOUBLE, tSET, appendMap_I64_Other) + registerMapAppendFunc(tDOUBLE, tLIST, appendMap_I64_Other) + registerMapAppendFunc(tENUM, tBYTE, appendMap_ENUM_I08) + registerMapAppendFunc(tENUM, tI16, appendMap_ENUM_I16) + registerMapAppendFunc(tENUM, tI32, appendMap_ENUM_I32) + registerMapAppendFunc(tENUM, tI64, appendMap_ENUM_I64) + registerMapAppendFunc(tENUM, tDOUBLE, appendMap_ENUM_I64) + registerMapAppendFunc(tENUM, tENUM, appendMap_ENUM_ENUM) + registerMapAppendFunc(tENUM, tSTRING, appendMap_ENUM_STRING) + registerMapAppendFunc(tENUM, tSTRUCT, appendMap_ENUM_Other) + registerMapAppendFunc(tENUM, tMAP, appendMap_ENUM_Other) + registerMapAppendFunc(tENUM, tSET, appendMap_ENUM_Other) + registerMapAppendFunc(tENUM, tLIST, appendMap_ENUM_Other) + registerMapAppendFunc(tSTRING, tBYTE, appendMap_STRING_I08) + registerMapAppendFunc(tSTRING, tI16, appendMap_STRING_I16) + registerMapAppendFunc(tSTRING, tI32, appendMap_STRING_I32) + registerMapAppendFunc(tSTRING, tI64, appendMap_STRING_I64) + registerMapAppendFunc(tSTRING, tDOUBLE, appendMap_STRING_I64) + registerMapAppendFunc(tSTRING, tENUM, appendMap_STRING_ENUM) + registerMapAppendFunc(tSTRING, tSTRING, appendMap_STRING_STRING) + registerMapAppendFunc(tSTRING, tSTRUCT, appendMap_STRING_Other) + registerMapAppendFunc(tSTRING, tMAP, appendMap_STRING_Other) + registerMapAppendFunc(tSTRING, tSET, appendMap_STRING_Other) + registerMapAppendFunc(tSTRING, tLIST, appendMap_STRING_Other) + registerMapAppendFunc(tSTRUCT, tBYTE, appendMap_Other_I08) + registerMapAppendFunc(tSTRUCT, tI16, appendMap_Other_I16) + registerMapAppendFunc(tSTRUCT, tI32, appendMap_Other_I32) + registerMapAppendFunc(tSTRUCT, tI64, appendMap_Other_I64) + registerMapAppendFunc(tSTRUCT, tDOUBLE, appendMap_Other_I64) + registerMapAppendFunc(tSTRUCT, tENUM, appendMap_Other_ENUM) + registerMapAppendFunc(tSTRUCT, tSTRING, appendMap_Other_STRING) + registerMapAppendFunc(tSTRUCT, tSTRUCT, appendMap_Other_Other) + registerMapAppendFunc(tSTRUCT, tMAP, appendMap_Other_Other) + registerMapAppendFunc(tSTRUCT, tSET, appendMap_Other_Other) + registerMapAppendFunc(tSTRUCT, tLIST, appendMap_Other_Other) + registerMapAppendFunc(tMAP, tBYTE, appendMap_Other_I08) + registerMapAppendFunc(tMAP, tI16, appendMap_Other_I16) + registerMapAppendFunc(tMAP, tI32, appendMap_Other_I32) + registerMapAppendFunc(tMAP, tI64, appendMap_Other_I64) + registerMapAppendFunc(tMAP, tDOUBLE, appendMap_Other_I64) + registerMapAppendFunc(tMAP, tENUM, appendMap_Other_ENUM) + registerMapAppendFunc(tMAP, tSTRING, appendMap_Other_STRING) + registerMapAppendFunc(tMAP, tSTRUCT, appendMap_Other_Other) + registerMapAppendFunc(tMAP, tMAP, appendMap_Other_Other) + registerMapAppendFunc(tMAP, tSET, appendMap_Other_Other) + registerMapAppendFunc(tMAP, tLIST, appendMap_Other_Other) + registerMapAppendFunc(tSET, tBYTE, appendMap_Other_I08) + registerMapAppendFunc(tSET, tI16, appendMap_Other_I16) + registerMapAppendFunc(tSET, tI32, appendMap_Other_I32) + registerMapAppendFunc(tSET, tI64, appendMap_Other_I64) + registerMapAppendFunc(tSET, tDOUBLE, appendMap_Other_I64) + registerMapAppendFunc(tSET, tENUM, appendMap_Other_ENUM) + registerMapAppendFunc(tSET, tSTRING, appendMap_Other_STRING) + registerMapAppendFunc(tSET, tSTRUCT, appendMap_Other_Other) + registerMapAppendFunc(tSET, tMAP, appendMap_Other_Other) + registerMapAppendFunc(tSET, tSET, appendMap_Other_Other) + registerMapAppendFunc(tSET, tLIST, appendMap_Other_Other) + registerMapAppendFunc(tLIST, tBYTE, appendMap_Other_I08) + registerMapAppendFunc(tLIST, tI16, appendMap_Other_I16) + registerMapAppendFunc(tLIST, tI32, appendMap_Other_I32) + registerMapAppendFunc(tLIST, tI64, appendMap_Other_I64) + registerMapAppendFunc(tLIST, tDOUBLE, appendMap_Other_I64) + registerMapAppendFunc(tLIST, tENUM, appendMap_Other_ENUM) + registerMapAppendFunc(tLIST, tSTRING, appendMap_Other_STRING) + registerMapAppendFunc(tLIST, tSTRUCT, appendMap_Other_Other) + registerMapAppendFunc(tLIST, tMAP, appendMap_Other_Other) + registerMapAppendFunc(tLIST, tSET, appendMap_Other_Other) + registerMapAppendFunc(tLIST, tLIST, appendMap_Other_Other) +} + +func appendMap_I08_I08(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = append(b, *((*byte)(kp))) + b = append(b, *((*byte)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I08_I16(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = append(b, *((*byte)(kp))) + b = appendUint16(b, *((*uint16)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I08_I32(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = append(b, *((*byte)(kp))) + b = appendUint32(b, *((*uint32)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I08_I64(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = append(b, *((*byte)(kp))) + b = appendUint64(b, *((*uint64)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I08_ENUM(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = append(b, *((*byte)(kp))) + b = appendUint32(b, uint32(*((*int64)(vp)))) + } + return b, checkMapN(n) +} + +func appendMap_I08_STRING(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = append(b, *((*byte)(kp))) + s = *((*string)(vp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + } + return b, checkMapN(n) +} + +func appendMap_I08_Other(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = append(b, *((*byte)(kp))) + if t.V.IsPointer { + b, err = t.V.AppendFunc(t.V, b, *(*unsafe.Pointer)(vp)) + } else { + b, err = t.V.AppendFunc(t.V, b, vp) + } + if err != nil { + return b, err + } + } + return b, checkMapN(n) +} + +func appendMap_I16_I08(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint16(b, *((*uint16)(kp))) + b = append(b, *((*byte)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I16_I16(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint16(b, *((*uint16)(kp))) + b = appendUint16(b, *((*uint16)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I16_I32(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint16(b, *((*uint16)(kp))) + b = appendUint32(b, *((*uint32)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I16_I64(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint16(b, *((*uint16)(kp))) + b = appendUint64(b, *((*uint64)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I16_ENUM(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint16(b, *((*uint16)(kp))) + b = appendUint32(b, uint32(*((*int64)(vp)))) + } + return b, checkMapN(n) +} + +func appendMap_I16_STRING(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint16(b, *((*uint16)(kp))) + s = *((*string)(vp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + } + return b, checkMapN(n) +} + +func appendMap_I16_Other(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint16(b, *((*uint16)(kp))) + if t.V.IsPointer { + b, err = t.V.AppendFunc(t.V, b, *(*unsafe.Pointer)(vp)) + } else { + b, err = t.V.AppendFunc(t.V, b, vp) + } + if err != nil { + return b, err + } + } + return b, checkMapN(n) +} + +func appendMap_I32_I08(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, *((*uint32)(kp))) + b = append(b, *((*byte)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I32_I16(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, *((*uint32)(kp))) + b = appendUint16(b, *((*uint16)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I32_I32(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, *((*uint32)(kp))) + b = appendUint32(b, *((*uint32)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I32_I64(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, *((*uint32)(kp))) + b = appendUint64(b, *((*uint64)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I32_ENUM(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, *((*uint32)(kp))) + b = appendUint32(b, uint32(*((*int64)(vp)))) + } + return b, checkMapN(n) +} + +func appendMap_I32_STRING(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, *((*uint32)(kp))) + s = *((*string)(vp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + } + return b, checkMapN(n) +} + +func appendMap_I32_Other(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, *((*uint32)(kp))) + if t.V.IsPointer { + b, err = t.V.AppendFunc(t.V, b, *(*unsafe.Pointer)(vp)) + } else { + b, err = t.V.AppendFunc(t.V, b, vp) + } + if err != nil { + return b, err + } + } + return b, checkMapN(n) +} + +func appendMap_I64_I08(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint64(b, *((*uint64)(kp))) + b = append(b, *((*byte)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I64_I16(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint64(b, *((*uint64)(kp))) + b = appendUint16(b, *((*uint16)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I64_I32(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint64(b, *((*uint64)(kp))) + b = appendUint32(b, *((*uint32)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I64_I64(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint64(b, *((*uint64)(kp))) + b = appendUint64(b, *((*uint64)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_I64_ENUM(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint64(b, *((*uint64)(kp))) + b = appendUint32(b, uint32(*((*int64)(vp)))) + } + return b, checkMapN(n) +} + +func appendMap_I64_STRING(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint64(b, *((*uint64)(kp))) + s = *((*string)(vp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + } + return b, checkMapN(n) +} + +func appendMap_I64_Other(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint64(b, *((*uint64)(kp))) + if t.V.IsPointer { + b, err = t.V.AppendFunc(t.V, b, *(*unsafe.Pointer)(vp)) + } else { + b, err = t.V.AppendFunc(t.V, b, vp) + } + if err != nil { + return b, err + } + } + return b, checkMapN(n) +} + +func appendMap_ENUM_I08(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, uint32(*((*int64)(kp)))) + b = append(b, *((*byte)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_ENUM_I16(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, uint32(*((*int64)(kp)))) + b = appendUint16(b, *((*uint16)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_ENUM_I32(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, uint32(*((*int64)(kp)))) + b = appendUint32(b, *((*uint32)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_ENUM_I64(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, uint32(*((*int64)(kp)))) + b = appendUint64(b, *((*uint64)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_ENUM_ENUM(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, uint32(*((*int64)(kp)))) + b = appendUint32(b, uint32(*((*int64)(vp)))) + } + return b, checkMapN(n) +} + +func appendMap_ENUM_STRING(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, uint32(*((*int64)(kp)))) + s = *((*string)(vp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + } + return b, checkMapN(n) +} + +func appendMap_ENUM_Other(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + b = appendUint32(b, uint32(*((*int64)(kp)))) + if t.V.IsPointer { + b, err = t.V.AppendFunc(t.V, b, *(*unsafe.Pointer)(vp)) + } else { + b, err = t.V.AppendFunc(t.V, b, vp) + } + if err != nil { + return b, err + } + } + return b, checkMapN(n) +} + +func appendMap_STRING_I08(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + s = *((*string)(kp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + b = append(b, *((*byte)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_STRING_I16(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + s = *((*string)(kp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + b = appendUint16(b, *((*uint16)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_STRING_I32(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + s = *((*string)(kp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + b = appendUint32(b, *((*uint32)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_STRING_I64(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + s = *((*string)(kp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + b = appendUint64(b, *((*uint64)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_STRING_ENUM(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + s = *((*string)(kp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + b = appendUint32(b, uint32(*((*int64)(vp)))) + } + return b, checkMapN(n) +} + +func appendMap_STRING_STRING(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + s = *((*string)(kp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + s = *((*string)(vp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + } + return b, checkMapN(n) +} + +func appendMap_STRING_Other(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + s = *((*string)(kp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + if t.V.IsPointer { + b, err = t.V.AppendFunc(t.V, b, *(*unsafe.Pointer)(vp)) + } else { + b, err = t.V.AppendFunc(t.V, b, vp) + } + if err != nil { + return b, err + } + } + return b, checkMapN(n) +} + +func appendMap_Other_I08(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + if t.K.IsPointer { + b, err = t.K.AppendFunc(t.K, b, *(*unsafe.Pointer)(kp)) + } else { + b, err = t.K.AppendFunc(t.K, b, kp) + } + if err != nil { + return b, err + } + b = append(b, *((*byte)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_Other_I16(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + if t.K.IsPointer { + b, err = t.K.AppendFunc(t.K, b, *(*unsafe.Pointer)(kp)) + } else { + b, err = t.K.AppendFunc(t.K, b, kp) + } + if err != nil { + return b, err + } + b = appendUint16(b, *((*uint16)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_Other_I32(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + if t.K.IsPointer { + b, err = t.K.AppendFunc(t.K, b, *(*unsafe.Pointer)(kp)) + } else { + b, err = t.K.AppendFunc(t.K, b, kp) + } + if err != nil { + return b, err + } + b = appendUint32(b, *((*uint32)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_Other_I64(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + if t.K.IsPointer { + b, err = t.K.AppendFunc(t.K, b, *(*unsafe.Pointer)(kp)) + } else { + b, err = t.K.AppendFunc(t.K, b, kp) + } + if err != nil { + return b, err + } + b = appendUint64(b, *((*uint64)(vp))) + } + return b, checkMapN(n) +} + +func appendMap_Other_ENUM(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + if t.K.IsPointer { + b, err = t.K.AppendFunc(t.K, b, *(*unsafe.Pointer)(kp)) + } else { + b, err = t.K.AppendFunc(t.K, b, kp) + } + if err != nil { + return b, err + } + b = appendUint32(b, uint32(*((*int64)(vp)))) + } + return b, checkMapN(n) +} + +func appendMap_Other_STRING(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + var s string + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + if t.K.IsPointer { + b, err = t.K.AppendFunc(t.K, b, *(*unsafe.Pointer)(kp)) + } else { + b, err = t.K.AppendFunc(t.K, b, kp) + } + if err != nil { + return b, err + } + s = *((*string)(vp)) + b = appendUint32(b, uint32(len(s))) + b = append(b, s...) + } + return b, checkMapN(n) +} + +func appendMap_Other_Other(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) { + b, n := appendMapHeader(t, b, p) + if n == 0 { + return b, nil + } + var err error + it := newMapIter(rvWithPtr(t.RV, p)) + for kp, vp := it.Next(); kp != nil; kp, vp = it.Next() { + n-- + if t.K.IsPointer { + b, err = t.K.AppendFunc(t.K, b, *(*unsafe.Pointer)(kp)) + } else { + b, err = t.K.AppendFunc(t.K, b, kp) + } + if err != nil { + return b, err + } + if t.V.IsPointer { + b, err = t.V.AppendFunc(t.V, b, *(*unsafe.Pointer)(vp)) + } else { + b, err = t.V.AppendFunc(t.V, b, vp) + } + if err != nil { + return b, err + } + } + return b, checkMapN(n) +} diff --git a/internal/reflect/append_map_gen_test.go b/internal/reflect/append_map_gen_test.go new file mode 100644 index 0000000..1e14635 --- /dev/null +++ b/internal/reflect/append_map_gen_test.go @@ -0,0 +1,293 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import ( + "bytes" + "fmt" + "go/format" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGenAppendMapCode(t *testing.T) { + filename := os.Getenv("FRUGAL_GEN_APPEND_MAP_FILE") + if filename != "" { + genAppendMapCode(t, filename) + return + } + + type EnumType int64 + type EmptyStruct struct{} + + doTest := func(t *testing.T, p0, p1 interface{}) { + t.Helper() + b, err := Append(nil, p0) + require.NoError(t, err) + _, err = Decode(b, p1) + require.NoError(t, err) + require.Equal(t, p0, p1) + } + + { + type TestStruct struct { + M1 map[int8]int8 `frugal:"1,optional,map"` + M2 map[int8]int16 `frugal:"2,optional,map"` + M3 map[int8]int32 `frugal:"3,optional,map"` + M4 map[int8]int64 `frugal:"4,optional,map"` + M5 map[int8]EnumType `frugal:"5,optional,map"` + M6 map[int8]string `frugal:"6,optional,map"` + M7 map[int8]*EmptyStruct `frugal:"7,optional,map"` + } + p0 := &TestStruct{ + M1: map[int8]int8{11: 1, 12: 2}, + M2: map[int8]int16{21: 1, 22: 2}, + M3: map[int8]int32{31: 1, 32: 2}, + M4: map[int8]int64{41: 1, 42: 2}, + M5: map[int8]EnumType{51: 1, 52: 2}, + M6: map[int8]string{61: "1", 62: "2"}, + M7: map[int8]*EmptyStruct{71: {}, 72: {}}, + } + p1 := &TestStruct{} + doTest(t, p0, p1) + } + + { + type TestStruct struct { + M1 map[int16]int8 `frugal:"1,optional,map"` + M2 map[int16]int16 `frugal:"2,optional,map"` + M3 map[int16]int32 `frugal:"3,optional,map"` + M4 map[int16]int64 `frugal:"4,optional,map"` + M5 map[int16]EnumType `frugal:"5,optional,map"` + M6 map[int16]string `frugal:"6,optional,map"` + M7 map[int16]*EmptyStruct `frugal:"7,optional,map"` + } + p0 := &TestStruct{ + M1: map[int16]int8{11: 1, 12: 2}, + M2: map[int16]int16{21: 1, 22: 2}, + M3: map[int16]int32{31: 1, 32: 2}, + M4: map[int16]int64{41: 1, 42: 2}, + M5: map[int16]EnumType{51: 1, 52: 2}, + M6: map[int16]string{61: "1", 62: "2"}, + M7: map[int16]*EmptyStruct{71: {}, 72: {}}, + } + p1 := &TestStruct{} + doTest(t, p0, p1) + } + { + type TestStruct struct { + M1 map[int32]int8 `frugal:"1,optional,map"` + M2 map[int32]int16 `frugal:"2,optional,map"` + M3 map[int32]int32 `frugal:"3,optional,map"` + M4 map[int32]int64 `frugal:"4,optional,map"` + M5 map[int32]EnumType `frugal:"5,optional,map"` + M6 map[int32]string `frugal:"6,optional,map"` + M7 map[int32]*EmptyStruct `frugal:"7,optional,map"` + } + p0 := &TestStruct{ + M1: map[int32]int8{11: 1, 12: 2}, + M2: map[int32]int16{21: 1, 22: 2}, + M3: map[int32]int32{31: 1, 32: 2}, + M4: map[int32]int64{41: 1, 42: 2}, + M5: map[int32]EnumType{51: 1, 52: 2}, + M6: map[int32]string{61: "1", 62: "2"}, + M7: map[int32]*EmptyStruct{71: {}, 72: {}}, + } + p1 := &TestStruct{} + doTest(t, p0, p1) + } + { + type TestStruct struct { + M1 map[int64]int8 `frugal:"1,optional,map"` + M2 map[int64]int16 `frugal:"2,optional,map"` + M3 map[int64]int32 `frugal:"3,optional,map"` + M4 map[int64]int64 `frugal:"4,optional,map"` + M5 map[int64]EnumType `frugal:"5,optional,map"` + M6 map[int64]string `frugal:"6,optional,map"` + M7 map[int64]*EmptyStruct `frugal:"7,optional,map"` + } + p0 := &TestStruct{ + M1: map[int64]int8{11: 1, 12: 2}, + M2: map[int64]int16{21: 1, 22: 2}, + M3: map[int64]int32{31: 1, 32: 2}, + M4: map[int64]int64{41: 1, 42: 2}, + M5: map[int64]EnumType{51: 1, 52: 2}, + M6: map[int64]string{61: "1", 62: "2"}, + M7: map[int64]*EmptyStruct{71: {}, 72: {}}, + } + p1 := &TestStruct{} + doTest(t, p0, p1) + } + { + type TestStruct struct { + M1 map[EnumType]int8 `frugal:"1,optional,map"` + M2 map[EnumType]int16 `frugal:"2,optional,map"` + M3 map[EnumType]int32 `frugal:"3,optional,map"` + M4 map[EnumType]int64 `frugal:"4,optional,map"` + M5 map[EnumType]EnumType `frugal:"5,optional,map"` + M6 map[EnumType]string `frugal:"6,optional,map"` + M7 map[EnumType]*EmptyStruct `frugal:"7,optional,map"` + } + p0 := &TestStruct{ + M1: map[EnumType]int8{11: 1, 12: 2}, + M2: map[EnumType]int16{21: 1, 22: 2}, + M3: map[EnumType]int32{31: 1, 32: 2}, + M4: map[EnumType]int64{41: 1, 42: 2}, + M5: map[EnumType]EnumType{51: 1, 52: 2}, + M6: map[EnumType]string{61: "1", 62: "2"}, + M7: map[EnumType]*EmptyStruct{71: {}, 72: {}}, + } + p1 := &TestStruct{} + doTest(t, p0, p1) + } + { + type TestStruct struct { + M1 map[string]int8 `frugal:"1,optional,map"` + M2 map[string]int16 `frugal:"2,optional,map"` + M3 map[string]int32 `frugal:"3,optional,map"` + M4 map[string]int64 `frugal:"4,optional,map"` + M5 map[string]EnumType `frugal:"5,optional,map"` + M6 map[string]string `frugal:"6,optional,map"` + M7 map[string]*EmptyStruct `frugal:"7,optional,map"` + } + p0 := &TestStruct{ + M1: map[string]int8{"11": 1, "12": 2}, + M2: map[string]int16{"21": 1, "22": 2}, + M3: map[string]int32{"31": 1, "32": 2}, + M4: map[string]int64{"41": 1, "42": 2}, + M5: map[string]EnumType{"51": 1, "52": 2}, + M6: map[string]string{"61": "1", "62": "2"}, + M7: map[string]*EmptyStruct{"71": {}, "72": {}}, + } + p1 := &TestStruct{} + doTest(t, p0, p1) + } + { + type TestStruct struct { + M1 map[*EmptyStruct]int8 `frugal:"1,optional,map"` + M2 map[*EmptyStruct]int16 `frugal:"2,optional,map"` + M3 map[*EmptyStruct]int32 `frugal:"3,optional,map"` + M4 map[*EmptyStruct]int64 `frugal:"4,optional,map"` + M5 map[*EmptyStruct]EnumType `frugal:"5,optional,map"` + M6 map[*EmptyStruct]string `frugal:"6,optional,map"` + M7 map[*EmptyStruct]*EmptyStruct `frugal:"7,optional,map"` + } + p0 := &TestStruct{ + M1: map[*EmptyStruct]int8{{}: 1}, + M2: map[*EmptyStruct]int16{{}: 1}, + M3: map[*EmptyStruct]int32{{}: 1}, + M4: map[*EmptyStruct]int64{{}: 1}, + M5: map[*EmptyStruct]EnumType{{}: 1}, + M6: map[*EmptyStruct]string{{}: "1"}, + M7: map[*EmptyStruct]*EmptyStruct{{}: {}}, + } + p1 := &TestStruct{} + doTest(t, p0, p1) + } +} + +func genAppendMapCode(t *testing.T, filename string) { + f := &bytes.Buffer{} + f.WriteString(appendMapGenFileHeader) + + // func init + fmt.Fprintln(f, "func init() {") + supportTypes := []ttype{ + tBYTE, tI16, tI32, tI64, tDOUBLE, + tENUM, tSTRING, tSTRUCT, tMAP, tSET, tLIST, + } + t2var := map[ttype]string{ + tBYTE: "tBYTE", tI16: "tI16", tI32: "tI32", tI64: "tI64", tDOUBLE: "tDOUBLE", + tENUM: "tENUM", tSTRING: "tSTRING", + tSTRUCT: "tSTRUCT", tMAP: "tMAP", tSET: "tSET", tLIST: "tLIST", + } + for _, k := range supportTypes { + for _, v := range supportTypes { + fmt.Fprintf(f, "registerMapAppendFunc(%s, %s, %s)\n", + t2var[k], t2var[v], appendMapFuncName(k, v)) + } + } + fmt.Fprintln(f, "}") + fmt.Fprintln(f, "") + + // func appendMapXXX + for _, k := range []ttype{tBYTE, tI16, tI32, tI64, tENUM, tSTRING, tOTHER} { + for _, v := range []ttype{tBYTE, tI16, tI32, tI64, tENUM, tSTRING, tOTHER} { + fmt.Fprintf(f, "func %s(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) {\n", + appendMapFuncName(k, v)) + fmt.Fprintln(f, "b, n := appendMapHeader(t, b, p)") + fmt.Fprintln(f, "if n == 0 { return b, nil }") + if defineErr[k] || defineErr[v] { + fmt.Fprintln(f, "var err error") + } + if defineStr[k] || defineStr[v] { + fmt.Fprintln(f, "var s string") + } + fmt.Fprintln(f, "it := newMapIter(rvWithPtr(t.RV, p))") + fmt.Fprintln(f, "for kp, vp := it.Next(); kp != nil;kp, vp = it.Next() {") + fmt.Fprintln(f, "n--") + fmt.Fprintln(f, getAppendCode(k, "t.K", "kp")) + fmt.Fprintln(f, getAppendCode(v, "t.V", "vp")) + fmt.Fprintln(f, "}") + fmt.Fprintln(f, "return b, checkMapN(n)") + fmt.Fprintln(f, "}") + fmt.Fprintln(f, "") + } + } + + fileb, err := format.Source(f.Bytes()) + if err != nil { + t.Log(codeWithLine(f.Bytes())) + t.Fatal(err) + } + err = os.WriteFile(filename, fileb, 0o644) + if err != nil { + t.Fatal(err) + } + t.Logf("generated: %s", filename) +} + +func appendMapFuncName(k, v ttype) string { + return fmt.Sprintf("appendMap_%s_%s", ttype2FuncType(k), ttype2FuncType(v)) +} + +const appendMapGenFileHeader = `/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import "unsafe" + +// This File is generated by append_map_gen.sh. DO NOT EDIT. +// Template and code can be found in append_map_gen_test.go. + +` diff --git a/internal/reflect/append_map_test.go b/internal/reflect/append_map_test.go new file mode 100644 index 0000000..2ee718f --- /dev/null +++ b/internal/reflect/append_map_test.go @@ -0,0 +1,63 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAppendMapAnyAny(t *testing.T) { + var funcs map[struct{ k, v ttype }]appendFuncType + funcs, mapAppendFuncs = mapAppendFuncs, nil // reset mapAppendFuncs to use appendMapAnyAny + defer func() { + mapAppendFuncs = funcs + }() + + type EnumKey int64 + type EnumType int64 + type EmptyStruct struct{} + + type TestStruct struct { + M1 map[EnumKey]int8 `frugal:"1,optional,map"` + M2 map[EnumKey]int16 `frugal:"2,optional,map"` + M3 map[EnumKey]int32 `frugal:"3,optional,map"` + M4 map[EnumKey]int64 `frugal:"4,optional,map"` + M5 map[EnumKey]EnumType `frugal:"5,optional,map"` + M6 map[EnumKey]string `frugal:"6,optional,map"` + M7 map[EnumKey]*EmptyStruct `frugal:"7,optional,map"` + } + + p0 := &TestStruct{ + M1: map[EnumKey]int8{11: 1, 12: 2}, + M2: map[EnumKey]int16{21: 1, 22: 2}, + M3: map[EnumKey]int32{31: 1, 32: 2}, + M4: map[EnumKey]int64{41: 1, 42: 2}, + M5: map[EnumKey]EnumType{51: 1, 52: 2}, + M6: map[EnumKey]string{61: "1", 62: "2"}, + M7: map[EnumKey]*EmptyStruct{71: {}, 72: {}}, + } + + b, err := Append(nil, p0) + require.NoError(t, err) + + p1 := &TestStruct{} + _, err = Decode(b, p1) + require.NoError(t, err) + require.Equal(t, p0, p1) +} diff --git a/internal/reflect/append_test.go b/internal/reflect/append_test.go new file mode 100644 index 0000000..335876d --- /dev/null +++ b/internal/reflect/append_test.go @@ -0,0 +1,54 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAppendStruct(t *testing.T) { + type EnumType int64 + type TestStruct struct { + F11 *int8 `frugal:"11,optional,i8"` + F12 *bool `frugal:"12,optional,bool"` + + F2 *int16 `frugal:"2,optional,i16"` + F3 *int32 `frugal:"3,optional,i32"` + F4 *int64 `frugal:"4,optional,i64"` + F5 *EnumType `frugal:"5,optional,EnumType"` + F6 *string `frugal:"6,optional,string"` + } + + p0 := &TestStruct{ + F11: P(int8(1)), + F12: P(false), + F2: P(int16(2)), + F3: P(int32(3)), + F4: P(int64(4)), + F5: P(EnumType(5)), + F6: P("6"), + } + + b, err := Append(nil, p0) + require.NoError(t, err) + p1 := &TestStruct{} + _, err = Decode(b, p1) + require.NoError(t, err) + require.Equal(t, p0, p1) +} diff --git a/internal/reflect/decoder_test.go b/internal/reflect/decoder_test.go index 12ebb63..47aadc1 100644 --- a/internal/reflect/decoder_test.go +++ b/internal/reflect/decoder_test.go @@ -50,6 +50,11 @@ func TestDecode(t *testing.T) { } testcases := []testcase{ + { + name: "case_default", + update: func(p0 *TestTypes) {}, + test: func(t *testing.T, p1 *TestTypes) {}, + }, { name: "case_bool", update: func(p0 *TestTypes) { p0.FBool = true }, @@ -333,18 +338,15 @@ func TestDecodeUnknownFields(t *testing.T) { type Msg0 struct { I0 int32 `thrift:"i0,2" frugal:"2,default,i32"` S0 string `thrift:"s0,3" frugal:"3,default,string"` - S1 string `thrift:"s1,4" frugal:"4,default,string"` - I1 int32 `thrift:"i1,5" frugal:"5,default,i32"` } - type Msg1 struct { // without S0, I1 - I0 int32 `thrift:"i0,2" frugal:"2,default,i32"` - S1 string `thrift:"s1,4" frugal:"4,default,string"` + type Msg1 struct { // without S0 + I0 int32 `thrift:"i0,2" frugal:"2,default,i32"` _unknownFields []byte } - msg := Msg0{I0: 1, S0: "s0", S1: "s1", I1: 2} + msg := Msg0{I0: 1, S0: "s0"} b := make([]byte, EncodedSize(msg)) _, _ = Encode(b, msg) @@ -352,12 +354,13 @@ func TestDecodeUnknownFields(t *testing.T) { _, _ = Decode(b, p) assert.Equal(t, msg.I0, p.I0) - assert.Equal(t, msg.S1, p.S1) - sz := fieldHeaderLen + strHeaderLen + len(msg.S0) + fieldHeaderLen + 4 + sz := fieldHeaderLen + strHeaderLen + len(msg.S0) testb := make([]byte, sz) testb = appendStringField(testb[:0], 3, msg.S0) - testb = appendInt32Field(testb, 5, uint32(msg.I1)) assert.Equal(t, sz, len(testb)) assert.Equal(t, testb, p._unknownFields) + + ab, _ := Append(nil, p) + assert.Equal(t, b, ab) } diff --git a/internal/reflect/desc.go b/internal/reflect/desc.go index cd6801e..8f99cda 100644 --- a/internal/reflect/desc.go +++ b/internal/reflect/desc.go @@ -168,6 +168,10 @@ func newStructDesc(t reflect.Type) (*structDesc, error) { return d, nil } +func (d *structDesc) Name() string { + return d.rt.String() +} + func (d *structDesc) GetField(fid uint16) *tField { if fid > d.maxID { return nil diff --git a/internal/reflect/encoder.go b/internal/reflect/encoder.go index b202fc7..99c90b6 100644 --- a/internal/reflect/encoder.go +++ b/internal/reflect/encoder.go @@ -18,7 +18,7 @@ package reflect import ( "encoding/binary" - "errors" + "fmt" "unsafe" ) @@ -26,6 +26,10 @@ type tEncoder struct { // ... } +func withFieldErr(err error, sd *structDesc, f *tField) error { + return fmt.Errorf("%q field %d err: %w", sd.Name(), f.ID, err) +} + // Encode encodes a struct to buf. // base is the pointer of the struct func (e *tEncoder) Encode(b []byte, base unsafe.Pointer, sd *structDesc) (int, error) { @@ -60,13 +64,13 @@ func (e *tEncoder) Encode(b []byte, base unsafe.Pointer, sd *structDesc) (int, e // tSTRUCT always is pointer? n, err := e.Encode(b[i:], *(*unsafe.Pointer)(p), t.Sd) if err != nil { - return i, err + return i, withFieldErr(err, sd, f) } i += n } else { n, err := e.encodeContainerType(t, b[i:], p) if err != nil { - return i, err + return i, withFieldErr(err, sd, f) } i += n } @@ -168,10 +172,9 @@ func (e *tEncoder) encodeContainerType(t *tType, b []byte, p unsafe.Pointer) (in i += n } } - return i, nil } - return 0, errors.New("unknown type") + panic("[bug] unknown type: " + ttype2str(t.T)) } // NOTE: PLEASE ADD CODE CAREFULLY diff --git a/internal/reflect/reflect.go b/internal/reflect/reflect.go index e1b6b34..cd7c5fc 100644 --- a/internal/reflect/reflect.go +++ b/internal/reflect/reflect.go @@ -83,6 +83,34 @@ func Encode(b []byte, v interface{}) (n int, err error) { return e.Encode(b, p, sd) } +func Append(b []byte, v interface{}) ([]byte, error) { + panicIfHackErr() + + var err error + rv := reflect.ValueOf(v) + sd := getStructDesc(rv) // copy get and create funcs here for inlining + if sd == nil { + sd, err = createStructDesc(rv) + if err != nil { + return b, err + } + } + // get underlying pointer + var p unsafe.Pointer + if rv.Kind() == reflect.Struct { + // unaddressable, need to copy to heap, and then get the ptr + prv := sd.rvPool.Get().(*reflect.Value) + defer sd.rvPool.Put(prv) + (*prv).Elem().Set(rv) + p = (*rvtype)(unsafe.Pointer(prv)).ptr // like `rvPtr` without copy + } else { + // we doesn't support multilevel Pointer like **struct + // it checks in createStructDesc + p = rvPtr(rv) + } + return appendStruct(&tType{Sd: sd}, b, p) +} + func Decode(b []byte, v interface{}) (int, error) { panicIfHackErr() rv := reflect.ValueOf(v) diff --git a/internal/reflect/reflect_test.go b/internal/reflect/reflect_test.go index 9e6754c..eb4f0db 100644 --- a/internal/reflect/reflect_test.go +++ b/internal/reflect/reflect_test.go @@ -91,6 +91,16 @@ func BenchmarkEncode(b *testing.B) { } } +func BenchmarkAppend(b *testing.B) { + p := initTestTypesForBenchmark() + n := EncodedSize(p) + buf := make([]byte, 0, n) + b.SetBytes(int64(n)) + for i := 0; i < b.N; i++ { + _, _ = Append(buf, p) + } +} + func BenchmarkEncodedSize(b *testing.B) { p := initTestTypesForBenchmark() _ = EncodedSize(p) // pretouch diff --git a/internal/reflect/ttype.go b/internal/reflect/ttype.go index b8abeeb..717d3d6 100644 --- a/internal/reflect/ttype.go +++ b/internal/reflect/ttype.go @@ -62,7 +62,7 @@ var t2s = [256]string{ tMAP: "MAP", tSET: "SET", tLIST: "LIST", - // no tENUM, it's NOT a wiretype + tENUM: "ENUM", } func ttype2str(t ttype) string { @@ -84,6 +84,8 @@ var simpleTypes = [256]bool{ tSTRING: true, } +type appendFuncType func(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) + type tType struct { T ttype K *tType @@ -113,6 +115,7 @@ type tType struct { // for tLIST, tSET, tMAP, tSTRUCT EncodedSizeFunc func(p unsafe.Pointer) (int, error) + AppendFunc appendFuncType // tMAP only MapTmpVarsPool *sync.Pool // for decoder tmp vars @@ -194,6 +197,16 @@ func newTType(x *defs.Type) *tType { if x.V != nil { t.V = newTType(x.V) } + switch t.T { + case tLIST, tSET: + updateListAppendFunc(t) + case tMAP: + updateMapAppendFunc(t) + case tSTRUCT: + t.AppendFunc = appendStruct + default: + t.AppendFunc = appendAny + } if t.IsPointer && t.V.IsPointer { // XXX: make it simple... not support it, it's not common // The code is not generated by thriftgo? diff --git a/internal/reflect/utils.go b/internal/reflect/utils.go index b828727..0bbfe7e 100644 --- a/internal/reflect/utils.go +++ b/internal/reflect/utils.go @@ -201,3 +201,32 @@ func initOrGetMapTmpVarsPool(t *tType) *sync.Pool { }, } } + +func appendUint16(b []byte, v uint16) []byte { + return append(b, + byte(v>>8), + byte(v), + ) +} + +func appendUint32(b []byte, v uint32) []byte { + return append(b, + byte(v>>24), + byte(v>>16), + byte(v>>8), + byte(v), + ) +} + +func appendUint64(b []byte, v uint64) []byte { + return append(b, + byte(v>>56), + byte(v>>48), + byte(v>>40), + byte(v>>32), + byte(v>>24), + byte(v>>16), + byte(v>>8), + byte(v), + ) +} diff --git a/internal/reflect/utils_test.go b/internal/reflect/utils_test.go index 636c6fc..ff86ab2 100644 --- a/internal/reflect/utils_test.go +++ b/internal/reflect/utils_test.go @@ -24,6 +24,8 @@ import ( "github.com/stretchr/testify/assert" ) +func P[T any](v T) *T { return &v } + func TestCheckUniqueness(t *testing.T) { { // tBOOL typ := &tType{T: tBOOL, RT: reflect.TypeOf(bool(true))}