From 25d2eea12d9d5302a216d58fa32032447dd1abb5 Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Sat, 22 May 2021 11:02:39 +1000 Subject: [PATCH] bencode: Support anonymous embedded struct pointers More to come if this line of improvement is retained. --- bencode/decode.go | 18 ++++++++++------ bencode/encode.go | 52 +++++++++++++++++++++++++++++++---------------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/bencode/decode.go b/bencode/decode.go index 51804614..e891f5be 100644 --- a/bencode/decode.go +++ b/bencode/decode.go @@ -243,21 +243,27 @@ func getDictField(dict reflect.Type, key string) dictField { } } -type structField struct { - r reflect.StructField - tag tag -} - var ( structFieldsMu sync.Mutex structFields = map[reflect.Type]map[string]dictField{} ) -func parseStructFields(struct_ reflect.Type, each func(string, dictField)) { +func parseStructFields(struct_ reflect.Type, each func(key string, df dictField)) { for _i, n := 0, struct_.NumField(); _i < n; _i++ { i := _i f := struct_.Field(i) if f.Anonymous { + parseStructFields(f.Type.Elem(), func(key string, df dictField) { + innerGet := df.Get + df.Get = func(value reflect.Value) func(reflect.Value) { + anonPtr := value.Field(i) + if anonPtr.IsNil() { + anonPtr.Set(reflect.New(f.Type.Elem())) + } + return innerGet(anonPtr.Elem()) + } + each(key, df) + }) continue } tagStr := f.Tag.Get("bencode") diff --git a/bencode/encode.go b/bencode/encode.go index 443c11e7..f25cfef8 100644 --- a/bencode/encode.go +++ b/bencode/encode.go @@ -133,13 +133,16 @@ func (e *Encoder) reflectValue(v reflect.Value) { e.reflectString(v.String()) case reflect.Struct: e.writeString("d") - for _, ef := range encodeFields(v.Type()) { - field_value := v.Field(ef.i) - if ef.omit_empty && isEmptyValue(field_value) { + for _, ef := range getEncodeFields(v.Type()) { + fieldValue := ef.i(v) + if !fieldValue.IsValid() { + continue + } + if ef.omitEmpty && isEmptyValue(fieldValue) { continue } e.reflectString(ef.tag) - e.reflectValue(field_value) + e.reflectValue(fieldValue) } e.writeString("e") case reflect.Map: @@ -190,9 +193,9 @@ func (e *Encoder) reflectValue(v reflect.Value) { } type encodeField struct { - i int - tag string - omit_empty bool + i func(v reflect.Value) reflect.Value + tag string + omitEmpty bool } type encodeFieldsSortType []encodeField @@ -206,31 +209,47 @@ var ( encodeFieldsCache = make(map[reflect.Type][]encodeField) ) -func encodeFields(t reflect.Type) []encodeField { +func getEncodeFields(t reflect.Type) []encodeField { typeCacheLock.RLock() fs, ok := encodeFieldsCache[t] typeCacheLock.RUnlock() if ok { return fs } - + fs = makeEncodeFields(t) typeCacheLock.Lock() defer typeCacheLock.Unlock() - fs, ok = encodeFieldsCache[t] - if ok { - return fs - } + encodeFieldsCache[t] = fs + return fs +} - for i, n := 0, t.NumField(); i < n; i++ { +func makeEncodeFields(t reflect.Type) (fs []encodeField) { + for _i, n := 0, t.NumField(); _i < n; _i++ { + i := _i f := t.Field(i) if f.PkgPath != "" { continue } if f.Anonymous { + anonEFs := makeEncodeFields(f.Type.Elem()) + for aefi := range anonEFs { + anonEF := anonEFs[aefi] + bottomField := anonEF + bottomField.i = func(v reflect.Value) reflect.Value { + v = v.Field(i) + if v.IsNil() { + return reflect.Value{} + } + return anonEF.i(v.Elem()) + } + fs = append(fs, bottomField) + } continue } var ef encodeField - ef.i = i + ef.i = func(v reflect.Value) reflect.Value { + return v.Field(i) + } ef.tag = f.Name tv := getTag(f.Tag) @@ -240,11 +259,10 @@ func encodeFields(t reflect.Type) []encodeField { if tv.Key() != "" { ef.tag = tv.Key() } - ef.omit_empty = tv.OmitEmpty() + ef.omitEmpty = tv.OmitEmpty() fs = append(fs, ef) } fss := encodeFieldsSortType(fs) sort.Sort(fss) - encodeFieldsCache[t] = fs return fs } -- 2.48.1