瀏覽代碼

support recursive struct

Tao Wen 7 年之前
父節點
當前提交
a3866383f5
共有 14 個文件被更改,包括 221 次插入151 次删除
  1. 2 2
      any.go
  2. 105 34
      reflect.go
  3. 4 4
      reflect_array.go
  4. 21 21
      reflect_extension.go
  5. 2 2
      reflect_json_number.go
  6. 2 2
      reflect_json_raw_message.go
  7. 17 17
      reflect_map.go
  8. 13 13
      reflect_marshaler.go
  9. 34 34
      reflect_native.go
  10. 5 5
      reflect_optional.go
  11. 4 4
      reflect_slice.go
  12. 6 6
      reflect_struct_decoder.go
  13. 5 5
      reflect_struct_encoder.go
  14. 1 2
      value_tests/struct_test.go

+ 2 - 2
any.go

@@ -247,7 +247,7 @@ func locatePath(iter *Iterator, path []interface{}) Any {
 
 var anyType = reflect.TypeOf((*Any)(nil)).Elem()
 
-func createDecoderOfAny(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
+func createDecoderOfAny(ctx *ctx, typ reflect.Type) ValDecoder {
 	if typ == anyType {
 		return &directAnyCodec{}
 	}
@@ -259,7 +259,7 @@ func createDecoderOfAny(cfg *frozenConfig, prefix string, typ reflect.Type) ValD
 	return nil
 }
 
-func createEncoderOfAny(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
+func createEncoderOfAny(ctx *ctx, typ reflect.Type) ValEncoder {
 	if typ == anyType {
 		return &directAnyCodec{}
 	}

+ 105 - 34
reflect.go

@@ -33,6 +33,22 @@ type checkIsEmpty interface {
 	IsEmpty(ptr unsafe.Pointer) bool
 }
 
+type ctx struct {
+	*frozenConfig
+	prefix   string
+	encoders map[reflect.Type]ValEncoder
+	decoders map[reflect.Type]ValDecoder
+}
+
+func (b *ctx) append(prefix string) *ctx {
+	return &ctx{
+		frozenConfig: b.frozenConfig,
+		prefix:       b.prefix + " " + prefix,
+		encoders:     b.encoders,
+		decoders:     b.decoders,
+	}
+}
+
 // ReadVal copy the underlying JSON into go interface, same as json.Unmarshal
 func (iter *Iterator) ReadVal(obj interface{}) {
 	typ := reflect.TypeOf(obj)
@@ -66,44 +82,62 @@ func (cfg *frozenConfig) DecoderOf(typ reflect.Type) ValDecoder {
 	if decoder != nil {
 		return decoder
 	}
-	decoder = decoderOfType(cfg, "", typ.Elem())
+	ctx := &ctx{
+		frozenConfig: cfg,
+		prefix:       "",
+		decoders:     map[reflect.Type]ValDecoder{},
+		encoders:     map[reflect.Type]ValEncoder{},
+	}
+	decoder = decoderOfType(ctx, typ.Elem())
 	cfg.addDecoderToCache(cacheKey, decoder)
 	return decoder
 }
 
-func decoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
-	decoder := getTypeDecoderFromExtension(cfg, typ)
+func decoderOfType(ctx *ctx, typ reflect.Type) ValDecoder {
+	decoder := getTypeDecoderFromExtension(ctx, typ)
 	if decoder != nil {
 		return decoder
 	}
-	decoder = createDecoderOfType(cfg, prefix, typ)
+	decoder = createDecoderOfType(ctx, typ)
 	for _, extension := range extensions {
 		decoder = extension.DecorateDecoder(typ, decoder)
 	}
-	for _, extension := range cfg.extensions {
+	for _, extension := range ctx.extensions {
 		decoder = extension.DecorateDecoder(typ, decoder)
 	}
 	return decoder
 }
 
-func createDecoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
-	decoder := createDecoderOfJsonRawMessage(cfg, prefix, typ)
+func createDecoderOfType(ctx *ctx, typ reflect.Type) ValDecoder {
+	decoder := ctx.decoders[typ]
 	if decoder != nil {
 		return decoder
 	}
-	decoder = createDecoderOfJsonNumber(cfg, prefix, typ)
+	placeholder := &placeholderDecoder{}
+	ctx.decoders[typ] = placeholder
+	decoder = _createDecoderOfType(ctx, typ)
+	placeholder.decoder = decoder
+	return decoder
+}
+
+func _createDecoderOfType(ctx *ctx, typ reflect.Type) ValDecoder {
+	decoder := createDecoderOfJsonRawMessage(ctx, typ)
+	if decoder != nil {
+		return decoder
+	}
+	decoder = createDecoderOfJsonNumber(ctx, typ)
 	if decoder != nil {
 		return decoder
 	}
-	decoder = createDecoderOfMarshaler(cfg, prefix, typ)
+	decoder = createDecoderOfMarshaler(ctx, typ)
 	if decoder != nil {
 		return decoder
 	}
-	decoder = createDecoderOfAny(cfg, prefix, typ)
+	decoder = createDecoderOfAny(ctx, typ)
 	if decoder != nil {
 		return decoder
 	}
-	decoder = createDecoderOfNative(cfg, prefix, typ)
+	decoder = createDecoderOfNative(ctx, typ)
 	if decoder != nil {
 		return decoder
 	}
@@ -115,17 +149,17 @@ func createDecoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) Val
 		}
 		return &efaceDecoder{}
 	case reflect.Struct:
-		return decoderOfStruct(cfg, prefix, typ)
+		return decoderOfStruct(ctx, typ)
 	case reflect.Array:
-		return decoderOfArray(cfg, prefix, typ)
+		return decoderOfArray(ctx, typ)
 	case reflect.Slice:
-		return decoderOfSlice(cfg, prefix, typ)
+		return decoderOfSlice(ctx, typ)
 	case reflect.Map:
-		return decoderOfMap(cfg, prefix, typ)
+		return decoderOfMap(ctx, typ)
 	case reflect.Ptr:
-		return decoderOfOptional(cfg, prefix, typ)
+		return decoderOfOptional(ctx, typ)
 	default:
-		return &lazyErrorDecoder{err: fmt.Errorf("%s%s is unsupported type", prefix, typ.String())}
+		return &lazyErrorDecoder{err: fmt.Errorf("%s%s is unsupported type", ctx.prefix, typ.String())}
 	}
 }
 
@@ -135,7 +169,13 @@ func (cfg *frozenConfig) EncoderOf(typ reflect.Type) ValEncoder {
 	if encoder != nil {
 		return encoder
 	}
-	encoder = encoderOfType(cfg, "", typ)
+	ctx := &ctx{
+		frozenConfig: cfg,
+		prefix:       "",
+		decoders:     map[reflect.Type]ValDecoder{},
+		encoders:     map[reflect.Type]ValEncoder{},
+	}
+	encoder = encoderOfType(ctx, typ)
 	if shouldFixOnePtr(typ) {
 		encoder = &onePtrEncoder{encoder}
 	}
@@ -159,39 +199,50 @@ func shouldFixOnePtr(typ reflect.Type) bool {
 	return reflect2.Type2(typ).LikePtr()
 }
 
-func encoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
-	encoder := getTypeEncoderFromExtension(cfg, typ)
+func encoderOfType(ctx *ctx, typ reflect.Type) ValEncoder {
+	encoder := getTypeEncoderFromExtension(ctx, typ)
 	if encoder != nil {
 		return encoder
 	}
-	encoder = createEncoderOfType(cfg, prefix, typ)
+	encoder = createEncoderOfType(ctx, typ)
 	for _, extension := range extensions {
 		encoder = extension.DecorateEncoder(typ, encoder)
 	}
-	for _, extension := range cfg.extensions {
+	for _, extension := range ctx.extensions {
 		encoder = extension.DecorateEncoder(typ, encoder)
 	}
 	return encoder
 }
 
-func createEncoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
-	encoder := createEncoderOfJsonRawMessage(cfg, prefix, typ)
+func createEncoderOfType(ctx *ctx, typ reflect.Type) ValEncoder {
+	encoder := ctx.encoders[typ]
 	if encoder != nil {
 		return encoder
 	}
-	encoder = createEncoderOfJsonNumber(cfg, prefix, typ)
+	placeholder := &placeholderEncoder{}
+	ctx.encoders[typ] = placeholder
+	encoder = _createEncoderOfType(ctx, typ)
+	placeholder.encoder = encoder
+	return encoder
+}
+func _createEncoderOfType(ctx *ctx, typ reflect.Type) ValEncoder {
+	encoder := createEncoderOfJsonRawMessage(ctx, typ)
 	if encoder != nil {
 		return encoder
 	}
-	encoder = createEncoderOfMarshaler(cfg, prefix, typ)
+	encoder = createEncoderOfJsonNumber(ctx, typ)
 	if encoder != nil {
 		return encoder
 	}
-	encoder = createEncoderOfAny(cfg, prefix, typ)
+	encoder = createEncoderOfMarshaler(ctx, typ)
 	if encoder != nil {
 		return encoder
 	}
-	encoder = createEncoderOfNative(cfg, prefix, typ)
+	encoder = createEncoderOfAny(ctx, typ)
+	if encoder != nil {
+		return encoder
+	}
+	encoder = createEncoderOfNative(ctx, typ)
 	if encoder != nil {
 		return encoder
 	}
@@ -200,17 +251,17 @@ func createEncoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) Val
 	case reflect.Interface:
 		return &dynamicEncoder{reflect2.Type2(typ)}
 	case reflect.Struct:
-		return encoderOfStruct(cfg, prefix, typ)
+		return encoderOfStruct(ctx, typ)
 	case reflect.Array:
-		return encoderOfArray(cfg, prefix, typ)
+		return encoderOfArray(ctx, typ)
 	case reflect.Slice:
-		return encoderOfSlice(cfg, prefix, typ)
+		return encoderOfSlice(ctx, typ)
 	case reflect.Map:
-		return encoderOfMap(cfg, prefix, typ)
+		return encoderOfMap(ctx, typ)
 	case reflect.Ptr:
-		return encoderOfOptional(cfg, prefix, typ)
+		return encoderOfOptional(ctx, typ)
 	default:
-		return &lazyErrorEncoder{err: fmt.Errorf("%s%s is unsupported type", prefix, typ.String())}
+		return &lazyErrorEncoder{err: fmt.Errorf("%s%s is unsupported type", ctx.prefix, typ.String())}
 	}
 }
 
@@ -243,3 +294,23 @@ func (encoder *lazyErrorEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
 func (encoder *lazyErrorEncoder) IsEmpty(ptr unsafe.Pointer) bool {
 	return false
 }
+
+type placeholderDecoder struct {
+	decoder ValDecoder
+}
+
+func (decoder *placeholderDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
+	decoder.decoder.Decode(ptr, iter)
+}
+
+type placeholderEncoder struct {
+	encoder ValEncoder
+}
+
+func (encoder *placeholderEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
+	encoder.encoder.Encode(ptr, stream)
+}
+
+func (encoder *placeholderEncoder) IsEmpty(ptr unsafe.Pointer) bool {
+	return encoder.encoder.IsEmpty(ptr)
+}

+ 4 - 4
reflect_array.go

@@ -7,16 +7,16 @@ import (
 	"unsafe"
 )
 
-func decoderOfArray(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
-	decoder := decoderOfType(cfg, prefix+"[array]->", typ.Elem())
+func decoderOfArray(ctx *ctx, typ reflect.Type) ValDecoder {
+	decoder := decoderOfType(ctx.append("[arrayElem]"), typ.Elem())
 	return &arrayDecoder{typ, typ.Elem(), decoder}
 }
 
-func encoderOfArray(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
+func encoderOfArray(ctx *ctx, typ reflect.Type) ValEncoder {
 	if typ.Len() == 0 {
 		return emptyArrayEncoder{}
 	}
-	encoder := encoderOfType(cfg, prefix+"[array]->", typ.Elem())
+	encoder := encoderOfType(ctx.append("[arrayElem]"), typ.Elem())
 	return &arrayEncoder{typ, typ.Elem(), encoder}
 }
 

+ 21 - 21
reflect_extension.go

@@ -208,26 +208,26 @@ func RegisterExtension(extension Extension) {
 	extensions = append(extensions, extension)
 }
 
-func getTypeDecoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValDecoder {
-	decoder := _getTypeDecoderFromExtension(cfg, typ)
+func getTypeDecoderFromExtension(ctx *ctx, typ reflect.Type) ValDecoder {
+	decoder := _getTypeDecoderFromExtension(ctx, typ)
 	if decoder != nil {
 		for _, extension := range extensions {
 			decoder = extension.DecorateDecoder(typ, decoder)
 		}
-		for _, extension := range cfg.extensions {
+		for _, extension := range ctx.extensions {
 			decoder = extension.DecorateDecoder(typ, decoder)
 		}
 	}
 	return decoder
 }
-func _getTypeDecoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValDecoder {
+func _getTypeDecoderFromExtension(ctx *ctx, typ reflect.Type) ValDecoder {
 	for _, extension := range extensions {
 		decoder := extension.CreateDecoder(typ)
 		if decoder != nil {
 			return decoder
 		}
 	}
-	for _, extension := range cfg.extensions {
+	for _, extension := range ctx.extensions {
 		decoder := extension.CreateDecoder(typ)
 		if decoder != nil {
 			return decoder
@@ -247,27 +247,27 @@ func _getTypeDecoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValDecode
 	return nil
 }
 
-func getTypeEncoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValEncoder {
-	encoder := _getTypeEncoderFromExtension(cfg, typ)
+func getTypeEncoderFromExtension(ctx *ctx, typ reflect.Type) ValEncoder {
+	encoder := _getTypeEncoderFromExtension(ctx, typ)
 	if encoder != nil {
 		for _, extension := range extensions {
 			encoder = extension.DecorateEncoder(typ, encoder)
 		}
-		for _, extension := range cfg.extensions {
+		for _, extension := range ctx.extensions {
 			encoder = extension.DecorateEncoder(typ, encoder)
 		}
 	}
 	return encoder
 }
 
-func _getTypeEncoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValEncoder {
+func _getTypeEncoderFromExtension(ctx *ctx, typ reflect.Type) ValEncoder {
 	for _, extension := range extensions {
 		encoder := extension.CreateEncoder(typ)
 		if encoder != nil {
 			return encoder
 		}
 	}
-	for _, extension := range cfg.extensions {
+	for _, extension := range ctx.extensions {
 		encoder := extension.CreateEncoder(typ)
 		if encoder != nil {
 			return encoder
@@ -287,13 +287,13 @@ func _getTypeEncoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValEncode
 	return nil
 }
 
-func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructDescriptor {
+func describeStruct(ctx *ctx, typ reflect.Type) *StructDescriptor {
 	embeddedBindings := []*Binding{}
 	bindings := []*Binding{}
 	for i := 0; i < typ.NumField(); i++ {
 		field := typ.Field(i)
-		tag, hastag := field.Tag.Lookup(cfg.getTagKey())
-		if cfg.onlyTaggedField && !hastag {
+		tag, hastag := field.Tag.Lookup(ctx.getTagKey())
+		if ctx.onlyTaggedField && !hastag {
 			continue
 		}
 		tagParts := strings.Split(tag, ",")
@@ -302,7 +302,7 @@ func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructD
 		}
 		if field.Anonymous && (tag == "" || tagParts[0] == "") {
 			if field.Type.Kind() == reflect.Struct {
-				structDescriptor := describeStruct(cfg, prefix, field.Type)
+				structDescriptor := describeStruct(ctx, field.Type)
 				for _, binding := range structDescriptor.Fields {
 					binding.levels = append([]int{i}, binding.levels...)
 					omitempty := binding.Encoder.(*structFieldEncoder).omitempty
@@ -312,7 +312,7 @@ func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructD
 				}
 				continue
 			} else if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
-				structDescriptor := describeStruct(cfg, prefix, field.Type.Elem())
+				structDescriptor := describeStruct(ctx, field.Type.Elem())
 				for _, binding := range structDescriptor.Fields {
 					binding.levels = append([]int{i}, binding.levels...)
 					omitempty := binding.Encoder.(*structFieldEncoder).omitempty
@@ -329,11 +329,11 @@ func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructD
 		fieldCacheKey := fmt.Sprintf("%s/%s", typ.String(), field.Name)
 		decoder := fieldDecoders[fieldCacheKey]
 		if decoder == nil {
-			decoder = decoderOfType(cfg, prefix+typ.String()+"."+field.Name+"->", field.Type)
+			decoder = decoderOfType(ctx.append(field.Name), field.Type)
 		}
 		encoder := fieldEncoders[fieldCacheKey]
 		if encoder == nil {
-			encoder = encoderOfType(cfg, prefix+typ.String()+"."+field.Name+"->", field.Type)
+			encoder = encoderOfType(ctx.append(field.Name), field.Type)
 		}
 		binding := &Binding{
 			Field:     &field,
@@ -345,9 +345,9 @@ func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructD
 		binding.levels = []int{i}
 		bindings = append(bindings, binding)
 	}
-	return createStructDescriptor(cfg, typ, bindings, embeddedBindings)
+	return createStructDescriptor(ctx, typ, bindings, embeddedBindings)
 }
-func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor {
+func createStructDescriptor(ctx *ctx, typ reflect.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor {
 	structDescriptor := &StructDescriptor{
 		Type:               typ,
 		Fields:             bindings,
@@ -355,10 +355,10 @@ func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Bin
 	for _, extension := range extensions {
 		extension.UpdateStructDescriptor(structDescriptor)
 	}
-	for _, extension := range cfg.extensions {
+	for _, extension := range ctx.extensions {
 		extension.UpdateStructDescriptor(structDescriptor)
 	}
-	processTags(structDescriptor, cfg)
+	processTags(structDescriptor, ctx.frozenConfig)
 	// merge normal & embedded bindings & sort with original order
 	allBindings := sortableBindings(append(embeddedBindings, structDescriptor.Fields...))
 	sort.Sort(allBindings)

+ 2 - 2
reflect_json_number.go

@@ -35,7 +35,7 @@ func CastJsonNumber(val interface{}) (string, bool) {
 var jsonNumberType = reflect.TypeOf((*json.Number)(nil)).Elem()
 var jsoniterNumberType = reflect.TypeOf((*Number)(nil)).Elem()
 
-func createDecoderOfJsonNumber(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
+func createDecoderOfJsonNumber(ctx *ctx, typ reflect.Type) ValDecoder {
 	if typ.AssignableTo(jsonNumberType) {
 		return &jsonNumberCodec{}
 	}
@@ -45,7 +45,7 @@ func createDecoderOfJsonNumber(cfg *frozenConfig, prefix string, typ reflect.Typ
 	return nil
 }
 
-func createEncoderOfJsonNumber(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
+func createEncoderOfJsonNumber(ctx *ctx, typ reflect.Type) ValEncoder {
 	if typ.AssignableTo(jsonNumberType) {
 		return &jsonNumberCodec{}
 	}

+ 2 - 2
reflect_json_raw_message.go

@@ -9,7 +9,7 @@ import (
 var jsonRawMessageType = reflect.TypeOf((*json.RawMessage)(nil)).Elem()
 var jsoniterRawMessageType = reflect.TypeOf((*RawMessage)(nil)).Elem()
 
-func createEncoderOfJsonRawMessage(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
+func createEncoderOfJsonRawMessage(ctx *ctx, typ reflect.Type) ValEncoder {
 	if typ == jsonRawMessageType {
 		return &jsonRawMessageCodec{}
 	}
@@ -19,7 +19,7 @@ func createEncoderOfJsonRawMessage(cfg *frozenConfig, prefix string, typ reflect
 	return nil
 }
 
-func createDecoderOfJsonRawMessage(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
+func createDecoderOfJsonRawMessage(ctx *ctx, typ reflect.Type) ValDecoder {
 	if typ == jsonRawMessageType {
 		return &jsonRawMessageCodec{}
 	}

+ 17 - 17
reflect_map.go

@@ -8,9 +8,9 @@ import (
 	"fmt"
 )
 
-func decoderOfMap(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
-	keyDecoder := decoderOfMapKey(cfg, prefix+" [mapKey]", typ.Key())
-	elemDecoder := decoderOfType(cfg, prefix+" [mapElem]", typ.Elem())
+func decoderOfMap(ctx *ctx, typ reflect.Type) ValDecoder {
+	keyDecoder := decoderOfMapKey(ctx.append("[mapKey]"), typ.Key())
+	elemDecoder := decoderOfType(ctx.append("[mapElem]"), typ.Elem())
 	mapType := reflect2.Type2(typ).(*reflect2.UnsafeMapType)
 	return &mapDecoder{
 		mapType:     mapType,
@@ -21,25 +21,25 @@ func decoderOfMap(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder
 	}
 }
 
-func encoderOfMap(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
-	if cfg.sortMapKeys {
+func encoderOfMap(ctx *ctx, typ reflect.Type) ValEncoder {
+	if ctx.sortMapKeys {
 		return &sortKeysMapEncoder{
 			mapType:     reflect2.Type2(typ).(*reflect2.UnsafeMapType),
-			keyEncoder:  encoderOfMapKey(cfg, prefix+" [mapKey]", typ.Key()),
-			elemEncoder: encoderOfType(cfg, prefix+" [mapElem]", typ.Elem()),
+			keyEncoder:  encoderOfMapKey(ctx.append("[mapKey]"), typ.Key()),
+			elemEncoder: encoderOfType(ctx.append("[mapElem]"), typ.Elem()),
 		}
 	}
 	return &mapEncoder{
 		mapType:     reflect2.Type2(typ).(*reflect2.UnsafeMapType),
-		keyEncoder:  encoderOfMapKey(cfg, prefix+" [mapKey]", typ.Key()),
-		elemEncoder: encoderOfType(cfg, prefix+" [mapElem]", typ.Elem()),
+		keyEncoder:  encoderOfMapKey(ctx.append("[mapKey]"), typ.Key()),
+		elemEncoder: encoderOfType(ctx.append("[mapElem]"), typ.Elem()),
 	}
 }
 
-func decoderOfMapKey(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
+func decoderOfMapKey(ctx *ctx, typ reflect.Type) ValDecoder {
 	switch typ.Kind() {
 	case reflect.String:
-		return decoderOfType(cfg, prefix, reflect2.DefaultTypeOfKind(reflect.String).Type1())
+		return decoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String).Type1())
 	case reflect.Bool,
 		reflect.Uint8, reflect.Int8,
 		reflect.Uint16, reflect.Int16,
@@ -49,7 +49,7 @@ func decoderOfMapKey(cfg *frozenConfig, prefix string, typ reflect.Type) ValDeco
 		reflect.Float32, reflect.Float64,
 		reflect.Uintptr:
 		typ = reflect2.DefaultTypeOfKind(typ.Kind()).Type1()
-		return &numericMapKeyDecoder{decoderOfType(cfg, prefix, typ)}
+		return &numericMapKeyDecoder{decoderOfType(ctx, typ)}
 	default:
 		ptrType := reflect.PtrTo(typ)
 		if ptrType.Implements(textMarshalerType) {
@@ -68,10 +68,10 @@ func decoderOfMapKey(cfg *frozenConfig, prefix string, typ reflect.Type) ValDeco
 	}
 }
 
-func encoderOfMapKey(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
+func encoderOfMapKey(ctx *ctx, typ reflect.Type) ValEncoder {
 	switch typ.Kind() {
 	case reflect.String:
-		return encoderOfType(cfg, prefix, reflect2.DefaultTypeOfKind(reflect.String).Type1())
+		return encoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String).Type1())
 	case reflect.Bool,
 		reflect.Uint8, reflect.Int8,
 		reflect.Uint16, reflect.Int16,
@@ -81,17 +81,17 @@ func encoderOfMapKey(cfg *frozenConfig, prefix string, typ reflect.Type) ValEnco
 		reflect.Float32, reflect.Float64,
 		reflect.Uintptr:
 		typ = reflect2.DefaultTypeOfKind(typ.Kind()).Type1()
-		return &numericMapKeyEncoder{encoderOfType(cfg, prefix, typ)}
+		return &numericMapKeyEncoder{encoderOfType(ctx, typ)}
 	default:
 		if typ == textMarshalerType {
 			return &directTextMarshalerEncoder{
-				stringEncoder: cfg.EncoderOf(reflect.TypeOf("")),
+				stringEncoder: ctx.EncoderOf(reflect.TypeOf("")),
 			}
 		}
 		if typ.Implements(textMarshalerType) {
 			return &textMarshalerEncoder{
 				valType:       reflect2.Type2(typ),
-				stringEncoder: cfg.EncoderOf(reflect.TypeOf("")),
+				stringEncoder: ctx.EncoderOf(reflect.TypeOf("")),
 			}
 		}
 		return &lazyErrorEncoder{err: fmt.Errorf("unsupported map key type: %v", typ)}

+ 13 - 13
reflect_marshaler.go

@@ -13,7 +13,7 @@ var unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
 var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
 var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
 
-func createDecoderOfMarshaler(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
+func createDecoderOfMarshaler(ctx *ctx, typ reflect.Type) ValDecoder {
 	ptrType := reflect.PtrTo(typ)
 	if ptrType.Implements(unmarshalerType) {
 		return &referenceDecoder{
@@ -28,16 +28,16 @@ func createDecoderOfMarshaler(cfg *frozenConfig, prefix string, typ reflect.Type
 	return nil
 }
 
-func createEncoderOfMarshaler(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
+func createEncoderOfMarshaler(ctx *ctx, typ reflect.Type) ValEncoder {
 	if typ == marshalerType {
-		checkIsEmpty := createCheckIsEmpty(cfg, typ)
+		checkIsEmpty := createCheckIsEmpty(ctx, typ)
 		var encoder ValEncoder = &directMarshalerEncoder{
 			checkIsEmpty: checkIsEmpty,
 		}
 		return encoder
 	}
 	if typ.Implements(marshalerType) {
-		checkIsEmpty := createCheckIsEmpty(cfg, typ)
+		checkIsEmpty := createCheckIsEmpty(ctx, typ)
 		var encoder ValEncoder = &marshalerEncoder{
 			valType:      reflect2.Type2(typ),
 			checkIsEmpty: checkIsEmpty,
@@ -45,8 +45,8 @@ func createEncoderOfMarshaler(cfg *frozenConfig, prefix string, typ reflect.Type
 		return encoder
 	}
 	ptrType := reflect.PtrTo(typ)
-	if prefix != "" && ptrType.Implements(marshalerType) {
-		checkIsEmpty := createCheckIsEmpty(cfg, ptrType)
+	if ctx.prefix != "" && ptrType.Implements(marshalerType) {
+		checkIsEmpty := createCheckIsEmpty(ctx, ptrType)
 		var encoder ValEncoder = &marshalerEncoder{
 			valType:      reflect2.Type2(ptrType),
 			checkIsEmpty: checkIsEmpty,
@@ -54,28 +54,28 @@ func createEncoderOfMarshaler(cfg *frozenConfig, prefix string, typ reflect.Type
 		return &referenceEncoder{encoder}
 	}
 	if typ == textMarshalerType {
-		checkIsEmpty := createCheckIsEmpty(cfg, typ)
+		checkIsEmpty := createCheckIsEmpty(ctx, typ)
 		var encoder ValEncoder = &directTextMarshalerEncoder{
 			checkIsEmpty:  checkIsEmpty,
-			stringEncoder: cfg.EncoderOf(reflect.TypeOf("")),
+			stringEncoder: ctx.EncoderOf(reflect.TypeOf("")),
 		}
 		return encoder
 	}
 	if typ.Implements(textMarshalerType) {
-		checkIsEmpty := createCheckIsEmpty(cfg, typ)
+		checkIsEmpty := createCheckIsEmpty(ctx, typ)
 		var encoder ValEncoder = &textMarshalerEncoder{
 			valType:       reflect2.Type2(typ),
-			stringEncoder: cfg.EncoderOf(reflect.TypeOf("")),
+			stringEncoder: ctx.EncoderOf(reflect.TypeOf("")),
 			checkIsEmpty:  checkIsEmpty,
 		}
 		return encoder
 	}
 	// if prefix is empty, the type is the root type
-	if prefix != "" && ptrType.Implements(textMarshalerType) {
-		checkIsEmpty := createCheckIsEmpty(cfg, ptrType)
+	if ctx.prefix != "" && ptrType.Implements(textMarshalerType) {
+		checkIsEmpty := createCheckIsEmpty(ctx, ptrType)
 		var encoder ValEncoder = &textMarshalerEncoder{
 			valType:       reflect2.Type2(ptrType),
-			stringEncoder: cfg.EncoderOf(reflect.TypeOf("")),
+			stringEncoder: ctx.EncoderOf(reflect.TypeOf("")),
 			checkIsEmpty:  checkIsEmpty,
 		}
 		return &referenceEncoder{encoder}

+ 34 - 34
reflect_native.go

@@ -7,9 +7,9 @@ import (
 	"github.com/v2pro/plz/reflect2"
 )
 
-func createEncoderOfNative(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
+func createEncoderOfNative(ctx *ctx, typ reflect.Type) ValEncoder {
 	if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
-		sliceDecoder := decoderOfSlice(cfg, prefix, typ)
+		sliceDecoder := decoderOfSlice(ctx, typ)
 		return &base64Codec{sliceDecoder: sliceDecoder}
 	}
 	typeName := typ.String()
@@ -17,163 +17,163 @@ func createEncoderOfNative(cfg *frozenConfig, prefix string, typ reflect.Type) V
 	switch kind {
 	case reflect.String:
 		if typeName != "string" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*string)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*string)(nil)).Elem())
 		}
 		return &stringCodec{}
 	case reflect.Int:
 		if typeName != "int" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*int)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*int)(nil)).Elem())
 		}
 		return &intCodec{}
 	case reflect.Int8:
 		if typeName != "int8" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*int8)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*int8)(nil)).Elem())
 		}
 		return &int8Codec{}
 	case reflect.Int16:
 		if typeName != "int16" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*int16)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*int16)(nil)).Elem())
 		}
 		return &int16Codec{}
 	case reflect.Int32:
 		if typeName != "int32" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*int32)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*int32)(nil)).Elem())
 		}
 		return &int32Codec{}
 	case reflect.Int64:
 		if typeName != "int64" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*int64)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*int64)(nil)).Elem())
 		}
 		return &int64Codec{}
 	case reflect.Uint:
 		if typeName != "uint" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*uint)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*uint)(nil)).Elem())
 		}
 		return &uintCodec{}
 	case reflect.Uint8:
 		if typeName != "uint8" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*uint8)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*uint8)(nil)).Elem())
 		}
 		return &uint8Codec{}
 	case reflect.Uint16:
 		if typeName != "uint16" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*uint16)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*uint16)(nil)).Elem())
 		}
 		return &uint16Codec{}
 	case reflect.Uint32:
 		if typeName != "uint32" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*uint32)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*uint32)(nil)).Elem())
 		}
 		return &uint32Codec{}
 	case reflect.Uintptr:
 		if typeName != "uintptr" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*uintptr)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*uintptr)(nil)).Elem())
 		}
 		return &uintptrCodec{}
 	case reflect.Uint64:
 		if typeName != "uint64" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*uint64)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*uint64)(nil)).Elem())
 		}
 		return &uint64Codec{}
 	case reflect.Float32:
 		if typeName != "float32" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*float32)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*float32)(nil)).Elem())
 		}
 		return &float32Codec{}
 	case reflect.Float64:
 		if typeName != "float64" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*float64)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*float64)(nil)).Elem())
 		}
 		return &float64Codec{}
 	case reflect.Bool:
 		if typeName != "bool" {
-			return encoderOfType(cfg, prefix, reflect.TypeOf((*bool)(nil)).Elem())
+			return encoderOfType(ctx, reflect.TypeOf((*bool)(nil)).Elem())
 		}
 		return &boolCodec{}
 	}
 	return nil
 }
 
-func createDecoderOfNative(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
+func createDecoderOfNative(ctx *ctx, typ reflect.Type) ValDecoder {
 	if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
-		sliceDecoder := decoderOfSlice(cfg, prefix, typ)
+		sliceDecoder := decoderOfSlice(ctx, typ)
 		return &base64Codec{sliceDecoder: sliceDecoder}
 	}
 	typeName := typ.String()
 	switch typ.Kind() {
 	case reflect.String:
 		if typeName != "string" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*string)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*string)(nil)).Elem())
 		}
 		return &stringCodec{}
 	case reflect.Int:
 		if typeName != "int" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*int)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*int)(nil)).Elem())
 		}
 		return &intCodec{}
 	case reflect.Int8:
 		if typeName != "int8" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*int8)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*int8)(nil)).Elem())
 		}
 		return &int8Codec{}
 	case reflect.Int16:
 		if typeName != "int16" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*int16)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*int16)(nil)).Elem())
 		}
 		return &int16Codec{}
 	case reflect.Int32:
 		if typeName != "int32" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*int32)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*int32)(nil)).Elem())
 		}
 		return &int32Codec{}
 	case reflect.Int64:
 		if typeName != "int64" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*int64)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*int64)(nil)).Elem())
 		}
 		return &int64Codec{}
 	case reflect.Uint:
 		if typeName != "uint" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*uint)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*uint)(nil)).Elem())
 		}
 		return &uintCodec{}
 	case reflect.Uint8:
 		if typeName != "uint8" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*uint8)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*uint8)(nil)).Elem())
 		}
 		return &uint8Codec{}
 	case reflect.Uint16:
 		if typeName != "uint16" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*uint16)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*uint16)(nil)).Elem())
 		}
 		return &uint16Codec{}
 	case reflect.Uint32:
 		if typeName != "uint32" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*uint32)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*uint32)(nil)).Elem())
 		}
 		return &uint32Codec{}
 	case reflect.Uintptr:
 		if typeName != "uintptr" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*uintptr)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*uintptr)(nil)).Elem())
 		}
 		return &uintptrCodec{}
 	case reflect.Uint64:
 		if typeName != "uint64" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*uint64)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*uint64)(nil)).Elem())
 		}
 		return &uint64Codec{}
 	case reflect.Float32:
 		if typeName != "float32" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*float32)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*float32)(nil)).Elem())
 		}
 		return &float32Codec{}
 	case reflect.Float64:
 		if typeName != "float64" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*float64)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*float64)(nil)).Elem())
 		}
 		return &float64Codec{}
 	case reflect.Bool:
 		if typeName != "bool" {
-			return decoderOfType(cfg, prefix, reflect.TypeOf((*bool)(nil)).Elem())
+			return decoderOfType(ctx, reflect.TypeOf((*bool)(nil)).Elem())
 		}
 		return &boolCodec{}
 	}

+ 5 - 5
reflect_optional.go

@@ -6,18 +6,18 @@ import (
 	"github.com/v2pro/plz/reflect2"
 )
 
-func decoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
+func decoderOfOptional(ctx *ctx, typ reflect.Type) ValDecoder {
 	elemType := typ.Elem()
-	decoder := decoderOfType(cfg, prefix, elemType)
-	if prefix == "" && elemType.Kind() == reflect.Ptr {
+	decoder := decoderOfType(ctx, elemType)
+	if ctx.prefix == "" && elemType.Kind() == reflect.Ptr {
 		return &dereferenceDecoder{reflect2.Type2(elemType), decoder}
 	}
 	return &OptionalDecoder{reflect2.Type2(elemType), decoder}
 }
 
-func encoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
+func encoderOfOptional(ctx *ctx, typ reflect.Type) ValEncoder {
 	elemType := typ.Elem()
-	elemEncoder := encoderOfType(cfg, prefix, elemType)
+	elemEncoder := encoderOfType(ctx, elemType)
 	encoder := &OptionalEncoder{elemEncoder}
 	return encoder
 }

+ 4 - 4
reflect_slice.go

@@ -8,14 +8,14 @@ import (
 	"github.com/v2pro/plz/reflect2"
 )
 
-func decoderOfSlice(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
-	decoder := decoderOfType(cfg, prefix+"[slice]->", typ.Elem())
+func decoderOfSlice(ctx *ctx, typ reflect.Type) ValDecoder {
+	decoder := decoderOfType(ctx.append("[sliceElem]"), typ.Elem())
 	sliceType := reflect2.Type2(typ).(*reflect2.UnsafeSliceType)
 	return &sliceDecoder{sliceType, decoder}
 }
 
-func encoderOfSlice(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
-	encoder := encoderOfType(cfg, prefix+"[slice]->", typ.Elem())
+func encoderOfSlice(ctx *ctx, typ reflect.Type) ValEncoder {
+	encoder := encoderOfType(ctx.append("[sliceElem]"), typ.Elem())
 	sliceType := reflect2.Type2(typ).(*reflect2.UnsafeSliceType)
 	return &sliceEncoder{sliceType, encoder}
 }

+ 6 - 6
reflect_struct_decoder.go

@@ -8,9 +8,9 @@ import (
 	"unsafe"
 )
 
-func decoderOfStruct(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
+func decoderOfStruct(ctx *ctx, typ reflect.Type) ValDecoder {
 	bindings := map[string]*Binding{}
-	structDescriptor := describeStruct(cfg, prefix, typ)
+	structDescriptor := describeStruct(ctx, typ)
 	for _, binding := range structDescriptor.Fields {
 		for _, fromName := range binding.FromNames {
 			old := bindings[fromName]
@@ -18,7 +18,7 @@ func decoderOfStruct(cfg *frozenConfig, prefix string, typ reflect.Type) ValDeco
 				bindings[fromName] = binding
 				continue
 			}
-			ignoreOld, ignoreNew := resolveConflictBinding(cfg, old, binding)
+			ignoreOld, ignoreNew := resolveConflictBinding(ctx.frozenConfig, old, binding)
 			if ignoreOld {
 				delete(bindings, fromName)
 			}
@@ -31,11 +31,11 @@ func decoderOfStruct(cfg *frozenConfig, prefix string, typ reflect.Type) ValDeco
 	for k, binding := range bindings {
 		fields[k] = binding.Decoder.(*structFieldDecoder)
 	}
-	return createStructDecoder(cfg, typ, fields)
+	return createStructDecoder(ctx, typ, fields)
 }
 
-func createStructDecoder(cfg *frozenConfig, typ reflect.Type, fields map[string]*structFieldDecoder) ValDecoder {
-	if cfg.disallowUnknownFields {
+func createStructDecoder(ctx *ctx, typ reflect.Type, fields map[string]*structFieldDecoder) ValDecoder {
+	if ctx.disallowUnknownFields {
 		return &generalStructDecoder{typ: typ, fields: fields, disallowUnknownFields: true}
 	}
 	knownHash := map[int64]struct{}{

+ 5 - 5
reflect_struct_encoder.go

@@ -8,14 +8,14 @@ import (
 	"github.com/v2pro/plz/reflect2"
 )
 
-func encoderOfStruct(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
+func encoderOfStruct(ctx *ctx, typ reflect.Type) ValEncoder {
 	type bindingTo struct {
 		binding *Binding
 		toName  string
 		ignored bool
 	}
 	orderedBindings := []*bindingTo{}
-	structDescriptor := describeStruct(cfg, prefix, typ)
+	structDescriptor := describeStruct(ctx, typ)
 	for _, binding := range structDescriptor.Fields {
 		for _, toName := range binding.ToNames {
 			new := &bindingTo{
@@ -26,7 +26,7 @@ func encoderOfStruct(cfg *frozenConfig, prefix string, typ reflect.Type) ValEnco
 				if old.toName != toName {
 					continue
 				}
-				old.ignored, new.ignored = resolveConflictBinding(cfg, old.binding, new.binding)
+				old.ignored, new.ignored = resolveConflictBinding(ctx.frozenConfig, old.binding, new.binding)
 			}
 			orderedBindings = append(orderedBindings, new)
 		}
@@ -46,7 +46,7 @@ func encoderOfStruct(cfg *frozenConfig, prefix string, typ reflect.Type) ValEnco
 	return &structEncoder{typ, finalOrderedFields}
 }
 
-func createCheckIsEmpty(cfg *frozenConfig, typ reflect.Type) checkIsEmpty {
+func createCheckIsEmpty(ctx *ctx, typ reflect.Type) checkIsEmpty {
 	kind := typ.Kind()
 	switch kind {
 	case reflect.String:
@@ -88,7 +88,7 @@ func createCheckIsEmpty(cfg *frozenConfig, typ reflect.Type) checkIsEmpty {
 	case reflect.Slice:
 		return &sliceEncoder{}
 	case reflect.Map:
-		return encoderOfMap(cfg, "", typ)
+		return encoderOfMap(ctx, typ)
 	case reflect.Ptr:
 		return &OptionalEncoder{}
 	default:

+ 1 - 2
value_tests/struct_test.go

@@ -120,8 +120,7 @@ func init() {
 			Field1 string `json:"field-1,omitempty"`
 			Field2 func() `json:"-"`
 		}{},
-		// TODO: fix me
-		//structRecursive{},
+		structRecursive{},
 		struct {
 			*CacheItem