Skip to content

Commit

Permalink
feat(reflect): new append method
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaost committed Nov 3, 2024
1 parent 8a2ce77 commit d23314d
Show file tree
Hide file tree
Showing 13 changed files with 1,396 additions and 6 deletions.
76 changes: 76 additions & 0 deletions internal/reflect/append.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* 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))

if t.IsPointer {
p = *(*unsafe.Pointer)(p)
}

// field value
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
}
188 changes: 188 additions & 0 deletions internal/reflect/append_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* 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 updateListAppendFunc(t *tType) {
if t.T != tLIST && t.T != tSET {
panic("[bug] type mismatch, got: " + ttype2str(t.T))
}
switch t.V.T {
case tBYTE, tBOOL:
t.AppendFunc = appendByteList
case tI16:
t.AppendFunc = appendI16List
case tI32:
t.AppendFunc = appendI32List
case tI64:
t.AppendFunc = appendI64List
case tENUM:
t.AppendFunc = appendEnumList
case tSTRING:
t.AppendFunc = appendStringList
default:
t.AppendFunc = appendOtherList
}
}

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 appendByteList(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) {
t = t.V
if t.T != tBYTE && t.T != tBOOL {
panic("[bug] type mismatch, got: " + ttype2str(t.T))
}
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) // move to next element
}
b = append(b, *((*byte)(vp)))
}
return b, nil
}

func appendI16List(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) {
t = t.V
if t.T != tI16 {
panic("[bug] type mismatch, got: " + ttype2str(t.T))
}
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) // move to next element
}
b = appendUint16(b, *((*uint16)(vp)))
}
return b, nil
}

func appendI32List(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) {
t = t.V
if t.T != tI32 {
panic("[bug] type mismatch, got: " + ttype2str(t.T))
}
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) // move to next element
}
b = appendUint32(b, *((*uint32)(vp)))
}
return b, nil
}

func appendI64List(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) {
t = t.V
if t.T != tI64 {
panic("[bug] type mismatch, got: " + ttype2str(t.T))
}
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) // move to next element
}
b = appendUint64(b, *((*uint64)(vp)))
}
return b, nil
}

func appendEnumList(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) {
t = t.V
if t.T != tENUM {
panic("[bug] type mismatch, got: " + ttype2str(t.T))
}
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) // move to next element
}
b = appendUint32(b, uint32(*((*int64)(vp))))
}
return b, nil
}

func appendStringList(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) {
t = t.V
if t.T != tSTRING {
panic("[bug] type mismatch, got: " + ttype2str(t.T))
}
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) // move to next element
}
s := *((*string)(vp))
b = appendUint32(b, uint32(len(s)))
b = append(b, s...)
}
return b, nil
}

func appendOtherList(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) {
t = t.V
if t.T != tLIST && t.T != tSET && t.T != tMAP && t.T != tSTRUCT {
panic("[bug] type mismatch, got: " + ttype2str(t.T))
}
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
}
p = vp
if t.IsPointer {
p = *(*unsafe.Pointer)(p) // only tSTRUCT?
}
b, err = t.AppendFunc(t, b, p)
if err != nil {
return b, err
}
}
return b, nil
}
108 changes: 108 additions & 0 deletions internal/reflect/append_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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 = appendMapKOrV(t.K, b, kp)
if err != nil {
return b, err
}
b, err = appendMapKOrV(t.V, b, vp)
if err != nil {
return b, err
}
}
return b, checkMapN(n)
}

func appendMapKOrV(t *tType, b []byte, p unsafe.Pointer) ([]byte, error) {
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 {
if t.IsPointer {
p = *(*unsafe.Pointer)(p) // only tSTRUCT?
}
return t.AppendFunc(t, b, p)
}
}
Loading

0 comments on commit d23314d

Please sign in to comment.