mirror of https://github.com/k3s-io/k3s
Update dependency on ugorji/go/codec.
parent
f3e0bb0d41
commit
6a035833ac
|
@ -564,7 +564,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/ugorji/go/codec",
|
||||
"Rev": "2f4b94206aae781e63846a9bf02ad83c387d5296"
|
||||
"Rev": "8a2a3a8c488c3ebd98f422a965260278267a0551"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vaughan0/go-ini",
|
||||
|
|
|
@ -41,10 +41,6 @@ package {{ $.PackageName }}
|
|||
|
||||
import (
|
||||
{{ if not .CodecPkgFiles }}{{ .CodecPkgName }} "{{ .CodecImportPath }}"{{ end }}
|
||||
{{/*
|
||||
{{ if .Types }}"{{ .ImportPath }}"{{ end }}
|
||||
"io"
|
||||
*/}}
|
||||
"os"
|
||||
"reflect"
|
||||
"bytes"
|
||||
|
@ -52,14 +48,6 @@ import (
|
|||
"go/format"
|
||||
)
|
||||
|
||||
{{/* This is not used anymore. Remove it.
|
||||
func write(w io.Writer, s string) {
|
||||
if _, err := io.WriteString(w, s); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
*/}}
|
||||
|
||||
func CodecGenTempWrite{{ .RandString }}() {
|
||||
fout, err := os.Create("{{ .OutFile }}")
|
||||
if err != nil {
|
||||
|
|
|
@ -95,6 +95,14 @@ type DecodeOptions struct {
|
|||
// If nil, we use []interface{}
|
||||
SliceType reflect.Type
|
||||
|
||||
// MaxInitLen defines the initial length that we "make" a collection (slice, chan or map) with.
|
||||
// If 0 or negative, we default to a sensible value based on the size of an element in the collection.
|
||||
//
|
||||
// For example, when decoding, a stream may say that it has MAX_UINT elements.
|
||||
// We should not auto-matically provision a slice of that length, to prevent Out-Of-Memory crash.
|
||||
// Instead, we provision up to MaxInitLen, fill that up, and start appending after that.
|
||||
MaxInitLen int
|
||||
|
||||
// If ErrorIfNoField, return an error when decoding a map
|
||||
// from a codec stream into a struct, and no matching struct field is found.
|
||||
ErrorIfNoField bool
|
||||
|
@ -107,13 +115,42 @@ type DecodeOptions struct {
|
|||
// If SignedInteger, use the int64 during schema-less decoding of unsigned values (not uint64).
|
||||
SignedInteger bool
|
||||
|
||||
// MaxInitLen defines the initial length that we "make" a collection (slice, chan or map) with.
|
||||
// If 0 or negative, we default to a sensible value based on the size of an element in the collection.
|
||||
// MapValueReset controls how we decode into a map value.
|
||||
//
|
||||
// For example, when decoding, a stream may say that it has MAX_UINT elements.
|
||||
// We should not auto-matically provision a slice of that length, to prevent Out-Of-Memory crash.
|
||||
// Instead, we provision up to MaxInitLen, fill that up, and start appending after that.
|
||||
MaxInitLen int
|
||||
// By default, we MAY retrieve the mapping for a key, and then decode into that.
|
||||
// However, especially with big maps, that retrieval may be expensive and unnecessary
|
||||
// if the stream already contains all that is necessary to recreate the value.
|
||||
//
|
||||
// If true, we will never retrieve the previous mapping,
|
||||
// but rather decode into a new value and set that in the map.
|
||||
//
|
||||
// If false, we will retrieve the previous mapping if necessary e.g.
|
||||
// the previous mapping is a pointer, or is a struct or array with pre-set state,
|
||||
// or is an interface.
|
||||
MapValueReset bool
|
||||
|
||||
// InterfaceReset controls how we decode into an interface.
|
||||
//
|
||||
// By default, when we see a field that is an interface{...},
|
||||
// or a map with interface{...} value, we will attempt decoding into the
|
||||
// "contained" value.
|
||||
//
|
||||
// However, this prevents us from reading a string into an interface{}
|
||||
// that formerly contained a number.
|
||||
//
|
||||
// If true, we will decode into a new "blank" value, and set that in the interface.
|
||||
// If false, we will decode into whatever is contained in the interface.
|
||||
InterfaceReset bool
|
||||
|
||||
// InternString controls interning of strings during decoding.
|
||||
//
|
||||
// Some handles, e.g. json, typically will read map keys as strings.
|
||||
// If the set of keys are finite, it may help reduce allocation to
|
||||
// look them up from a map (than to allocate them afresh).
|
||||
//
|
||||
// Note: Handles will be smart when using the intern functionality.
|
||||
// So everything will not be interned.
|
||||
InternString bool
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
@ -582,25 +619,34 @@ func (f *decFnInfo) kInterface(rv reflect.Value) {
|
|||
// to decode into what was there before.
|
||||
// We do not replace with a generic value (as got from decodeNaked).
|
||||
|
||||
var rvn reflect.Value
|
||||
if rv.IsNil() {
|
||||
rvn := f.kInterfaceNaked()
|
||||
rvn = f.kInterfaceNaked()
|
||||
if rvn.IsValid() {
|
||||
rv.Set(rvn)
|
||||
}
|
||||
} else if f.d.h.InterfaceReset {
|
||||
rvn = f.kInterfaceNaked()
|
||||
if rvn.IsValid() {
|
||||
rv.Set(rvn)
|
||||
} else {
|
||||
// reset to zero value based on current type in there.
|
||||
rv.Set(reflect.Zero(rv.Elem().Type()))
|
||||
}
|
||||
} else {
|
||||
rve := rv.Elem()
|
||||
rvn = rv.Elem()
|
||||
// Note: interface{} is settable, but underlying type may not be.
|
||||
// Consequently, we have to set the reflect.Value directly.
|
||||
// if underlying type is settable (e.g. ptr or interface),
|
||||
// we just decode into it.
|
||||
// Else we create a settable value, decode into it, and set on the interface.
|
||||
if rve.CanSet() {
|
||||
f.d.decodeValue(rve, nil)
|
||||
if rvn.CanSet() {
|
||||
f.d.decodeValue(rvn, nil)
|
||||
} else {
|
||||
rve2 := reflect.New(rve.Type()).Elem()
|
||||
rve2.Set(rve)
|
||||
f.d.decodeValue(rve2, nil)
|
||||
rv.Set(rve2)
|
||||
rvn2 := reflect.New(rvn.Type()).Elem()
|
||||
rvn2.Set(rvn)
|
||||
f.d.decodeValue(rvn2, nil)
|
||||
rv.Set(rvn2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -887,22 +933,45 @@ func (f *decFnInfo) kMap(rv reflect.Value) {
|
|||
for xtyp = vtype; xtyp.Kind() == reflect.Ptr; xtyp = xtyp.Elem() {
|
||||
}
|
||||
valFn = d.getDecFn(xtyp, true, true)
|
||||
var mapGet bool
|
||||
if !f.d.h.MapValueReset {
|
||||
// if pointer, mapGet = true
|
||||
// if interface, mapGet = true if !DecodeNakedAlways (else false)
|
||||
// if builtin, mapGet = false
|
||||
// else mapGet = true
|
||||
vtypeKind := vtype.Kind()
|
||||
if vtypeKind == reflect.Ptr {
|
||||
mapGet = true
|
||||
} else if vtypeKind == reflect.Interface {
|
||||
if !f.d.h.InterfaceReset {
|
||||
mapGet = true
|
||||
}
|
||||
} else if !isImmutableKind(vtypeKind) {
|
||||
mapGet = true
|
||||
}
|
||||
}
|
||||
|
||||
var rvk, rvv reflect.Value
|
||||
|
||||
// for j := 0; j < containerLen; j++ {
|
||||
if containerLen > 0 {
|
||||
for j := 0; j < containerLen; j++ {
|
||||
rvk := reflect.New(ktype).Elem()
|
||||
rvk = reflect.New(ktype).Elem()
|
||||
d.decodeValue(rvk, keyFn)
|
||||
|
||||
// special case if a byte array.
|
||||
if ktypeId == intfTypId {
|
||||
rvk = rvk.Elem()
|
||||
if rvk.Type() == uint8SliceTyp {
|
||||
rvk = reflect.ValueOf(string(rvk.Bytes()))
|
||||
rvk = reflect.ValueOf(d.string(rvk.Bytes()))
|
||||
}
|
||||
}
|
||||
rvv := rv.MapIndex(rvk)
|
||||
// TODO: is !IsValid check required?
|
||||
if !rvv.IsValid() {
|
||||
if mapGet {
|
||||
rvv = rv.MapIndex(rvk)
|
||||
if !rvv.IsValid() {
|
||||
rvv = reflect.New(vtype).Elem()
|
||||
}
|
||||
} else {
|
||||
rvv = reflect.New(vtype).Elem()
|
||||
}
|
||||
d.decodeValue(rvv, valFn)
|
||||
|
@ -910,18 +979,22 @@ func (f *decFnInfo) kMap(rv reflect.Value) {
|
|||
}
|
||||
} else {
|
||||
for j := 0; !dd.CheckBreak(); j++ {
|
||||
rvk := reflect.New(ktype).Elem()
|
||||
rvk = reflect.New(ktype).Elem()
|
||||
d.decodeValue(rvk, keyFn)
|
||||
|
||||
// special case if a byte array.
|
||||
if ktypeId == intfTypId {
|
||||
rvk = rvk.Elem()
|
||||
if rvk.Type() == uint8SliceTyp {
|
||||
rvk = reflect.ValueOf(string(rvk.Bytes()))
|
||||
rvk = reflect.ValueOf(d.string(rvk.Bytes()))
|
||||
}
|
||||
}
|
||||
rvv := rv.MapIndex(rvk)
|
||||
if !rvv.IsValid() {
|
||||
if mapGet {
|
||||
rvv = rv.MapIndex(rvk)
|
||||
if !rvv.IsValid() {
|
||||
rvv = reflect.New(vtype).Elem()
|
||||
}
|
||||
} else {
|
||||
rvv = reflect.New(vtype).Elem()
|
||||
}
|
||||
d.decodeValue(rvv, valFn)
|
||||
|
@ -957,6 +1030,8 @@ type Decoder struct {
|
|||
|
||||
ri ioDecReader
|
||||
f map[uintptr]*decFn
|
||||
is map[string]string // used for interning strings
|
||||
|
||||
// _ uintptr // for alignment purposes, so next one starts from a cache line
|
||||
|
||||
b [scratchByteArrayLen]byte
|
||||
|
@ -977,6 +1052,9 @@ func NewDecoder(r io.Reader, h Handle) (d *Decoder) {
|
|||
d.ri.br = &d.ri.bs
|
||||
}
|
||||
d.r = &d.ri
|
||||
if d.h.InternString {
|
||||
d.is = make(map[string]string, 32)
|
||||
}
|
||||
_, d.js = h.(*JsonHandle)
|
||||
d.d = h.newDecDriver(d)
|
||||
return
|
||||
|
@ -990,6 +1068,9 @@ func NewDecoderBytes(in []byte, h Handle) (d *Decoder) {
|
|||
d.rb.b = in
|
||||
d.rb.a = len(in)
|
||||
d.r = &d.rb
|
||||
if d.h.InternString {
|
||||
d.is = make(map[string]string, 32)
|
||||
}
|
||||
_, d.js = h.(*JsonHandle)
|
||||
d.d = h.newDecDriver(d)
|
||||
// d.d = h.newDecDriver(decReaderT{true, &d.rb, &d.ri})
|
||||
|
@ -1472,6 +1553,24 @@ func (d *Decoder) errorf(format string, params ...interface{}) {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
func (d *Decoder) string(v []byte) (s string) {
|
||||
if d.is != nil {
|
||||
s, ok := d.is[string(v)] // no allocation here.
|
||||
if !ok {
|
||||
s = string(v)
|
||||
d.is[s] = s
|
||||
}
|
||||
return s
|
||||
}
|
||||
return string(v) // don't return stringView, as we need a real string here.
|
||||
}
|
||||
|
||||
func (d *Decoder) intern(s string) {
|
||||
if d.is != nil {
|
||||
d.is[s] = s
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
// decSliceHelper assists when decoding into a slice, from a map or an array in the stream.
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
package codec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -80,17 +79,6 @@ type encNoSeparator struct{}
|
|||
|
||||
func (_ encNoSeparator) EncodeEnd() {}
|
||||
|
||||
type encStructFieldBytesV struct {
|
||||
b []byte
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
type encStructFieldBytesVslice []encStructFieldBytesV
|
||||
|
||||
func (p encStructFieldBytesVslice) Len() int { return len(p) }
|
||||
func (p encStructFieldBytesVslice) Less(i, j int) bool { return bytes.Compare(p[i].b, p[j].b) == -1 }
|
||||
func (p encStructFieldBytesVslice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
type ioEncWriterWriter interface {
|
||||
WriteByte(c byte) error
|
||||
WriteString(s string) (n int, err error)
|
||||
|
@ -109,8 +97,16 @@ type EncodeOptions struct {
|
|||
// sequence of bytes.
|
||||
//
|
||||
// This only affects maps, as the iteration order for maps is random.
|
||||
// In this case, the map keys will first be encoded into []byte, and then sorted,
|
||||
// before writing the sorted keys and the corresponding map values to the stream.
|
||||
//
|
||||
// The implementation MAY use the natural sort order for the map keys if possible:
|
||||
//
|
||||
// - If there is a natural sort order (ie for number, bool, string or []byte keys),
|
||||
// then the map keys are first sorted in natural order and then written
|
||||
// with corresponding map values to the strema.
|
||||
// - If there is no natural sort order, then the map keys will first be
|
||||
// encoded into []byte, and then sorted,
|
||||
// before writing the sorted keys and the corresponding map values to the stream.
|
||||
//
|
||||
Canonical bool
|
||||
|
||||
// AsSymbols defines what should be encoded as symbols.
|
||||
|
@ -493,27 +489,27 @@ func (f *encFnInfo) kStruct(rv reflect.Value) {
|
|||
tisfi = fti.sfi
|
||||
}
|
||||
newlen = 0
|
||||
var kv encStructFieldKV
|
||||
var kv stringRv
|
||||
for _, si := range tisfi {
|
||||
kv.v = si.field(rv, false)
|
||||
kv.r = si.field(rv, false)
|
||||
// if si.i != -1 {
|
||||
// rvals[newlen] = rv.Field(int(si.i))
|
||||
// } else {
|
||||
// rvals[newlen] = rv.FieldByIndex(si.is)
|
||||
// }
|
||||
if toMap {
|
||||
if si.omitEmpty && isEmptyValue(kv.v) {
|
||||
if si.omitEmpty && isEmptyValue(kv.r) {
|
||||
continue
|
||||
}
|
||||
kv.k = si.encName
|
||||
kv.v = si.encName
|
||||
} else {
|
||||
// use the zero value.
|
||||
// if a reference or struct, set to nil (so you do not output too much)
|
||||
if si.omitEmpty && isEmptyValue(kv.v) {
|
||||
switch kv.v.Kind() {
|
||||
if si.omitEmpty && isEmptyValue(kv.r) {
|
||||
switch kv.r.Kind() {
|
||||
case reflect.Struct, reflect.Interface, reflect.Ptr, reflect.Array,
|
||||
reflect.Map, reflect.Slice:
|
||||
kv.v = reflect.Value{} //encode as nil
|
||||
kv.r = reflect.Value{} //encode as nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -532,17 +528,17 @@ func (f *encFnInfo) kStruct(rv reflect.Value) {
|
|||
for j := 0; j < newlen; j++ {
|
||||
kv = fkvs[j]
|
||||
if asSymbols {
|
||||
ee.EncodeSymbol(kv.k)
|
||||
ee.EncodeSymbol(kv.v)
|
||||
} else {
|
||||
ee.EncodeString(c_UTF8, kv.k)
|
||||
ee.EncodeString(c_UTF8, kv.v)
|
||||
}
|
||||
e.encodeValue(kv.v, nil)
|
||||
e.encodeValue(kv.r, nil)
|
||||
}
|
||||
} else {
|
||||
ee.EncodeArrayStart(newlen)
|
||||
for j := 0; j < newlen; j++ {
|
||||
kv = fkvs[j]
|
||||
e.encodeValue(kv.v, nil)
|
||||
e.encodeValue(kv.r, nil)
|
||||
}
|
||||
}
|
||||
ee.EncodeEnd()
|
||||
|
@ -621,24 +617,9 @@ func (f *encFnInfo) kMap(rv reflect.Value) {
|
|||
}
|
||||
mks := rv.MapKeys()
|
||||
// for j, lmks := 0, len(mks); j < lmks; j++ {
|
||||
|
||||
if e.h.Canonical {
|
||||
// first encode each key to a []byte first, then sort them, then record
|
||||
// println(">>>>>>>> CANONICAL <<<<<<<<")
|
||||
var mksv []byte = make([]byte, 0, len(mks)*16) // temporary byte slice for the encoding
|
||||
e2 := NewEncoderBytes(&mksv, e.hh)
|
||||
mksbv := make([]encStructFieldBytesV, len(mks))
|
||||
for i, k := range mks {
|
||||
l := len(mksv)
|
||||
e2.MustEncode(k)
|
||||
mksbv[i].v = k
|
||||
mksbv[i].b = mksv[l:]
|
||||
// fmt.Printf(">>>>> %s\n", mksv[l:])
|
||||
}
|
||||
sort.Sort(encStructFieldBytesVslice(mksbv))
|
||||
for j := range mksbv {
|
||||
e.asis(mksbv[j].b)
|
||||
e.encodeValue(rv.MapIndex(mksbv[j].v), valFn)
|
||||
}
|
||||
e.kMapCanonical(rtkeyid, rtkey, rv, mks, valFn, asSymbols)
|
||||
} else {
|
||||
for j := range mks {
|
||||
if keyTypeIsString {
|
||||
|
@ -653,9 +634,128 @@ func (f *encFnInfo) kMap(rv reflect.Value) {
|
|||
e.encodeValue(rv.MapIndex(mks[j]), valFn)
|
||||
}
|
||||
}
|
||||
|
||||
ee.EncodeEnd()
|
||||
}
|
||||
|
||||
func (e *Encoder) kMapCanonical(rtkeyid uintptr, rtkey reflect.Type, rv reflect.Value, mks []reflect.Value, valFn *encFn, asSymbols bool) {
|
||||
ee := e.e
|
||||
// we previously did out-of-band if an extension was registered.
|
||||
// This is not necessary, as the natural kind is sufficient for ordering.
|
||||
|
||||
if rtkeyid == uint8SliceTypId {
|
||||
mksv := make([]bytesRv, len(mks))
|
||||
for i, k := range mks {
|
||||
v := &mksv[i]
|
||||
v.r = k
|
||||
v.v = k.Bytes()
|
||||
}
|
||||
sort.Sort(bytesRvSlice(mksv))
|
||||
for i := range mksv {
|
||||
ee.EncodeStringBytes(c_RAW, mksv[i].v)
|
||||
e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
|
||||
}
|
||||
} else {
|
||||
switch rtkey.Kind() {
|
||||
case reflect.Bool:
|
||||
mksv := make([]boolRv, len(mks))
|
||||
for i, k := range mks {
|
||||
v := &mksv[i]
|
||||
v.r = k
|
||||
v.v = k.Bool()
|
||||
}
|
||||
sort.Sort(boolRvSlice(mksv))
|
||||
for i := range mksv {
|
||||
ee.EncodeBool(mksv[i].v)
|
||||
e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
|
||||
}
|
||||
case reflect.String:
|
||||
mksv := make([]stringRv, len(mks))
|
||||
for i, k := range mks {
|
||||
v := &mksv[i]
|
||||
v.r = k
|
||||
v.v = k.String()
|
||||
}
|
||||
sort.Sort(stringRvSlice(mksv))
|
||||
for i := range mksv {
|
||||
if asSymbols {
|
||||
ee.EncodeSymbol(mksv[i].v)
|
||||
} else {
|
||||
ee.EncodeString(c_UTF8, mksv[i].v)
|
||||
}
|
||||
e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
|
||||
}
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr:
|
||||
mksv := make([]uintRv, len(mks))
|
||||
for i, k := range mks {
|
||||
v := &mksv[i]
|
||||
v.r = k
|
||||
v.v = k.Uint()
|
||||
}
|
||||
sort.Sort(uintRvSlice(mksv))
|
||||
for i := range mksv {
|
||||
ee.EncodeUint(mksv[i].v)
|
||||
e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
|
||||
}
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
mksv := make([]intRv, len(mks))
|
||||
for i, k := range mks {
|
||||
v := &mksv[i]
|
||||
v.r = k
|
||||
v.v = k.Int()
|
||||
}
|
||||
sort.Sort(intRvSlice(mksv))
|
||||
for i := range mksv {
|
||||
ee.EncodeInt(mksv[i].v)
|
||||
e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
|
||||
}
|
||||
case reflect.Float32:
|
||||
mksv := make([]floatRv, len(mks))
|
||||
for i, k := range mks {
|
||||
v := &mksv[i]
|
||||
v.r = k
|
||||
v.v = k.Float()
|
||||
}
|
||||
sort.Sort(floatRvSlice(mksv))
|
||||
for i := range mksv {
|
||||
ee.EncodeFloat32(float32(mksv[i].v))
|
||||
e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
|
||||
}
|
||||
case reflect.Float64:
|
||||
mksv := make([]floatRv, len(mks))
|
||||
for i, k := range mks {
|
||||
v := &mksv[i]
|
||||
v.r = k
|
||||
v.v = k.Float()
|
||||
}
|
||||
sort.Sort(floatRvSlice(mksv))
|
||||
for i := range mksv {
|
||||
ee.EncodeFloat64(mksv[i].v)
|
||||
e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
|
||||
}
|
||||
default:
|
||||
// out-of-band
|
||||
// first encode each key to a []byte first, then sort them, then record
|
||||
var mksv []byte = make([]byte, 0, len(mks)*16) // temporary byte slice for the encoding
|
||||
e2 := NewEncoderBytes(&mksv, e.hh)
|
||||
mksbv := make([]bytesRv, len(mks))
|
||||
for i, k := range mks {
|
||||
v := &mksbv[i]
|
||||
l := len(mksv)
|
||||
e2.MustEncode(k)
|
||||
v.r = k
|
||||
v.v = mksv[l:]
|
||||
// fmt.Printf(">>>>> %s\n", mksv[l:])
|
||||
}
|
||||
sort.Sort(bytesRvSlice(mksbv))
|
||||
for j := range mksbv {
|
||||
e.asis(mksbv[j].v)
|
||||
e.encodeValue(rv.MapIndex(mksbv[j].r), valFn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
// encFn encapsulates the captured variables and the encode function.
|
||||
|
@ -903,16 +1003,9 @@ func (e *Encoder) encode(iv interface{}) {
|
|||
e.e.EncodeStringBytes(c_RAW, *v)
|
||||
|
||||
default:
|
||||
// canonical mode is not supported for fastpath of maps (but is fine for slices)
|
||||
const checkCodecSelfer1 = true // in case T is passed, where *T is a Selfer, still checkCodecSelfer
|
||||
if e.h.Canonical {
|
||||
if !fastpathEncodeTypeSwitchSlice(iv, e) {
|
||||
e.encodeI(iv, false, checkCodecSelfer1)
|
||||
}
|
||||
} else {
|
||||
if !fastpathEncodeTypeSwitch(iv, e) {
|
||||
e.encodeI(iv, false, checkCodecSelfer1)
|
||||
}
|
||||
if !fastpathEncodeTypeSwitch(iv, e) {
|
||||
e.encodeI(iv, false, checkCodecSelfer1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1019,8 +1112,7 @@ func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkFastpath, checkCo
|
|||
fn.f = (*encFnInfo).textMarshal
|
||||
} else {
|
||||
rk := rt.Kind()
|
||||
// if fastpathEnabled && checkFastpath && (rk == reflect.Map || rk == reflect.Slice) {
|
||||
if fastpathEnabled && checkFastpath && (rk == reflect.Slice || (rk == reflect.Map && !e.h.Canonical)) {
|
||||
if fastpathEnabled && checkFastpath && (rk == reflect.Map || rk == reflect.Slice) {
|
||||
if rt.PkgPath() == "" {
|
||||
if idx := fastpathAV.index(rtid); idx != -1 {
|
||||
fn.f = fastpathAV[idx].encfn
|
||||
|
@ -1114,11 +1206,6 @@ func (e *Encoder) errorf(format string, params ...interface{}) {
|
|||
|
||||
// ----------------------------------------
|
||||
|
||||
type encStructFieldKV struct {
|
||||
k string
|
||||
v reflect.Value
|
||||
}
|
||||
|
||||
const encStructPoolLen = 5
|
||||
|
||||
// encStructPool is an array of sync.Pool.
|
||||
|
@ -1133,33 +1220,33 @@ const encStructPoolLen = 5
|
|||
var encStructPool [encStructPoolLen]sync.Pool
|
||||
|
||||
func init() {
|
||||
encStructPool[0].New = func() interface{} { return new([8]encStructFieldKV) }
|
||||
encStructPool[1].New = func() interface{} { return new([16]encStructFieldKV) }
|
||||
encStructPool[2].New = func() interface{} { return new([32]encStructFieldKV) }
|
||||
encStructPool[3].New = func() interface{} { return new([64]encStructFieldKV) }
|
||||
encStructPool[4].New = func() interface{} { return new([128]encStructFieldKV) }
|
||||
encStructPool[0].New = func() interface{} { return new([8]stringRv) }
|
||||
encStructPool[1].New = func() interface{} { return new([16]stringRv) }
|
||||
encStructPool[2].New = func() interface{} { return new([32]stringRv) }
|
||||
encStructPool[3].New = func() interface{} { return new([64]stringRv) }
|
||||
encStructPool[4].New = func() interface{} { return new([128]stringRv) }
|
||||
}
|
||||
|
||||
func encStructPoolGet(newlen int) (p *sync.Pool, v interface{}, s []encStructFieldKV) {
|
||||
func encStructPoolGet(newlen int) (p *sync.Pool, v interface{}, s []stringRv) {
|
||||
// if encStructPoolLen != 5 { // constant chec, so removed at build time.
|
||||
// panic(errors.New("encStructPoolLen must be equal to 4")) // defensive, in case it is changed
|
||||
// }
|
||||
// idxpool := newlen / 8
|
||||
|
||||
// if pool == nil {
|
||||
// fkvs = make([]encStructFieldKV, newlen)
|
||||
// fkvs = make([]stringRv, newlen)
|
||||
// } else {
|
||||
// poolv = pool.Get()
|
||||
// switch vv := poolv.(type) {
|
||||
// case *[8]encStructFieldKV:
|
||||
// case *[8]stringRv:
|
||||
// fkvs = vv[:newlen]
|
||||
// case *[16]encStructFieldKV:
|
||||
// case *[16]stringRv:
|
||||
// fkvs = vv[:newlen]
|
||||
// case *[32]encStructFieldKV:
|
||||
// case *[32]stringRv:
|
||||
// fkvs = vv[:newlen]
|
||||
// case *[64]encStructFieldKV:
|
||||
// case *[64]stringRv:
|
||||
// fkvs = vv[:newlen]
|
||||
// case *[128]encStructFieldKV:
|
||||
// case *[128]stringRv:
|
||||
// fkvs = vv[:newlen]
|
||||
// }
|
||||
// }
|
||||
|
@ -1167,25 +1254,25 @@ func encStructPoolGet(newlen int) (p *sync.Pool, v interface{}, s []encStructFie
|
|||
if newlen <= 8 {
|
||||
p = &encStructPool[0]
|
||||
v = p.Get()
|
||||
s = v.(*[8]encStructFieldKV)[:newlen]
|
||||
s = v.(*[8]stringRv)[:newlen]
|
||||
} else if newlen <= 16 {
|
||||
p = &encStructPool[1]
|
||||
v = p.Get()
|
||||
s = v.(*[16]encStructFieldKV)[:newlen]
|
||||
s = v.(*[16]stringRv)[:newlen]
|
||||
} else if newlen <= 32 {
|
||||
p = &encStructPool[2]
|
||||
v = p.Get()
|
||||
s = v.(*[32]encStructFieldKV)[:newlen]
|
||||
s = v.(*[32]stringRv)[:newlen]
|
||||
} else if newlen <= 64 {
|
||||
p = &encStructPool[3]
|
||||
v = p.Get()
|
||||
s = v.(*[64]encStructFieldKV)[:newlen]
|
||||
s = v.(*[64]stringRv)[:newlen]
|
||||
} else if newlen <= 128 {
|
||||
p = &encStructPool[4]
|
||||
v = p.Get()
|
||||
s = v.(*[128]encStructFieldKV)[:newlen]
|
||||
s = v.(*[128]stringRv)[:newlen]
|
||||
} else {
|
||||
s = make([]encStructFieldKV, newlen)
|
||||
s = make([]stringRv, newlen)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -116,6 +116,7 @@ func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool {
|
|||
fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
|
||||
{{end}}{{end}}
|
||||
default:
|
||||
_ = v // TODO: workaround https://github.com/golang/go/issues/12927 (remove after go 1.6 release)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -182,14 +183,50 @@ func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Ele
|
|||
return
|
||||
}
|
||||
ee.EncodeMapStart(len(v))
|
||||
{{if eq .MapKey "string"}}asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0{{end}}
|
||||
for k2, v2 := range v {
|
||||
{{if eq .MapKey "string"}}if asSymbols {
|
||||
ee.EncodeSymbol(k2)
|
||||
} else {
|
||||
ee.EncodeString(c_UTF8, k2)
|
||||
}{{else}}{{ encmd .MapKey "k2"}}{{end}}
|
||||
{{ encmd .Elem "v2"}}
|
||||
{{if eq .MapKey "string"}}asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
|
||||
{{end}}if e.h.Canonical {
|
||||
{{if eq .MapKey "interface{}"}}{{/* out of band
|
||||
*/}}var mksv []byte = make([]byte, 0, len(v)*16) // temporary byte slice for the encoding
|
||||
e2 := NewEncoderBytes(&mksv, e.hh)
|
||||
v2 := make([]bytesI, len(v))
|
||||
var i, l int
|
||||
var vp *bytesI {{/* put loop variables outside. seems currently needed for better perf */}}
|
||||
for k2, _ := range v {
|
||||
l = len(mksv)
|
||||
e2.MustEncode(k2)
|
||||
vp = &v2[i]
|
||||
vp.v = mksv[l:]
|
||||
vp.i = k2
|
||||
i++
|
||||
}
|
||||
sort.Sort(bytesISlice(v2))
|
||||
for j := range v2 {
|
||||
e.asis(v2[j].v)
|
||||
e.encode(v[v2[j].i])
|
||||
} {{else}}{{ $x := sorttype .MapKey true}}v2 := make([]{{ $x }}, len(v))
|
||||
var i int
|
||||
for k, _ := range v {
|
||||
v2[i] = {{ $x }}(k)
|
||||
i++
|
||||
}
|
||||
sort.Sort({{ sorttype .MapKey false}}(v2))
|
||||
for _, k2 := range v2 {
|
||||
{{if eq .MapKey "string"}}if asSymbols {
|
||||
ee.EncodeSymbol(k2)
|
||||
} else {
|
||||
ee.EncodeString(c_UTF8, k2)
|
||||
}{{else}}{{ $y := printf "%s(k2)" .MapKey }}{{ encmd .MapKey $y }}{{end}}
|
||||
{{ $y := printf "v[%s(k2)]" .MapKey }}{{ encmd .Elem $y }}
|
||||
} {{end}}
|
||||
} else {
|
||||
for k2, v2 := range v {
|
||||
{{if eq .MapKey "string"}}if asSymbols {
|
||||
ee.EncodeSymbol(k2)
|
||||
} else {
|
||||
ee.EncodeString(c_UTF8, k2)
|
||||
}{{else}}{{ encmd .MapKey "k2"}}{{end}}
|
||||
{{ encmd .Elem "v2"}}
|
||||
}
|
||||
}
|
||||
ee.EncodeEnd()
|
||||
}
|
||||
|
@ -382,30 +419,31 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Ele
|
|||
v = make(map[{{ .MapKey }}]{{ .Elem }}, xlen)
|
||||
changed = true
|
||||
}
|
||||
{{ if eq .Elem "interface{}" }}mapGet := !d.h.MapValueReset && !d.h.InterfaceReset{{end}}
|
||||
var mk {{ .MapKey }}
|
||||
var mv {{ .Elem }}
|
||||
if containerLen > 0 {
|
||||
for j := 0; j < containerLen; j++ {
|
||||
{{ if eq .MapKey "interface{}" }}var mk interface{}
|
||||
{{ if eq .MapKey "interface{}" }}mk = nil
|
||||
d.decode(&mk)
|
||||
if bv, bok := mk.([]byte); bok {
|
||||
mk = string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
|
||||
}{{ else }}mk := {{ decmd .MapKey }}{{ end }}
|
||||
mv := v[mk]
|
||||
{{ if eq .Elem "interface{}" }}d.decode(&mv)
|
||||
{{ else }}mv = {{ decmd .Elem }}{{ end }}
|
||||
mk = d.string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
|
||||
}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
|
||||
{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
|
||||
d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
|
||||
if v != nil {
|
||||
v[mk] = mv
|
||||
}
|
||||
}
|
||||
} else if containerLen < 0 {
|
||||
for j := 0; !dd.CheckBreak(); j++ {
|
||||
{{ if eq .MapKey "interface{}" }}var mk interface{}
|
||||
{{ if eq .MapKey "interface{}" }}mk = nil
|
||||
d.decode(&mk)
|
||||
if bv, bok := mk.([]byte); bok {
|
||||
mk = string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
|
||||
}{{ else }}mk := {{ decmd .MapKey }}{{ end }}
|
||||
mv := v[mk]
|
||||
{{ if eq .Elem "interface{}" }}d.decode(&mv)
|
||||
{{ else }}mv = {{ decmd .Elem }}{{ end }}
|
||||
mk = d.string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
|
||||
}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
|
||||
{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
|
||||
d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
|
||||
if v != nil {
|
||||
v[mk] = mv
|
||||
}
|
||||
|
|
|
@ -1,20 +1,28 @@
|
|||
{{var "v"}} := *{{ .Varname }}
|
||||
{{var "l"}} := r.ReadMapStart()
|
||||
{{var "bh"}} := z.DecBasicHandle()
|
||||
if {{var "v"}} == nil {
|
||||
{{var "rl"}}, _ := z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
|
||||
{{var "rl"}}, _ := z.DecInferLen({{var "l"}}, {{var "bh"}}.MaxInitLen, {{ .Size }})
|
||||
{{var "v"}} = make(map[{{ .KTyp }}]{{ .Typ }}, {{var "rl"}})
|
||||
*{{ .Varname }} = {{var "v"}}
|
||||
}
|
||||
var {{var "mk"}} {{ .KTyp }}
|
||||
var {{var "mv"}} {{ .Typ }}
|
||||
var {{var "mg"}} bool
|
||||
if {{var "bh"}}.MapValueReset {
|
||||
{{if decElemKindPtr}}{{var "mg"}} = true
|
||||
{{else if decElemKindIntf}}if !{{var "bh"}}.InterfaceReset { {{var "mg"}} = true }
|
||||
{{else if not decElemKindImmutable}}{{var "mg"}} = true
|
||||
{{end}} }
|
||||
if {{var "l"}} > 0 {
|
||||
for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
|
||||
var {{var "mk"}} {{ .KTyp }}
|
||||
{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
|
||||
{{ if eq .KTyp "interface{}" }}// special case if a byte array.
|
||||
if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||
{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||
{{var "mk"}} = string({{var "bv"}})
|
||||
}
|
||||
{{ end }}
|
||||
{{var "mv"}} := {{var "v"}}[{{var "mk"}}]
|
||||
}{{ end }}
|
||||
if {{var "mg"}} {
|
||||
{{var "mv"}} = {{var "v"}}[{{var "mk"}}]
|
||||
} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
|
||||
{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
|
||||
if {{var "v"}} != nil {
|
||||
{{var "v"}}[{{var "mk"}}] = {{var "mv"}}
|
||||
|
@ -22,14 +30,13 @@ for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
|
|||
}
|
||||
} else if {{var "l"}} < 0 {
|
||||
for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
|
||||
var {{var "mk"}} {{ .KTyp }}
|
||||
{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
|
||||
{{ if eq .KTyp "interface{}" }}// special case if a byte array.
|
||||
if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||
{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||
{{var "mk"}} = string({{var "bv"}})
|
||||
}
|
||||
{{ end }}
|
||||
{{var "mv"}} := {{var "v"}}[{{var "mk"}}]
|
||||
}{{ end }}
|
||||
if {{var "mg"}} {
|
||||
{{var "mv"}} = {{var "v"}}[{{var "mk"}}]
|
||||
} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
|
||||
{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
|
||||
if {{var "v"}} != nil {
|
||||
{{var "v"}}[{{var "mk"}}] = {{var "mv"}}
|
||||
|
|
|
@ -8,21 +8,29 @@ package codec
|
|||
const genDecMapTmpl = `
|
||||
{{var "v"}} := *{{ .Varname }}
|
||||
{{var "l"}} := r.ReadMapStart()
|
||||
{{var "bh"}} := z.DecBasicHandle()
|
||||
if {{var "v"}} == nil {
|
||||
{{var "rl"}}, _ := z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
|
||||
{{var "rl"}}, _ := z.DecInferLen({{var "l"}}, {{var "bh"}}.MaxInitLen, {{ .Size }})
|
||||
{{var "v"}} = make(map[{{ .KTyp }}]{{ .Typ }}, {{var "rl"}})
|
||||
*{{ .Varname }} = {{var "v"}}
|
||||
}
|
||||
var {{var "mk"}} {{ .KTyp }}
|
||||
var {{var "mv"}} {{ .Typ }}
|
||||
var {{var "mg"}} bool
|
||||
if {{var "bh"}}.MapValueReset {
|
||||
{{if decElemKindPtr}}{{var "mg"}} = true
|
||||
{{else if decElemKindIntf}}if !{{var "bh"}}.InterfaceReset { {{var "mg"}} = true }
|
||||
{{else if not decElemKindImmutable}}{{var "mg"}} = true
|
||||
{{end}} }
|
||||
if {{var "l"}} > 0 {
|
||||
for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
|
||||
var {{var "mk"}} {{ .KTyp }}
|
||||
{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
|
||||
{{ if eq .KTyp "interface{}" }}// special case if a byte array.
|
||||
if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||
{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||
{{var "mk"}} = string({{var "bv"}})
|
||||
}
|
||||
{{ end }}
|
||||
{{var "mv"}} := {{var "v"}}[{{var "mk"}}]
|
||||
}{{ end }}
|
||||
if {{var "mg"}} {
|
||||
{{var "mv"}} = {{var "v"}}[{{var "mk"}}]
|
||||
} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
|
||||
{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
|
||||
if {{var "v"}} != nil {
|
||||
{{var "v"}}[{{var "mk"}}] = {{var "mv"}}
|
||||
|
@ -30,14 +38,13 @@ for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
|
|||
}
|
||||
} else if {{var "l"}} < 0 {
|
||||
for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
|
||||
var {{var "mk"}} {{ .KTyp }}
|
||||
{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
|
||||
{{ if eq .KTyp "interface{}" }}// special case if a byte array.
|
||||
if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||
{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
|
||||
{{var "mk"}} = string({{var "bv"}})
|
||||
}
|
||||
{{ end }}
|
||||
{{var "mv"}} := {{var "v"}}[{{var "mk"}}]
|
||||
}{{ end }}
|
||||
if {{var "mg"}} {
|
||||
{{var "mv"}} = {{var "v"}}[{{var "mk"}}]
|
||||
} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
|
||||
{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
|
||||
if {{var "v"}} != nil {
|
||||
{{var "v"}}[{{var "mk"}}] = {{var "mv"}}
|
||||
|
|
|
@ -169,7 +169,7 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, ti *TypeIn
|
|||
ts: []reflect.Type{},
|
||||
bp: genImportPath(typ[0]),
|
||||
xs: uid,
|
||||
ti: ti, //TODO: make it configurable
|
||||
ti: ti,
|
||||
}
|
||||
if x.ti == nil {
|
||||
x.ti = defTypeInfos
|
||||
|
@ -928,6 +928,7 @@ func (x *genRunner) encListFallback(varname string, t reflect.Type) {
|
|||
}
|
||||
|
||||
func (x *genRunner) encMapFallback(varname string, t reflect.Type) {
|
||||
// TODO: expand this to handle canonical.
|
||||
i := x.varsfx()
|
||||
x.line("r.EncodeMapStart(len(" + varname + "))")
|
||||
x.linef("for %sk%s, %sv%s := range %s {", genTempVarPfx, i, genTempVarPfx, i, varname)
|
||||
|
@ -1301,8 +1302,24 @@ func (x *genRunner) decMapFallback(varname string, rtid uintptr, t reflect.Type)
|
|||
}
|
||||
telem := t.Elem()
|
||||
tkey := t.Key()
|
||||
ts := tstruc{genTempVarPfx, x.varsfx(), varname, x.genTypeName(tkey), x.genTypeName(telem), int(telem.Size() + tkey.Size())}
|
||||
ts := tstruc{
|
||||
genTempVarPfx, x.varsfx(), varname, x.genTypeName(tkey),
|
||||
x.genTypeName(telem), int(telem.Size() + tkey.Size()),
|
||||
}
|
||||
|
||||
funcs := make(template.FuncMap)
|
||||
funcs["decElemZero"] = func() string {
|
||||
return x.genZeroValueR(telem)
|
||||
}
|
||||
funcs["decElemKindImmutable"] = func() bool {
|
||||
return genIsImmutable(telem)
|
||||
}
|
||||
funcs["decElemKindPtr"] = func() bool {
|
||||
return telem.Kind() == reflect.Ptr
|
||||
}
|
||||
funcs["decElemKindIntf"] = func() bool {
|
||||
return telem.Kind() == reflect.Interface
|
||||
}
|
||||
funcs["decLineVarK"] = func(varname string) string {
|
||||
x.decVar(varname, tkey, false)
|
||||
return ""
|
||||
|
@ -1726,6 +1743,8 @@ func genInternalDecCommandAsString(s string) string {
|
|||
return "uint32(dd.DecodeUint(32))"
|
||||
case "uint64":
|
||||
return "dd.DecodeUint(64)"
|
||||
case "uintptr":
|
||||
return "uintptr(dd.DecodeUint(uintBitsize))"
|
||||
case "int":
|
||||
return "int(dd.DecodeInt(intBitsize))"
|
||||
case "int8":
|
||||
|
@ -1746,9 +1765,24 @@ func genInternalDecCommandAsString(s string) string {
|
|||
case "bool":
|
||||
return "dd.DecodeBool()"
|
||||
default:
|
||||
panic(errors.New("unknown type for decode: " + s))
|
||||
panic(errors.New("gen internal: unknown type for decode: " + s))
|
||||
}
|
||||
}
|
||||
|
||||
func genInternalSortType(s string, elem bool) string {
|
||||
for _, v := range [...]string{"int", "uint", "float", "bool", "string"} {
|
||||
if strings.HasPrefix(s, v) {
|
||||
if elem {
|
||||
if v == "int" || v == "uint" || v == "float" {
|
||||
return v + "64"
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return v + "Slice"
|
||||
}
|
||||
}
|
||||
panic("sorttype: unexpected type: " + s)
|
||||
}
|
||||
|
||||
// var genInternalMu sync.Mutex
|
||||
|
@ -1767,6 +1801,7 @@ func genInternalInit() {
|
|||
"uint16",
|
||||
"uint32",
|
||||
"uint64",
|
||||
"uintptr",
|
||||
"int",
|
||||
"int8",
|
||||
"int16",
|
||||
|
@ -1784,6 +1819,7 @@ func genInternalInit() {
|
|||
"uint16",
|
||||
"uint32",
|
||||
"uint64",
|
||||
"uintptr",
|
||||
"int",
|
||||
"int8",
|
||||
"int16",
|
||||
|
@ -1803,6 +1839,7 @@ func genInternalInit() {
|
|||
"uint16": 2,
|
||||
"uint32": 4,
|
||||
"uint64": 8,
|
||||
"uintptr": 1 * wordSizeBytes,
|
||||
"int": 1 * wordSizeBytes,
|
||||
"int8": 1,
|
||||
"int16": 2,
|
||||
|
@ -1837,16 +1874,18 @@ func genInternalInit() {
|
|||
funcs["encmd"] = genInternalEncCommandAsString
|
||||
funcs["decmd"] = genInternalDecCommandAsString
|
||||
funcs["zerocmd"] = genInternalZeroValue
|
||||
funcs["hasprefix"] = strings.HasPrefix
|
||||
funcs["sorttype"] = genInternalSortType
|
||||
|
||||
genInternalV = gt
|
||||
genInternalTmplFuncs = funcs
|
||||
}
|
||||
|
||||
// GenInternalGoFile is used to generate source files from templates.
|
||||
// genInternalGoFile is used to generate source files from templates.
|
||||
// It is run by the program author alone.
|
||||
// Unfortunately, it has to be exported so that it can be called from a command line tool.
|
||||
// *** DO NOT USE ***
|
||||
func GenInternalGoFile(r io.Reader, w io.Writer, safe bool) (err error) {
|
||||
func genInternalGoFile(r io.Reader, w io.Writer, safe bool) (err error) {
|
||||
genInternalOnce.Do(genInternalInit)
|
||||
|
||||
gt := genInternalV
|
||||
|
|
|
@ -101,6 +101,7 @@ package codec
|
|||
// check for these error conditions.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
@ -975,3 +976,114 @@ func (_ checkOverflow) SignedInt(v uint64) (i int64, overflow bool) {
|
|||
i = int64(v)
|
||||
return
|
||||
}
|
||||
|
||||
// ------------------ SORT -----------------
|
||||
|
||||
func isNaN(f float64) bool { return f != f }
|
||||
|
||||
// -----------------------
|
||||
|
||||
type intSlice []int64
|
||||
type uintSlice []uint64
|
||||
type floatSlice []float64
|
||||
type boolSlice []bool
|
||||
type stringSlice []string
|
||||
type bytesSlice [][]byte
|
||||
|
||||
func (p intSlice) Len() int { return len(p) }
|
||||
func (p intSlice) Less(i, j int) bool { return p[i] < p[j] }
|
||||
func (p intSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
func (p uintSlice) Len() int { return len(p) }
|
||||
func (p uintSlice) Less(i, j int) bool { return p[i] < p[j] }
|
||||
func (p uintSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
func (p floatSlice) Len() int { return len(p) }
|
||||
func (p floatSlice) Less(i, j int) bool {
|
||||
return p[i] < p[j] || isNaN(p[i]) && !isNaN(p[j])
|
||||
}
|
||||
func (p floatSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
func (p stringSlice) Len() int { return len(p) }
|
||||
func (p stringSlice) Less(i, j int) bool { return p[i] < p[j] }
|
||||
func (p stringSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
func (p bytesSlice) Len() int { return len(p) }
|
||||
func (p bytesSlice) Less(i, j int) bool { return bytes.Compare(p[i], p[j]) == -1 }
|
||||
func (p bytesSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
func (p boolSlice) Len() int { return len(p) }
|
||||
func (p boolSlice) Less(i, j int) bool { return !p[i] && p[j] }
|
||||
func (p boolSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
// ---------------------
|
||||
|
||||
type intRv struct {
|
||||
v int64
|
||||
r reflect.Value
|
||||
}
|
||||
type intRvSlice []intRv
|
||||
type uintRv struct {
|
||||
v uint64
|
||||
r reflect.Value
|
||||
}
|
||||
type uintRvSlice []uintRv
|
||||
type floatRv struct {
|
||||
v float64
|
||||
r reflect.Value
|
||||
}
|
||||
type floatRvSlice []floatRv
|
||||
type boolRv struct {
|
||||
v bool
|
||||
r reflect.Value
|
||||
}
|
||||
type boolRvSlice []boolRv
|
||||
type stringRv struct {
|
||||
v string
|
||||
r reflect.Value
|
||||
}
|
||||
type stringRvSlice []stringRv
|
||||
type bytesRv struct {
|
||||
v []byte
|
||||
r reflect.Value
|
||||
}
|
||||
type bytesRvSlice []bytesRv
|
||||
|
||||
func (p intRvSlice) Len() int { return len(p) }
|
||||
func (p intRvSlice) Less(i, j int) bool { return p[i].v < p[j].v }
|
||||
func (p intRvSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
func (p uintRvSlice) Len() int { return len(p) }
|
||||
func (p uintRvSlice) Less(i, j int) bool { return p[i].v < p[j].v }
|
||||
func (p uintRvSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
func (p floatRvSlice) Len() int { return len(p) }
|
||||
func (p floatRvSlice) Less(i, j int) bool {
|
||||
return p[i].v < p[j].v || isNaN(p[i].v) && !isNaN(p[j].v)
|
||||
}
|
||||
func (p floatRvSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
func (p stringRvSlice) Len() int { return len(p) }
|
||||
func (p stringRvSlice) Less(i, j int) bool { return p[i].v < p[j].v }
|
||||
func (p stringRvSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
func (p bytesRvSlice) Len() int { return len(p) }
|
||||
func (p bytesRvSlice) Less(i, j int) bool { return bytes.Compare(p[i].v, p[j].v) == -1 }
|
||||
func (p bytesRvSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
func (p boolRvSlice) Len() int { return len(p) }
|
||||
func (p boolRvSlice) Less(i, j int) bool { return !p[i].v && p[j].v }
|
||||
func (p boolRvSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
// -----------------
|
||||
|
||||
type bytesI struct {
|
||||
v []byte
|
||||
i interface{}
|
||||
}
|
||||
|
||||
type bytesISlice []bytesI
|
||||
|
||||
func (p bytesISlice) Len() int { return len(p) }
|
||||
func (p bytesISlice) Less(i, j int) bool { return bytes.Compare(p[i].v, p[j].v) == -1 }
|
||||
func (p bytesISlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
|
|
@ -411,7 +411,7 @@ func (e *jsonEncDriver) quoteStr(s string) {
|
|||
//--------------------------------
|
||||
|
||||
type jsonNum struct {
|
||||
bytes []byte // may have [+-.eE0-9]
|
||||
// bytes []byte // may have [+-.eE0-9]
|
||||
mantissa uint64 // where mantissa ends, and maybe dot begins.
|
||||
exponent int16 // exponent value.
|
||||
manOverflow bool
|
||||
|
@ -421,7 +421,6 @@ type jsonNum struct {
|
|||
}
|
||||
|
||||
func (x *jsonNum) reset() {
|
||||
x.bytes = x.bytes[:0]
|
||||
x.manOverflow = false
|
||||
x.neg = false
|
||||
x.dot = false
|
||||
|
@ -454,29 +453,26 @@ func (x *jsonNum) uintExp() (n uint64, overflow bool) {
|
|||
// return
|
||||
}
|
||||
|
||||
func (x *jsonNum) floatVal() (f float64) {
|
||||
// these constants are only used withn floatVal.
|
||||
// They are brought out, so that floatVal can be inlined.
|
||||
const (
|
||||
jsonUint64MantissaBits = 52
|
||||
jsonMaxExponent = int16(len(jsonFloat64Pow10)) - 1
|
||||
)
|
||||
|
||||
func (x *jsonNum) floatVal() (f float64, parseUsingStrConv bool) {
|
||||
// We do not want to lose precision.
|
||||
// Consequently, we will delegate to strconv.ParseFloat if any of the following happen:
|
||||
// - There are more digits than in math.MaxUint64: 18446744073709551615 (20 digits)
|
||||
// We expect up to 99.... (19 digits)
|
||||
// - The mantissa cannot fit into a 52 bits of uint64
|
||||
// - The exponent is beyond our scope ie beyong 22.
|
||||
const uint64MantissaBits = 52
|
||||
const maxExponent = int16(len(jsonFloat64Pow10)) - 1
|
||||
parseUsingStrConv = x.manOverflow ||
|
||||
x.exponent > jsonMaxExponent ||
|
||||
(x.exponent < 0 && -(x.exponent) > jsonMaxExponent) ||
|
||||
x.mantissa>>jsonUint64MantissaBits != 0
|
||||
|
||||
parseUsingStrConv := x.manOverflow ||
|
||||
x.exponent > maxExponent ||
|
||||
(x.exponent < 0 && -(x.exponent) > maxExponent) ||
|
||||
x.mantissa>>uint64MantissaBits != 0
|
||||
if parseUsingStrConv {
|
||||
var err error
|
||||
if f, err = strconv.ParseFloat(stringView(x.bytes), 64); err != nil {
|
||||
panic(fmt.Errorf("parse float: %s, %v", x.bytes, err))
|
||||
return
|
||||
}
|
||||
if x.neg {
|
||||
f = -f
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -500,8 +496,9 @@ type jsonDecDriver struct {
|
|||
r decReader // *bytesDecReader decReader
|
||||
ct valueType // container type. one of unset, array or map.
|
||||
bstr [8]byte // scratch used for string \UXXX parsing
|
||||
b [64]byte // scratch
|
||||
b2 [64]byte
|
||||
b [64]byte // scratch, used for parsing strings or numbers
|
||||
b2 [64]byte // scratch, used only for decodeBytes (after base64)
|
||||
bs []byte // scratch. Initialized from b. Used for parsing strings or numbers.
|
||||
|
||||
wsSkipped bool // whitespace skipped
|
||||
|
||||
|
@ -662,8 +659,6 @@ func (d *jsonDecDriver) IsContainerType(vt valueType) bool {
|
|||
}
|
||||
|
||||
func (d *jsonDecDriver) decNum(storeBytes bool) {
|
||||
// storeBytes = true // TODO: remove.
|
||||
|
||||
// If it is has a . or an e|E, decode as a float; else decode as an int.
|
||||
b := d.skipWhitespace(false)
|
||||
if !(b == '+' || b == '-' || b == '.' || (b >= '0' && b <= '9')) {
|
||||
|
@ -674,10 +669,9 @@ func (d *jsonDecDriver) decNum(storeBytes bool) {
|
|||
const cutoff = (1<<64-1)/uint64(10) + 1 // cutoff64(base)
|
||||
const jsonNumUintMaxVal = 1<<uint64(64) - 1
|
||||
|
||||
// var n jsonNum // create stack-copy jsonNum, and set to pointer at end.
|
||||
// n.bytes = d.n.bytes[:0]
|
||||
n := &d.n
|
||||
n.reset()
|
||||
d.bs = d.bs[:0]
|
||||
|
||||
// The format of a number is as below:
|
||||
// parsing: sign? digit* dot? digit* e? sign? digit*
|
||||
|
@ -773,7 +767,7 @@ LOOP:
|
|||
break LOOP
|
||||
}
|
||||
if storeBytes {
|
||||
n.bytes = append(n.bytes, b)
|
||||
d.bs = append(d.bs, b)
|
||||
}
|
||||
b, eof = d.r.readn1eof()
|
||||
}
|
||||
|
@ -834,13 +828,28 @@ func (d *jsonDecDriver) DecodeInt(bitsize uint8) (i int64) {
|
|||
i = -i
|
||||
}
|
||||
if chkOvf.Int(i, bitsize) {
|
||||
d.d.errorf("json: overflow %v bits: %s", bitsize, n.bytes)
|
||||
d.d.errorf("json: overflow %v bits: %s", bitsize, d.bs)
|
||||
return
|
||||
}
|
||||
// fmt.Printf("DecodeInt: %v\n", i)
|
||||
return
|
||||
}
|
||||
|
||||
// floatVal MUST only be called after a decNum, as d.bs now contains the bytes of the number
|
||||
func (d *jsonDecDriver) floatVal() (f float64) {
|
||||
f, useStrConv := d.n.floatVal()
|
||||
if useStrConv {
|
||||
var err error
|
||||
if f, err = strconv.ParseFloat(stringView(d.bs), 64); err != nil {
|
||||
panic(fmt.Errorf("parse float: %s, %v", d.bs, err))
|
||||
}
|
||||
if d.n.neg {
|
||||
f = -f
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *jsonDecDriver) DecodeUint(bitsize uint8) (u uint64) {
|
||||
if c := d.s.sc.sep(); c != 0 {
|
||||
d.expectChar(c)
|
||||
|
@ -868,7 +877,7 @@ func (d *jsonDecDriver) DecodeUint(bitsize uint8) (u uint64) {
|
|||
}
|
||||
}
|
||||
if chkOvf.Uint(u, bitsize) {
|
||||
d.d.errorf("json: overflow %v bits: %s", bitsize, n.bytes)
|
||||
d.d.errorf("json: overflow %v bits: %s", bitsize, d.bs)
|
||||
return
|
||||
}
|
||||
// fmt.Printf("DecodeUint: %v\n", u)
|
||||
|
@ -880,10 +889,9 @@ func (d *jsonDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
|
|||
d.expectChar(c)
|
||||
}
|
||||
d.decNum(true)
|
||||
n := &d.n
|
||||
f = n.floatVal()
|
||||
f = d.floatVal()
|
||||
if chkOverflow32 && chkOvf.Float32(f) {
|
||||
d.d.errorf("json: overflow float32: %v, %s", f, n.bytes)
|
||||
d.d.errorf("json: overflow float32: %v, %s", f, d.bs)
|
||||
return
|
||||
}
|
||||
return
|
||||
|
@ -916,12 +924,13 @@ func (d *jsonDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut [
|
|||
if c := d.s.sc.sep(); c != 0 {
|
||||
d.expectChar(c)
|
||||
}
|
||||
bs0 := d.appendStringAsBytes(d.b[:0])
|
||||
d.appendStringAsBytes()
|
||||
// if isstring, then just return the bytes, even if it is using the scratch buffer.
|
||||
// the bytes will be converted to a string as needed.
|
||||
if isstring {
|
||||
return bs0
|
||||
return d.bs
|
||||
}
|
||||
bs0 := d.bs
|
||||
slen := base64.StdEncoding.DecodedLen(len(bs0))
|
||||
if slen <= cap(bs) {
|
||||
bsOut = bs[:slen]
|
||||
|
@ -945,13 +954,23 @@ func (d *jsonDecDriver) DecodeString() (s string) {
|
|||
if c := d.s.sc.sep(); c != 0 {
|
||||
d.expectChar(c)
|
||||
}
|
||||
return string(d.appendStringAsBytes(d.b[:0]))
|
||||
return d.decString()
|
||||
}
|
||||
|
||||
func (d *jsonDecDriver) appendStringAsBytes(v []byte) []byte {
|
||||
func (d *jsonDecDriver) decString() (s string) {
|
||||
d.appendStringAsBytes()
|
||||
if x := d.s.sc; x != nil && x.st == '}' && x.so { // map key
|
||||
return d.d.string(d.bs)
|
||||
}
|
||||
return string(d.bs)
|
||||
}
|
||||
|
||||
func (d *jsonDecDriver) appendStringAsBytes() {
|
||||
d.expectChar('"')
|
||||
v := d.bs[:0]
|
||||
var c uint8
|
||||
for {
|
||||
c := d.r.readn1()
|
||||
c = d.r.readn1()
|
||||
if c == '"' {
|
||||
break
|
||||
} else if c == '\\' {
|
||||
|
@ -979,7 +998,6 @@ func (d *jsonDecDriver) appendStringAsBytes(v []byte) []byte {
|
|||
v = append(v, d.bstr[:w2]...)
|
||||
default:
|
||||
d.d.errorf("json: unsupported escaped value: %c", c)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
v = append(v, c)
|
||||
|
@ -988,7 +1006,7 @@ func (d *jsonDecDriver) appendStringAsBytes(v []byte) []byte {
|
|||
if jsonTrackSkipWhitespace {
|
||||
d.wsSkipped = false
|
||||
}
|
||||
return v
|
||||
d.bs = v
|
||||
}
|
||||
|
||||
func (d *jsonDecDriver) jsonU4(checkSlashU bool) rune {
|
||||
|
@ -1040,7 +1058,7 @@ func (d *jsonDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurthe
|
|||
decodeFurther = true
|
||||
case '"':
|
||||
vt = valueTypeString
|
||||
v = string(d.appendStringAsBytes(d.b[:0])) // same as d.DecodeString(), but skipping sep() call.
|
||||
v = d.decString() // same as d.DecodeString(), but skipping sep() call.
|
||||
default: // number
|
||||
d.decNum(true)
|
||||
n := &d.n
|
||||
|
@ -1048,7 +1066,7 @@ func (d *jsonDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurthe
|
|||
switch {
|
||||
case n.explicitExponent, n.dot, n.exponent < 0, n.manOverflow:
|
||||
vt = valueTypeFloat
|
||||
v = n.floatVal()
|
||||
v = d.floatVal()
|
||||
case n.exponent == 0:
|
||||
u := n.mantissa
|
||||
switch {
|
||||
|
@ -1067,7 +1085,7 @@ func (d *jsonDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurthe
|
|||
switch {
|
||||
case overflow:
|
||||
vt = valueTypeFloat
|
||||
v = n.floatVal()
|
||||
v = d.floatVal()
|
||||
case n.neg:
|
||||
vt = valueTypeInt
|
||||
v = -int64(u)
|
||||
|
@ -1122,8 +1140,8 @@ func (h *JsonHandle) newEncDriver(e *Encoder) encDriver {
|
|||
func (h *JsonHandle) newDecDriver(d *Decoder) decDriver {
|
||||
// d := jsonDecDriver{r: r.(*bytesDecReader), h: h}
|
||||
hd := jsonDecDriver{d: d, r: d.r, h: h}
|
||||
hd.bs = hd.b[:0]
|
||||
hd.se.i = h.RawBytesExt
|
||||
hd.n.bytes = d.b[:]
|
||||
return &hd
|
||||
}
|
||||
|
||||
|
|
|
@ -99,6 +99,14 @@ var fastpathAV fastpathA
|
|||
|
||||
EOF
|
||||
|
||||
cat > gen-from-tmpl.codec.generated.go <<EOF
|
||||
package codec
|
||||
import "io"
|
||||
func GenInternalGoFile(r io.Reader, w io.Writer, safe bool) error {
|
||||
return genInternalGoFile(r, w, safe)
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > gen-from-tmpl.generated.go <<EOF
|
||||
//+build ignore
|
||||
|
||||
|
@ -130,7 +138,7 @@ run("gen-helper.go.tmpl", "gen-helper.generated.go", false)
|
|||
|
||||
EOF
|
||||
go run gen-from-tmpl.generated.go && \
|
||||
rm -f gen-from-tmpl.generated.go
|
||||
rm -f gen-from-tmpl.*generated.go
|
||||
}
|
||||
|
||||
_codegenerators() {
|
||||
|
@ -140,15 +148,26 @@ _codegenerators() {
|
|||
"1" == $( _needgen "values_ffjson${zsfx}" ) ||
|
||||
1 == 0 ]]
|
||||
then
|
||||
true && \
|
||||
echo "codecgen - !unsafe ... " && \
|
||||
codecgen -rt codecgen -t 'x,codecgen,!unsafe' -o values_codecgen${zsfx} -d 1978 $zfin && \
|
||||
echo "codecgen - unsafe ... " && \
|
||||
codecgen -u -rt codecgen -t 'x,codecgen,unsafe' -o values_codecgen_unsafe${zsfx} -d 1978 $zfin && \
|
||||
echo "msgp ... " && \
|
||||
msgp -tests=false -pkg=codec -o=values_msgp${zsfx} -file=$zfin && \
|
||||
echo "ffjson ... " && \
|
||||
ffjson -w values_ffjson${zsfx} $zfin && \
|
||||
# codecgen creates some temporary files in the directory (main, pkg).
|
||||
# Consequently, we should start msgp and ffjson first, and also put a small time latency before
|
||||
# starting codecgen.
|
||||
# Without this, ffjson chokes on one of the temporary files from codecgen.
|
||||
echo "ffjson ... " && \
|
||||
ffjson -w values_ffjson${zsfx} $zfin &
|
||||
zzzIdFF=$!
|
||||
echo "msgp ... " && \
|
||||
msgp -tests=false -pkg=codec -o=values_msgp${zsfx} -file=$zfin &
|
||||
zzzIdMsgp=$!
|
||||
|
||||
sleep 1 # give ffjson and msgp some buffer time. see note above.
|
||||
|
||||
echo "codecgen - !unsafe ... " && \
|
||||
codecgen -rt codecgen -t 'x,codecgen,!unsafe' -o values_codecgen${zsfx} -d 19780 $zfin &
|
||||
zzzIdC=$!
|
||||
echo "codecgen - unsafe ... " && \
|
||||
codecgen -u -rt codecgen -t 'x,codecgen,unsafe' -o values_codecgen_unsafe${zsfx} -d 19781 $zfin &
|
||||
zzzIdCU=$!
|
||||
wait $zzzIdC $zzzIdCU $zzzIdMsgp $zzzIdFF && \
|
||||
# remove (M|Unm)arshalJSON implementations, so they don't conflict with encoding/json bench \
|
||||
sed -i 's+ MarshalJSON(+ _MarshalJSON(+g' values_ffjson${zsfx} && \
|
||||
sed -i 's+ UnmarshalJSON(+ _UnmarshalJSON(+g' values_ffjson${zsfx} && \
|
||||
|
|
|
@ -45,7 +45,7 @@ _run() {
|
|||
}
|
||||
|
||||
# echo ">>>>>>> RUNNING VARIATIONS OF TESTS"
|
||||
if [[ "x$@" = x ]]; then
|
||||
if [[ "x$@" = "x" ]]; then
|
||||
# r, x, g, gu
|
||||
_run "-rtcins"
|
||||
_run "-xtcins"
|
||||
|
|
Loading…
Reference in New Issue