github.com/urso/go-structform@v0.0.2/gotype/unfold_struct.go (about) 1 package gotype 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 "unicode" 8 "unicode/utf8" 9 "unsafe" 10 11 structform "github.com/urso/go-structform" 12 ) 13 14 type unfolderStruct struct { 15 unfolderErrExpectKey 16 fields map[string]fieldUnfolder 17 } 18 19 type unfolderStructStart struct { 20 unfolderErrObjectStart 21 } 22 23 type fieldUnfolder struct { 24 offset uintptr 25 initState func(ctx *unfoldCtx, sp unsafe.Pointer) 26 } 27 28 var ( 29 _singletonUnfolderStructStart = &unfolderStructStart{} 30 31 _ignoredField = &fieldUnfolder{ 32 initState: _singletonUnfoldIgnorePtr.initState, 33 } 34 ) 35 36 func createUnfolderReflStruct(ctx *unfoldCtx, t reflect.Type) (*unfolderStruct, error) { 37 // assume t is pointer to struct 38 t = t.Elem() 39 40 fields, err := fieldUnfolders(ctx, t) 41 if err != nil { 42 return nil, err 43 } 44 45 u := &unfolderStruct{fields: fields} 46 return u, nil 47 } 48 49 func fieldUnfolders(ctx *unfoldCtx, t reflect.Type) (map[string]fieldUnfolder, error) { 50 count := t.NumField() 51 fields := map[string]fieldUnfolder{} 52 53 for i := 0; i < count; i++ { 54 st := t.Field(i) 55 56 name := st.Name 57 rune, _ := utf8.DecodeRuneInString(name) 58 if !unicode.IsUpper(rune) { 59 continue 60 } 61 62 tagName, tagOpts := parseTags(st.Tag.Get(ctx.opts.tag)) 63 if tagOpts.squash { 64 if st.Type.Kind() != reflect.Struct { 65 return nil, errSquashNeedObject 66 } 67 68 sub, err := fieldUnfolders(ctx, st.Type) 69 if err != nil { 70 return nil, err 71 } 72 73 for name, fu := range sub { 74 fu.offset += st.Offset 75 if _, exists := fields[name]; exists { 76 return nil, fmt.Errorf("duplicate field name %v", name) 77 } 78 79 fields[name] = fu 80 } 81 } else { 82 if tagName != "" { 83 name = tagName 84 } else { 85 name = strings.ToLower(name) 86 } 87 88 if _, exists := fields[name]; exists { 89 return nil, fmt.Errorf("duplicate field name %v", name) 90 } 91 92 fu, err := makeFieldUnfolder(ctx, st) 93 if err != nil { 94 return nil, err 95 } 96 97 fields[name] = fu 98 } 99 } 100 101 return fields, nil 102 } 103 104 func makeFieldUnfolder(ctx *unfoldCtx, st reflect.StructField) (fieldUnfolder, error) { 105 fu := fieldUnfolder{offset: st.Offset} 106 107 if pu := lookupGoPtrUnfolder(st.Type); pu != nil { 108 fu.initState = pu.initState 109 } else { 110 targetType := reflect.PtrTo(st.Type) 111 ru, err := lookupReflUnfolder(ctx, targetType) 112 if err != nil { 113 return fu, err 114 } 115 116 if su, ok := ru.(*unfolderStruct); ok { 117 fu.initState = su.initStatePtr 118 } else { 119 fu.initState = wrapReflUnfolder(st.Type, ru) 120 } 121 } 122 123 return fu, nil 124 } 125 126 func wrapReflUnfolder(t reflect.Type, ru reflUnfolder) func(*unfoldCtx, unsafe.Pointer) { 127 return func(ctx *unfoldCtx, ptr unsafe.Pointer) { 128 v := reflect.NewAt(t, ptr) 129 ru.initState(ctx, v) 130 } 131 } 132 133 func (u *unfolderStruct) initState(ctx *unfoldCtx, v reflect.Value) { 134 u.initStatePtr(ctx, unsafe.Pointer(v.Pointer())) 135 } 136 137 func (u *unfolderStruct) initStatePtr(ctx *unfoldCtx, ptr unsafe.Pointer) { 138 ctx.ptr.push(ptr) 139 ctx.unfolder.push(u) 140 ctx.unfolder.push(_singletonUnfolderStructStart) 141 } 142 143 func (u *unfolderStructStart) OnObjectStart(ctx *unfoldCtx, l int, bt structform.BaseType) error { 144 ctx.unfolder.pop() 145 return nil 146 } 147 148 func (u *unfolderStruct) OnObjectFinished(ctx *unfoldCtx) error { 149 ctx.unfolder.pop() 150 ctx.ptr.pop() 151 return nil 152 } 153 154 func (u *unfolderStruct) OnChildObjectDone(ctx *unfoldCtx) error { return nil } 155 func (u *unfolderStruct) OnChildArrayDone(ctx *unfoldCtx) error { return nil } 156 157 func (u *unfolderStruct) OnKeyRef(ctx *unfoldCtx, key []byte) error { 158 return u.OnKey(ctx, bytes2Str(key)) 159 } 160 161 func (u *unfolderStruct) OnKey(ctx *unfoldCtx, key string) error { 162 field, exists := u.fields[key] 163 if !exists { 164 _ignoredField.initState(ctx, nil) 165 return nil 166 } 167 168 structPtr := ctx.ptr.current 169 fieldAddr := uintptr(structPtr) + field.offset 170 fieldPtr := unsafe.Pointer(fieldAddr) 171 field.initState(ctx, fieldPtr) 172 return nil 173 }