diff options
| author | 2025-03-09 17:47:56 +0100 | |
|---|---|---|
| committer | 2025-12-01 22:08:04 +0100 | |
| commit | b1af8fd87760b34e3ff2fd3bda38f211815a0473 (patch) | |
| tree | 9317fad1a7ec298d7a8d2678e4e422953bbc6f33 /vendor/github.com/ugorji/go/codec/encode.go | |
| parent | [chore] update URLs to forked source (diff) | |
| download | gotosocial-b1af8fd87760b34e3ff2fd3bda38f211815a0473.tar.xz | |
[chore] remove vendor
Diffstat (limited to 'vendor/github.com/ugorji/go/codec/encode.go')
| -rw-r--r-- | vendor/github.com/ugorji/go/codec/encode.go | 1652 |
1 files changed, 0 insertions, 1652 deletions
diff --git a/vendor/github.com/ugorji/go/codec/encode.go b/vendor/github.com/ugorji/go/codec/encode.go deleted file mode 100644 index b203036b4..000000000 --- a/vendor/github.com/ugorji/go/codec/encode.go +++ /dev/null @@ -1,1652 +0,0 @@ -//go:build notmono || codec.notmono - -// Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved. -// Use of this source code is governed by a MIT license found in the LICENSE file. - -package codec - -import ( - "encoding" - "io" - "reflect" - "slices" - "sort" - "strconv" - "sync" - "time" -) - -type helperEncDriver[T encDriver] struct{} - -// encFn encapsulates the captured variables and the encode function. -// This way, we only do some calculations one times, and pass to the -// code block that should be called (encapsulated in a function) -// instead of executing the checks every time. -type encFn[T encDriver] struct { - i encFnInfo - fe func(*encoder[T], *encFnInfo, reflect.Value) - // _ [1]uint64 // padding (cache-aligned) -} - -type encRtidFn[T encDriver] struct { - rtid uintptr - fn *encFn[T] -} - -// Encoder writes an object to an output stream in a supported format. -// -// Encoder is NOT safe for concurrent use i.e. a Encoder cannot be used -// concurrently in multiple goroutines. -// -// However, as Encoder could be allocation heavy to initialize, a Reset method is provided -// so its state can be reused to decode new input streams repeatedly. -// This is the idiomatic way to use. -type encoder[T encDriver] struct { - dh helperEncDriver[T] - fp *fastpathEs[T] - e T - encoderBase -} - -func (e *encoder[T]) rawExt(_ *encFnInfo, rv reflect.Value) { - if re := rv2i(rv).(*RawExt); re == nil { - e.e.EncodeNil() - } else { - e.e.EncodeRawExt(re) - } -} - -func (e *encoder[T]) ext(f *encFnInfo, rv reflect.Value) { - e.e.EncodeExt(rv2i(rv), f.ti.rt, f.xfTag, f.xfFn) -} - -func (e *encoder[T]) selferMarshal(_ *encFnInfo, rv reflect.Value) { - rv2i(rv).(Selfer).CodecEncodeSelf(&Encoder{e}) -} - -func (e *encoder[T]) binaryMarshal(_ *encFnInfo, rv reflect.Value) { - bs, fnerr := rv2i(rv).(encoding.BinaryMarshaler).MarshalBinary() - e.marshalRaw(bs, fnerr) -} - -func (e *encoder[T]) textMarshal(_ *encFnInfo, rv reflect.Value) { - bs, fnerr := rv2i(rv).(encoding.TextMarshaler).MarshalText() - e.marshalUtf8(bs, fnerr) -} - -func (e *encoder[T]) jsonMarshal(_ *encFnInfo, rv reflect.Value) { - bs, fnerr := rv2i(rv).(jsonMarshaler).MarshalJSON() - e.marshalAsis(bs, fnerr) -} - -func (e *encoder[T]) raw(_ *encFnInfo, rv reflect.Value) { - e.rawBytes(rv2i(rv).(Raw)) -} - -func (e *encoder[T]) encodeComplex64(v complex64) { - if imag(v) != 0 { - halt.errorf("cannot encode complex number: %v, with imaginary values: %v", any(v), any(imag(v))) - } - e.e.EncodeFloat32(real(v)) -} - -func (e *encoder[T]) encodeComplex128(v complex128) { - if imag(v) != 0 { - halt.errorf("cannot encode complex number: %v, with imaginary values: %v", any(v), any(imag(v))) - } - e.e.EncodeFloat64(real(v)) -} - -func (e *encoder[T]) kBool(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeBool(rvGetBool(rv)) -} - -func (e *encoder[T]) kTime(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeTime(rvGetTime(rv)) -} - -func (e *encoder[T]) kString(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeString(rvGetString(rv)) -} - -func (e *encoder[T]) kFloat32(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeFloat32(rvGetFloat32(rv)) -} - -func (e *encoder[T]) kFloat64(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeFloat64(rvGetFloat64(rv)) -} - -func (e *encoder[T]) kComplex64(_ *encFnInfo, rv reflect.Value) { - e.encodeComplex64(rvGetComplex64(rv)) -} - -func (e *encoder[T]) kComplex128(_ *encFnInfo, rv reflect.Value) { - e.encodeComplex128(rvGetComplex128(rv)) -} - -func (e *encoder[T]) kInt(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeInt(int64(rvGetInt(rv))) -} - -func (e *encoder[T]) kInt8(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeInt(int64(rvGetInt8(rv))) -} - -func (e *encoder[T]) kInt16(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeInt(int64(rvGetInt16(rv))) -} - -func (e *encoder[T]) kInt32(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeInt(int64(rvGetInt32(rv))) -} - -func (e *encoder[T]) kInt64(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeInt(int64(rvGetInt64(rv))) -} - -func (e *encoder[T]) kUint(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeUint(uint64(rvGetUint(rv))) -} - -func (e *encoder[T]) kUint8(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeUint(uint64(rvGetUint8(rv))) -} - -func (e *encoder[T]) kUint16(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeUint(uint64(rvGetUint16(rv))) -} - -func (e *encoder[T]) kUint32(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeUint(uint64(rvGetUint32(rv))) -} - -func (e *encoder[T]) kUint64(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeUint(uint64(rvGetUint64(rv))) -} - -func (e *encoder[T]) kUintptr(_ *encFnInfo, rv reflect.Value) { - e.e.EncodeUint(uint64(rvGetUintptr(rv))) -} - -func (e *encoder[T]) kSeqFn(rt reflect.Type) (fn *encFn[T]) { - // if kind is reflect.Interface, do not pre-determine the encoding type, - // because preEncodeValue may break it down to a concrete type and kInterface will bomb. - if rt = baseRT(rt); rt.Kind() != reflect.Interface { - fn = e.fn(rt) - } - return -} - -func (e *encoder[T]) kArrayWMbs(rv reflect.Value, ti *typeInfo, isSlice bool) { - var l int - if isSlice { - l = rvLenSlice(rv) - } else { - l = rv.Len() - } - if l == 0 { - e.e.WriteMapEmpty() - return - } - e.haltOnMbsOddLen(l) - e.mapStart(l >> 1) // e.mapStart(l / 2) - - var fn *encFn[T] - builtin := ti.tielem.flagEncBuiltin - if !builtin { - fn = e.kSeqFn(ti.elem) - } - - // simulate do...while, since we already handled case of 0-length - j := 0 - e.c = containerMapKey - e.e.WriteMapElemKey(true) - for { - rvv := rvArrayIndex(rv, j, ti, isSlice) - if builtin { - e.encodeIB(rv2i(baseRVRV(rvv))) - } else { - e.encodeValue(rvv, fn) - } - j++ - if j == l { - break - } - if j&1 == 0 { // j%2 == 0 { - e.c = containerMapKey - e.e.WriteMapElemKey(false) - } else { - e.mapElemValue() - } - } - e.c = 0 - e.e.WriteMapEnd() - - // for j := 0; j < l; j++ { - // if j&1 == 0 { // j%2 == 0 { - // e.mapElemKey(j == 0) - // } else { - // e.mapElemValue() - // } - // rvv := rvSliceIndex(rv, j, ti) - // if builtin { - // e.encode(rv2i(baseRVRV(rvv))) - // } else { - // e.encodeValue(rvv, fn) - // } - // } - // e.mapEnd() -} - -func (e *encoder[T]) kArrayW(rv reflect.Value, ti *typeInfo, isSlice bool) { - var l int - if isSlice { - l = rvLenSlice(rv) - } else { - l = rv.Len() - } - if l <= 0 { - e.e.WriteArrayEmpty() - return - } - e.arrayStart(l) - - var fn *encFn[T] - if !ti.tielem.flagEncBuiltin { - fn = e.kSeqFn(ti.elem) - } - - j := 0 - e.c = containerArrayElem - e.e.WriteArrayElem(true) - builtin := ti.tielem.flagEncBuiltin - for { - rvv := rvArrayIndex(rv, j, ti, isSlice) - if builtin { - e.encodeIB(rv2i(baseRVRV(rvv))) - } else { - e.encodeValue(rvv, fn) - } - j++ - if j == l { - break - } - e.c = containerArrayElem - e.e.WriteArrayElem(false) - } - - // if ti.tielem.flagEncBuiltin { - // for j := 0; j < l; j++ { - // e.arrayElem() - // e.encode(rv2i(baseRVRV(rIndex(rv, j, ti)))) - // } - // } else { - // fn := e.kSeqFn(ti.elem) - // for j := 0; j < l; j++ { - // e.arrayElem() - // e.encodeValue(rvArrayIndex(rv, j, ti), fn) - // } - // } - - e.c = 0 - e.e.WriteArrayEnd() -} - -func (e *encoder[T]) kChan(f *encFnInfo, rv reflect.Value) { - if f.ti.chandir&uint8(reflect.RecvDir) == 0 { - halt.errorStr("send-only channel cannot be encoded") - } - if !f.ti.mbs && uint8TypId == rt2id(f.ti.elem) { - e.kSliceBytesChan(rv) - return - } - rtslice := reflect.SliceOf(f.ti.elem) - rv = chanToSlice(rv, rtslice, e.h.ChanRecvTimeout) - ti := e.h.getTypeInfo(rt2id(rtslice), rtslice) - if f.ti.mbs { - e.kArrayWMbs(rv, ti, true) - } else { - e.kArrayW(rv, ti, true) - } -} - -func (e *encoder[T]) kSlice(f *encFnInfo, rv reflect.Value) { - if f.ti.mbs { - e.kArrayWMbs(rv, f.ti, true) - } else if f.ti.rtid == uint8SliceTypId || uint8TypId == rt2id(f.ti.elem) { - // 'uint8TypId == rt2id(f.ti.elem)' checks types having underlying []byte - e.e.EncodeBytes(rvGetBytes(rv)) - } else { - e.kArrayW(rv, f.ti, true) - } -} - -func (e *encoder[T]) kArray(f *encFnInfo, rv reflect.Value) { - if f.ti.mbs { - e.kArrayWMbs(rv, f.ti, false) - } else if handleBytesWithinKArray && uint8TypId == rt2id(f.ti.elem) { - e.e.EncodeStringBytesRaw(rvGetArrayBytes(rv, nil)) // bytes from array never nil - } else { - e.kArrayW(rv, f.ti, false) - } -} - -func (e *encoder[T]) kSliceBytesChan(rv reflect.Value) { - // do not use range, so that the number of elements encoded - // does not change, and encoding does not hang waiting on someone to close chan. - - bs0 := e.blist.peek(32, true) - bs := bs0 - - irv := rv2i(rv) - ch, ok := irv.(<-chan byte) - if !ok { - ch = irv.(chan byte) - } - -L1: - switch timeout := e.h.ChanRecvTimeout; { - case timeout == 0: // only consume available - for { - select { - case b := <-ch: - bs = append(bs, b) - default: - break L1 - } - } - case timeout > 0: // consume until timeout - tt := time.NewTimer(timeout) - for { - select { - case b := <-ch: - bs = append(bs, b) - case <-tt.C: - // close(tt.C) - break L1 - } - } - default: // consume until close - for b := range ch { - bs = append(bs, b) - } - } - - e.e.EncodeBytes(bs) - e.blist.put(bs) - if !byteSliceSameData(bs0, bs) { - e.blist.put(bs0) - } -} - -func (e *encoder[T]) kStructFieldKey(keyType valueType, encName string) { - // use if (not switch) block, so that branch prediction picks valueTypeString first - if keyType == valueTypeString { - e.e.EncodeString(encName) - } else if keyType == valueTypeInt { - e.e.EncodeInt(must.Int(strconv.ParseInt(encName, 10, 64))) - } else if keyType == valueTypeUint { - e.e.EncodeUint(must.Uint(strconv.ParseUint(encName, 10, 64))) - } else if keyType == valueTypeFloat { - e.e.EncodeFloat64(must.Float(strconv.ParseFloat(encName, 64))) - } else { - halt.errorStr2("invalid struct key type: ", keyType.String()) - } - // e.dh.encStructFieldKey(e.e, encName, keyType, encNameAsciiAlphaNum, e.js) -} - -func (e *encoder[T]) kStructSimple(f *encFnInfo, rv reflect.Value) { - _ = e.e // early asserts e, e.e are not nil once - tisfi := f.ti.sfi.source() - - // To bypass encodeValue, we need to handle cases where - // the field is an interface kind. To do this, we need to handle an - // interface or a pointer to an interface differently. - // - // Easiest to just delegate to encodeValue. - - chkCirRef := e.h.CheckCircularRef - var si *structFieldInfo - var j int - // use value of chkCirRef ie if true, then send the addr of the value - if f.ti.toArray || e.h.StructToArray { // toArray - if len(tisfi) == 0 { - e.e.WriteArrayEmpty() - return - } - e.arrayStart(len(tisfi)) - for j, si = range tisfi { - e.c = containerArrayElem - e.e.WriteArrayElem(j == 0) - if si.encBuiltin { - e.encodeIB(rv2i(si.fieldNoAlloc(rv, true))) - } else { - e.encodeValue(si.fieldNoAlloc(rv, !chkCirRef), nil) - } - } - e.c = 0 - e.e.WriteArrayEnd() - } else { - if len(tisfi) == 0 { - e.e.WriteMapEmpty() - return - } - if e.h.Canonical { - tisfi = f.ti.sfi.sorted() - } - e.mapStart(len(tisfi)) - for j, si = range tisfi { - e.c = containerMapKey - e.e.WriteMapElemKey(j == 0) - e.e.EncodeStringNoEscape4Json(si.encName) - e.mapElemValue() - if si.encBuiltin { - e.encodeIB(rv2i(si.fieldNoAlloc(rv, true))) - } else { - e.encodeValue(si.fieldNoAlloc(rv, !chkCirRef), nil) - } - } - e.c = 0 - e.e.WriteMapEnd() - } -} - -func (e *encoder[T]) kStruct(f *encFnInfo, rv reflect.Value) { - _ = e.e // early asserts e, e.e are not nil once - ti := f.ti - toMap := !(ti.toArray || e.h.StructToArray) - var mf map[string]interface{} - if ti.flagMissingFielder { - toMap = true - mf = rv2i(rv).(MissingFielder).CodecMissingFields() - } else if ti.flagMissingFielderPtr { - toMap = true - if rv.CanAddr() { - mf = rv2i(rvAddr(rv, ti.ptr)).(MissingFielder).CodecMissingFields() - } else { - mf = rv2i(e.addrRV(rv, ti.rt, ti.ptr)).(MissingFielder).CodecMissingFields() - } - } - newlen := len(mf) - tisfi := ti.sfi.source() - newlen += len(tisfi) - - var fkvs = e.slist.get(newlen)[:newlen] - - recur := e.h.RecursiveEmptyCheck - chkCirRef := e.h.CheckCircularRef - - var xlen int - - var kv sfiRv - var j int - var sf encStructFieldObj - if toMap { - newlen = 0 - if e.h.Canonical { - tisfi = f.ti.sfi.sorted() - } - for _, si := range tisfi { - // kv.r = si.path.field(rv, false, si.encBuiltin || !chkCirRef) - // if si.omitEmpty && isEmptyValue(kv.r, e.h.TypeInfos, recur) { - // continue - // } - if si.omitEmpty { - kv.r = si.fieldNoAlloc(rv, false) // test actual field val - if isEmptyValue(kv.r, e.h.TypeInfos, recur) { - continue - } - } else { - kv.r = si.fieldNoAlloc(rv, si.encBuiltin || !chkCirRef) - } - kv.v = si - fkvs[newlen] = kv - newlen++ - } - - var mf2s []stringIntf - if len(mf) != 0 { - mf2s = make([]stringIntf, 0, len(mf)) - for k, v := range mf { - if k == "" { - continue - } - if ti.infoFieldOmitempty && isEmptyValue(reflect.ValueOf(v), e.h.TypeInfos, recur) { - continue - } - mf2s = append(mf2s, stringIntf{k, v}) - } - } - - xlen = newlen + len(mf2s) - if xlen == 0 { - e.e.WriteMapEmpty() - goto END - } - - e.mapStart(xlen) - - // When there are missing fields, and Canonical flag is set, - // we cannot have the missing fields and struct fields sorted independently. - // We have to capture them together and sort as a unit. - - if len(mf2s) != 0 && e.h.Canonical { - mf2w := make([]encStructFieldObj, newlen+len(mf2s)) - for j = 0; j < newlen; j++ { - kv = fkvs[j] - mf2w[j] = encStructFieldObj{kv.v.encName, kv.r, nil, true, - !kv.v.encNameEscape4Json, kv.v.encBuiltin} - } - for _, v := range mf2s { - mf2w[j] = encStructFieldObj{v.v, reflect.Value{}, v.i, false, false, false} - j++ - } - sort.Sort((encStructFieldObjSlice)(mf2w)) - for j, sf = range mf2w { - e.c = containerMapKey - e.e.WriteMapElemKey(j == 0) - if ti.keyType == valueTypeString && sf.noEsc4json { - e.e.EncodeStringNoEscape4Json(sf.key) - } else { - e.kStructFieldKey(ti.keyType, sf.key) - } - e.mapElemValue() - if sf.isRv { - if sf.builtin { - e.encodeIB(rv2i(baseRVRV(sf.rv))) - } else { - e.encodeValue(sf.rv, nil) - } - } else { - if !e.encodeBuiltin(sf.intf) { - e.encodeR(reflect.ValueOf(sf.intf)) - } - //e.encodeI(sf.intf) // MARKER inlined - } - } - } else { - keytyp := ti.keyType - for j = 0; j < newlen; j++ { - kv = fkvs[j] - e.c = containerMapKey - e.e.WriteMapElemKey(j == 0) - if ti.keyType == valueTypeString && !kv.v.encNameEscape4Json { - e.e.EncodeStringNoEscape4Json(kv.v.encName) - } else { - e.kStructFieldKey(keytyp, kv.v.encName) - } - e.mapElemValue() - if kv.v.encBuiltin { - e.encodeIB(rv2i(baseRVRV(kv.r))) - } else { - e.encodeValue(kv.r, nil) - } - } - for _, v := range mf2s { - e.c = containerMapKey - e.e.WriteMapElemKey(j == 0) - e.kStructFieldKey(keytyp, v.v) - e.mapElemValue() - if !e.encodeBuiltin(v.i) { - e.encodeR(reflect.ValueOf(v.i)) - } - // e.encodeI(v.i) // MARKER inlined - j++ - } - } - - e.c = 0 - e.e.WriteMapEnd() - } else { - newlen = len(tisfi) - for i, si := range tisfi { // use unsorted array (to match sequence in struct) - // kv.r = si.path.field(rv, false, si.encBuiltin || !chkCirRef) - // kv.r = si.path.field(rv, false, !si.omitEmpty || si.encBuiltin || !chkCirRef) - if si.omitEmpty { - // use the zero value. - // if a reference or struct, set to nil (so you do not output too much) - kv.r = si.fieldNoAlloc(rv, false) // test actual field val - if isEmptyContainerValue(kv.r, e.h.TypeInfos, recur) { - kv.r = reflect.Value{} //encode as nil - } - } else { - kv.r = si.fieldNoAlloc(rv, si.encBuiltin || !chkCirRef) - } - kv.v = si - fkvs[i] = kv - } - - if newlen == 0 { - e.e.WriteArrayEmpty() - goto END - } - - // encode it all - e.arrayStart(newlen) - for j = 0; j < newlen; j++ { - e.c = containerArrayElem - e.e.WriteArrayElem(j == 0) - kv = fkvs[j] - if !kv.r.IsValid() { - e.e.EncodeNil() - } else if kv.v.encBuiltin { - e.encodeIB(rv2i(baseRVRV(kv.r))) - } else { - e.encodeValue(kv.r, nil) - } - } - e.c = 0 - e.e.WriteArrayEnd() - } - -END: - // do not use defer. Instead, use explicit pool return at end of function. - // defer has a cost we are trying to avoid. - // If there is a panic and these slices are not returned, it is ok. - e.slist.put(fkvs) -} - -func (e *encoder[T]) kMap(f *encFnInfo, rv reflect.Value) { - _ = e.e // early asserts e, e.e are not nil once - l := rvLenMap(rv) - if l == 0 { - e.e.WriteMapEmpty() - return - } - e.mapStart(l) - - // determine the underlying key and val encFn's for the map. - // This eliminates some work which is done for each loop iteration i.e. - // rv.Type(), ref.ValueOf(rt).Pointer(), then check map/list for fn. - // - // However, if kind is reflect.Interface, do not pre-determine the - // encoding type, because preEncodeValue may break it down to - // a concrete type and kInterface will bomb. - - var keyFn, valFn *encFn[T] - - ktypeKind := reflect.Kind(f.ti.keykind) - vtypeKind := reflect.Kind(f.ti.elemkind) - - rtval := f.ti.elem - rtvalkind := vtypeKind - for rtvalkind == reflect.Ptr { - rtval = rtval.Elem() - rtvalkind = rtval.Kind() - } - if rtvalkind != reflect.Interface { - valFn = e.fn(rtval) - } - - var rvv = mapAddrLoopvarRV(f.ti.elem, vtypeKind) - - rtkey := f.ti.key - var keyTypeIsString = stringTypId == rt2id(rtkey) // rtkeyid - if keyTypeIsString { - keyFn = e.fn(rtkey) - } else { - for rtkey.Kind() == reflect.Ptr { - rtkey = rtkey.Elem() - } - if rtkey.Kind() != reflect.Interface { - keyFn = e.fn(rtkey) - } - } - - if e.h.Canonical { - e.kMapCanonical(f.ti, rv, rvv, keyFn, valFn) - e.c = 0 - e.e.WriteMapEnd() - return - } - - var rvk = mapAddrLoopvarRV(f.ti.key, ktypeKind) - - var it mapIter - mapRange(&it, rv, rvk, rvv, true) - - kbuiltin := f.ti.tikey.flagEncBuiltin - vbuiltin := f.ti.tielem.flagEncBuiltin - for j := 0; it.Next(); j++ { - rv = it.Key() - e.c = containerMapKey - e.e.WriteMapElemKey(j == 0) - if keyTypeIsString { - e.e.EncodeString(rvGetString(rv)) - } else if kbuiltin { - e.encodeIB(rv2i(baseRVRV(rv))) - } else { - e.encodeValue(rv, keyFn) - } - e.mapElemValue() - rv = it.Value() - if vbuiltin { - e.encodeIB(rv2i(baseRVRV(rv))) - } else { - e.encodeValue(it.Value(), valFn) - } - } - it.Done() - - e.c = 0 - e.e.WriteMapEnd() -} - -func (e *encoder[T]) kMapCanonical(ti *typeInfo, rv, rvv reflect.Value, keyFn, valFn *encFn[T]) { - _ = e.e // early asserts e, e.e are not nil once - // 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() - mparams := getMapReqParams(ti) - - // kfast := mapKeyFastKindFor(rtkeyKind) - // visindirect := mapStoresElemIndirect(uintptr(ti.elemsize)) - // visref := refBitset.isset(ti.elemkind) - - switch rtkeyKind { - case reflect.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] - } - for i := range mks { - e.c = containerMapKey - e.e.WriteMapElemKey(i == 0) - if rtkeydecl { - e.e.EncodeBool(mks[i].Bool()) - } else { - e.encodeValueNonNil(mks[i], keyFn) - } - e.mapElemValue() - e.encodeValue(mapGet(rv, mks[i], rvv, mparams), valFn) - } - case reflect.String: - mksv := make([]orderedRv[string], len(mks)) - for i, k := range mks { - v := &mksv[i] - v.r = k - v.v = rvGetString(k) - } - slices.SortFunc(mksv, cmpOrderedRv) - for i := range mksv { - e.c = containerMapKey - e.e.WriteMapElemKey(i == 0) - 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, mparams), valFn) - } - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr: - mksv := make([]orderedRv[uint64], len(mks)) - for i, k := range mks { - v := &mksv[i] - v.r = k - v.v = k.Uint() - } - slices.SortFunc(mksv, cmpOrderedRv) - for i := range mksv { - e.c = containerMapKey - e.e.WriteMapElemKey(i == 0) - 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, mparams), valFn) - } - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - mksv := make([]orderedRv[int64], len(mks)) - for i, k := range mks { - v := &mksv[i] - v.r = k - v.v = k.Int() - } - slices.SortFunc(mksv, cmpOrderedRv) - for i := range mksv { - e.c = containerMapKey - e.e.WriteMapElemKey(i == 0) - 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, mparams), valFn) - } - case reflect.Float32: - mksv := make([]orderedRv[float64], len(mks)) - for i, k := range mks { - v := &mksv[i] - v.r = k - v.v = k.Float() - } - slices.SortFunc(mksv, cmpOrderedRv) - for i := range mksv { - e.c = containerMapKey - e.e.WriteMapElemKey(i == 0) - 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, mparams), valFn) - } - case reflect.Float64: - mksv := make([]orderedRv[float64], len(mks)) - for i, k := range mks { - v := &mksv[i] - v.r = k - v.v = k.Float() - } - slices.SortFunc(mksv, cmpOrderedRv) - for i := range mksv { - e.c = containerMapKey - e.e.WriteMapElemKey(i == 0) - 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, mparams), valFn) - } - default: - if rtkey == timeTyp { - mksv := make([]timeRv, len(mks)) - for i, k := range mks { - v := &mksv[i] - v.r = k - v.v = rv2i(k).(time.Time) - } - slices.SortFunc(mksv, cmpTimeRv) - for i := range mksv { - e.c = containerMapKey - e.e.WriteMapElemKey(i == 0) - e.e.EncodeTime(mksv[i].v) - e.mapElemValue() - e.encodeValue(mapGet(rv, mksv[i].r, rvv, mparams), valFn) - } - break - } - - // out-of-band - // first encode each key to a []byte first, then sort them, then record - bs0 := e.blist.get(len(mks) * 16) - mksv := bs0 - mksbv := make([]bytesRv, len(mks)) - - sideEncode(e.hh, &e.h.sideEncPool, func(se encoderI) { - se.ResetBytes(&mksv) - for i, k := range mks { - v := &mksbv[i] - l := len(mksv) - se.setContainerState(containerMapKey) - se.encodeR(baseRVRV(k)) - se.atEndOfEncode() - se.writerEnd() - v.r = k - v.v = mksv[l:] - } - }) - - slices.SortFunc(mksbv, cmpBytesRv) - for j := range mksbv { - e.c = containerMapKey - e.e.WriteMapElemKey(j == 0) - e.e.writeBytesAsis(mksbv[j].v) - e.mapElemValue() - e.encodeValue(mapGet(rv, mksbv[j].r, rvv, mparams), valFn) - } - e.blist.put(mksv) - if !byteSliceSameData(bs0, mksv) { - e.blist.put(bs0) - } - } -} - -func (e *encoder[T]) init(h Handle) { - initHandle(h) - callMake(&e.e) - e.hh = h - e.h = h.getBasicHandle() - // e.be = e.hh.isBinary() - e.err = errEncoderNotInitialized - - // e.fp = fastpathEList[T]() - e.fp = e.e.init(h, &e.encoderBase, e).(*fastpathEs[T]) - - if e.bytes { - e.rtidFn = &e.h.rtidFnsEncBytes - e.rtidFnNoExt = &e.h.rtidFnsEncNoExtBytes - } else { - e.rtidFn = &e.h.rtidFnsEncIO - e.rtidFnNoExt = &e.h.rtidFnsEncNoExtIO - } - - e.reset() -} - -func (e *encoder[T]) reset() { - e.e.reset() - if e.ci != nil { - e.ci = e.ci[:0] - } - e.c = 0 - e.calls = 0 - e.seq = 0 - e.err = nil -} - -// Encode writes an object into a stream. -// -// Encoding can be configured via the struct tag for the fields. -// The key (in the struct tags) that we look at is configurable. -// -// By default, we look up the "codec" key in the struct field's tags, -// and fall bak to the "json" key if "codec" is absent. -// That key in struct field's tag value is the key name, -// followed by an optional comma and options. -// -// 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) -// -// 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. -// -// When encoding as a map, the first string in the tag (before the comma) -// is the map key string to use when encoding. -// ... -// This key is typically encoded as a string. -// However, there are instances where the encoded stream has mapping keys encoded as numbers. -// For example, some cbor streams have keys as integer codes in the stream, but they should map -// to fields in a structured object. Consequently, a struct is the natural representation in code. -// For these, configure the struct to encode/decode the keys as numbers (instead of string). -// 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 -// -// Note that omitempty is ignored when encoding struct values as arrays, -// as an entry must be encoded for each field, to maintain its position. -// -// Values with types that implement MapBySlice are encoded as stream maps. -// -// The empty values (for omitempty option) are false, 0, any nil pointer -// 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 -// -// 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 -// ... -// } -// -// 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) -// } -// -// The mode of encoding is based on the type of the value. When a value is seen: -// - If a Selfer, call its CodecEncodeSelf method -// - If an extension is registered for it, call that extension function -// - If implements encoding.(Binary|Text|JSON)Marshaler, call Marshal(Binary|Text|JSON) method -// - Else encode it based on its reflect.Kind -// -// Note that struct field names and keys in map[string]XXX will be treated as symbols. -// Some formats support symbols (e.g. binc) and will properly encode the string -// only once in the stream, and use a tag to refer to it thereafter. -// -// Note that an error from an Encode call will make the Encoder unusable moving forward. -// This is because the state of the Encoder, it's output stream, etc are no longer stable. -// Any subsequent calls to Encode will trigger the same error. -func (e *encoder[T]) Encode(v interface{}) (err error) { - // tried to use closure, as runtime optimizes defer with no params. - // This seemed to be causing weird issues (like circular reference found, unexpected panic, etc). - // Also, see https://github.com/golang/go/issues/14939#issuecomment-417836139 - defer panicValToErr(e, callRecoverSentinel, &e.err, &err, debugging) - e.mustEncode(v) - return -} - -// MustEncode is like Encode, but panics if unable to Encode. -// -// Note: This provides insight to the code location that triggered the error. -// -// Note that an error from an Encode call will make the Encoder unusable moving forward. -// This is because the state of the Encoder, it's output stream, etc are no longer stable. -// Any subsequent calls to Encode will trigger the same error. -func (e *encoder[T]) MustEncode(v interface{}) { - defer panicValToErr(e, callRecoverSentinel, &e.err, nil, true) - e.mustEncode(v) - return -} - -func (e *encoder[T]) mustEncode(v interface{}) { - halt.onerror(e.err) - if e.hh == nil { - halt.onerror(errNoFormatHandle) - } - - e.calls++ - if !e.encodeBuiltin(v) { - e.encodeR(reflect.ValueOf(v)) - } - // e.encodeI(v) // MARKER inlined - e.calls-- - if e.calls == 0 { - e.e.atEndOfEncode() - e.e.writerEnd() - } -} - -func (e *encoder[T]) encodeI(iv interface{}) { - if !e.encodeBuiltin(iv) { - e.encodeR(reflect.ValueOf(iv)) - } -} - -func (e *encoder[T]) encodeIB(iv interface{}) { - if !e.encodeBuiltin(iv) { - // panic("invalid type passed to encodeBuiltin") - // halt.errorf("invalid type passed to encodeBuiltin: %T", iv) - // MARKER: calling halt.errorf pulls in fmt.Sprintf/Errorf which makes this non-inlineable - halt.errorStr("[should not happen] invalid type passed to encodeBuiltin") - } -} - -func (e *encoder[T]) encodeR(base reflect.Value) { - e.encodeValue(base, nil) -} - -func (e *encoder[T]) encodeBuiltin(iv interface{}) (ok bool) { - ok = true - switch v := iv.(type) { - case nil: - e.e.EncodeNil() - // case Selfer: - case Raw: - e.rawBytes(v) - case string: - e.e.EncodeString(v) - case bool: - e.e.EncodeBool(v) - case int: - e.e.EncodeInt(int64(v)) - case int8: - e.e.EncodeInt(int64(v)) - case int16: - e.e.EncodeInt(int64(v)) - case int32: - e.e.EncodeInt(int64(v)) - case int64: - e.e.EncodeInt(v) - case uint: - e.e.EncodeUint(uint64(v)) - case uint8: - e.e.EncodeUint(uint64(v)) - case uint16: - e.e.EncodeUint(uint64(v)) - case uint32: - e.e.EncodeUint(uint64(v)) - case uint64: - e.e.EncodeUint(v) - case uintptr: - e.e.EncodeUint(uint64(v)) - case float32: - e.e.EncodeFloat32(v) - case float64: - e.e.EncodeFloat64(v) - case complex64: - e.encodeComplex64(v) - case complex128: - e.encodeComplex128(v) - case time.Time: - e.e.EncodeTime(v) - case []byte: - e.e.EncodeBytes(v) // e.e.EncodeStringBytesRaw(v) - default: - // we can't check non-predefined types, as they might be a Selfer or extension. - ok = !skipFastpathTypeSwitchInDirectCall && e.dh.fastpathEncodeTypeSwitch(iv, e) - } - return -} - -// encodeValue will encode a value. -// -// Note that encodeValue will handle nil in the stream early, so that the -// subsequent calls i.e. kXXX methods, etc do not have to handle it themselves. -func (e *encoder[T]) encodeValue(rv reflect.Value, fn *encFn[T]) { - // MARKER: We check if value is nil here, so that the kXXX method do not have to. - // if a valid fn is passed, it MUST BE for the dereferenced type of rv - - var ciPushes int - // if e.h.CheckCircularRef { - // ciPushes = e.ci.pushRV(rv) - // } - - var rvp reflect.Value - var rvpValid bool - -RV: - switch rv.Kind() { - case reflect.Ptr: - if rvIsNil(rv) { - e.e.EncodeNil() - goto END - } - rvpValid = true - rvp = rv - rv = rv.Elem() - // fn = nil // underlying type still same - no change - if e.h.CheckCircularRef && e.ci.canPushElemKind(rv.Kind()) { - e.ci.push(rv2i(rvp)) - ciPushes++ - } - goto RV - case reflect.Interface: - if rvIsNil(rv) { - e.e.EncodeNil() - goto END - } - rvpValid = false - rvp = reflect.Value{} - rv = rv.Elem() - fn = nil // underlying type may change, so prompt a reset - goto RV - case reflect.Map: - if rvIsNil(rv) { - if e.h.NilCollectionToZeroLength { - e.e.WriteMapEmpty() - } else { - e.e.EncodeNil() - } - goto END - } - case reflect.Slice, reflect.Chan: - if rvIsNil(rv) { - if e.h.NilCollectionToZeroLength { - e.e.WriteArrayEmpty() - } else { - e.e.EncodeNil() - } - goto END - } - case reflect.Invalid, reflect.Func: - e.e.EncodeNil() - goto END - } - - if fn == nil { - fn = e.fn(rv.Type()) - } - - if !fn.i.addrE { // typically, addrE = false, so check it first - // keep rv same - } else if rvpValid { - rv = rvp - } else if rv.CanAddr() { - rv = rvAddr(rv, fn.i.ti.ptr) - } else { - rv = e.addrRV(rv, fn.i.ti.rt, fn.i.ti.ptr) - } - fn.fe(e, &fn.i, rv) - -END: - if ciPushes > 0 { - e.ci.pop(ciPushes) - } -} - -func (e *encoder[T]) encodeValueNonNil(rv reflect.Value, fn *encFn[T]) { - // only call this if a primitive (number, bool, string) OR - // a non-nil collection (map/slice/chan). - // - // Expects fn to be non-nil - if fn.i.addrE { // typically, addrE = false, so check it first - if rv.CanAddr() { - rv = rvAddr(rv, fn.i.ti.ptr) - } else { - rv = e.addrRV(rv, fn.i.ti.rt, fn.i.ti.ptr) - } - } - fn.fe(e, &fn.i, rv) -} - -func (e *encoder[T]) encodeAs(v interface{}, t reflect.Type, ext bool) { - if ext { - e.encodeValue(baseRV(v), e.fn(t)) - } else { - e.encodeValue(baseRV(v), e.fnNoExt(t)) - } -} - -func (e *encoder[T]) marshalUtf8(bs []byte, fnerr error) { - halt.onerror(fnerr) - if bs == nil { - e.e.EncodeNil() - } else { - e.e.EncodeString(stringView(bs)) - } -} - -func (e *encoder[T]) marshalAsis(bs []byte, fnerr error) { - halt.onerror(fnerr) - if bs == nil { - e.e.EncodeNil() - } else { - e.e.writeBytesAsis(bs) // e.asis(bs) - } -} - -func (e *encoder[T]) marshalRaw(bs []byte, fnerr error) { - halt.onerror(fnerr) - e.e.EncodeBytes(bs) -} - -func (e *encoder[T]) rawBytes(vv Raw) { - v := []byte(vv) - if !e.h.Raw { - halt.errorBytes("Raw values cannot be encoded: ", v) - } - e.e.writeBytesAsis(v) -} - -func (e *encoder[T]) fn(t reflect.Type) *encFn[T] { - return e.dh.encFnViaBH(t, e.rtidFn, e.h, e.fp, false) -} - -func (e *encoder[T]) fnNoExt(t reflect.Type) *encFn[T] { - return e.dh.encFnViaBH(t, e.rtidFnNoExt, e.h, e.fp, true) -} - -// ---- container tracker methods -// Note: We update the .c after calling the callback. -// -// Callbacks ie Write(Map|Array)XXX should not use the containerState. -// It is there for post-callback use. -// Instead, callbacks have a parameter to tell if first time or not. -// -// Some code is commented out below, as they are manually inlined. -// Commented code is retained here for convernience. - -func (e *encoder[T]) mapStart(length int) { - e.e.WriteMapStart(length) - e.c = containerMapStart -} - -// func (e *encoder[T]) mapElemKey(firstTime bool) { -// e.e.WriteMapElemKey(firstTime) -// e.c = containerMapKey -// } - -func (e *encoder[T]) mapElemValue() { - e.e.WriteMapElemValue() - e.c = containerMapValue -} - -// func (e *encoder[T]) mapEnd() { -// e.e.WriteMapEnd() -// e.c = 0 -// } - -func (e *encoder[T]) arrayStart(length int) { - e.e.WriteArrayStart(length) - e.c = containerArrayStart -} - -// func (e *encoder[T]) arrayElem(firstTime bool) { -// e.e.WriteArrayElem(firstTime) -// e.c = containerArrayElem -// } - -// func (e *encoder[T]) arrayEnd() { -// e.e.WriteArrayEnd() -// e.c = 0 -// } - -// ---------- - -func (e *encoder[T]) writerEnd() { - e.e.writerEnd() -} - -func (e *encoder[T]) atEndOfEncode() { - e.e.atEndOfEncode() -} - -// Reset resets the Encoder with a new output stream. -// -// This accommodates using the state of the Encoder, -// where it has "cached" information about sub-engines. -func (e *encoder[T]) Reset(w io.Writer) { - if e.bytes { - halt.onerror(errEncNoResetBytesWithWriter) - } - e.reset() - if w == nil { - w = io.Discard - } - e.e.resetOutIO(w) -} - -// ResetBytes resets the Encoder with a new destination output []byte. -func (e *encoder[T]) ResetBytes(out *[]byte) { - if !e.bytes { - halt.onerror(errEncNoResetWriterWithBytes) - } - e.resetBytes(out) -} - -// only call this iff you are sure it is a bytes encoder -func (e *encoder[T]) resetBytes(out *[]byte) { - e.reset() - if out == nil { - out = &bytesEncAppenderDefOut - } - e.e.resetOutBytes(out) -} - -// ---- - -func (helperEncDriver[T]) newEncoderBytes(out *[]byte, h Handle) *encoder[T] { - var c1 encoder[T] - c1.bytes = true - c1.init(h) - c1.ResetBytes(out) - return &c1 -} - -func (helperEncDriver[T]) newEncoderIO(out io.Writer, h Handle) *encoder[T] { - var c1 encoder[T] - c1.bytes = false - c1.init(h) - c1.Reset(out) - return &c1 -} - -func (helperEncDriver[T]) encFnloadFastpathUnderlying(ti *typeInfo, fp *fastpathEs[T]) (f *fastpathE[T], u reflect.Type) { - rtid := rt2id(ti.fastpathUnderlying) - idx, ok := fastpathAvIndex(rtid) - if !ok { - return - } - f = &fp[idx] - if uint8(reflect.Array) == ti.kind { - u = reflect.ArrayOf(ti.rt.Len(), ti.elem) - } else { - u = f.rt - } - return -} - -// ---- - -func (helperEncDriver[T]) encFindRtidFn(s []encRtidFn[T], rtid uintptr) (i uint, fn *encFn[T]) { - // binary search. Adapted from sort/search.go. Use goto (not for loop) to allow inlining. - var h uint // var h, i uint - var j = uint(len(s)) -LOOP: - if i < j { - h = (i + j) >> 1 // avoid overflow when computing h // h = i + (j-i)/2 - if s[h].rtid < rtid { - i = h + 1 - } else { - j = h - } - goto LOOP - } - if i < uint(len(s)) && s[i].rtid == rtid { - fn = s[i].fn - } - return -} - -func (helperEncDriver[T]) encFromRtidFnSlice(fns *atomicRtidFnSlice) (s []encRtidFn[T]) { - if v := fns.load(); v != nil { - s = *(lowLevelToPtr[[]encRtidFn[T]](v)) - } - return -} - -func (dh helperEncDriver[T]) encFnViaBH(rt reflect.Type, fns *atomicRtidFnSlice, - x *BasicHandle, fp *fastpathEs[T], checkExt bool) (fn *encFn[T]) { - return dh.encFnVia(rt, fns, x.typeInfos(), &x.mu, x.extHandle, fp, - checkExt, x.CheckCircularRef, x.timeBuiltin, x.binaryHandle, x.jsonHandle) -} - -func (dh helperEncDriver[T]) encFnVia(rt reflect.Type, fns *atomicRtidFnSlice, - tinfos *TypeInfos, mu *sync.Mutex, exth extHandle, fp *fastpathEs[T], - checkExt, checkCircularRef, timeBuiltin, binaryEncoding, json bool) (fn *encFn[T]) { - rtid := rt2id(rt) - var sp []encRtidFn[T] = dh.encFromRtidFnSlice(fns) - if sp != nil { - _, fn = dh.encFindRtidFn(sp, rtid) - } - if fn == nil { - fn = dh.encFnViaLoader(rt, rtid, fns, tinfos, mu, exth, fp, checkExt, checkCircularRef, timeBuiltin, binaryEncoding, json) - } - return -} - -func (dh helperEncDriver[T]) encFnViaLoader(rt reflect.Type, rtid uintptr, fns *atomicRtidFnSlice, - tinfos *TypeInfos, mu *sync.Mutex, exth extHandle, fp *fastpathEs[T], - checkExt, checkCircularRef, timeBuiltin, binaryEncoding, json bool) (fn *encFn[T]) { - - fn = dh.encFnLoad(rt, rtid, tinfos, exth, fp, checkExt, checkCircularRef, timeBuiltin, binaryEncoding, json) - var sp []encRtidFn[T] - mu.Lock() - sp = dh.encFromRtidFnSlice(fns) - // since this is an atomic load/store, we MUST use a different array each time, - // else we have a data race when a store is happening simultaneously with a encFindRtidFn call. - if sp == nil { - sp = []encRtidFn[T]{{rtid, fn}} - fns.store(ptrToLowLevel(&sp)) - } else { - idx, fn2 := dh.encFindRtidFn(sp, rtid) - if fn2 == nil { - sp2 := make([]encRtidFn[T], len(sp)+1) - copy(sp2[idx+1:], sp[idx:]) - copy(sp2, sp[:idx]) - sp2[idx] = encRtidFn[T]{rtid, fn} - fns.store(ptrToLowLevel(&sp2)) - } - } - mu.Unlock() - return -} - -func (dh helperEncDriver[T]) encFnLoad(rt reflect.Type, rtid uintptr, tinfos *TypeInfos, - exth extHandle, fp *fastpathEs[T], - checkExt, checkCircularRef, timeBuiltin, binaryEncoding, json bool) (fn *encFn[T]) { - fn = new(encFn[T]) - fi := &(fn.i) - ti := tinfos.get(rtid, rt) - fi.ti = ti - rk := reflect.Kind(ti.kind) - - // anything can be an extension except the built-in ones: time, raw and rawext. - // ensure we check for these types, then if extension, before checking if - // it implementes one of the pre-declared interfaces. - - // fi.addrEf = true - - if rtid == timeTypId && timeBuiltin { - fn.fe = (*encoder[T]).kTime - } else if rtid == rawTypId { - fn.fe = (*encoder[T]).raw - } else if rtid == rawExtTypId { - fn.fe = (*encoder[T]).rawExt - fi.addrE = true - } else if xfFn := exth.getExt(rtid, checkExt); xfFn != nil { - fi.xfTag, fi.xfFn = xfFn.tag, xfFn.ext - fn.fe = (*encoder[T]).ext - if rk == reflect.Struct || rk == reflect.Array { - fi.addrE = true - } - } else if ti.flagSelfer || ti.flagSelferPtr { - fn.fe = (*encoder[T]).selferMarshal - fi.addrE = ti.flagSelferPtr - } else if supportMarshalInterfaces && binaryEncoding && - (ti.flagBinaryMarshaler || ti.flagBinaryMarshalerPtr) && - (ti.flagBinaryUnmarshaler || ti.flagBinaryUnmarshalerPtr) { - fn.fe = (*encoder[T]).binaryMarshal - fi.addrE = ti.flagBinaryMarshalerPtr - } else if supportMarshalInterfaces && !binaryEncoding && json && - (ti.flagJsonMarshaler || ti.flagJsonMarshalerPtr) && - (ti.flagJsonUnmarshaler || ti.flagJsonUnmarshalerPtr) { - //If JSON, we should check JSONMarshal before textMarshal - fn.fe = (*encoder[T]).jsonMarshal - fi.addrE = ti.flagJsonMarshalerPtr - } else if supportMarshalInterfaces && !binaryEncoding && - (ti.flagTextMarshaler || ti.flagTextMarshalerPtr) && - (ti.flagTextUnmarshaler || ti.flagTextUnmarshalerPtr) { - fn.fe = (*encoder[T]).textMarshal - fi.addrE = ti.flagTextMarshalerPtr - } else { - if fastpathEnabled && (rk == reflect.Map || rk == reflect.Slice || rk == reflect.Array) { - // by default (without using unsafe), - // if an array is not addressable, converting from an array to a slice - // requires an allocation (see helper_not_unsafe.go: func rvGetSlice4Array). - // - // (Non-addressable arrays mostly occur as keys/values from a map). - // - // However, fastpath functions are mostly for slices of numbers or strings, - // which are small by definition and thus allocation should be fast/cheap in time. - // - // Consequently, the value of doing this quick allocation to elide the overhead cost of - // non-optimized (not-unsafe) reflection is a fair price. - var rtid2 uintptr - if !ti.flagHasPkgPath { // un-named type (slice or mpa or array) - rtid2 = rtid - if rk == reflect.Array { - rtid2 = rt2id(ti.key) // ti.key for arrays = reflect.SliceOf(ti.elem) - } - if idx, ok := fastpathAvIndex(rtid2); ok { - fn.fe = fp[idx].encfn - } - } else { // named type (with underlying type of map or slice or array) - // try to use mapping for underlying type - xfe, xrt := dh.encFnloadFastpathUnderlying(ti, fp) - if xfe != nil { - xfnf := xfe.encfn - fn.fe = func(e *encoder[T], xf *encFnInfo, xrv reflect.Value) { - xfnf(e, xf, rvConvert(xrv, xrt)) - } - } - } - } - if fn.fe == nil { - switch rk { - case reflect.Bool: - fn.fe = (*encoder[T]).kBool - case reflect.String: - // Do not use different functions based on StringToRaw option, as that will statically - // set the function for a string type, and if the Handle is modified thereafter, - // behaviour is non-deterministic - // i.e. DO NOT DO: - // if x.StringToRaw { - // fn.fe = (*encoder[T]).kStringToRaw - // } else { - // fn.fe = (*encoder[T]).kStringEnc - // } - - fn.fe = (*encoder[T]).kString - case reflect.Int: - fn.fe = (*encoder[T]).kInt - case reflect.Int8: - fn.fe = (*encoder[T]).kInt8 - case reflect.Int16: - fn.fe = (*encoder[T]).kInt16 - case reflect.Int32: - fn.fe = (*encoder[T]).kInt32 - case reflect.Int64: - fn.fe = (*encoder[T]).kInt64 - case reflect.Uint: - fn.fe = (*encoder[T]).kUint - case reflect.Uint8: - fn.fe = (*encoder[T]).kUint8 - case reflect.Uint16: - fn.fe = (*encoder[T]).kUint16 - case reflect.Uint32: - fn.fe = (*encoder[T]).kUint32 - case reflect.Uint64: - fn.fe = (*encoder[T]).kUint64 - case reflect.Uintptr: - fn.fe = (*encoder[T]).kUintptr - case reflect.Float32: - fn.fe = (*encoder[T]).kFloat32 - case reflect.Float64: - fn.fe = (*encoder[T]).kFloat64 - case reflect.Complex64: - fn.fe = (*encoder[T]).kComplex64 - case reflect.Complex128: - fn.fe = (*encoder[T]).kComplex128 - case reflect.Chan: - fn.fe = (*encoder[T]).kChan - case reflect.Slice: - fn.fe = (*encoder[T]).kSlice - case reflect.Array: - fn.fe = (*encoder[T]).kArray - case reflect.Struct: - if ti.simple { - fn.fe = (*encoder[T]).kStructSimple - } else { - fn.fe = (*encoder[T]).kStruct - } - case reflect.Map: - fn.fe = (*encoder[T]).kMap - case reflect.Interface: - // encode: reflect.Interface are handled already by preEncodeValue - fn.fe = (*encoder[T]).kErr - default: - // reflect.Ptr and reflect.Interface are handled already by preEncodeValue - fn.fe = (*encoder[T]).kErr - } - } - } - return -} |
