From b2544f34527e60fe962665bbcbe6526cad713d19 Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 5 May 2024 11:20:09 +0900 Subject: [PATCH 1/6] support A -> B -> A recursive types (#227) --- encode.go | 30 ++----- encode_test.go | 70 ---------------- encodefunc.go | 50 ++++++------ encoding.go | 56 +++---------- encoding_test.go | 86 ++++++++++++++++++++ reflect.go | 202 ++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 330 insertions(+), 164 deletions(-) diff --git a/encode.go b/encode.go index cc6d022..6e6f6b5 100644 --- a/encode.go +++ b/encode.go @@ -52,7 +52,7 @@ func marshal(v interface{}, flags encodeFlags) (*dynamodb.AttributeValue, error) if err != nil { return nil, err } - enc, err := def.encodeType(rt, flags) + enc, err := def.encodeType(rt, flags, nil) if err != nil { return nil, err } @@ -90,7 +90,9 @@ func encodeItem(fields []structField, rv reflect.Value) (Item, error) { continue } } - + if field.enc == nil { + continue + } av, err := field.enc(fv, field.flags) if err != nil { return nil, err @@ -165,27 +167,11 @@ func isZeroIface[T any](rt reflect.Type, isZero func(v T) bool) func(rv reflect. } func (def *typedef) isZeroStruct(rt reflect.Type) func(rv reflect.Value) bool { - fields, err := def.structFields(rt, false) - if err != nil { - return nil - } - return func(rv reflect.Value) bool { - for _, info := range *fields { - if info.isZero == nil { - continue - } - - field := dig(rv, info.index) - if !field.IsValid() { - return true - } - - if !info.isZero(field) { - return false - } - } - return true + if fn := def.info.findZero(rt); fn != nil { + return fn } + child, _ := def.structInfo(rt, def.info) + return child.isZero } func (def *typedef) isZeroArray(rt reflect.Type) func(reflect.Value) bool { diff --git a/encode_test.go b/encode_test.go index 405402a..ab8361c 100644 --- a/encode_test.go +++ b/encode_test.go @@ -187,73 +187,3 @@ func TestMarshalItemBypass(t *testing.T) { t.Error("bad unmarshal") } } - -func TestMarshalRecursive(t *testing.T) { - t.SkipNow() - - type Person struct { - Spouse *Person - Children []Person - Name string - } - type Friend struct { - ID int - Person Person - Nickname string - } - children := []Person{ - {Name: "Bobby"}, - } - - hank := Person{ - Spouse: &Person{ - Name: "Peggy", - Children: children, - }, - Children: children, - Name: "Hank", - } - - t.Run("self-recursive", func(t *testing.T) { - - want := map[string]*dynamodb.AttributeValue{ - "Name": {S: aws.String("Hank")}, - "Spouse": {M: map[string]*dynamodb.AttributeValue{ - "Name": {S: aws.String("Peggy")}, - "Children": {L: []*dynamodb.AttributeValue{ - {M: map[string]*dynamodb.AttributeValue{ - "Name": {S: aws.String("Bobby")}, - "Children": {L: []*dynamodb.AttributeValue{}}, - }}, - }, - }, - }}, - "Children": {L: []*dynamodb.AttributeValue{ - {M: map[string]*dynamodb.AttributeValue{ - "Name": {S: aws.String("Bobby")}, - "Children": {L: []*dynamodb.AttributeValue{}}, - }}, - }}, - } - - got, err := MarshalItem(hank) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(got, want) { - t.Error("bad", got) - } - }) - - t.Run("field is recursive", func(t *testing.T) { - friend := Friend{ - Person: hank, - Nickname: "H-Dawg", - } - got, err := MarshalItem(friend) - if err != nil { - t.Fatal(err) - } - t.Fatal(got) - }) -} diff --git a/encodefunc.go b/encodefunc.go index d5ddca8..c37acb0 100644 --- a/encodefunc.go +++ b/encodefunc.go @@ -14,7 +14,12 @@ import ( type encodeFunc func(rv reflect.Value, flags encodeFlags) (*dynamodb.AttributeValue, error) -func (def *typedef) encodeType(rt reflect.Type, flags encodeFlags) (encodeFunc, error) { +func (def *typedef) encodeType(rt reflect.Type, flags encodeFlags, info *structInfo) (encodeFunc, error) { + encKey := encodeKey{rt, flags} + if fn := info.findEncoder(encKey); fn != nil { + return fn, nil + } + try := rt for { switch try { @@ -53,7 +58,7 @@ func (def *typedef) encodeType(rt reflect.Type, flags encodeFlags) (encodeFunc, switch rt.Kind() { case reflect.Pointer: - return def.encodePtr(rt, flags) + return def.encodePtr(rt, flags, info) // BOOL case reflect.Bool: @@ -83,7 +88,7 @@ func (def *typedef) encodeType(rt reflect.Type, flags encodeFlags) (encodeFunc, return encodeSet(rt, flags) } // lists (L) - return def.encodeList(rt, flags) + return def.encodeList(rt, flags, info) case reflect.Map: // sets (NS, SS, BS) @@ -91,11 +96,11 @@ func (def *typedef) encodeType(rt reflect.Type, flags encodeFlags) (encodeFunc, return encodeSet(rt, flags) } // M - return def.encodeMapM(rt, flags) + return def.encodeMapM(rt, flags, info) // M case reflect.Struct: - return def.encodeStruct(rt) + return def.encodeStruct(rt, flags, info) case reflect.Interface: if rt.NumMethod() == 0 { @@ -105,8 +110,8 @@ func (def *typedef) encodeType(rt reflect.Type, flags encodeFlags) (encodeFunc, return nil, fmt.Errorf("dynamo marshal: unsupported type %s", rt.String()) } -func (def *typedef) encodePtr(rt reflect.Type, flags encodeFlags) (encodeFunc, error) { - elem, err := def.encodeType(rt.Elem(), flags) +func (def *typedef) encodePtr(rt reflect.Type, flags encodeFlags, info *structInfo) (encodeFunc, error) { + elem, err := def.encodeType(rt.Elem(), flags, info) if err != nil { return nil, err } @@ -209,24 +214,19 @@ func encodeBytes(rt reflect.Type, flags encodeFlags) encodeFunc { } } -func (def *typedef) encodeStruct(rt reflect.Type) (encodeFunc, error) { - var fields *[]structField - var err error - if def.sameAsRoot(rt) { - fields, err = def.structFields(rt, false) - } else { - var subdef *typedef - subdef, err = typedefOf(rt) - if subdef != nil { - fields = &subdef.fields - } - } +func (def *typedef) encodeStruct(rt reflect.Type, flags encodeFlags, info *structInfo) (encodeFunc, error) { + info2, err := def.structInfo(rt, info) if err != nil { return nil, err } + var fields []structField + for _, field := range info2.fields { + fields = append(fields, *field) + } + return func(rv reflect.Value, flags encodeFlags) (*dynamodb.AttributeValue, error) { - item, err := encodeItem(*fields, rv) + item, err := encodeItem(fields, rv) if err != nil { return nil, err } @@ -313,7 +313,7 @@ func encodeSliceBS(rv reflect.Value, flags encodeFlags) (*dynamodb.AttributeValu return &dynamodb.AttributeValue{BS: bs}, nil } -func (def *typedef) encodeMapM(rt reflect.Type, flags encodeFlags) (encodeFunc, error) { +func (def *typedef) encodeMapM(rt reflect.Type, flags encodeFlags, info *structInfo) (encodeFunc, error) { keyString := encodeMapKeyFunc(rt) if keyString == nil { return nil, fmt.Errorf("dynamo marshal: map key type must be string or encoding.TextMarshaler, have %v", rt) @@ -329,7 +329,7 @@ func (def *typedef) encodeMapM(rt reflect.Type, flags encodeFlags) (encodeFunc, subflags |= flagOmitEmpty } - valueEnc, err := def.encodeType(rt.Elem(), subflags) + valueEnc, err := def.encodeType(rt.Elem(), subflags, info) if err != nil { return nil, err } @@ -526,7 +526,7 @@ func encodeSet(rt /* []T | map[T]bool | map[T]struct{} */ reflect.Type, flags en return nil, fmt.Errorf("dynamo: marshal: invalid type for set %s", rt.String()) } -func (def *typedef) encodeList(rt reflect.Type, flags encodeFlags) (encodeFunc, error) { +func (def *typedef) encodeList(rt reflect.Type, flags encodeFlags, info *structInfo) (encodeFunc, error) { // lists CAN be empty subflags := flagNone if flags&flagOmitEmptyElem == 0 { @@ -540,7 +540,7 @@ func (def *typedef) encodeList(rt reflect.Type, flags encodeFlags) (encodeFunc, subflags |= flagAllowEmptyElem } - valueEnc, err := def.encodeType(rt.Elem(), subflags) + valueEnc, err := def.encodeType(rt.Elem(), subflags, info) if err != nil { return nil, err } @@ -577,7 +577,7 @@ func (def *typedef) encodeAny(rv reflect.Value, flags encodeFlags) (*dynamodb.At } return nil, nil } - enc, err := def.encodeType(rv.Elem().Type(), flags) + enc, err := def.encodeType(rv.Elem().Type(), flags, nil) if err != nil { return nil, err } diff --git a/encoding.go b/encoding.go index 864b9cb..ec27a3b 100644 --- a/encoding.go +++ b/encoding.go @@ -17,6 +17,7 @@ type typedef struct { decoders map[unmarshalKey]decodeFunc fields []structField root reflect.Type + info *structInfo } func newTypedef(rt reflect.Type) (*typedef, error) { @@ -46,11 +47,15 @@ func (def *typedef) init(rt reflect.Type) error { return nil } - fieldptr, err := def.structFields(rt, true) - if fieldptr != nil { - def.fields = *fieldptr + info, err := def.structInfo(rt, nil) + if err != nil { + return err + } + for _, field := range info.fields { + def.fields = append(def.fields, *field) } - return err + def.info = info + return nil } func registerTypedef(gotype reflect.Type, def *typedef) *typedef { @@ -99,7 +104,7 @@ func (def *typedef) encodeItem(rv reflect.Value) (Item, error) { case reflect.Struct: return encodeItem(def.fields, rv) case reflect.Map: - enc, err := def.encodeMapM(rv.Type(), flagNone) + enc, err := def.encodeMapM(rv.Type(), flagNone, def.info) if err != nil { return nil, err } @@ -399,47 +404,6 @@ type structField struct { isZero func(reflect.Value) bool } -// type encodeKey struct { -// rt reflect.Type -// flags encodeFlags -// } - -func (def *typedef) sameAsRoot(rt reflect.Type) bool { - switch { - case rt == def.root: - return true - case def.root.Kind() == reflect.Pointer && rt.Kind() != reflect.Pointer: - return def.root.Elem() == rt - case def.root.Kind() != reflect.Pointer && rt.Kind() == reflect.Pointer: - return rt.Elem() == def.root - } - return false -} - -func (def *typedef) structFields(rt reflect.Type, isRoot bool) (*[]structField, error) { - if !isRoot && def.sameAsRoot(rt) { - return &def.fields, nil - } - - var fields []structField - err := visitTypeFields(rt, nil, nil, func(name string, index []int, flags encodeFlags, vt reflect.Type) error { - enc, err := def.encodeType(vt, flags) - if err != nil { - return err - } - field := structField{ - index: index, - name: name, - flags: flags, - enc: enc, - isZero: def.isZeroFunc(vt), - } - fields = append(fields, field) - return nil - }) - return &fields, err -} - var ( nullAV = &dynamodb.AttributeValue{NULL: aws.Bool(true)} emptyB = &dynamodb.AttributeValue{B: []byte("")} diff --git a/encoding_test.go b/encoding_test.go index e2ad0ac..8c63567 100644 --- a/encoding_test.go +++ b/encoding_test.go @@ -778,6 +778,78 @@ var itemEncodingTests = []struct { }, }, }, + { + name: "mega recursion A -> B -> *A -> B", + in: MegaRecursiveA{ + ID: 123, + Text: "hello", + Child: MegaRecursiveB{ + ID: "test", + Blah: 555, + Child: &MegaRecursiveA{ + ID: 222, + Text: "help", + Child: MegaRecursiveB{ + ID: "why", + Blah: 1337, + }, + Friends: []MegaRecursiveA{}, + Enemies: []MegaRecursiveB{}, + }, + }, + Friends: []MegaRecursiveA{ + {ID: 1, Text: "suffering", Child: MegaRecursiveB{ID: "pain"}, Friends: []MegaRecursiveA{}, Enemies: []MegaRecursiveB{}}, + {ID: 2, Text: "love", Child: MegaRecursiveB{ID: "understanding"}, Friends: []MegaRecursiveA{}, Enemies: []MegaRecursiveB{}}, + }, + Enemies: []MegaRecursiveB{ + {ID: "recursion", Blah: 30}, + }, + }, + out: map[string]*dynamodb.AttributeValue{ + "ID": {N: aws.String("123")}, + "Text": {S: aws.String("hello")}, + "Friends": {L: []*dynamodb.AttributeValue{ + {M: map[string]*dynamodb.AttributeValue{ + "ID": {N: aws.String("1")}, + "Text": {S: aws.String("suffering")}, + "Child": {M: map[string]*dynamodb.AttributeValue{ + "ID": {S: aws.String("pain")}, + }}, + "Friends": {L: []*dynamodb.AttributeValue{}}, + "Enemies": {L: []*dynamodb.AttributeValue{}}, + }}, + {M: map[string]*dynamodb.AttributeValue{ + "ID": {N: aws.String("2")}, + "Text": {S: aws.String("love")}, + "Child": {M: map[string]*dynamodb.AttributeValue{ + "ID": {S: aws.String("understanding")}, + }}, + "Friends": {L: []*dynamodb.AttributeValue{}}, + "Enemies": {L: []*dynamodb.AttributeValue{}}, + }}, + }}, + "Enemies": {L: []*dynamodb.AttributeValue{ + {M: map[string]*dynamodb.AttributeValue{ + "ID": {S: aws.String("recursion")}, + "Blah": {N: aws.String("30")}, + }}, + }}, + "Child": {M: map[string]*dynamodb.AttributeValue{ + "ID": {S: aws.String("test")}, + "Blah": {N: aws.String("555")}, + "Child": {M: map[string]*dynamodb.AttributeValue{ + "ID": {N: aws.String("222")}, + "Text": {S: aws.String("help")}, + "Friends": {L: []*dynamodb.AttributeValue{}}, + "Enemies": {L: []*dynamodb.AttributeValue{}}, + "Child": {M: map[string]*dynamodb.AttributeValue{ + "ID": {S: aws.String("why")}, + "Blah": {N: aws.String("1337")}, + }}, + }}, + }}, + }, + }, } type embedded struct { @@ -880,6 +952,20 @@ type Friend struct { Nickname string } +type MegaRecursiveA struct { + ID int + Child MegaRecursiveB + Text string + Friends []MegaRecursiveA + Enemies []MegaRecursiveB +} + +type MegaRecursiveB struct { + ID string + Child *MegaRecursiveA + Blah int `dynamo:",omitempty"` +} + func byteSlicePtr(a []byte) *[]byte { return &a } diff --git a/reflect.go b/reflect.go index 526ead7..64dfab5 100644 --- a/reflect.go +++ b/reflect.go @@ -183,6 +183,206 @@ func visitFields(item map[string]*dynamodb.AttributeValue, rv reflect.Value, see return nil } +type encodeKey struct { + rt reflect.Type + flags encodeFlags +} + +type structInfo struct { + root reflect.Type + fields map[string]*structField // by name + refs map[encodeKey][]*structField + types map[encodeKey]encodeFunc + zeros map[reflect.Type]func(reflect.Value) bool + parent *structInfo + + seen map[encodeKey]struct{} + queue []encodeKey +} + +func (info *structInfo) encode(rv reflect.Value, flags encodeFlags) (*dynamodb.AttributeValue, error) { + item := make(Item, len(info.fields)) + for _, field := range info.fields { + fv := dig(rv, field.index) + if !fv.IsValid() { + // TODO: encode NULL? + continue + } + + if field.flags&flagOmitEmpty != 0 && field.isZero != nil { + if field.isZero(fv) { + continue + } + } + + av, err := field.enc(fv, field.flags) + if err != nil { + return nil, err + } + if av == nil { + if field.flags&flagNull != 0 { + item[field.name] = nullAV + } + continue + } + item[field.name] = av + } + return &dynamodb.AttributeValue{M: item}, nil +} + +func (info *structInfo) isZero(rv reflect.Value) bool { + if info == nil { + return false + } + for _, field := range info.fields { + fv := dig(rv, field.index) + if !fv.IsValid() { + // TODO: encode NULL? + continue + } + if !field.isZero(fv) { + return false + } + } + return true +} + +func (info *structInfo) findEncoder(key encodeKey) encodeFunc { + if info == nil { + return nil + } + if key.rt == info.root { + return info.encode + } + if enc, ok := info.types[key]; ok { + return enc + } + if enc := info.parent.findEncoder(key); enc != nil { + return enc + } + return info.parent.findEncoder(key) +} + +func (info *structInfo) findZero(rt reflect.Type) func(reflect.Value) bool { + if info == nil { + return nil + } + if rt == info.root { + return info.isZero + } + if isZero, ok := info.zeros[rt]; ok { + return isZero + } + return info.parent.findZero(rt) +} + +func (def *typedef) structInfo(rt reflect.Type, parent *structInfo) (*structInfo, error) { + info := &structInfo{ + root: rt, + parent: parent, + fields: make(map[string]*structField), + refs: make(map[encodeKey][]*structField), + types: make(map[encodeKey]encodeFunc), + zeros: make(map[reflect.Type]func(reflect.Value) bool), + seen: make(map[encodeKey]struct{}), + } + + collectTypes(rt, info, nil) + + for _, key := range info.queue { + fn, err := def.encodeType(key.rt, key.flags, info) + if err != nil { + return info, err + } + isZero := info.findZero(key.rt) + if isZero == nil { + isZero = def.isZeroFunc(key.rt) + } + for _, sf := range info.refs[key] { + sf.enc = fn + sf.isZero = isZero + } + info.types[key] = fn + info.zeros[key.rt] = isZero + } + + // don't need these anymore + info.queue = nil + info.seen = nil + + return info, nil +} + +func collectTypes(rt reflect.Type, info *structInfo, trail []int) *structInfo { + for rt.Kind() == reflect.Pointer { + rt = rt.Elem() + } + if rt.Kind() != reflect.Struct { + panic("not a struct") + } + + if info == nil { + info = &structInfo{ + root: rt, + fields: make(map[string]*structField), + refs: make(map[encodeKey][]*structField), + types: make(map[encodeKey]encodeFunc), + seen: make(map[encodeKey]struct{}), + } + } + + // fields := make(map[string]reflect.Value) + for i := 0; i < rt.NumField(); i++ { + field := rt.Field(i) + ft := field.Type + isPtr := ft.Kind() == reflect.Ptr + + name, flags := fieldInfo(field) + if name == "-" { + // skip + continue + } + + key := encodeKey{ + rt: ft, + flags: flags, + } + + idx := field.Index + if len(trail) > 0 { + idx = append(trail, idx...) + } + + sf := &structField{ + index: idx, + name: name, + flags: flags, + } + public := field.IsExported() + if _, ok := info.fields[name]; !ok { + if public { + info.fields[name] = sf + } + info.refs[key] = append(info.refs[key], sf) + } + + // embed anonymous structs, they could be pointers so test that too + if (ft.Kind() == reflect.Struct || isPtr && ft.Elem().Kind() == reflect.Struct) && field.Anonymous { + collectTypes(ft, info, idx) + continue + } + + if !public { + continue + } + if _, ok := info.seen[key]; ok { + continue + } + info.queue = append(info.queue, key) + } + return info +} + func visitTypeFields(rt reflect.Type, seen map[string]struct{}, trail []int, fn func(name string, index []int, flags encodeFlags, vt reflect.Type) error) error { for rt.Kind() == reflect.Pointer { rt = rt.Elem() @@ -252,7 +452,7 @@ func reallocMap(v reflect.Value, size int) { type decodeKeyFunc func(reflect.Value, string) error func decodeMapKeyFunc(rt reflect.Type) decodeKeyFunc { - if reflect.PtrTo(rt.Key()).Implements(rtypeTextUnmarshaler) { + if reflect.PointerTo(rt.Key()).Implements(rtypeTextUnmarshaler) { return func(kv reflect.Value, s string) error { tm := kv.Interface().(encoding.TextUnmarshaler) if err := tm.UnmarshalText([]byte(s)); err != nil { From a7bdc72616fe687c2e35cc7953fe2370e10515bb Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 5 May 2024 12:21:08 +0900 Subject: [PATCH 2/6] skip structInfo for non-structs --- encode.go | 2 +- reflect.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/encode.go b/encode.go index 6e6f6b5..fc75c57 100644 --- a/encode.go +++ b/encode.go @@ -52,7 +52,7 @@ func marshal(v interface{}, flags encodeFlags) (*dynamodb.AttributeValue, error) if err != nil { return nil, err } - enc, err := def.encodeType(rt, flags, nil) + enc, err := def.encodeType(rt, flags, def.info) if err != nil { return nil, err } diff --git a/reflect.go b/reflect.go index 64dfab5..48b352d 100644 --- a/reflect.go +++ b/reflect.go @@ -277,6 +277,14 @@ func (info *structInfo) findZero(rt reflect.Type) func(reflect.Value) bool { } func (def *typedef) structInfo(rt reflect.Type, parent *structInfo) (*structInfo, error) { + rti := rt + for rti.Kind() == reflect.Pointer { + rti = rti.Elem() + } + if rti.Kind() != reflect.Struct { + return nil, nil + } + info := &structInfo{ root: rt, parent: parent, From 538592de6fd1ee4097047c0216f8cadbada7761d Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 5 May 2024 12:27:06 +0900 Subject: [PATCH 3/6] Revert "skip structInfo for non-structs" This reverts commit a7bdc72616fe687c2e35cc7953fe2370e10515bb. --- encode.go | 2 +- reflect.go | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/encode.go b/encode.go index fc75c57..6e6f6b5 100644 --- a/encode.go +++ b/encode.go @@ -52,7 +52,7 @@ func marshal(v interface{}, flags encodeFlags) (*dynamodb.AttributeValue, error) if err != nil { return nil, err } - enc, err := def.encodeType(rt, flags, def.info) + enc, err := def.encodeType(rt, flags, nil) if err != nil { return nil, err } diff --git a/reflect.go b/reflect.go index 48b352d..64dfab5 100644 --- a/reflect.go +++ b/reflect.go @@ -277,14 +277,6 @@ func (info *structInfo) findZero(rt reflect.Type) func(reflect.Value) bool { } func (def *typedef) structInfo(rt reflect.Type, parent *structInfo) (*structInfo, error) { - rti := rt - for rti.Kind() == reflect.Pointer { - rti = rti.Elem() - } - if rti.Kind() != reflect.Struct { - return nil, nil - } - info := &structInfo{ root: rt, parent: parent, From 887fb083943990c6b179c5a1827b5ab9363a524b Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 5 May 2024 12:28:39 +0900 Subject: [PATCH 4/6] skip structInfo for non-structs --- reflect.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/reflect.go b/reflect.go index 64dfab5..48b352d 100644 --- a/reflect.go +++ b/reflect.go @@ -277,6 +277,14 @@ func (info *structInfo) findZero(rt reflect.Type) func(reflect.Value) bool { } func (def *typedef) structInfo(rt reflect.Type, parent *structInfo) (*structInfo, error) { + rti := rt + for rti.Kind() == reflect.Pointer { + rti = rti.Elem() + } + if rti.Kind() != reflect.Struct { + return nil, nil + } + info := &structInfo{ root: rt, parent: parent, From 54e2f544a81ea55e60ca98bfd3e7253c63bed7da Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 5 May 2024 12:33:41 +0900 Subject: [PATCH 5/6] clean up a bit --- reflect.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/reflect.go b/reflect.go index 48b352d..1c70bd1 100644 --- a/reflect.go +++ b/reflect.go @@ -257,9 +257,6 @@ func (info *structInfo) findEncoder(key encodeKey) encodeFunc { if enc, ok := info.types[key]; ok { return enc } - if enc := info.parent.findEncoder(key); enc != nil { - return enc - } return info.parent.findEncoder(key) } @@ -329,16 +326,6 @@ func collectTypes(rt reflect.Type, info *structInfo, trail []int) *structInfo { panic("not a struct") } - if info == nil { - info = &structInfo{ - root: rt, - fields: make(map[string]*structField), - refs: make(map[encodeKey][]*structField), - types: make(map[encodeKey]encodeFunc), - seen: make(map[encodeKey]struct{}), - } - } - // fields := make(map[string]reflect.Value) for i := 0; i < rt.NumField(); i++ { field := rt.Field(i) From 17ebbafbed5ffc374211b05ee5faafccc2e9207f Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 5 May 2024 12:40:43 +0900 Subject: [PATCH 6/6] disable RequestLimit for now (save for next release) awaiting feedback on performance before baking the API --- query.go | 8 ++++---- scan.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/query.go b/query.go index a4593dc..498e65e 100644 --- a/query.go +++ b/query.go @@ -185,10 +185,10 @@ func (q *Query) SearchLimit(limit int64) *Query { } // RequestLimit specifies the maximum amount of requests to make against DynamoDB's API. -func (q *Query) RequestLimit(limit int) *Query { - q.reqLimit = limit - return q -} +// func (q *Query) RequestLimit(limit int) *Query { +// q.reqLimit = limit +// return q +// } // Order specifies the desired result order. // Requires a range key (a.k.a. partition key) to be specified. diff --git a/scan.go b/scan.go index 449e7be..44e3e40 100644 --- a/scan.go +++ b/scan.go @@ -131,10 +131,10 @@ func (s *Scan) SearchLimit(limit int64) *Scan { } // RequestLimit specifies the maximum amount of requests to make against DynamoDB's API. -func (s *Scan) RequestLimit(limit int) *Scan { - s.reqLimit = limit - return s -} +// func (s *Scan) RequestLimit(limit int) *Scan { +// s.reqLimit = limit +// return s +// } // ConsumedCapacity will measure the throughput capacity consumed by this operation and add it to cc. func (s *Scan) ConsumedCapacity(cc *ConsumedCapacity) *Scan {