summaryrefslogtreecommitdiff
path: root/vendor/github.com/ugorji/go/codec/encode.go
diff options
context:
space:
mode:
authorLibravatar Daenney <daenney@users.noreply.github.com>2023-02-25 13:12:40 +0100
committerLibravatar GitHub <noreply@github.com>2023-02-25 12:12:40 +0000
commitecdc8379fa8f9d88faca626e7de748c2afbe4910 (patch)
tree8c20a5826db2136fc89bee45e15355c5899fa65b /vendor/github.com/ugorji/go/codec/encode.go
parent[bugfix] Fix deleted status causing issues when getting bookmark (#1551) (diff)
downloadgotosocial-ecdc8379fa8f9d88faca626e7de748c2afbe4910.tar.xz
[chore] Update gin to v1.9.0 (#1553)
Diffstat (limited to 'vendor/github.com/ugorji/go/codec/encode.go')
-rw-r--r--vendor/github.com/ugorji/go/codec/encode.go165
1 files changed, 105 insertions, 60 deletions
diff --git a/vendor/github.com/ugorji/go/codec/encode.go b/vendor/github.com/ugorji/go/codec/encode.go
index e411bdb81..53389b085 100644
--- a/vendor/github.com/ugorji/go/codec/encode.go
+++ b/vendor/github.com/ugorji/go/codec/encode.go
@@ -686,15 +686,11 @@ func (e *Encoder) kMap(f *codecFnInfo, rv reflect.Value) {
var rvv = mapAddrLoopvarRV(f.ti.elem, vtypeKind)
- if e.h.Canonical {
- e.kMapCanonical(f.ti, rv, rvv, valFn)
- e.mapEnd()
- return
- }
-
rtkey := f.ti.key
var keyTypeIsString = stringTypId == rt2id(rtkey) // rtkeyid
- if !keyTypeIsString {
+ if keyTypeIsString {
+ keyFn = e.h.fn(rtkey)
+ } else {
for rtkey.Kind() == reflect.Ptr {
rtkey = rtkey.Elem()
}
@@ -703,6 +699,12 @@ func (e *Encoder) kMap(f *codecFnInfo, rv reflect.Value) {
}
}
+ if e.h.Canonical {
+ e.kMapCanonical(f.ti, rv, rvv, keyFn, valFn)
+ e.mapEnd()
+ return
+ }
+
var rvk = mapAddrLoopvarRV(f.ti.key, ktypeKind)
var it mapIter
@@ -723,11 +725,14 @@ func (e *Encoder) kMap(f *codecFnInfo, rv reflect.Value) {
e.mapEnd()
}
-func (e *Encoder) kMapCanonical(ti *typeInfo, rv, rvv reflect.Value, valFn *codecFn) {
- // we previously did out-of-band if an extension was registered.
- // This is not necessary, as the natural kind is sufficient for ordering.
-
+func (e *Encoder) kMapCanonical(ti *typeInfo, rv, rvv reflect.Value, keyFn, valFn *codecFn) {
+ // The base kind of the type of the map key is sufficient for ordering.
+ // We only do out of band if that kind is not ordered (number or string), bool or time.Time.
+ // If the key is a predeclared type, directly call methods on encDriver e.g. EncodeString
+ // but if not, call encodeValue, in case it has an extension registered or otherwise.
rtkey := ti.key
+ rtkeydecl := rtkey.PkgPath() == "" && rtkey.Name() != "" // key type is predeclared
+
mks := rv.MapKeys()
rtkeyKind := rtkey.Kind()
kfast := mapKeyFastKindFor(rtkeyKind)
@@ -736,18 +741,24 @@ func (e *Encoder) kMapCanonical(ti *typeInfo, rv, rvv reflect.Value, valFn *code
switch rtkeyKind {
case reflect.Bool:
- mksv := make([]boolRv, len(mks))
- for i, k := range mks {
- v := &mksv[i]
- v.r = k
- v.v = k.Bool()
+ // though bool keys make no sense in a map, it *could* happen.
+ // in that case, we MUST support it in reflection mode,
+ // as that is the fallback for even codecgen and others.
+
+ // sort the keys so that false comes before true
+ // ie if 2 keys in order (true, false), then swap them
+ if len(mks) == 2 && mks[0].Bool() {
+ mks[0], mks[1] = mks[1], mks[0]
}
- sort.Sort(boolRvSlice(mksv))
- for i := range mksv {
+ for i := range mks {
e.mapElemKey()
- e.e.EncodeBool(mksv[i].v)
+ if rtkeydecl {
+ e.e.EncodeBool(mks[i].Bool())
+ } else {
+ e.encodeValueNonNil(mks[i], keyFn)
+ }
e.mapElemValue()
- e.encodeValue(mapGet(rv, mksv[i].r, rvv, kfast, visindirect, visref), valFn)
+ e.encodeValue(mapGet(rv, mks[i], rvv, kfast, visindirect, visref), valFn)
}
case reflect.String:
mksv := make([]stringRv, len(mks))
@@ -759,7 +770,11 @@ func (e *Encoder) kMapCanonical(ti *typeInfo, rv, rvv reflect.Value, valFn *code
sort.Sort(stringRvSlice(mksv))
for i := range mksv {
e.mapElemKey()
- e.e.EncodeString(mksv[i].v)
+ if rtkeydecl {
+ e.e.EncodeString(mksv[i].v)
+ } else {
+ e.encodeValueNonNil(mksv[i].r, keyFn)
+ }
e.mapElemValue()
e.encodeValue(mapGet(rv, mksv[i].r, rvv, kfast, visindirect, visref), valFn)
}
@@ -773,7 +788,11 @@ func (e *Encoder) kMapCanonical(ti *typeInfo, rv, rvv reflect.Value, valFn *code
sort.Sort(uint64RvSlice(mksv))
for i := range mksv {
e.mapElemKey()
- e.e.EncodeUint(mksv[i].v)
+ if rtkeydecl {
+ e.e.EncodeUint(mksv[i].v)
+ } else {
+ e.encodeValueNonNil(mksv[i].r, keyFn)
+ }
e.mapElemValue()
e.encodeValue(mapGet(rv, mksv[i].r, rvv, kfast, visindirect, visref), valFn)
}
@@ -787,7 +806,11 @@ func (e *Encoder) kMapCanonical(ti *typeInfo, rv, rvv reflect.Value, valFn *code
sort.Sort(int64RvSlice(mksv))
for i := range mksv {
e.mapElemKey()
- e.e.EncodeInt(mksv[i].v)
+ if rtkeydecl {
+ e.e.EncodeInt(mksv[i].v)
+ } else {
+ e.encodeValueNonNil(mksv[i].r, keyFn)
+ }
e.mapElemValue()
e.encodeValue(mapGet(rv, mksv[i].r, rvv, kfast, visindirect, visref), valFn)
}
@@ -801,7 +824,11 @@ func (e *Encoder) kMapCanonical(ti *typeInfo, rv, rvv reflect.Value, valFn *code
sort.Sort(float64RvSlice(mksv))
for i := range mksv {
e.mapElemKey()
- e.e.EncodeFloat32(float32(mksv[i].v))
+ if rtkeydecl {
+ e.e.EncodeFloat32(float32(mksv[i].v))
+ } else {
+ e.encodeValueNonNil(mksv[i].r, keyFn)
+ }
e.mapElemValue()
e.encodeValue(mapGet(rv, mksv[i].r, rvv, kfast, visindirect, visref), valFn)
}
@@ -815,11 +842,15 @@ func (e *Encoder) kMapCanonical(ti *typeInfo, rv, rvv reflect.Value, valFn *code
sort.Sort(float64RvSlice(mksv))
for i := range mksv {
e.mapElemKey()
- e.e.EncodeFloat64(mksv[i].v)
+ if rtkeydecl {
+ e.e.EncodeFloat64(mksv[i].v)
+ } else {
+ e.encodeValueNonNil(mksv[i].r, keyFn)
+ }
e.mapElemValue()
e.encodeValue(mapGet(rv, mksv[i].r, rvv, kfast, visindirect, visref), valFn)
}
- case reflect.Struct:
+ default:
if rtkey == timeTyp {
mksv := make([]timeRv, len(mks))
for i, k := range mks {
@@ -836,8 +867,7 @@ func (e *Encoder) kMapCanonical(ti *typeInfo, rv, rvv reflect.Value, valFn *code
}
break
}
- fallthrough
- default:
+
// out-of-band
// first encode each key to a []byte first, then sort them, then record
bs0 := e.blist.get(len(mks) * 16)
@@ -1010,16 +1040,17 @@ func (e *Encoder) ResetBytes(out *[]byte) {
// To set an option on all fields (e.g. omitempty on all fields), you
// can create a field called _struct, and set flags on it. The options
// which can be set on _struct are:
-// - omitempty: so all fields are omitted if empty
-// - toarray: so struct is encoded as an array
-// - int: so struct key names are encoded as signed integers (instead of strings)
-// - uint: so struct key names are encoded as unsigned integers (instead of strings)
-// - float: so struct key names are encoded as floats (instead of strings)
+// - omitempty: so all fields are omitted if empty
+// - toarray: so struct is encoded as an array
+// - int: so struct key names are encoded as signed integers (instead of strings)
+// - uint: so struct key names are encoded as unsigned integers (instead of strings)
+// - float: so struct key names are encoded as floats (instead of strings)
+//
// More details on these below.
//
// Struct values "usually" encode as maps. Each exported struct field is encoded unless:
-// - the field's tag is "-", OR
-// - the field is empty (empty or the zero value) and its tag specifies the "omitempty" option.
+// - the field's tag is "-", OR
+// - the field is empty (empty or the zero value) and its tag specifies the "omitempty" option.
//
// When encoding as a map, the first string in the tag (before the comma)
// is the map key string to use when encoding.
@@ -1032,8 +1063,9 @@ func (e *Encoder) ResetBytes(out *[]byte) {
// This is done with the int,uint or float option on the _struct field (see above).
//
// However, struct values may encode as arrays. This happens when:
-// - StructToArray Encode option is set, OR
-// - the tag on the _struct field sets the "toarray" option
+// - StructToArray Encode option is set, OR
+// - the tag on the _struct field sets the "toarray" option
+//
// Note that omitempty is ignored when encoding struct values as arrays,
// as an entry must be encoded for each field, to maintain its position.
//
@@ -1043,33 +1075,33 @@ func (e *Encoder) ResetBytes(out *[]byte) {
// or interface value, and any array, slice, map, or string of length zero.
//
// Anonymous fields are encoded inline except:
-// - the struct tag specifies a replacement name (first value)
-// - the field is of an interface type
+// - the struct tag specifies a replacement name (first value)
+// - the field is of an interface type
//
// Examples:
//
-// // NOTE: 'json:' can be used as struct tag key, in place 'codec:' below.
-// type MyStruct struct {
-// _struct bool `codec:",omitempty"` //set omitempty for every field
-// Field1 string `codec:"-"` //skip this field
-// Field2 int `codec:"myName"` //Use key "myName" in encode stream
-// Field3 int32 `codec:",omitempty"` //use key "Field3". Omit if empty.
-// Field4 bool `codec:"f4,omitempty"` //use key "f4". Omit if empty.
-// io.Reader //use key "Reader".
-// MyStruct `codec:"my1" //use key "my1".
-// MyStruct //inline it
-// ...
-// }
+// // NOTE: 'json:' can be used as struct tag key, in place 'codec:' below.
+// type MyStruct struct {
+// _struct bool `codec:",omitempty"` //set omitempty for every field
+// Field1 string `codec:"-"` //skip this field
+// Field2 int `codec:"myName"` //Use key "myName" in encode stream
+// Field3 int32 `codec:",omitempty"` //use key "Field3". Omit if empty.
+// Field4 bool `codec:"f4,omitempty"` //use key "f4". Omit if empty.
+// io.Reader //use key "Reader".
+// MyStruct `codec:"my1" //use key "my1".
+// MyStruct //inline it
+// ...
+// }
//
-// type MyStruct struct {
-// _struct bool `codec:",toarray"` //encode struct as an array
-// }
+// type MyStruct struct {
+// _struct bool `codec:",toarray"` //encode struct as an array
+// }
//
-// type MyStruct struct {
-// _struct bool `codec:",uint"` //encode struct with "unsigned integer" keys
-// Field1 string `codec:"1"` //encode Field1 key using: EncodeInt(1)
-// Field2 string `codec:"2"` //encode Field2 key using: EncodeInt(2)
-// }
+// type MyStruct struct {
+// _struct bool `codec:",uint"` //encode struct with "unsigned integer" keys
+// Field1 string `codec:"1"` //encode Field1 key using: EncodeInt(1)
+// Field2 string `codec:"2"` //encode Field2 key using: EncodeInt(2)
+// }
//
// The mode of encoding is based on the type of the value. When a value is seen:
// - If a Selfer, call its CodecEncodeSelf method
@@ -1293,7 +1325,7 @@ TOP:
}
if fn == nil {
- fn = e.h.fn(rvType(rv))
+ fn = e.h.fn(rv.Type())
}
if !fn.i.addrE { // typically, addrE = false, so check it first
@@ -1310,6 +1342,19 @@ TOP:
}
}
+// encodeValueNonNil can encode a number, bool, or string
+// OR non-nil values of kind map, slice and chan.
+func (e *Encoder) encodeValueNonNil(rv reflect.Value, fn *codecFn) {
+ if fn == nil {
+ fn = e.h.fn(rv.Type())
+ }
+
+ if fn.i.addrE { // typically, addrE = false, so check it first
+ rv = e.addrRV(rv, fn.i.ti.rt, fn.i.ti.ptr)
+ }
+ fn.fe(e, &fn.i, rv)
+}
+
// addrRV returns a addressable value which may be readonly
func (e *Encoder) addrRV(rv reflect.Value, typ, ptrType reflect.Type) (rva reflect.Value) {
if rv.CanAddr() {