Repository: goccy/go-json
Pull request #301 (2021-11-27 17:20:17)

Refactor compiler for encoder
- Introduced a two phase compilation to calculate Opcode index accurately
- Fix display number of Opcode
- Improve memory footprint for Opcode


Patch:
diff --git a/internal/cmd/generator/main.go b/internal/cmd/generator/main.go
index ac2df79..9482807 100644
--- a/internal/cmd/generator/main.go
+++ b/internal/cmd/generator/main.go
@@ -201,7 +201,6 @@ func (t OpType) FieldToOmitEmptyField() OpType {
 		createOpType("RecursivePtr", "Op"),
 		createOpType("RecursiveEnd", "Op"),
 		createOpType("InterfaceEnd", "Op"),
-		createOpType("StructAnonymousEnd", "StructEnd"),
 	}
 	for _, typ := range primitiveTypesUpper {
 		typ := typ
diff --git a/internal/cmd/generator/vm.go.tmpl b/internal/cmd/generator/vm.go.tmpl
index 7701501..351982a 100644
--- a/internal/cmd/generator/vm.go.tmpl
+++ b/internal/cmd/generator/vm.go.tmpl
@@ -4349,8 +4349,6 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
 				code = code.Next
 				store(ctxptr, code.Idx, p)
 			}
-		case encoder.OpStructAnonymousEnd:
-			code = code.Next
 		case encoder.OpStructEnd:
 			b = appendStructEndSkipLast(ctx, code, b)
 			code = code.Next
diff --git a/internal/encoder/code.go b/internal/encoder/code.go
new file mode 100644
index 0000000..aee0101
--- /dev/null
+++ b/internal/encoder/code.go
@@ -0,0 +1,875 @@
+package encoder
+
+import (
+	"fmt"
+	"unsafe"
+
+	"github.com/goccy/go-json/internal/runtime"
+)
+
+type Code interface {
+	Kind() CodeKind
+	ToOpcode(*compileContext) Opcodes
+}
+
+type AnonymousCode interface {
+	ToAnonymousOpcode(*compileContext) Opcodes
+}
+
+type Opcodes []*Opcode
+
+func (o Opcodes) First() *Opcode {
+	if len(o) == 0 {
+		return nil
+	}
+	return o[0]
+}
+
+func (o Opcodes) Last() *Opcode {
+	if len(o) == 0 {
+		return nil
+	}
+	return o[len(o)-1]
+}
+
+func (o Opcodes) Add(codes ...*Opcode) Opcodes {
+	return append(o, codes...)
+}
+
+type CodeKind int
+
+const (
+	CodeKindInterface CodeKind = iota
+	CodeKindPtr
+	CodeKindInt
+	CodeKindUint
+	CodeKindFloat
+	CodeKindString
+	CodeKindBool
+	CodeKindStruct
+	CodeKindMap
+	CodeKindSlice
+	CodeKindArray
+	CodeKindBytes
+	CodeKindMarshalJSON
+	CodeKindMarshalText
+	CodeKindRecursive
+)
+
+type IntCode struct {
+	typ      *runtime.Type
+	bitSize  uint8
+	isString bool
+	isPtr    bool
+}
+
+func (c *IntCode) Kind() CodeKind {
+	return CodeKindInt
+}
+
+func (c *IntCode) ToOpcode(ctx *compileContext) Opcodes {
+	var code *Opcode
+	switch {
+	case c.isPtr:
+		code = newOpCode(ctx, c.typ, OpIntPtr)
+	case c.isString:
+		code = newOpCode(ctx, c.typ, OpIntString)
+	default:
+		code = newOpCode(ctx, c.typ, OpInt)
+	}
+	code.NumBitSize = c.bitSize
+	ctx.incIndex()
+	return Opcodes{code}
+}
+
+type UintCode struct {
+	typ      *runtime.Type
+	bitSize  uint8
+	isString bool
+	isPtr    bool
+}
+
+func (c *UintCode) Kind() CodeKind {
+	return CodeKindUint
+}
+
+func (c *UintCode) ToOpcode(ctx *compileContext) Opcodes {
+	var code *Opcode
+	switch {
+	case c.isPtr:
+		code = newOpCode(ctx, c.typ, OpUintPtr)
+	case c.isString:
+		code = newOpCode(ctx, c.typ, OpUintString)
+	default:
+		code = newOpCode(ctx, c.typ, OpUint)
+	}
+	code.NumBitSize = c.bitSize
+	ctx.incIndex()
+	return Opcodes{code}
+}
+
+type FloatCode struct {
+	typ     *runtime.Type
+	bitSize uint8
+	isPtr   bool
+}
+
+func (c *FloatCode) Kind() CodeKind {
+	return CodeKindFloat
+}
+
+func (c *FloatCode) ToOpcode(ctx *compileContext) Opcodes {
+	var code *Opcode
+	switch {
+	case c.isPtr:
+		switch c.bitSize {
+		case 32:
+			code = newOpCode(ctx, c.typ, OpFloat32Ptr)
+		default:
+			code = newOpCode(ctx, c.typ, OpFloat64Ptr)
+		}
+	default:
+		switch c.bitSize {
+		case 32:
+			code = newOpCode(ctx, c.typ, OpFloat32)
+		default:
+			code = newOpCode(ctx, c.typ, OpFloat64)
+		}
+	}
+	ctx.incIndex()
+	return Opcodes{code}
+}
+
+type StringCode struct {
+	typ   *runtime.Type
+	isPtr bool
+}
+
+func (c *StringCode) Kind() CodeKind {
+	return CodeKindString
+}
+
+func (c *StringCode) ToOpcode(ctx *compileContext) Opcodes {
+	isJSONNumberType := c.typ == runtime.Type2RType(jsonNumberType)
+	var code *Opcode
+	if c.isPtr {
+		if isJSONNumberType {
+			code = newOpCode(ctx, c.typ, OpNumberPtr)
+		} else {
+			code = newOpCode(ctx, c.typ, OpStringPtr)
+		}
+	} else {
+		if isJSONNumberType {
+			code = newOpCode(ctx, c.typ, OpNumber)
+		} else {
+			code = newOpCode(ctx, c.typ, OpString)
+		}
+	}
+	ctx.incIndex()
+	return Opcodes{code}
+}
+
+type BoolCode struct {
+	typ   *runtime.Type
+	isPtr bool
+}
+
+func (c *BoolCode) Kind() CodeKind {
+	return CodeKindBool
+}
+
+func (c *BoolCode) ToOpcode(ctx *compileContext) Opcodes {
+	var code *Opcode
+	switch {
+	case c.isPtr:
+		code = newOpCode(ctx, c.typ, OpBoolPtr)
+	default:
+		code = newOpCode(ctx, c.typ, OpBool)
+	}
+	ctx.incIndex()
+	return Opcodes{code}
+}
+
+type BytesCode struct {
+	typ   *runtime.Type
+	isPtr bool
+}
+
+func (c *BytesCode) Kind() CodeKind {
+	return CodeKindBytes
+}
+
+func (c *BytesCode) ToOpcode(ctx *compileContext) Opcodes {
+	var code *Opcode
+	switch {
+	case c.isPtr:
+		code = newOpCode(ctx, c.typ, OpBytesPtr)
+	default:
+		code = newOpCode(ctx, c.typ, OpBytes)
+	}
+	ctx.incIndex()
+	return Opcodes{code}
+}
+
+type SliceCode struct {
+	typ   *runtime.Type
+	value Code
+}
+
+func (c *SliceCode) Kind() CodeKind {
+	return CodeKindSlice
+}
+
+func (c *SliceCode) ToOpcode(ctx *compileContext) Opcodes {
+	// header => opcode => elem => end
+	//             ^        |
+	//             |________|
+	size := c.typ.Elem().Size()
+	header := newSliceHeaderCode(ctx, c.typ)
+	ctx.incIndex()
+
+	ctx.incIndent()
+	codes := c.value.ToOpcode(ctx)
+	ctx.decIndent()
+
+	codes.First().Flags |= IndirectFlags
+	elemCode := newSliceElemCode(ctx, c.typ.Elem(), header, size)
+	ctx.incIndex()
+	end := newOpCode(ctx, c.typ, OpSliceEnd)
+	ctx.incIndex()
+	header.End = end
+	header.Next = codes.First()
+	codes.Last().Next = elemCode
+	elemCode.Next = codes.First()
+	elemCode.End = end
+	return Opcodes{header}.Add(codes...).Add(elemCode).Add(end)
+}
+
+type ArrayCode struct {
+	typ   *runtime.Type
+	value Code
+}
+
+func (c *ArrayCode) Kind() CodeKind {
+	return CodeKindArray
+}
+
+func (c *ArrayCode) ToOpcode(ctx *compileContext) Opcodes {
+	// header => opcode => elem => end
+	//             ^        |
+	//             |________|
+	elem := c.typ.Elem()
+	alen := c.typ.Len()
+	size := elem.Size()
+
+	header := newArrayHeaderCode(ctx, c.typ, alen)
+	ctx.incIndex()
+
+	ctx.incIndent()
+	codes := c.value.ToOpcode(ctx)
+	ctx.decIndent()
+
+	codes.First().Flags |= IndirectFlags
+
+	elemCode := newArrayElemCode(ctx, elem, header, alen, size)
+	ctx.incIndex()
+
+	end := newOpCode(ctx, c.typ, OpArrayEnd)
+	ctx.incIndex()
+
+	header.End = end
+	header.Next = codes.First()
+	codes.Last().Next = elemCode
+	elemCode.Next = codes.First()
+	elemCode.End = end
+
+	return Opcodes{header}.Add(codes...).Add(elemCode).Add(end)
+}
+
+type MapCode struct {
+	typ   *runtime.Type
+	key   Code
+	value Code
+}
+
+func (c *MapCode) Kind() CodeKind {
+	return CodeKindMap
+}
+
+func (c *MapCode) ToOpcode(ctx *compileContext) Opcodes {
+	// header => code => value => code => key => code => value => code => end
+	//                                     ^                       |
+	//                                     |_______________________|
+	header := newMapHeaderCode(ctx, c.typ)
+	ctx.incIndex()
+
+	keyCodes := c.key.ToOpcode(ctx)
+
+	value := newMapValueCode(ctx, c.typ.Elem(), header)
+	ctx.incIndex()
+
+	ctx.incIndent()
+	valueCodes := c.value.ToOpcode(ctx)
+	ctx.decIndent()
+
+	valueCodes.First().Flags |= IndirectFlags
+
+	key := newMapKeyCode(ctx, c.typ.Key(), header)
+	ctx.incIndex()
+
+	end := newMapEndCode(ctx, c.typ, header)
+	ctx.incIndex()
+
+	header.Next = keyCodes.First()
+	keyCodes.Last().Next = value
+	value.Next = valueCodes.First()
+	valueCodes.Last().Next = key
+	key.Next = keyCodes.First()
+
+	header.End = end
+	key.End = end
+	value.End = end
+	return Opcodes{header}.Add(keyCodes...).Add(value).Add(valueCodes...).Add(key).Add(end)
+}
+
+type StructCode struct {
+	typ                       *runtime.Type
+	fields                    []*StructFieldCode
+	isPtr                     bool
+	disableIndirectConversion bool
+	isIndirect                bool
+	isRecursive               bool
+}
+
+func (c *StructCode) Kind() CodeKind {
+	return CodeKindStruct
+}
+
+func (c *StructCode) lastFieldCode(field *StructFieldCode, firstField *Opcode) *Opcode {
+	if field.isAnonymous {
+		return c.lastAnonymousFieldCode(firstField)
+	}
+	lastField := firstField
+	for lastField.NextField != nil {
+		lastField = lastField.NextField
+	}
+	return lastField
+}
+
+func (c *StructCode) lastAnonymousFieldCode(firstField *Opcode) *Opcode {
+	// firstField is special StructHead operation for anonymous structure.
+	// So, StructHead's next operation is truly struct head operation.
+	lastField := firstField.Next
+	for lastField.NextField != nil {
+		lastField = lastField.NextField
+	}
+	return lastField
+}
+
+func (c *StructCode) ToOpcode(ctx *compileContext) Opcodes {
+	// header => code => structField => code => end
+	//                        ^          |
+	//                        |__________|
+	if c.isRecursive {
+		recursive := newRecursiveCode(ctx, c.typ, &CompiledCode{})
+		recursive.Type = c.typ
+		ctx.incIndex()
+		*ctx.recursiveCodes = append(*ctx.recursiveCodes, recursive)
+		return Opcodes{recursive}
+	}
+	codes := Opcodes{}
+	var prevField *Opcode
+	ctx.incIndent()
+	for idx, field := range c.fields {
+		isFirstField := idx == 0
+		isEndField := idx == len(c.fields)-1
+		fieldCodes := field.ToOpcode(ctx, isFirstField, isEndField)
+		for _, code := range fieldCodes {
+			if c.isIndirect {
+				code.Flags |= IndirectFlags
+			}
+		}
+		firstField := fieldCodes.First()
+		if len(codes) > 0 {
+			codes.Last().Next = firstField
+			firstField.Idx = codes.First().Idx
+		}
+		if prevField != nil {
+			prevField.NextField = firstField
+		}
+		if isEndField {
+			endField := fieldCodes.Last()
+			if len(codes) > 0 {
+				codes.First().End = endField
+			} else if field.isAnonymous {
+				firstField.End = endField
+				lastField := c.lastAnonymousFieldCode(firstField)
+				lastField.NextField = endField
+			} else {
+				firstField.End = endField
+			}
+			codes = codes.Add(fieldCodes...)
+			break
+		}
+		prevField = c.lastFieldCode(field, firstField)
+		codes = codes.Add(fieldCodes...)
+	}
+	if len(codes) == 0 {
+		head := &Opcode{
+			Op:         OpStructHead,
+			Idx:        opcodeOffset(ctx.ptrIndex),
+			Type:       c.typ,
+			DisplayIdx: ctx.opcodeIndex,
+			Indent:     ctx.indent,
+		}
+		ctx.incOpcodeIndex()
+		end := &Opcode{
+			Op:         OpStructEnd,
+			Idx:        opcodeOffset(ctx.ptrIndex),
+			DisplayIdx: ctx.opcodeIndex,
+			Indent:     ctx.indent,
+		}
+		head.NextField = end
+		head.Next = end
+		head.End = end
+		codes = codes.Add(head, end)
+		ctx.incIndex()
+	}
+	ctx.decIndent()
+	ctx.structTypeToCodes[uintptr(unsafe.Pointer(c.typ))] = codes
+	return codes
+}
+
+func (c *StructCode) ToAnonymousOpcode(ctx *compileContext) Opcodes {
+	// header => code => structField => code => end
+	//                        ^          |
+	//                        |__________|
+	if c.isRecursive {
+		recursive := newRecursiveCode(ctx, c.typ, &CompiledCode{})
+		recursive.Type = c.typ
+		ctx.incIndex()
+		*ctx.recursiveCodes = append(*ctx.recursiveCodes, recursive)
+		return Opcodes{recursive}
+	}
+	codes := Opcodes{}
+	var prevField *Opcode
+	for idx, field := range c.fields {
+		isFirstField := idx == 0
+		isEndField := idx == len(c.fields)-1
+		fieldCodes := field.ToAnonymousOpcode(ctx, isFirstField, isEndField)
+		for _, code := range fieldCodes {
+			if c.isIndirect {
+				code.Flags |= IndirectFlags
+			}
+		}
+		firstField := fieldCodes.First()
+		if len(codes) > 0 {
+			codes.Last().Next = firstField
+			firstField.Idx = codes.First().Idx
+		}
+		if prevField != nil {
+			prevField.NextField = firstField
+		}
+		if isEndField {
+			lastField := fieldCodes.Last()
+			if len(codes) > 0 {
+				codes.First().End = lastField
+			} else {
+				firstField.End = lastField
+			}
+		}
+		prevField = firstField
+		codes = codes.Add(fieldCodes...)
+	}
+	return codes
+}
+
+func (c *StructCode) removeFieldsByTags(tags runtime.StructTags) {
+	fields := make([]*StructFieldCode, 0, len(c.fields))
+	for _, field := range c.fields {
+		if field.isAnonymous {
+			structCode := field.getAnonymousStruct()
+			if structCode != nil && !structCode.isRecursive {
+				structCode.removeFieldsByTags(tags)
+				if len(structCode.fields) > 0 {
+					fields = append(fields, field)
+				}
+				continue
+			}
+		}
+		if tags.ExistsKey(field.key) {
+			continue
+		}
+		fields = append(fields, field)
+	}
+	c.fields = fields
+}
+
+func (c *StructCode) enableIndirect() {
+	if c.isIndirect {
+		return
+	}
+	c.isIndirect = true
+	if len(c.fields) == 0 {
+		return
+	}
+	structCode := c.fields[0].getStruct()
+	if structCode == nil {
+		return
+	}
+	structCode.enableIndirect()
+}
+
+type StructFieldCode struct {
+	typ                *runtime.Type
+	key                string
+	tag                *runtime.StructTag
+	value              Code
+	offset             uintptr
+	isAnonymous        bool
+	isTaggedKey        bool
+	isNilableType      bool
+	isNilCheck         bool
+	isAddrForMarshaler bool
+	isNextOpPtrType    bool
+}
+
+func (c *StructFieldCode) getStruct() *StructCode {
+	value := c.value
+	ptr, ok := value.(*PtrCode)
+	if ok {
+		value = ptr.value
+	}
+	structCode, ok := value.(*StructCode)
+	if ok {
+		return structCode
+	}
+	return nil
+}
+
+func (c *StructFieldCode) getAnonymousStruct() *StructCode {
+	if !c.isAnonymous {
+		return nil
+	}
+	return c.getStruct()
+}
+
+func optimizeStructHeader(code *Opcode, tag *runtime.StructTag) OpType {
+	headType := code.ToHeaderType(tag.IsString)
+	if tag.IsOmitEmpty {
+		headType = headType.HeadToOmitEmptyHead()
+	}
+	return headType
+}
+
+func optimizeStructField(code *Opcode, tag *runtime.StructTag) OpType {
+	fieldType := code.ToFieldType(tag.IsString)
+	if tag.IsOmitEmpty {
+		fieldType = fieldType.FieldToOmitEmptyField()
+	}
+	return fieldType
+}
+
+func (c *StructFieldCode) headerOpcodes(ctx *compileContext, field *Opcode, valueCodes Opcodes) Opcodes {
+	value := valueCodes.First()
+	op := optimizeStructHeader(value, c.tag)
+	field.Op = op
+	field.NumBitSize = value.NumBitSize
+	field.PtrNum = value.PtrNum
+	fieldCodes := Opcodes{field}
+	if op.IsMultipleOpHead() {
+		field.Next = value
+		fieldCodes = fieldCodes.Add(valueCodes...)
+	} else {
+		ctx.decIndex()
+	}
+	return fieldCodes
+}
+
+func (c *StructFieldCode) fieldOpcodes(ctx *compileContext, field *Opcode, valueCodes Opcodes) Opcodes {
+	value := valueCodes.First()
+	op := optimizeStructField(value, c.tag)
+	field.Op = op
+	field.NumBitSize = value.NumBitSize
+	field.PtrNum = value.PtrNum
+
+	fieldCodes := Opcodes{field}
+	if op.IsMultipleOpField() {
+		field.Next = value
+		fieldCodes = fieldCodes.Add(valueCodes...)
+	} else {
+		ctx.decIndex()
+	}
+	return fieldCodes
+}
+
+func (c *StructFieldCode) addStructEndCode(ctx *compileContext, codes Opcodes) Opcodes {
+	end := &Opcode{
+		Op:         OpStructEnd,
+		Idx:        opcodeOffset(ctx.ptrIndex),
+		DisplayIdx: ctx.opcodeIndex,
+		Indent:     ctx.indent,
+	}
+	codes.Last().Next = end
+	codes.First().NextField = end
+	codes = codes.Add(end)
+	ctx.incOpcodeIndex()
+	return codes
+}
+
+func (c *StructFieldCode) structKey(ctx *compileContext) string {
+	if ctx.escapeKey {
+		rctx := &RuntimeContext{Option: &Option{Flag: HTMLEscapeOption}}
+		return fmt.Sprintf(`%s:`, string(AppendString(rctx, []byte{}, c.key)))
+	}
+	return fmt.Sprintf(`"%s":`, c.key)
+}
+
+func (c *StructFieldCode) flags() OpFlags {
+	var flags OpFlags
+	if c.isTaggedKey {
+		flags |= IsTaggedKeyFlags
+	}
+	if c.isNilableType {
+		flags |= IsNilableTypeFlags
+	}
+	if c.isNilCheck {
+		flags |= NilCheckFlags
+	}
+	if c.isAddrForMarshaler {
+		flags |= AddrForMarshalerFlags
+	}
+	if c.isNextOpPtrType {
+		flags |= IsNextOpPtrTypeFlags
+	}
+	if c.isAnonymous {
+		flags |= AnonymousKeyFlags
+	}
+	return flags
+}
+
+func (c *StructFieldCode) toValueOpcodes(ctx *compileContext) Opcodes {
+	if c.isAnonymous {
+		anonymCode, ok := c.value.(AnonymousCode)
+		if ok {
+			return anonymCode.ToAnonymousOpcode(ctx)
+		}
+	}
+	return c.value.ToOpcode(ctx)
+}
+
+func (c *StructFieldCode) ToOpcode(ctx *compileContext, isFirstField, isEndField bool) Opcodes {
+	field := &Opcode{
+		Idx:        opcodeOffset(ctx.ptrIndex),
+		Flags:      c.flags(),
+		Key:        c.structKey(ctx),
+		Offset:     uint32(c.offset),
+		Type:       c.typ,
+		DisplayIdx: ctx.opcodeIndex,
+		Indent:     ctx.indent,
+		DisplayKey: c.key,
+	}
+	ctx.incIndex()
+	valueCodes := c.toValueOpcodes(ctx)
+	if isFirstField {
+		codes := c.headerOpcodes(ctx, field, valueCodes)
+		if isEndField {
+			codes = c.addStructEndCode(ctx, codes)
+		}
+		return codes
+	}
+	codes := c.fieldOpcodes(ctx, field, valueCodes)
+	if isEndField {
+		if isEnableStructEndOptimization(c.value) {
+			field.Op = field.Op.FieldToEnd()
+		} else {
+			codes = c.addStructEndCode(ctx, codes)
+		}
+	}
+	return codes
+}
+
+func (c *StructFieldCode) ToAnonymousOpcode(ctx *compileContext, isFirstField, isEndField bool) Opcodes {
+	field := &Opcode{
+		Idx:        opcodeOffset(ctx.ptrIndex),
+		Flags:      c.flags() | AnonymousHeadFlags,
+		Key:        c.structKey(ctx),
+		Offset:     uint32(c.offset),
+		Type:       c.typ,
+		DisplayIdx: ctx.opcodeIndex,
+		Indent:     ctx.indent,
+		DisplayKey: c.key,
+	}
+	ctx.incIndex()
+	valueCodes := c.toValueOpcodes(ctx)
+	if isFirstField {
+		return c.headerOpcodes(ctx, field, valueCodes)
+	}
+	return c.fieldOpcodes(ctx, field, valueCodes)
+}
+
+func isEnableStructEndOptimization(value Code) bool {
+	switch value.Kind() {
+	case CodeKindInt,
+		CodeKindUint,
+		CodeKindFloat,
+		CodeKindString,
+		CodeKindBool,
+		CodeKindBytes:
+		return true
+	case CodeKindPtr:
+		return isEnableStructEndOptimization(value.(*PtrCode).value)
+	default:
+		return false
+	}
+}
+
+type InterfaceCode struct {
+	typ   *runtime.Type
+	isPtr bool
+}
+
+func (c *InterfaceCode) Kind() CodeKind {
+	return CodeKindInterface
+}
+
+func (c *InterfaceCode) ToOpcode(ctx *compileContext) Opcodes {
+	var code *Opcode
+	switch {
+	case c.isPtr:
+		code = newOpCode(ctx, c.typ, OpInterfacePtr)
+	default:
+		code = newOpCode(ctx, c.typ, OpInterface)
+	}
+	if c.typ.NumMethod() > 0 {
+		code.Flags |= NonEmptyInterfaceFlags
+	}
+	ctx.incIndex()
+	return Opcodes{code}
+}
+
+type MarshalJSONCode struct {
+	typ                *runtime.Type
+	isAddrForMarshaler bool
+	isNilableType      bool
+	isMarshalerContext bool
+}
+
+func (c *MarshalJSONCode) Kind() CodeKind {
+	return CodeKindMarshalJSON
+}
+
+func (c *MarshalJSONCode) ToOpcode(ctx *compileContext) Opcodes {
+	code := newOpCode(ctx, c.typ, OpMarshalJSON)
+	if c.isAddrForMarshaler {
+		code.Flags |= AddrForMarshalerFlags
+	}
+	if c.isMarshalerContext {
+		code.Flags |= MarshalerContextFlags
+	}
+	if c.isNilableType {
+		code.Flags |= IsNilableTypeFlags
+	} else {
+		code.Flags &= ^IsNilableTypeFlags
+	}
+	ctx.incIndex()
+	return Opcodes{code}
+}
+
+type MarshalTextCode struct {
+	typ                *runtime.Type
+	isAddrForMarshaler bool
+	isNilableType      bool
+}
+
+func (c *MarshalTextCode) Kind() CodeKind {
+	return CodeKindMarshalText
+}
+
+func (c *MarshalTextCode) ToOpcode(ctx *compileContext) Opcodes {
+	code := newOpCode(ctx, c.typ, OpMarshalText)
+	if c.isAddrForMarshaler {
+		code.Flags |= AddrForMarshalerFlags
+	}
+	if c.isNilableType {
+		code.Flags |= IsNilableTypeFlags
+	} else {
+		code.Flags &= ^IsNilableTypeFlags
+	}
+	ctx.incIndex()
+	return Opcodes{code}
+}
+
+type PtrCode struct {
+	typ    *runtime.Type
+	value  Code
+	ptrNum uint8
+}
+
+func (c *PtrCode) Kind() CodeKind {
+	return CodeKindPtr
+}
+
+func (c *PtrCode) ToOpcode(ctx *compileContext) Opcodes {
+	codes := c.value.ToOpcode(ctx)
+	codes.First().Op = convertPtrOp(codes.First())
+	codes.First().PtrNum = c.ptrNum
+	return codes
+}
+
+func (c *PtrCode) ToAnonymousOpcode(ctx *compileContext) Opcodes {
+	var codes Opcodes
+	anonymCode, ok := c.value.(AnonymousCode)
+	if ok {
+		codes = anonymCode.ToAnonymousOpcode(ctx)
+	} else {
+		codes = c.value.ToOpcode(ctx)
+	}
+	codes.First().Op = convertPtrOp(codes.First())
+	codes.First().PtrNum = c.ptrNum
+	return codes
+}
+
+func convertPtrOp(code *Opcode) OpType {
+	ptrHeadOp := code.Op.HeadToPtrHead()
+	if code.Op != ptrHeadOp {
+		if code.PtrNum > 0 {
+			// ptr field and ptr head
+			code.PtrNum--
+		}
+		return ptrHeadOp
+	}
+	switch code.Op {
+	case OpInt:
+		return OpIntPtr
+	case OpUint:
+		return OpUintPtr
+	case OpFloat32:
+		return OpFloat32Ptr
+	case OpFloat64:
+		return OpFloat64Ptr
+	case OpString:
+		return OpStringPtr
+	case OpBool:
+		return OpBoolPtr
+	case OpBytes:
+		return OpBytesPtr
+	case OpNumber:
+		return OpNumberPtr
+	case OpArray:
+		return OpArrayPtr
+	case OpSlice:
+		return OpSlicePtr
+	case OpMap:
+		return OpMapPtr
+	case OpMarshalJSON:
+		return OpMarshalJSONPtr
+	case OpMarshalText:
+		return OpMarshalTextPtr
+	case OpInterface:
+		return OpInterfacePtr
+	case OpRecursive:
+		return OpRecursivePtr
+	}
+	return code.Op
+}
diff --git a/internal/encoder/compiler.go b/internal/encoder/compiler.go
index 9fcec21..cbcdb65 100644
--- a/internal/encoder/compiler.go
+++ b/internal/encoder/compiler.go
@@ -4,9 +4,7 @@ import (
 	"context"
 	"encoding"
 	"encoding/json"
-	"fmt"
 	"reflect"
-	"strings"
 	"sync/atomic"
 	"unsafe"
 
@@ -57,25 +55,43 @@ func compileToGetCodeSetSlowPath(typeptr uintptr) (*OpcodeSet, error) {
 	if codeSet, exists := opcodeMap[typeptr]; exists {
 		return codeSet, nil
 	}
+	codeSet, err := newCompiler().compile(typeptr)
+	if err != nil {
+		return nil, err
+	}
+	storeOpcodeSet(typeptr, codeSet, opcodeMap)
+	return codeSet, nil
+}
 
-	// noescape trick for header.typ ( reflect.*rtype )
-	copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
+type Compiler struct {
+	structTypeToCode map[uintptr]*StructCode
+}
 
-	noescapeKeyCode, err := compileHead(&compileContext{
-		typ:                      copiedType,
-		structTypeToCompiledCode: map[uintptr]*CompiledCode{},
-	})
+func newCompiler() *Compiler {
+	return &Compiler{
+		structTypeToCode: map[uintptr]*StructCode{},
+	}
+}
+
+func (c *Compiler) compile(typeptr uintptr) (*OpcodeSet, error) {
+	// noescape trick for header.typ ( reflect.*rtype )
+	typ := *(**runtime.Type)(unsafe.Pointer(&typeptr))
+	code, err := c.typeToCode(typ)
 	if err != nil {
 		return nil, err
 	}
-	escapeKeyCode, err := compileHead(&compileContext{
-		typ:                      copiedType,
-		structTypeToCompiledCode: map[uintptr]*CompiledCode{},
-		escapeKey:                true,
-	})
-	if err != nil {
+	noescapeKeyCode := c.codeToOpcode(&compileContext{
+		structTypeToCodes: map[uintptr]Opcodes{},
+		recursiveCodes:    &Opcodes{},
+	}, typ, code)
+	if err := noescapeKeyCode.Validate(); err != nil {
 		return nil, err
 	}
+	escapeKeyCode := c.codeToOpcode(&compileContext{
+		structTypeToCodes: map[uintptr]Opcodes{},
+		recursiveCodes:    &Opcodes{},
+		escapeKey:         true,
+	}, typ, code)
 	noescapeKeyCode = copyOpcode(noescapeKeyCode)
 	escapeKeyCode = copyOpcode(escapeKeyCode)
 	setTotalLengthToInterfaceOp(noescapeKeyCode)
@@ -83,26 +99,23 @@ func compileToGetCodeSetSlowPath(typeptr uintptr) (*OpcodeSet, error) {
 	interfaceNoescapeKeyCode := copyToInterfaceOpcode(noescapeKeyCode)
 	interfaceEscapeKeyCode := copyToInterfaceOpcode(escapeKeyCode)
 	codeLength := noescapeKeyCode.TotalLength()
-	codeSet := &OpcodeSet{
-		Type:                     copiedType,
+	return &OpcodeSet{
+		Type:                     typ,
 		NoescapeKeyCode:          noescapeKeyCode,
 		EscapeKeyCode:            escapeKeyCode,
 		InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
 		InterfaceEscapeKeyCode:   interfaceEscapeKeyCode,
 		CodeLength:               codeLength,
 		EndCode:                  ToEndCode(interfaceNoescapeKeyCode),
-	}
-	storeOpcodeSet(typeptr, codeSet, opcodeMap)
-	return codeSet, nil
+	}, nil
 }
 
-func compileHead(ctx *compileContext) (*Opcode, error) {
-	typ := ctx.typ
+func (c *Compiler) typeToCode(typ *runtime.Type) (Code, error) {
 	switch {
-	case implementsMarshalJSON(typ):
-		return compileMarshalJSON(ctx)
-	case implementsMarshalText(typ):
-		return compileMarshalText(ctx)
+	case c.implementsMarshalJSON(typ):
+		return c.marshalJSONCode(typ)
+	case c.implementsMarshalText(typ):
+		return c.marshalTextCode(typ)
 	}
 
 	isPtr := false
@@ -112,1189 +125,654 @@ func compileHead(ctx *compileContext) (*Opcode, error) {
 		isPtr = true
 	}
 	switch {
-	case implementsMarshalJSON(typ):
-		return compileMarshalJSON(ctx)
-	case implementsMarshalText(typ):
-		return compileMarshalText(ctx)
+	case c.implementsMarshalJSON(typ):
+		return c.marshalJSONCode(orgType)
+	case c.implementsMarshalText(typ):
+		return c.marshalTextCode(orgType)
 	}
 	switch typ.Kind() {
 	case reflect.Slice:
-		ctx := ctx.withType(typ)
 		elem := typ.Elem()
 		if elem.Kind() == reflect.Uint8 {
 			p := runtime.PtrTo(elem)
-			if !implementsMarshalJSONType(p) && !p.Implements(marshalTextType) {
-				if isPtr {
-					return compileBytesPtr(ctx)
-				}
-				return compileBytes(ctx)
+			if !c.implementsMarshalJSONType(p) && !p.Implements(marshalTextType) {
+				return c.bytesCode(typ, isPtr)
 			}
 		}
-		code, err := compileSlice(ctx)
-		if err != nil {
-			return nil, err
-		}
-		optimizeStructEnd(code)
-		linkRecursiveCode(code)
-		return code, nil
+		return c.sliceCode(typ)
 	case reflect.Map:
 		if isPtr {
-			return compilePtr(ctx.withType(runtime.PtrTo(typ)))
+			return c.ptrCode(runtime.PtrTo(typ))
 		}
-		code, err := compileMap(ctx.withType(typ))
-		if err != nil {
-			return nil, err
-		}
-		optimizeStructEnd(code)
-		linkRecursiveCode(code)
-		return code, nil
+		return c.mapCode(typ)
 	case reflect.Struct:
-		code, err := compileStruct(ctx.withType(typ), isPtr)
-		if err != nil {
-			return nil, err
-		}
-		optimizeStructEnd(code)
-		linkRecursiveCode(code)
-		return code, nil
+		return c.structCode(typ, isPtr)
 	case reflect.Int:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileIntPtr(ctx)
-		}
-		return compileInt(ctx)
+		return c.intCode(typ, isPtr)
 	case reflect.Int8:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileInt8Ptr(ctx)
-		}
-		return compileInt8(ctx)
+		return c.int8Code(typ, isPtr)
 	case reflect.Int16:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileInt16Ptr(ctx)
-		}
-		return compileInt16(ctx)
+		return c.int16Code(typ, isPtr)
 	case reflect.Int32:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileInt32Ptr(ctx)
-		}
-		return compileInt32(ctx)
+		return c.int32Code(typ, isPtr)
 	case reflect.Int64:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileInt64Ptr(ctx)
-		}
-		return compileInt64(ctx)
+		return c.int64Code(typ, isPtr)
 	case reflect.Uint, reflect.Uintptr:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileUintPtr(ctx)
-		}
-		return compileUint(ctx)
+		return c.uintCode(typ, isPtr)
 	case reflect.Uint8:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileUint8Ptr(ctx)
-		}
-		return compileUint8(ctx)
+		return c.uint8Code(typ, isPtr)
 	case reflect.Uint16:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileUint16Ptr(ctx)
-		}
-		return compileUint16(ctx)
+		return c.uint16Code(typ, isPtr)
 	case reflect.Uint32:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileUint32Ptr(ctx)
-		}
-		return compileUint32(ctx)
+		return c.uint32Code(typ, isPtr)
 	case reflect.Uint64:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileUint64Ptr(ctx)
-		}
-		return compileUint64(ctx)
+		return c.uint64Code(typ, isPtr)
 	case reflect.Float32:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileFloat32Ptr(ctx)
-		}
-		return compileFloat32(ctx)
+		return c.float32Code(typ, isPtr)
 	case reflect.Float64:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileFloat64Ptr(ctx)
-		}
-		return compileFloat64(ctx)
+		return c.float64Code(typ, isPtr)
 	case reflect.String:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileStringPtr(ctx)
-		}
-		return compileString(ctx)
+		return c.stringCode(typ, isPtr)
 	case reflect.Bool:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileBoolPtr(ctx)
-		}
-		return compileBool(ctx)
+		return c.boolCode(typ, isPtr)
 	case reflect.Interface:
-		ctx := ctx.withType(typ)
-		if isPtr {
-			return compileInterfacePtr(ctx)
-		}
-		return compileInterface(ctx)
+		return c.interfaceCode(typ, isPtr)
 	default:
 		if isPtr && typ.Implements(marshalTextType) {
 			typ = orgType
 		}
-		code, err := compile(ctx.withType(typ), isPtr)
-		if err != nil {
-			return nil, err
-		}
-		optimizeStructEnd(code)
-		linkRecursiveCode(code)
-		return code, nil
-	}
-}
-
-func linkRecursiveCode(c *Opcode) {
-	for code := c; code.Op != OpEnd && code.Op != OpRecursiveEnd; {
-		switch code.Op {
-		case OpRecursive, OpRecursivePtr:
-			if code.Jmp.Linked {
-				code = code.Next
-				continue
-			}
-			code.Jmp.Code = copyOpcode(code.Jmp.Code)
-
-			c := code.Jmp.Code
-			c.End.Next = newEndOp(&compileContext{})
-			c.Op = c.Op.PtrHeadToHead()
-
-			beforeLastCode := c.End
-			lastCode := beforeLastCode.Next
-
-			lastCode.Idx = beforeLastCode.Idx + uintptrSize
-			lastCode.ElemIdx = lastCode.Idx + uintptrSize
-			lastCode.Length = lastCode.Idx + 2*uintptrSize
-
-			// extend length to alloc slot for elemIdx + length
-			totalLength := uintptr(code.TotalLength() + 3)
-			nextTotalLength := uintptr(c.TotalLength() + 3)
-
-			c.End.Next.Op = OpRecursiveEnd
-
-			code.Jmp.CurLen = totalLength
-			code.Jmp.NextLen = nextTotalLength
-			code.Jmp.Linked = true
-
-			linkRecursiveCode(code.Jmp.Code)
-
-			code = code.Next
-			continue
-		}
-		switch code.Op.CodeType() {
-		case CodeArrayElem, CodeSliceElem, CodeMapKey:
-			code = code.End
-		default:
-			code = code.Next
-		}
-	}
-}
-
-func optimizeStructEnd(c *Opcode) {
-	for code := c; code.Op != OpEnd; {
-		if code.Op == OpRecursive || code.Op == OpRecursivePtr {
-			// ignore if exists recursive operation
-			return
-		}
-		switch code.Op.CodeType() {
-		case CodeArrayElem, CodeSliceElem, CodeMapKey:
-			code = code.End
-		default:
-			code = code.Next
-		}
-	}
-
-	for code := c; code.Op != OpEnd; {
-		switch code.Op.CodeType() {
-		case CodeArrayElem, CodeSliceElem, CodeMapKey:
-			code = code.End
-		case CodeStructEnd:
-			switch code.Op {
-			case OpStructEnd:
-				prev := code.PrevField
-				prevOp := prev.Op.String()
-				if strings.Contains(prevOp, "Head") ||
-					strings.Contains(prevOp, "Slice") ||
-					strings.Contains(prevOp, "Array") ||
-					strings.Contains(prevOp, "Map") ||
-					strings.Contains(prevOp, "MarshalJSON") ||
-					strings.Contains(prevOp, "MarshalText") {
-					// not exists field
-					code = code.Next
-					break
-				}
-				if prev.Op != prev.Op.FieldToEnd() {
-					prev.Op = prev.Op.FieldToEnd()
-					prev.Next = code.Next
-				}
-				code = code.Next
-			default:
-				code = code.Next
-			}
-		default:
-			code = code.Next
-		}
+		return c.typeToCodeWithPtr(typ, isPtr)
 	}
 }
 
-func implementsMarshalJSON(typ *runtime.Type) bool {
-	if !implementsMarshalJSONType(typ) {
-		return false
-	}
-	if typ.Kind() != reflect.Ptr {
-		return true
-	}
-	// type kind is reflect.Ptr
-	if !implementsMarshalJSONType(typ.Elem()) {
-		return true
-	}
-	// needs to dereference
-	return false
-}
-
-func implementsMarshalText(typ *runtime.Type) bool {
-	if !typ.Implements(marshalTextType) {
-		return false
-	}
-	if typ.Kind() != reflect.Ptr {
-		return true
-	}
-	// type kind is reflect.Ptr
-	if !typ.Elem().Implements(marshalTextType) {
-		return true
-	}
-	// needs to dereference
-	return false
-}
-
-func compile(ctx *compileContext, isPtr bool) (*Opcode, error) {
-	typ := ctx.typ
+func (c *Compiler) typeToCodeWithPtr(typ *runtime.Type, isPtr bool) (Code, error) {
 	switch {
-	case implementsMarshalJSON(typ):
-		return compileMarshalJSON(ctx)
-	case implementsMarshalText(typ):
-		return compileMarshalText(ctx)
+	case c.implementsMarshalJSON(typ):
+		return c.marshalJSONCode(typ)
+	case c.implementsMarshalText(typ):
+		return c.marshalTextCode(typ)
 	}
 	switch typ.Kind() {
 	case reflect.Ptr:
-		return compilePtr(ctx)
+		return c.ptrCode(typ)
 	case reflect.Slice:
 		elem := typ.Elem()
 		if elem.Kind() == reflect.Uint8 {
 			p := runtime.PtrTo(elem)
-			if !implementsMarshalJSONType(p) && !p.Implements(marshalTextType) {
-				return compileBytes(ctx)
+			if !c.implementsMarshalJSONType(p) && !p.Implements(marshalTextType) {
+				return c.bytesCode(typ, false)
 			}
 		}
-		return compileSlice(ctx)
+		return c.sliceCode(typ)
 	case reflect.Array:
-		return compileArray(ctx)
+		return c.arrayCode(typ)
 	case reflect.Map:
-		return compileMap(ctx)
+		return c.mapCode(typ)
 	case reflect.Struct:
-		return compileStruct(ctx, isPtr)
+		return c.structCode(typ, isPtr)
 	case reflect.Interface:
-		return compileInterface(ctx)
+		return c.interfaceCode(typ, false)
 	case reflect.Int:
-		return compileInt(ctx)
+		return c.intCode(typ, false)
 	case reflect.Int8:
-		return compileInt8(ctx)
+		return c.int8Code(typ, false)
 	case reflect.Int16:
-		return compileInt16(ctx)
+		return c.int16Code(typ, false)
 	case reflect.Int32:
-		return compileInt32(ctx)
+		return c.int32Code(typ, false)
 	case reflect.Int64:
-		return compileInt64(ctx)
+		return c.int64Code(typ, false)
 	case reflect.Uint:
-		return compileUint(ctx)
+		return c.uintCode(typ, false)
 	case reflect.Uint8:
-		return compileUint8(ctx)
+		return c.uint8Code(typ, false)
 	case reflect.Uint16:
-		return compileUint16(ctx)
+		return c.uint16Code(typ, false)
 	case reflect.Uint32:
-		return compileUint32(ctx)
+		return c.uint32Code(typ, false)
 	case reflect.Uint64:
-		return compileUint64(ctx)
+		return c.uint64Code(typ, false)
 	case reflect.Uintptr:
-		return compileUint(ctx)
+		return c.uintCode(typ, false)
 	case reflect.Float32:
-		return compileFloat32(ctx)
+		return c.float32Code(typ, false)
 	case reflect.Float64:
-		return compileFloat64(ctx)
+		return c.float64Code(typ, false)
 	case reflect.String:
-		return compileString(ctx)
+		return c.stringCode(typ, false)
 	case reflect.Bool:
-		return compileBool(ctx)
+		return c.boolCode(typ, false)
 	}
 	return nil, &errors.UnsupportedTypeError{Type: runtime.RType2Type(typ)}
 }
 
-func convertPtrOp(code *Opcode) OpType {
-	ptrHeadOp := code.Op.HeadToPtrHead()
-	if code.Op != ptrHeadOp {
-		if code.PtrNum > 0 {
-			// ptr field and ptr head
-			code.PtrNum--
-		}
-		return ptrHeadOp
-	}
-	switch code.Op {
-	case OpInt:
-		return OpIntPtr
-	case OpUint:
-		return OpUintPtr
-	case OpFloat32:
-		return OpFloat32Ptr
-	case OpFloat64:
-		return OpFloat64Ptr
-	case OpString:
-		return OpStringPtr
-	case OpBool:
-		return OpBoolPtr
-	case OpBytes:
-		return OpBytesPtr
-	case OpNumber:
-		return OpNumberPtr
-	case OpArray:
-		return OpArrayPtr
-	case OpSlice:
-		return OpSlicePtr
-	case OpMap:
-		return OpMapPtr
-	case OpMarshalJSON:
-		return OpMarshalJSONPtr
-	case OpMarshalText:
-		return OpMarshalTextPtr
-	case OpInterface:
-		return OpInterfacePtr
-	case OpRecursive:
-		return OpRecursivePtr
-	}
-	return code.Op
-}
-
-func compileKey(ctx *compileContext) (*Opcode, error) {
-	typ := ctx.typ
-	switch {
-	case implementsMarshalJSON(typ):
-		return compileMarshalJSON(ctx)
-	case implementsMarshalText(typ):
-		return compileMarshalText(ctx)
-	}
-	switch typ.Kind() {
-	case reflect.Ptr:
-		return compilePtr(ctx)
-	case reflect.String:
-		return compileString(ctx)
-	case reflect.Int:
-		return compileIntString(ctx)
-	case reflect.Int8:
-		return compileInt8String(ctx)
-	case reflect.Int16:
-		return compileInt16String(ctx)
-	case reflect.Int32:
-		return compileInt32String(ctx)
-	case reflect.Int64:
-		return compileInt64String(ctx)
-	case reflect.Uint:
-		return compileUintString(ctx)
-	case reflect.Uint8:
-		return compileUint8String(ctx)
-	case reflect.Uint16:
-		return compileUint16String(ctx)
-	case reflect.Uint32:
-		return compileUint32String(ctx)
-	case reflect.Uint64:
-		return compileUint64String(ctx)
-	case reflect.Uintptr:
-		return compileUintString(ctx)
-	}
-	return nil, &errors.UnsupportedTypeError{Type: runtime.RType2Type(typ)}
-}
-
-func compilePtr(ctx *compileContext) (*Opcode, error) {
-	code, err := compile(ctx.withType(ctx.typ.Elem()), true)
-	if err != nil {
-		return nil, err
-	}
-	code.Op = convertPtrOp(code)
-	code.PtrNum++
-	return code, nil
-}
-
-func compileMarshalJSON(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpMarshalJSON)
-	typ := ctx.typ
-	if isPtrMarshalJSONType(typ) {
-		code.Flags |= AddrForMarshalerFlags
-	}
-	if typ.Implements(marshalJSONContextType) || runtime.PtrTo(typ).Implements(marshalJSONContextType) {
-		code.Flags |= MarshalerContextFlags
-	}
-	if isNilableType(typ) {
-		code.Flags |= IsNilableTypeFlags
-	} else {
-		code.Flags &= ^IsNilableTypeFlags
-	}
-	ctx.incIndex()
-	return code, nil
-}
-
-func compileMarshalText(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpMarshalText)
-	typ := ctx.typ
-	if !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType) {
-		code.Flags |= AddrForMarshalerFlags
-	}
-	if isNilableType(typ) {
-		code.Flags |= IsNilableTypeFlags
-	} else {
-		code.Flags &= ^IsNilableTypeFlags
-	}
-	ctx.incIndex()
-	return code, nil
-}
-
 const intSize = 32 << (^uint(0) >> 63)
 
-func compileInt(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpInt)
-	code.NumBitSize = intSize
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) intCode(typ *runtime.Type, isPtr bool) (*IntCode, error) {
+	return &IntCode{typ: typ, bitSize: intSize, isPtr: isPtr}, nil
 }
 
-func compileIntPtr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileInt(ctx)
-	if err != nil {
-		return nil, err
-	}
-	code.Op = OpIntPtr
-	return code, nil
+//nolint:unparam
+func (c *Compiler) int8Code(typ *runtime.Type, isPtr bool) (*IntCode, error) {
+	return &IntCode{typ: typ, bitSize: 8, isPtr: isPtr}, nil
 }
 
-func compileInt8(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpInt)
-	code.NumBitSize = 8
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) int16Code(typ *runtime.Type, isPtr bool) (*IntCode, error) {
+	return &IntCode{typ: typ, bitSize: 16, isPtr: isPtr}, nil
 }
 
-func compileInt8Ptr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileInt8(ctx)
-	if err != nil {
-		return nil, err
-	}
-	code.Op = OpIntPtr
-	return code, nil
+//nolint:unparam
+func (c *Compiler) int32Code(typ *runtime.Type, isPtr bool) (*IntCode, error) {
+	return &IntCode{typ: typ, bitSize: 32, isPtr: isPtr}, nil
 }
 
-func compileInt16(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpInt)
-	code.NumBitSize = 16
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) int64Code(typ *runtime.Type, isPtr bool) (*IntCode, error) {
+	return &IntCode{typ: typ, bitSize: 64, isPtr: isPtr}, nil
 }
 
-func compileInt16Ptr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileInt16(ctx)
-	if err != nil {
-		return nil, err
-	}
-	code.Op = OpIntPtr
-	return code, nil
+//nolint:unparam
+func (c *Compiler) uintCode(typ *runtime.Type, isPtr bool) (*UintCode, error) {
+	return &UintCode{typ: typ, bitSize: intSize, isPtr: isPtr}, nil
 }
 
-func compileInt32(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpInt)
-	code.NumBitSize = 32
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) uint8Code(typ *runtime.Type, isPtr bool) (*UintCode, error) {
+	return &UintCode{typ: typ, bitSize: 8, isPtr: isPtr}, nil
 }
 
-func compileInt32Ptr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileInt32(ctx)
-	if err != nil {
-		return nil, err
-	}
-	code.Op = OpIntPtr
-	return code, nil
+//nolint:unparam
+func (c *Compiler) uint16Code(typ *runtime.Type, isPtr bool) (*UintCode, error) {
+	return &UintCode{typ: typ, bitSize: 16, isPtr: isPtr}, nil
 }
 
-func compileInt64(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpInt)
-	code.NumBitSize = 64
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) uint32Code(typ *runtime.Type, isPtr bool) (*UintCode, error) {
+	return &UintCode{typ: typ, bitSize: 32, isPtr: isPtr}, nil
 }
 
-func compileInt64Ptr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileInt64(ctx)
-	if err != nil {
-		return nil, err
-	}
-	code.Op = OpIntPtr
-	return code, nil
+//nolint:unparam
+func (c *Compiler) uint64Code(typ *runtime.Type, isPtr bool) (*UintCode, error) {
+	return &UintCode{typ: typ, bitSize: 64, isPtr: isPtr}, nil
 }
 
-func compileUint(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpUint)
-	code.NumBitSize = intSize
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) float32Code(typ *runtime.Type, isPtr bool) (*FloatCode, error) {
+	return &FloatCode{typ: typ, bitSize: 32, isPtr: isPtr}, nil
 }
 
-func compileUintPtr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileUint(ctx)
-	if err != nil {
-		return nil, err
-	}
-	code.Op = OpUintPtr
-	return code, nil
+//nolint:unparam
+func (c *Compiler) float64Code(typ *runtime.Type, isPtr bool) (*FloatCode, error) {
+	return &FloatCode{typ: typ, bitSize: 64, isPtr: isPtr}, nil
 }
 
-func compileUint8(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpUint)
-	code.NumBitSize = 8
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) stringCode(typ *runtime.Type, isPtr bool) (*StringCode, error) {
+	return &StringCode{typ: typ, isPtr: isPtr}, nil
 }
 
-func compileUint8Ptr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileUint8(ctx)
-	if err != nil {
-		return nil, err
-	}
-	code.Op = OpUintPtr
-	return code, nil
+//nolint:unparam
+func (c *Compiler) boolCode(typ *runtime.Type, isPtr bool) (*BoolCode, error) {
+	return &BoolCode{typ: typ, isPtr: isPtr}, nil
 }
 
-func compileUint16(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpUint)
-	code.NumBitSize = 16
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) intStringCode(typ *runtime.Type) (*IntCode, error) {
+	return &IntCode{typ: typ, bitSize: intSize, isString: true}, nil
 }
 
-func compileUint16Ptr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileUint16(ctx)
-	if err != nil {
-		return nil, err
-	}
-	code.Op = OpUintPtr
-	return code, nil
+//nolint:unparam
+func (c *Compiler) int8StringCode(typ *runtime.Type) (*IntCode, error) {
+	return &IntCode{typ: typ, bitSize: 8, isString: true}, nil
 }
 
-func compileUint32(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpUint)
-	code.NumBitSize = 32
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) int16StringCode(typ *runtime.Type) (*IntCode, error) {
+	return &IntCode{typ: typ, bitSize: 16, isString: true}, nil
 }
 
-func compileUint32Ptr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileUint32(ctx)
-	if err != nil {
-		return nil, err
-	}
-	code.Op = OpUintPtr
-	return code, nil
+//nolint:unparam
+func (c *Compiler) int32StringCode(typ *runtime.Type) (*IntCode, error) {
+	return &IntCode{typ: typ, bitSize: 32, isString: true}, nil
 }
 
-func compileUint64(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpUint)
-	code.NumBitSize = 64
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) int64StringCode(typ *runtime.Type) (*IntCode, error) {
+	return &IntCode{typ: typ, bitSize: 64, isString: true}, nil
 }
 
-func compileUint64Ptr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileUint64(ctx)
-	if err != nil {
-		return nil, err
-	}
-	code.Op = OpUintPtr
-	return code, nil
+//nolint:unparam
+func (c *Compiler) uintStringCode(typ *runtime.Type) (*UintCode, error) {
+	return &UintCode{typ: typ, bitSize: intSize, isString: true}, nil
 }
 
-func compileIntString(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpIntString)
-	code.NumBitSize = intSize
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) uint8StringCode(typ *runtime.Type) (*UintCode, error) {
+	return &UintCode{typ: typ, bitSize: 8, isString: true}, nil
 }
 
-func compileInt8String(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpIntString)
-	code.NumBitSize = 8
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) uint16StringCode(typ *runtime.Type) (*UintCode, error) {
+	return &UintCode{typ: typ, bitSize: 16, isString: true}, nil
 }
 
-func compileInt16String(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpIntString)
-	code.NumBitSize = 16
-	ctx.incIndex()
-	return code, nil
-}
-
-func compileInt32String(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpIntString)
-	code.NumBitSize = 32
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) uint32StringCode(typ *runtime.Type) (*UintCode, error) {
+	return &UintCode{typ: typ, bitSize: 32, isString: true}, nil
 }
 
-func compileInt64String(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpIntString)
-	code.NumBitSize = 64
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) uint64StringCode(typ *runtime.Type) (*UintCode, error) {
+	return &UintCode{typ: typ, bitSize: 64, isString: true}, nil
 }
 
-func compileUintString(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpUintString)
-	code.NumBitSize = intSize
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) bytesCode(typ *runtime.Type, isPtr bool) (*BytesCode, error) {
+	return &BytesCode{typ: typ, isPtr: isPtr}, nil
 }
 
-func compileUint8String(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpUintString)
-	code.NumBitSize = 8
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) interfaceCode(typ *runtime.Type, isPtr bool) (*InterfaceCode, error) {
+	return &InterfaceCode{typ: typ, isPtr: isPtr}, nil
 }
 
-func compileUint16String(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpUintString)
-	code.NumBitSize = 16
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) marshalJSONCode(typ *runtime.Type) (*MarshalJSONCode, error) {
+	return &MarshalJSONCode{
+		typ:                typ,
+		isAddrForMarshaler: c.isPtrMarshalJSONType(typ),
+		isNilableType:      c.isNilableType(typ),
+		isMarshalerContext: typ.Implements(marshalJSONContextType) || runtime.PtrTo(typ).Implements(marshalJSONContextType),
+	}, nil
 }
 
-func compileUint32String(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpUintString)
-	code.NumBitSize = 32
-	ctx.incIndex()
-	return code, nil
-}
-
-func compileUint64String(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpUintString)
-	code.NumBitSize = 64
-	ctx.incIndex()
-	return code, nil
-}
-
-func compileFloat32(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpFloat32)
-	ctx.incIndex()
-	return code, nil
+//nolint:unparam
+func (c *Compiler) marshalTextCode(typ *runtime.Type) (*MarshalTextCode, error) {
+	return &MarshalTextCode{
+		typ:                typ,
+		isAddrForMarshaler: c.isPtrMarshalTextType(typ),
+		isNilableType:      c.isNilableType(typ),
+	}, nil
 }
 
-func compileFloat32Ptr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileFloat32(ctx)
+func (c *Compiler) ptrCode(typ *runtime.Type) (*PtrCode, error) {
+	code, err := c.typeToCodeWithPtr(typ.Elem(), true)
 	if err != nil {
 		return nil, err
 	}
-	code.Op = OpFloat32Ptr
-	return code, nil
-}
-
-func compileFloat64(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpFloat64)
-	ctx.incIndex()
-	return code, nil
-}
-
-func compileFloat64Ptr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileFloat64(ctx)
-	if err != nil {
-		return nil, err
+	ptr, ok := code.(*PtrCode)
+	if ok {
+		return &PtrCode{typ: typ, value: ptr.value, ptrNum: ptr.ptrNum + 1}, nil
 	}
-	code.Op = OpFloat64Ptr
-	return code, nil
+	return &PtrCode{typ: typ, value: code, ptrNum: 1}, nil
 }
 
-func compileString(ctx *compileContext) (*Opcode, error) {
-	var op OpType
-	if ctx.typ == runtime.Type2RType(jsonNumberType) {
-		op = OpNumber
-	} else {
-		op = OpString
-	}
-	code := newOpCode(ctx, op)
-	ctx.incIndex()
-	return code, nil
-}
-
-func compileStringPtr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileString(ctx)
+func (c *Compiler) sliceCode(typ *runtime.Type) (*SliceCode, error) {
+	elem := typ.Elem()
+	code, err := c.listElemCode(elem)
 	if err != nil {
 		return nil, err
 	}
-	if code.Op == OpNumber {
-		code.Op = OpNumberPtr
-	} else {
-		code.Op = OpStringPtr
+	if code.Kind() == CodeKindStruct {
+		structCode := code.(*StructCode)
+		structCode.enableIndirect()
 	}
-	return code, nil
-}
-
-func compileBool(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpBool)
-	ctx.incIndex()
-	return code, nil
+	return &SliceCode{typ: typ, value: code}, nil
 }
 
-func compileBoolPtr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileBool(ctx)
+func (c *Compiler) arrayCode(typ *runtime.Type) (*ArrayCode, error) {
+	elem := typ.Elem()
+	code, err := c.listElemCode(elem)
 	if err != nil {
 		return nil, err
 	}
-	code.Op = OpBoolPtr
-	return code, nil
-}
-
-func compileBytes(ctx *compileContext) (*Opcode, error) {
-	code := newOpCode(ctx, OpBytes)
-	ctx.incIndex()
-	return code, nil
-}
-
-func compileBytesPtr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileBytes(ctx)
-	if err != nil {
-		return nil, err
+	if code.Kind() == CodeKindStruct {
+		structCode := code.(*StructCode)
+		structCode.enableIndirect()
 	}
-	code.Op = OpBytesPtr
-	return code, nil
-}
-
-func compileInterface(ctx *compileContext) (*Opcode, error) {
-	code := newInterfaceCode(ctx)
-	ctx.incIndex()
-	return code, nil
+	return &ArrayCode{typ: typ, value: code}, nil
 }
 
-func compileInterfacePtr(ctx *compileContext) (*Opcode, error) {
-	code, err := compileInterface(ctx)
+func (c *Compiler) mapCode(typ *runtime.Type) (*MapCode, error) {
+	keyCode, err := c.mapKeyCode(typ.Key())
 	if err != nil {
 		return nil, err
 	}
-	code.Op = OpInterfacePtr
-	return code, nil
-}
-
-func compileSlice(ctx *compileContext) (*Opcode, error) {
-	elem := ctx.typ.Elem()
-	size := elem.Size()
-
-	header := newSliceHeaderCode(ctx)
-	ctx.incIndex()
-
-	code, err := compileListElem(ctx.withType(elem).incIndent())
+	valueCode, err := c.mapValueCode(typ.Elem())
 	if err != nil {
 		return nil, err
 	}
-	code.Flags |= IndirectFlags
-
-	// header => opcode => elem => end
-	//             ^        |
-	//             |________|
-
-	elemCode := newSliceElemCode(ctx, header, size)
-	ctx.incIndex()
-
-	end := newOpCode(ctx, OpSliceEnd)
-	ctx.incIndex()
-
-	header.End = end
-	header.Next = code
-	code.BeforeLastCode().Next = (*Opcode)(unsafe.Pointer(elemCode))
-	elemCode.Next = code
-	elemCode.End = end
-	return (*Opcode)(unsafe.Pointer(header)), nil
+	if valueCode.Kind() == CodeKindStruct {
+		structCode := valueCode.(*StructCode)
+		structCode.enableIndirect()
+	}
+	return &MapCode{typ: typ, key: keyCode, value: valueCode}, nil
 }
 
-func compileListElem(ctx *compileContext) (*Opcode, error) {
-	typ := ctx.typ
+func (c *Compiler) listElemCode(typ *runtime.Type) (Code, error) {
 	switch {
-	case isPtrMarshalJSONType(typ):
-		return compileMarshalJSON(ctx)
+	case c.isPtrMarshalJSONType(typ):
+		return c.marshalJSONCode(typ)
 	case !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType):
-		return compileMarshalText(ctx)
+		return c.marshalTextCode(typ)
 	case typ.Kind() == reflect.Map:
-		return compilePtr(ctx.withType(runtime.PtrTo(typ)))
+		return c.ptrCode(runtime.PtrTo(typ))
 	default:
-		code, err := compile(ctx, false)
+		code, err := c.typeToCodeWithPtr(typ, false)
 		if err != nil {
 			return nil, err
 		}
-		if code.Op == OpMapPtr {
-			code.PtrNum++
+		ptr, ok := code.(*PtrCode)
+		if ok {
+			if ptr.value.Kind() == CodeKindMap {
+				ptr.ptrNum++
+			}
 		}
 		return code, nil
 	}
 }
 
-func compileArray(ctx *compileContext) (*Opcode, error) {
-	typ := ctx.typ
-	elem := typ.Elem()
-	alen := typ.Len()
-	size := elem.Size()
-
-	header := newArrayHeaderCode(ctx, alen)
-	ctx.incIndex()
-
-	code, err := compileListElem(ctx.withType(elem).incIndent())
-	if err != nil {
-		return nil, err
-	}
-	code.Flags |= IndirectFlags
-	// header => opcode => elem => end
-	//             ^        |
-	//             |________|
-
-	elemCode := newArrayElemCode(ctx, header, alen, size)
-	ctx.incIndex()
-
-	end := newOpCode(ctx, OpArrayEnd)
-	ctx.incIndex()
-
-	header.End = end
-	header.Next = code
-	code.BeforeLastCode().Next = (*Opcode)(unsafe.Pointer(elemCode))
-	elemCode.Next = code
-	elemCode.End = end
-	return (*Opcode)(unsafe.Pointer(header)), nil
-}
-
-func compileMap(ctx *compileContext) (*Opcode, error) {
-	// header => code => value => code => key => code => value => code => end
-	//                                     ^                       |
-	//                                     |_______________________|
-	ctx = ctx.incIndent()
-	header := newMapHeaderCode(ctx)
-	ctx.incIndex()
-
-	typ := ctx.typ
-	keyType := ctx.typ.Key()
-	keyCode, err := compileKey(ctx.withType(keyType))
-	if err != nil {
-		return nil, err
+func (c *Compiler) mapKeyCode(typ *runtime.Type) (Code, error) {
+	switch {
+	case c.implementsMarshalJSON(typ):
+		return c.marshalJSONCode(typ)
+	case c.implementsMarshalText(typ):
+		return c.marshalTextCode(typ)
 	}
-
-	value := newMapValueCode(ctx, header)
-	ctx.incIndex()
-
-	valueCode, err := compileMapValue(ctx.withType(typ.Elem()))
-	if err != nil {
-		return nil, err
+	switch typ.Kind() {
+	case reflect.Ptr:
+		return c.ptrCode(typ)
+	case reflect.String:
+		return c.stringCode(typ, false)
+	case reflect.Int:
+		return c.intStringCode(typ)
+	case reflect.Int8:
+		return c.int8StringCode(typ)
+	case reflect.Int16:
+		return c.int16StringCode(typ)
+	case reflect.Int32:
+		return c.int32StringCode(typ)
+	case reflect.Int64:
+		return c.int64StringCode(typ)
+	case reflect.Uint:
+		return c.uintStringCode(typ)
+	case reflect.Uint8:
+		return c.uint8StringCode(typ)
+	case reflect.Uint16:
+		return c.uint16StringCode(typ)
+	case reflect.Uint32:
+		return c.uint32StringCode(typ)
+	case reflect.Uint64:
+		return c.uint64StringCode(typ)
+	case reflect.Uintptr:
+		return c.uintStringCode(typ)
 	}
-	valueCode.Flags |= IndirectFlags
-
-	key := newMapKeyCode(ctx, header)
-	ctx.incIndex()
-
-	ctx = ctx.decIndent()
-
-	end := newMapEndCode(ctx, header)
-	ctx.incIndex()
-
-	header.Next = keyCode
-	keyCode.BeforeLastCode().Next = (*Opcode)(unsafe.Pointer(value))
-	value.Next = valueCode
-	valueCode.BeforeLastCode().Next = (*Opcode)(unsafe.Pointer(key))
-	key.Next = keyCode
-
-	header.End = end
-	key.End = end
-	value.End = end
-
-	return (*Opcode)(unsafe.Pointer(header)), nil
+	return nil, &errors.UnsupportedTypeError{Type: runtime.RType2Type(typ)}
 }
 
-func compileMapValue(ctx *compileContext) (*Opcode, error) {
-	switch ctx.typ.Kind() {
+func (c *Compiler) mapValueCode(typ *runtime.Type) (Code, error) {
+	switch typ.Kind() {
 	case reflect.Map:
-		return compilePtr(ctx.withType(runtime.PtrTo(ctx.typ)))
+		return c.ptrCode(runtime.PtrTo(typ))
 	default:
-		code, err := compile(ctx, false)
+		code, err := c.typeToCodeWithPtr(typ, false)
 		if err != nil {
 			return nil, err
 		}
-		if code.Op == OpMapPtr {
-			code.PtrNum++
+		ptr, ok := code.(*PtrCode)
+		if ok {
+			if ptr.value.Kind() == CodeKindMap {
+				ptr.ptrNum++
+			}
 		}
 		return code, nil
 	}
 }
 
-func optimizeStructHeader(code *Opcode, tag *runtime.StructTag) OpType {
-	headType := code.ToHeaderType(tag.IsString)
-	if tag.IsOmitEmpty {
-		headType = headType.HeadToOmitEmptyHead()
+func (c *Compiler) structCode(typ *runtime.Type, isPtr bool) (*StructCode, error) {
+	typeptr := uintptr(unsafe.Pointer(typ))
+	if code, exists := c.structTypeToCode[typeptr]; exists {
+		derefCode := *code
+		derefCode.isRecursive = true
+		return &derefCode, nil
 	}
-	return headType
-}
+	indirect := runtime.IfaceIndir(typ)
+	code := &StructCode{typ: typ, isPtr: isPtr, isIndirect: indirect}
+	c.structTypeToCode[typeptr] = code
 
-func optimizeStructField(code *Opcode, tag *runtime.StructTag) OpType {
-	fieldType := code.ToFieldType(tag.IsString)
-	if tag.IsOmitEmpty {
-		fieldType = fieldType.FieldToOmitEmptyField()
+	fieldNum := typ.NumField()
+	tags := c.typeToStructTags(typ)
+	fields := []*StructFieldCode{}
+	for i, tag := range tags {
+		isOnlyOneFirstField := i == 0 && fieldNum == 1
+		field, err := c.structFieldCode(code, tag, isPtr, isOnlyOneFirstField)
+		if err != nil {
+			return nil, err
+		}
+		if field.isAnonymous {
+			structCode := field.getAnonymousStruct()
+			if structCode != nil {
+				structCode.removeFieldsByTags(tags)
+				if c.isAssignableIndirect(field, isPtr) {
+					if indirect {
+						structCode.isIndirect = true
+					} else {
+						structCode.isIndirect = false
+					}
+				}
+			}
+		} else {
+			structCode := field.getStruct()
+			if structCode != nil {
+				if indirect {
+					// if parent is indirect type, set child indirect property to true
+					structCode.isIndirect = true
+				} else {
+					// if parent is not indirect type, set child indirect property to false.
+					// but if parent's indirect is false and isPtr is true, then indirect must be true.
+					// Do this only if indirectConversion is enabled at the end of compileStruct.
+					structCode.isIndirect = false
+				}
+			}
+		}
+		fields = append(fields, field)
 	}
-	return fieldType
-}
-
-func recursiveCode(ctx *compileContext, jmp *CompiledCode) *Opcode {
-	code := newRecursiveCode(ctx, jmp)
-	ctx.incIndex()
-	return code
-}
-
-func compiledCode(ctx *compileContext) *Opcode {
-	typ := ctx.typ
-	typeptr := uintptr(unsafe.Pointer(typ))
-	if cc, exists := ctx.structTypeToCompiledCode[typeptr]; exists {
-		return recursiveCode(ctx, cc)
+	fieldMap := c.getFieldMap(fields)
+	duplicatedFieldMap := c.getDuplicatedFieldMap(fieldMap)
+	code.fields = c.filteredDuplicatedFields(fields, duplicatedFieldMap)
+	if !code.disableIndirectConversion && !indirect && isPtr {
+		code.enableIndirect()
 	}
-	return nil
+	delete(c.structTypeToCode, typeptr)
+	return code, nil
 }
 
-func structHeader(ctx *compileContext, fieldCode *Opcode, valueCode *Opcode, tag *runtime.StructTag) *Opcode {
-	op := optimizeStructHeader(valueCode, tag)
-	fieldCode.Op = op
-	fieldCode.NumBitSize = valueCode.NumBitSize
-	fieldCode.PtrNum = valueCode.PtrNum
-	if op.IsMultipleOpHead() {
-		return valueCode.BeforeLastCode()
+func (c *Compiler) structFieldCode(structCode *StructCode, tag *runtime.StructTag, isPtr, isOnlyOneFirstField bool) (*StructFieldCode, error) {
+	field := tag.Field
+	fieldType := runtime.Type2RType(field.Type)
+	isIndirectSpecialCase := isPtr && isOnlyOneFirstField
+	fieldCode := &StructFieldCode{
+		typ:           fieldType,
+		key:           tag.Key,
+		tag:           tag,
+		offset:        field.Offset,
+		isAnonymous:   field.Anonymous && !tag.IsTaggedKey,
+		isTaggedKey:   tag.IsTaggedKey,
+		isNilableType: c.isNilableType(fieldType),
+		isNilCheck:    true,
 	}
-	ctx.decOpcodeIndex()
-	return fieldCode
-}
-
-func structField(ctx *compileContext, fieldCode *Opcode, valueCode *Opcode, tag *runtime.StructTag) *Opcode {
-	op := optimizeStructField(valueCode, tag)
-	fieldCode.Op = op
-	fieldCode.NumBitSize = valueCode.NumBitSize
-	fieldCode.PtrNum = valueCode.PtrNum
-	if op.IsMultipleOpField() {
-		return valueCode.BeforeLastCode()
+	switch {
+	case c.isMovePointerPositionFromHeadToFirstMarshalJSONFieldCase(fieldType, isIndirectSpecialCase):
+		code, err := c.marshalJSONCode(fieldType)
+		if err != nil {
+			return nil, err
+		}
+		fieldCode.value = code
+		fieldCode.isAddrForMarshaler = true
+		fieldCode.isNilCheck = false
+		structCode.isIndirect = false
+		structCode.disableIndirectConversion = true
+	case c.isMovePointerPositionFromHeadToFirstMarshalTextFieldCase(fieldType, isIndirectSpecialCase):
+		code, err := c.marshalTextCode(fieldType)
+		if err != nil {
+			return nil, err
+		}
+		fieldCode.value = code
+		fieldCode.isAddrForMarshaler = true
+		fieldCode.isNilCheck = false
+		structCode.isIndirect = false
+		structCode.disableIndirectConversion = true
+	case isPtr && c.isPtrMarshalJSONType(fieldType):
+		// *struct{ field T }
+		// func (*T) MarshalJSON() ([]byte, error)
+		code, err := c.marshalJSONCode(fieldType)
+		if err != nil {
+			return nil, err
+		}
+		fieldCode.value = code
+		fieldCode.isAddrForMarshaler = true
+		fieldCode.isNilCheck = false
+	case isPtr && c.isPtrMarshalTextType(fieldType):
+		// *struct{ field T }
+		// func (*T) MarshalText() ([]byte, error)
+		code, err := c.marshalTextCode(fieldType)
+		if err != nil {
+			return nil, err
+		}
+		fieldCode.value = code
+		fieldCode.isAddrForMarshaler = true
+		fieldCode.isNilCheck = false
+	default:
+		code, err := c.typeToCodeWithPtr(fieldType, isPtr)
+		if err != nil {
+			return nil, err
+		}
+		switch code.Kind() {
+		case CodeKindPtr, CodeKindInterface:
+			fieldCode.isNextOpPtrType = true
+		}
+		fieldCode.value = code
 	}
-	ctx.decIndex()
-	return fieldCode
+	return fieldCode, nil
 }
 
-type structFieldPair struct {
-	prevField   *Opcode
-	curField    *Opcode
-	isTaggedKey bool
-	linked      bool
+func (c *Compiler) isAssignableIndirect(fieldCode *StructFieldCode, isPtr bool) bool {
+	if isPtr {
+		return false
+	}
+	codeType := fieldCode.value.Kind()
+	if codeType == CodeKindMarshalJSON {
+		return false
+	}
+	if codeType == CodeKindMarshalText {
+		return false
+	}
+	return true
 }
 
-func filterAnonymousStructFieldsByTags(value *Opcode, tags runtime.StructTags) *Opcode {
-	head := value
-	curField := head
-	removedFields := map[*Opcode]struct{}{}
-	for curField != nil {
-		existsKey := tags.ExistsKey(curField.DisplayKey)
-		if !existsKey || curField.Next.IsRecursiveOp() {
-			curField = curField.NextField
-			continue
-		}
-		diff := curField.NextField.DisplayIdx - curField.DisplayIdx
-		for i := uint32(0); i < diff; i++ {
-			curField.NextField.decOpcodeIndex()
-		}
-		if curField.IsStructHeadOp() || head == curField {
-			head = curField.NextField
-		} else {
-			linkPrevToNextField(curField, removedFields)
-		}
-		curField = curField.NextField
-	}
-	return head
-}
-
-func anonymousStructFieldPairMap(named string, valueCode *Opcode) map[string][]structFieldPair {
-	anonymousFields := map[string][]structFieldPair{}
-	f := valueCode
-	var prevAnonymousField *Opcode
-	for {
-		isHeadOp := strings.Contains(f.Op.String(), "Head")
-		if isHeadOp && (f.Flags&AnonymousHeadFlags) == 0 {
-			if named == "" {
-				f.Flags |= AnonymousHeadFlags
+func (c *Compiler) getFieldMap(fields []*StructFieldCode) map[string][]*StructFieldCode {
+	fieldMap := map[string][]*StructFieldCode{}
+	for _, field := range fields {
+		if field.isAnonymous {
+			for k, v := range c.getAnonymousFieldMap(field) {
+				fieldMap[k] = append(fieldMap[k], v...)
 			}
-		} else if named == "" && f.Op == OpStructEnd {
-			f.Op = OpStructAnonymousEnd
-		}
-		if f.DisplayKey == "" {
-			if f.NextField == nil {
-				break
-			}
-			prevAnonymousField = f
-			f = f.NextField
 			continue
 		}
+		fieldMap[field.key] = append(fieldMap[field.key], field)
+	}
+	return fieldMap
+}
 
-		key := fmt.Sprintf("%s.%s", named, f.DisplayKey)
-		anonymousFields[key] = append(anonymousFields[key], structFieldPair{
-			prevField:   prevAnonymousField,
-			curField:    f,
-			isTaggedKey: (f.Flags & IsTaggedKeyFlags) != 0,
-		})
-		if f.Next != nil && f.NextField != f.Next && f.Next.Op.CodeType() == CodeStructField {
-			for k, v := range anonymousFieldPairRecursively(named, f.Next) {
-				anonymousFields[k] = append(anonymousFields[k], v...)
-			}
-		}
-		if f.NextField == nil {
-			break
-		}
-		prevAnonymousField = f
-		f = f.NextField
+func (c *Compiler) getAnonymousFieldMap(field *StructFieldCode) map[string][]*StructFieldCode {
+	fieldMap := map[string][]*StructFieldCode{}
+	structCode := field.getAnonymousStruct()
+	if structCode == nil || structCode.isRecursive {
+		fieldMap[field.key] = append(fieldMap[field.key], field)
+		return fieldMap
+	}
+	for k, v := range c.getFieldMapFromAnonymousParent(structCode.fields) {
+		fieldMap[k] = append(fieldMap[k], v...)
 	}
-	return anonymousFields
-}
-
-func anonymousFieldPairRecursively(named string, valueCode *Opcode) map[string][]structFieldPair {
-	anonymousFields := map[string][]structFieldPair{}
-	f := valueCode
-	var prevAnonymousField *Opcode
-	for {
-		if f.DisplayKey != "" && (f.Flags&AnonymousHeadFlags) != 0 {
-			key := fmt.Sprintf("%s.%s", named, f.DisplayKey)
-			anonymousFields[key] = append(anonymousFields[key], structFieldPair{
-				prevField:   prevAnonymousField,
-				curField:    f,
-				isTaggedKey: (f.Flags & IsTaggedKeyFlags) != 0,
-			})
-			if f.Next != nil && f.NextField != f.Next && f.Next.Op.CodeType() == CodeStructField {
-				for k, v := range anonymousFieldPairRecursively(named, f.Next) {
-					anonymousFields[k] = append(anonymousFields[k], v...)
+	return fieldMap
+}
+
+func (c *Compiler) getFieldMapFromAnonymousParent(fields []*StructFieldCode) map[string][]*StructFieldCode {
+	fieldMap := map[string][]*StructFieldCode{}
+	for _, field := range fields {
+		if field.isAnonymous {
+			for k, v := range c.getAnonymousFieldMap(field) {
+				// Do not handle tagged key when embedding more than once
+				for _, vv := range v {
+					vv.isTaggedKey = false
 				}
+				fieldMap[k] = append(fieldMap[k], v...)
 			}
+			continue
 		}
-		if f.NextField == nil {
-			break
-		}
-		prevAnonymousField = f
-		f = f.NextField
+		fieldMap[field.key] = append(fieldMap[field.key], field)
 	}
-	return anonymousFields
+	return fieldMap
 }
 
-func optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) {
-	removedFields := map[*Opcode]struct{}{}
-	for _, fieldPairs := range anonymousFields {
-		if len(fieldPairs) == 1 {
+func (c *Compiler) getDuplicatedFieldMap(fieldMap map[string][]*StructFieldCode) map[*StructFieldCode]struct{} {
+	duplicatedFieldMap := map[*StructFieldCode]struct{}{}
+	for _, fields := range fieldMap {
+		if len(fields) == 1 {
 			continue
 		}
-		// conflict anonymous fields
-		taggedPairs := []structFieldPair{}
-		for _, fieldPair := range fieldPairs {
-			if fieldPair.isTaggedKey {
-				taggedPairs = append(taggedPairs, fieldPair)
-			} else {
-				if !fieldPair.linked {
-					if fieldPair.prevField == nil {
-						// head operation
-						fieldPair.curField.Op = OpStructHead
-						fieldPair.curField.Flags |= AnonymousHeadFlags
-						fieldPair.curField.Flags |= AnonymousKeyFlags
-					} else {
-						diff := fieldPair.curField.NextField.DisplayIdx - fieldPair.curField.DisplayIdx
-						for i := uint32(0); i < diff; i++ {
-							fieldPair.curField.NextField.decOpcodeIndex()
-						}
-						removedFields[fieldPair.curField] = struct{}{}
-						linkPrevToNextField(fieldPair.curField, removedFields)
-					}
-					fieldPair.linked = true
-				}
-			}
-		}
-		if len(taggedPairs) > 1 {
-			for _, fieldPair := range taggedPairs {
-				if !fieldPair.linked {
-					if fieldPair.prevField == nil {
-						// head operation
-						fieldPair.curField.Op = OpStructHead
-						fieldPair.curField.Flags |= AnonymousHeadFlags
-						fieldPair.curField.Flags |= AnonymousKeyFlags
-					} else {
-						diff := fieldPair.curField.NextField.DisplayIdx - fieldPair.curField.DisplayIdx
-						removedFields[fieldPair.curField] = struct{}{}
-						for i := uint32(0); i < diff; i++ {
-							fieldPair.curField.NextField.decOpcodeIndex()
-						}
-						linkPrevToNextField(fieldPair.curField, removedFields)
-					}
-					fieldPair.linked = true
+		if c.isTaggedKeyOnly(fields) {
+			for _, field := range fields {
+				if field.isTaggedKey {
+					continue
 				}
+				duplicatedFieldMap[field] = struct{}{}
 			}
 		} else {
-			for _, fieldPair := range taggedPairs {
-				fieldPair.curField.Flags &= ^IsTaggedKeyFlags
+			for _, field := range fields {
+				duplicatedFieldMap[field] = struct{}{}
 			}
 		}
 	}
+	return duplicatedFieldMap
 }
 
-func isNilableType(typ *runtime.Type) bool {
-	switch typ.Kind() {
-	case reflect.Ptr:
-		return true
-	case reflect.Map:
-		return true
-	case reflect.Func:
-		return true
-	default:
-		return false
+func (c *Compiler) filteredDuplicatedFields(fields []*StructFieldCode, duplicatedFieldMap map[*StructFieldCode]struct{}) []*StructFieldCode {
+	filteredFields := make([]*StructFieldCode, 0, len(fields))
+	for _, field := range fields {
+		if field.isAnonymous {
+			structCode := field.getAnonymousStruct()
+			if structCode != nil && !structCode.isRecursive {
+				structCode.fields = c.filteredDuplicatedFields(structCode.fields, duplicatedFieldMap)
+				if len(structCode.fields) > 0 {
+					filteredFields = append(filteredFields, field)
+				}
+				continue
+			}
+		}
+		if _, exists := duplicatedFieldMap[field]; exists {
+			continue
+		}
+		filteredFields = append(filteredFields, field)
 	}
+	return filteredFields
 }
 
-func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
-	if code := compiledCode(ctx); code != nil {
-		return code, nil
+func (c *Compiler) isTaggedKeyOnly(fields []*StructFieldCode) bool {
+	var taggedKeyFieldCount int
+	for _, field := range fields {
+		if field.isTaggedKey {
+			taggedKeyFieldCount++
+		}
 	}
-	typ := ctx.typ
-	typeptr := uintptr(unsafe.Pointer(typ))
-	compiled := &CompiledCode{}
-	ctx.structTypeToCompiledCode[typeptr] = compiled
-	// header => code => structField => code => end
-	//                        ^          |
-	//                        |__________|
-	fieldNum := typ.NumField()
-	indirect := runtime.IfaceIndir(typ)
-	fieldIdx := 0
-	disableIndirectConversion := false
-	var (
-		head      *Opcode
-		code      *Opcode
-		prevField *Opcode
-	)
-	ctx = ctx.incIndent()
+	return taggedKeyFieldCount == 1
+}
+
+func (c *Compiler) typeToStructTags(typ *runtime.Type) runtime.StructTags {
 	tags := runtime.StructTags{}
-	anonymousFields := map[string][]structFieldPair{}
+	fieldNum := typ.NumField()
 	for i := 0; i < fieldNum; i++ {
 		field := typ.Field(i)
 		if runtime.IsIgnoredStructField(field) {
@@ -1302,222 +780,107 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
 		}
 		tags = append(tags, runtime.StructTagFromField(field))
 	}
-	for i, tag := range tags {
-		field := tag.Field
-		fieldType := runtime.Type2RType(field.Type)
-		fieldOpcodeIndex := ctx.opcodeIndex
-		fieldPtrIndex := ctx.ptrIndex
-		ctx.incIndex()
-
-		nilcheck := true
-		addrForMarshaler := false
-		isIndirectSpecialCase := isPtr && i == 0 && fieldNum == 1
-		isNilableType := isNilableType(fieldType)
-
-		var valueCode *Opcode
-		switch {
-		case isIndirectSpecialCase && !isNilableType && isPtrMarshalJSONType(fieldType):
-			// *struct{ field T } => struct { field *T }
-			// func (*T) MarshalJSON() ([]byte, error)
-			// move pointer position from head to first field
-			code, err := compileMarshalJSON(ctx.withType(fieldType))
-			if err != nil {
-				return nil, err
-			}
-			addrForMarshaler = true
-			valueCode = code
-			nilcheck = false
-			indirect = false
-			disableIndirectConversion = true
-		case isIndirectSpecialCase && !isNilableType && isPtrMarshalTextType(fieldType):
-			// *struct{ field T } => struct { field *T }
-			// func (*T) MarshalText() ([]byte, error)
-			// move pointer position from head to first field
-			code, err := compileMarshalText(ctx.withType(fieldType))
-			if err != nil {
-				return nil, err
-			}
-			addrForMarshaler = true
-			valueCode = code
-			nilcheck = false
-			indirect = false
-			disableIndirectConversion = true
-		case isPtr && isPtrMarshalJSONType(fieldType):
-			// *struct{ field T }
-			// func (*T) MarshalJSON() ([]byte, error)
-			code, err := compileMarshalJSON(ctx.withType(fieldType))
-			if err != nil {
-				return nil, err
-			}
-			addrForMarshaler = true
-			nilcheck = false
-			valueCode = code
-		case isPtr && isPtrMarshalTextType(fieldType):
-			// *struct{ field T }
-			// func (*T) MarshalText() ([]byte, error)
-			code, err := compileMarshalText(ctx.withType(fieldType))
-			if err != nil {
-				return nil, err
-			}
-			addrForMarshaler = true
-			nilcheck = false
-			valueCode = code
-		default:
-			code, err := compile(ctx.withType(fieldType), isPtr)
-			if err != nil {
-				return nil, err
-			}
-			valueCode = code
-		}
+	return tags
+}
 
-		if field.Anonymous && !tag.IsTaggedKey {
-			tagKey := ""
-			if tag.IsTaggedKey {
-				tagKey = tag.Key
-			}
-			valueCode = filterAnonymousStructFieldsByTags(valueCode, tags)
-			for k, v := range anonymousStructFieldPairMap(tagKey, valueCode) {
-				anonymousFields[k] = append(anonymousFields[k], v...)
-			}
+// *struct{ field T } => struct { field *T }
+// func (*T) MarshalJSON() ([]byte, error)
+func (c *Compiler) isMovePointerPositionFromHeadToFirstMarshalJSONFieldCase(typ *runtime.Type, isIndirectSpecialCase bool) bool {
+	return isIndirectSpecialCase && !c.isNilableType(typ) && c.isPtrMarshalJSONType(typ)
+}
 
-			valueCode.decIndent()
+// *struct{ field T } => struct { field *T }
+// func (*T) MarshalText() ([]byte, error)
+func (c *Compiler) isMovePointerPositionFromHeadToFirstMarshalTextFieldCase(typ *runtime.Type, isIndirectSpecialCase bool) bool {
+	return isIndirectSpecialCase && !c.isNilableType(typ) && c.isPtrMarshalTextType(typ)
+}
 
-			// fix issue144
-			if !(isPtr && strings.Contains(valueCode.Op.String(), "Marshal")) {
-				if indirect {
-					valueCode.Flags |= IndirectFlags
-				} else {
-					valueCode.Flags &= ^IndirectFlags
-				}
-			}
-		} else {
-			if indirect {
-				// if parent is indirect type, set child indirect property to true
-				valueCode.Flags |= IndirectFlags
-			} else {
-				// if parent is not indirect type, set child indirect property to false.
-				// but if parent's indirect is false and isPtr is true, then indirect must be true.
-				// Do this only if indirectConversion is enabled at the end of compileStruct.
-				if i == 0 {
-					valueCode.Flags &= ^IndirectFlags
-				}
-			}
-		}
-		var flags OpFlags
-		if indirect {
-			flags |= IndirectFlags
-		}
-		if field.Anonymous {
-			flags |= AnonymousKeyFlags
-		}
-		if tag.IsTaggedKey {
-			flags |= IsTaggedKeyFlags
-		}
-		if nilcheck {
-			flags |= NilCheckFlags
-		}
-		if addrForMarshaler {
-			flags |= AddrForMarshalerFlags
-		}
-		if strings.Contains(valueCode.Op.String(), "Ptr") || valueCode.Op == OpInterface {
-			flags |= IsNextOpPtrTypeFlags
-		}
-		if isNilableType {
-			flags |= IsNilableTypeFlags
-		}
-		var key string
-		if ctx.escapeKey {
-			rctx := &RuntimeContext{Option: &Option{Flag: HTMLEscapeOption}}
-			key = fmt.Sprintf(`%s:`, string(AppendString(rctx, []byte{}, tag.Key)))
-		} else {
-			key = fmt.Sprintf(`"%s":`, tag.Key)
-		}
-		fieldCode := &Opcode{
-			Idx:        opcodeOffset(fieldPtrIndex),
-			Next:       valueCode,
-			Flags:      flags,
-			Key:        key,
-			Offset:     uint32(field.Offset),
-			Type:       valueCode.Type,
-			DisplayIdx: fieldOpcodeIndex,
-			Indent:     ctx.indent,
-			DisplayKey: tag.Key,
-		}
-		if fieldIdx == 0 {
-			code = structHeader(ctx, fieldCode, valueCode, tag)
-			head = fieldCode
-			prevField = fieldCode
-		} else {
-			fieldCode.Idx = head.Idx
-			code.Next = fieldCode
-			code = structField(ctx, fieldCode, valueCode, tag)
-			prevField.NextField = fieldCode
-			fieldCode.PrevField = prevField
-			prevField = fieldCode
-		}
-		fieldIdx++
+func (c *Compiler) implementsMarshalJSON(typ *runtime.Type) bool {
+	if !c.implementsMarshalJSONType(typ) {
+		return false
 	}
-
-	structEndCode := &Opcode{
-		Op:     OpStructEnd,
-		Type:   nil,
-		Indent: ctx.indent,
+	if typ.Kind() != reflect.Ptr {
+		return true
 	}
-
-	ctx = ctx.decIndent()
-
-	// no struct field
-	if head == nil {
-		head = &Opcode{
-			Op:         OpStructHead,
-			Idx:        opcodeOffset(ctx.ptrIndex),
-			NextField:  structEndCode,
-			Type:       typ,
-			DisplayIdx: ctx.opcodeIndex,
-			Indent:     ctx.indent,
-		}
-		structEndCode.PrevField = head
-		ctx.incIndex()
-		code = head
+	// type kind is reflect.Ptr
+	if !c.implementsMarshalJSONType(typ.Elem()) {
+		return true
 	}
+	// needs to dereference
+	return false
+}
 
-	structEndCode.DisplayIdx = ctx.opcodeIndex
-	structEndCode.Idx = opcodeOffset(ctx.ptrIndex)
-	ctx.incIndex()
-	structEndCode.Next = newEndOp(ctx)
-
-	if prevField != nil && prevField.NextField == nil {
-		prevField.NextField = structEndCode
-		structEndCode.PrevField = prevField
+func (c *Compiler) implementsMarshalText(typ *runtime.Type) bool {
+	if !typ.Implements(marshalTextType) {
+		return false
 	}
-
-	head.End = structEndCode
-	code.Next = structEndCode
-	optimizeConflictAnonymousFields(anonymousFields)
-	ret := (*Opcode)(unsafe.Pointer(head))
-	compiled.Code = ret
-
-	delete(ctx.structTypeToCompiledCode, typeptr)
-
-	if !disableIndirectConversion && (head.Flags&IndirectFlags == 0) && isPtr {
-		headCode := head
-		for strings.Contains(headCode.Op.String(), "Head") {
-			headCode.Flags |= IndirectFlags
-			headCode = headCode.Next
-		}
+	if typ.Kind() != reflect.Ptr {
+		return true
+	}
+	// type kind is reflect.Ptr
+	if !typ.Elem().Implements(marshalTextType) {
+		return true
 	}
+	// needs to dereference
+	return false
+}
 
-	return ret, nil
+func (c *Compiler) isNilableType(typ *runtime.Type) bool {
+	switch typ.Kind() {
+	case reflect.Ptr:
+		return true
+	case reflect.Map:
+		return true
+	case reflect.Func:
+		return true
+	default:
+		return false
+	}
 }
 
-func implementsMarshalJSONType(typ *runtime.Type) bool {
+func (c *Compiler) implementsMarshalJSONType(typ *runtime.Type) bool {
 	return typ.Implements(marshalJSONType) || typ.Implements(marshalJSONContextType)
 }
 
-func isPtrMarshalJSONType(typ *runtime.Type) bool {
-	return !implementsMarshalJSONType(typ) && implementsMarshalJSONType(runtime.PtrTo(typ))
+func (c *Compiler) isPtrMarshalJSONType(typ *runtime.Type) bool {
+	return !c.implementsMarshalJSONType(typ) && c.implementsMarshalJSONType(runtime.PtrTo(typ))
 }
 
-func isPtrMarshalTextType(typ *runtime.Type) bool {
+func (c *Compiler) isPtrMarshalTextType(typ *runtime.Type) bool {
 	return !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType)
 }
+
+func (c *Compiler) codeToOpcode(ctx *compileContext, typ *runtime.Type, code Code) *Opcode {
+	codes := code.ToOpcode(ctx)
+	codes.Last().Next = newEndOp(ctx, typ)
+	c.linkRecursiveCode(ctx)
+	return codes.First()
+}
+
+func (c *Compiler) linkRecursiveCode(ctx *compileContext) {
+	for _, recursive := range *ctx.recursiveCodes {
+		typeptr := uintptr(unsafe.Pointer(recursive.Type))
+		codes := ctx.structTypeToCodes[typeptr]
+		compiled := recursive.Jmp
+		compiled.Code = copyOpcode(codes.First())
+		code := compiled.Code
+		code.End.Next = newEndOp(&compileContext{}, recursive.Type)
+		code.Op = code.Op.PtrHeadToHead()
+
+		beforeLastCode := code.End
+		lastCode := beforeLastCode.Next
+
+		totalLength := code.TotalLength()
+		lastCode.Idx = uint32((totalLength + 1) * uintptrSize)
+		lastCode.ElemIdx = lastCode.Idx + uintptrSize
+		lastCode.Length = lastCode.Idx + 2*uintptrSize
+		code.End.Next.Op = OpRecursiveEnd
+
+		// extend length to alloc slot for elemIdx + length
+		curTotalLength := uintptr(recursive.TotalLength()) + 3
+		nextTotalLength := uintptr(totalLength) + 3
+		compiled.CurLen = curTotalLength
+		compiled.NextLen = nextTotalLength
+		compiled.Linked = true
+	}
+}
diff --git a/internal/encoder/compiler_norace.go b/internal/encoder/compiler_norace.go
index 9d337f1..afc5b66 100644
--- a/internal/encoder/compiler_norace.go
+++ b/internal/encoder/compiler_norace.go
@@ -1,13 +1,8 @@
+//go:build !race
 // +build !race
 
 package encoder
 
-import (
-	"unsafe"
-
-	"github.com/goccy/go-json/internal/runtime"
-)
-
 func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
 	if typeptr > typeAddr.MaxTypeAddr {
 		return compileToGetCodeSetSlowPath(typeptr)
@@ -16,41 +11,10 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
 	if codeSet := cachedOpcodeSets[index]; codeSet != nil {
 		return codeSet, nil
 	}
-
-	// noescape trick for header.typ ( reflect.*rtype )
-	copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
-
-	noescapeKeyCode, err := compileHead(&compileContext{
-		typ:                      copiedType,
-		structTypeToCompiledCode: map[uintptr]*CompiledCode{},
-	})
-	if err != nil {
-		return nil, err
-	}
-	escapeKeyCode, err := compileHead(&compileContext{
-		typ:                      copiedType,
-		structTypeToCompiledCode: map[uintptr]*CompiledCode{},
-		escapeKey:                true,
-	})
+	codeSet, err := newCompiler().compile(typeptr)
 	if err != nil {
 		return nil, err
 	}
-	noescapeKeyCode = copyOpcode(noescapeKeyCode)
-	escapeKeyCode = copyOpcode(escapeKeyCode)
-	setTotalLengthToInterfaceOp(noescapeKeyCode)
-	setTotalLengthToInterfaceOp(escapeKeyCode)
-	interfaceNoescapeKeyCode := copyToInterfaceOpcode(noescapeKeyCode)
-	interfaceEscapeKeyCode := copyToInterfaceOpcode(escapeKeyCode)
-	codeLength := noescapeKeyCode.TotalLength()
-	codeSet := &OpcodeSet{
-		Type:                     copiedType,
-		NoescapeKeyCode:          noescapeKeyCode,
-		EscapeKeyCode:            escapeKeyCode,
-		InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
-		InterfaceEscapeKeyCode:   interfaceEscapeKeyCode,
-		CodeLength:               codeLength,
-		EndCode:                  ToEndCode(interfaceNoescapeKeyCode),
-	}
 	cachedOpcodeSets[index] = codeSet
 	return codeSet, nil
 }
diff --git a/internal/encoder/compiler_race.go b/internal/encoder/compiler_race.go
index 3a239e9..846a898 100644
--- a/internal/encoder/compiler_race.go
+++ b/internal/encoder/compiler_race.go
@@ -1,12 +1,10 @@
+//go:build race
 // +build race
 
 package encoder
 
 import (
 	"sync"
-	"unsafe"
-
-	"github.com/goccy/go-json/internal/runtime"
 )
 
 var setsMu sync.RWMutex
@@ -23,41 +21,10 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
 	}
 	setsMu.RUnlock()
 
-	// noescape trick for header.typ ( reflect.*rtype )
-	copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
-
-	noescapeKeyCode, err := compileHead(&compileContext{
-		typ:                      copiedType,
-		structTypeToCompiledCode: map[uintptr]*CompiledCode{},
-	})
+	codeSet, err := newCompiler().compile(typeptr)
 	if err != nil {
 		return nil, err
 	}
-	escapeKeyCode, err := compileHead(&compileContext{
-		typ:                      copiedType,
-		structTypeToCompiledCode: map[uintptr]*CompiledCode{},
-		escapeKey:                true,
-	})
-	if err != nil {
-		return nil, err
-	}
-
-	noescapeKeyCode = copyOpcode(noescapeKeyCode)
-	escapeKeyCode = copyOpcode(escapeKeyCode)
-	setTotalLengthToInterfaceOp(noescapeKeyCode)
-	setTotalLengthToInterfaceOp(escapeKeyCode)
-	interfaceNoescapeKeyCode := copyToInterfaceOpcode(noescapeKeyCode)
-	interfaceEscapeKeyCode := copyToInterfaceOpcode(escapeKeyCode)
-	codeLength := noescapeKeyCode.TotalLength()
-	codeSet := &OpcodeSet{
-		Type:                     copiedType,
-		NoescapeKeyCode:          noescapeKeyCode,
-		EscapeKeyCode:            escapeKeyCode,
-		InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
-		InterfaceEscapeKeyCode:   interfaceEscapeKeyCode,
-		CodeLength:               codeLength,
-		EndCode:                  ToEndCode(interfaceNoescapeKeyCode),
-	}
 	setsMu.Lock()
 	cachedOpcodeSets[index] = codeSet
 	setsMu.Unlock()
diff --git a/internal/encoder/context.go b/internal/encoder/context.go
index 61b8908..3833d0c 100644
--- a/internal/encoder/context.go
+++ b/internal/encoder/context.go
@@ -9,44 +9,20 @@ import (
 )
 
 type compileContext struct {
-	typ                      *runtime.Type
-	opcodeIndex              uint32
-	ptrIndex                 int
-	indent                   uint32
-	escapeKey                bool
-	structTypeToCompiledCode map[uintptr]*CompiledCode
-
-	parent *compileContext
+	opcodeIndex       uint32
+	ptrIndex          int
+	indent            uint32
+	escapeKey         bool
+	structTypeToCodes map[uintptr]Opcodes
+	recursiveCodes    *Opcodes
 }
 
-func (c *compileContext) context() *compileContext {
-	return &compileContext{
-		typ:                      c.typ,
-		opcodeIndex:              c.opcodeIndex,
-		ptrIndex:                 c.ptrIndex,
-		indent:                   c.indent,
-		escapeKey:                c.escapeKey,
-		structTypeToCompiledCode: c.structTypeToCompiledCode,
-		parent:                   c,
-	}
-}
-
-func (c *compileContext) withType(typ *runtime.Type) *compileContext {
-	ctx := c.context()
-	ctx.typ = typ
-	return ctx
-}
-
-func (c *compileContext) incIndent() *compileContext {
-	ctx := c.context()
-	ctx.indent++
-	return ctx
+func (c *compileContext) incIndent() {
+	c.indent++
 }
 
-func (c *compileContext) decIndent() *compileContext {
-	ctx := c.context()
-	ctx.indent--
-	return ctx
+func (c *compileContext) decIndent() {
+	c.indent--
 }
 
 func (c *compileContext) incIndex() {
@@ -61,30 +37,18 @@ func (c *compileContext) decIndex() {
 
 func (c *compileContext) incOpcodeIndex() {
 	c.opcodeIndex++
-	if c.parent != nil {
-		c.parent.incOpcodeIndex()
-	}
 }
 
 func (c *compileContext) decOpcodeIndex() {
 	c.opcodeIndex--
-	if c.parent != nil {
-		c.parent.decOpcodeIndex()
-	}
 }
 
 func (c *compileContext) incPtrIndex() {
 	c.ptrIndex++
-	if c.parent != nil {
-		c.parent.incPtrIndex()
-	}
 }
 
 func (c *compileContext) decPtrIndex() {
 	c.ptrIndex--
-	if c.parent != nil {
-		c.parent.decPtrIndex()
-	}
 }
 
 const (
diff --git a/internal/encoder/opcode.go b/internal/encoder/opcode.go
index 446baa2..cfd2d5a 100644
--- a/internal/encoder/opcode.go
+++ b/internal/encoder/opcode.go
@@ -38,7 +38,6 @@ type Opcode struct {
 	Flags      OpFlags
 
 	Type       *runtime.Type // go type
-	PrevField  *Opcode       // prev struct field
 	Jmp        *CompiledCode // for recursive call
 	ElemIdx    uint32        // offset to access array/slice/map elem
 	Length     uint32        // offset to access slice/map length or array length
@@ -50,18 +49,40 @@ type Opcode struct {
 	DisplayKey string        // key text to display
 }
 
-func (c *Opcode) IsStructHeadOp() bool {
+func (c *Opcode) Validate() error {
+	var prevIdx uint32
+	for code := c; !code.IsEnd(); {
+		if prevIdx != 0 {
+			if code.DisplayIdx != prevIdx+1 {
+				return fmt.Errorf(
+					"invalid index. previous display index is %d but next is %d. dump = %s",
+					prevIdx, code.DisplayIdx, c.Dump(),
+				)
+			}
+		}
+		prevIdx = code.DisplayIdx
+		code = code.IterNext()
+	}
+	return nil
+}
+
+func (c *Opcode) IterNext() *Opcode {
 	if c == nil {
-		return false
+		return nil
+	}
+	switch c.Op.CodeType() {
+	case CodeArrayElem, CodeSliceElem, CodeMapKey:
+		return c.End
+	default:
+		return c.Next
 	}
-	return strings.Contains(c.Op.String(), "Head")
 }
 
-func (c *Opcode) IsRecursiveOp() bool {
+func (c *Opcode) IsEnd() bool {
 	if c == nil {
-		return false
+		return true
 	}
-	return strings.Contains(c.Op.String(), "Recursive")
+	return c.Op == OpEnd || c.Op == OpInterfaceEnd || c.Op == OpRecursiveEnd
 }
 
 func (c *Opcode) MaxIdx() uint32 {
@@ -287,43 +308,76 @@ func (c *Opcode) ToFieldType(isString bool) OpType {
 	return OpStructField
 }
 
-func newOpCode(ctx *compileContext, op OpType) *Opcode {
-	return newOpCodeWithNext(ctx, op, newEndOp(ctx))
+func newOpCode(ctx *compileContext, typ *runtime.Type, op OpType) *Opcode {
+	return newOpCodeWithNext(ctx, typ, op, newEndOp(ctx, typ))
 }
 
 func opcodeOffset(idx int) uint32 {
 	return uint32(idx) * uintptrSize
 }
 
+func getCodeAddrByIdx(head *Opcode, idx uint32) *Opcode {
+	addr := uintptr(unsafe.Pointer(head)) + uintptr(idx)*unsafe.Sizeof(Opcode{})
+	return *(**Opcode)(unsafe.Pointer(&addr))
+}
+
 func copyOpcode(code *Opcode) *Opcode {
-	codeMap := map[uintptr]*Opcode{}
-	return code.copy(codeMap)
+	codeNum := ToEndCode(code).DisplayIdx + 1
+	codeSlice := make([]Opcode, codeNum)
+	head := (*Opcode)((*runtime.SliceHeader)(unsafe.Pointer(&codeSlice)).Data)
+	ptr := head
+	c := code
+	for {
+		*ptr = Opcode{
+			Op:         c.Op,
+			Key:        c.Key,
+			PtrNum:     c.PtrNum,
+			NumBitSize: c.NumBitSize,
+			Flags:      c.Flags,
+			Idx:        c.Idx,
+			Offset:     c.Offset,
+			Type:       c.Type,
+			DisplayIdx: c.DisplayIdx,
+			DisplayKey: c.DisplayKey,
+			ElemIdx:    c.ElemIdx,
+			Length:     c.Length,
+			MapIter:    c.MapIter,
+			MapPos:     c.MapPos,
+			Size:       c.Size,
+			Indent:     c.Indent,
+			Jmp:        c.Jmp,
+		}
+		if c.End != nil {
+			ptr.End = getCodeAddrByIdx(head, c.End.DisplayIdx)
+		}
+		if c.NextField != nil {
+			ptr.NextField = getCodeAddrByIdx(head, c.NextField.DisplayIdx)
+		}
+		if c.Next != nil {
+			ptr.Next = getCodeAddrByIdx(head, c.Next.DisplayIdx)
+		}
+		if c.IsEnd() {
+			break
+		}
+		ptr = getCodeAddrByIdx(head, c.DisplayIdx+1)
+		c = c.IterNext()
+	}
+	return head
 }
 
 func setTotalLengthToInterfaceOp(code *Opcode) {
-	c := code
-	for c.Op != OpEnd && c.Op != OpInterfaceEnd {
+	for c := code; !c.IsEnd(); {
 		if c.Op == OpInterface {
 			c.Length = uint32(code.TotalLength())
 		}
-		switch c.Op.CodeType() {
-		case CodeArrayElem, CodeSliceElem, CodeMapKey:
-			c = c.End
-		default:
-			c = c.Next
-		}
+		c = c.IterNext()
 	}
 }
 
 func ToEndCode(code *Opcode) *Opcode {
 	c := code
-	for c.Op != OpEnd && c.Op != OpInterfaceEnd {
-		switch c.Op.CodeType() {
-		case CodeArrayElem, CodeSliceElem, CodeMapKey:
-			c = c.End
-		default:
-			c = c.Next
-		}
+	for !c.IsEnd() {
+		c = c.IterNext()
 	}
 	return c
 }
@@ -339,77 +393,25 @@ func copyToInterfaceOpcode(code *Opcode) *Opcode {
 	return copied
 }
 
-func newOpCodeWithNext(ctx *compileContext, op OpType, next *Opcode) *Opcode {
+func newOpCodeWithNext(ctx *compileContext, typ *runtime.Type, op OpType, next *Opcode) *Opcode {
 	return &Opcode{
 		Op:         op,
 		Idx:        opcodeOffset(ctx.ptrIndex),
 		Next:       next,
-		Type:       ctx.typ,
+		Type:       typ,
 		DisplayIdx: ctx.opcodeIndex,
 		Indent:     ctx.indent,
 	}
 }
 
-func newEndOp(ctx *compileContext) *Opcode {
-	return newOpCodeWithNext(ctx, OpEnd, nil)
-}
-
-func (c *Opcode) copy(codeMap map[uintptr]*Opcode) *Opcode {
-	if c == nil {
-		return nil
-	}
-	addr := uintptr(unsafe.Pointer(c))
-	if code, exists := codeMap[addr]; exists {
-		return code
-	}
-	copied := &Opcode{
-		Op:         c.Op,
-		Key:        c.Key,
-		PtrNum:     c.PtrNum,
-		NumBitSize: c.NumBitSize,
-		Flags:      c.Flags,
-		Idx:        c.Idx,
-		Offset:     c.Offset,
-		Type:       c.Type,
-		DisplayIdx: c.DisplayIdx,
-		DisplayKey: c.DisplayKey,
-		ElemIdx:    c.ElemIdx,
-		Length:     c.Length,
-		MapIter:    c.MapIter,
-		MapPos:     c.MapPos,
-		Size:       c.Size,
-		Indent:     c.Indent,
-	}
-	codeMap[addr] = copied
-	copied.End = c.End.copy(codeMap)
-	copied.PrevField = c.PrevField.copy(codeMap)
-	copied.NextField = c.NextField.copy(codeMap)
-	copied.Next = c.Next.copy(codeMap)
-	copied.Jmp = c.Jmp
-	return copied
-}
-
-func (c *Opcode) BeforeLastCode() *Opcode {
-	code := c
-	for {
-		var nextCode *Opcode
-		switch code.Op.CodeType() {
-		case CodeArrayElem, CodeSliceElem, CodeMapKey:
-			nextCode = code.End
-		default:
-			nextCode = code.Next
-		}
-		if nextCode.Op == OpEnd {
-			return code
-		}
-		code = nextCode
-	}
+func newEndOp(ctx *compileContext, typ *runtime.Type) *Opcode {
+	return newOpCodeWithNext(ctx, typ, OpEnd, nil)
 }
 
 func (c *Opcode) TotalLength() int {
 	var idx int
 	code := c
-	for code.Op != OpEnd && code.Op != OpInterfaceEnd {
+	for !code.IsEnd() {
 		maxIdx := int(code.MaxIdx() / uintptrSize)
 		if idx < maxIdx {
 			idx = maxIdx
@@ -417,12 +419,7 @@ func (c *Opcode) TotalLength() int {
 		if code.Op == OpRecursiveEnd {
 			break
 		}
-		switch code.Op.CodeType() {
-		case CodeArrayElem, CodeSliceElem, CodeMapKey:
-			code = code.End
-		default:
-			code = code.Next
-		}
+		code = code.IterNext()
 	}
 	maxIdx := int(code.MaxIdx() / uintptrSize)
 	if idx < maxIdx {
@@ -431,42 +428,6 @@ func (c *Opcode) TotalLength() int {
 	return idx + 1
 }
 
-func (c *Opcode) decOpcodeIndex() {
-	for code := c; code.Op != OpEnd; {
-		code.DisplayIdx--
-		if code.Idx > 0 {
-			code.Idx -= uintptrSize
-		}
-		if code.ElemIdx > 0 {
-			code.ElemIdx -= uintptrSize
-		}
-		if code.MapIter > 0 {
-			code.MapIter -= uintptrSize
-		}
-		if code.Length > 0 && code.Op.CodeType() != CodeArrayHead && code.Op.CodeType() != CodeArrayElem {
-			code.Length -= uintptrSize
-		}
-		switch code.Op.CodeType() {
-		case CodeArrayElem, CodeSliceElem, CodeMapKey:
-			code = code.End
-		default:
-			code = code.Next
-		}
-	}
-}
-
-func (c *Opcode) decIndent() {
-	for code := c; code.Op != OpEnd; {
-		code.Indent--
-		switch code.Op.CodeType() {
-		case CodeArrayElem, CodeSliceElem, CodeMapKey:
-			code = code.End
-		default:
-			code = code.Next
-		}
-	}
-}
-
 func (c *Opcode) dumpHead(code *Opcode) string {
 	var length uint32
 	if code.Op.CodeType() == CodeArrayHead {
@@ -475,7 +436,7 @@ func (c *Opcode) dumpHead(code *Opcode) string {
 		length = code.Length / uintptrSize
 	}
 	return fmt.Sprintf(
-		`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d])`,
+		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d])`,
 		code.DisplayIdx,
 		strings.Repeat("-", int(code.Indent)),
 		code.Op,
@@ -487,7 +448,7 @@ func (c *Opcode) dumpHead(code *Opcode) string {
 
 func (c *Opcode) dumpMapHead(code *Opcode) string {
 	return fmt.Sprintf(
-		`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
+		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
 		code.DisplayIdx,
 		strings.Repeat("-", int(code.Indent)),
 		code.Op,
@@ -500,7 +461,7 @@ func (c *Opcode) dumpMapHead(code *Opcode) string {
 
 func (c *Opcode) dumpMapEnd(code *Opcode) string {
 	return fmt.Sprintf(
-		`[%d]%s%s ([idx:%d][mapPos:%d][length:%d])`,
+		`[%03d]%s%s ([idx:%d][mapPos:%d][length:%d])`,
 		code.DisplayIdx,
 		strings.Repeat("-", int(code.Indent)),
 		code.Op,
@@ -518,7 +479,7 @@ func (c *Opcode) dumpElem(code *Opcode) string {
 		length = code.Length / uintptrSize
 	}
 	return fmt.Sprintf(
-		`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][size:%d])`,
+		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d][size:%d])`,
 		code.DisplayIdx,
 		strings.Repeat("-", int(code.Indent)),
 		code.Op,
@@ -531,7 +492,7 @@ func (c *Opcode) dumpElem(code *Opcode) string {
 
 func (c *Opcode) dumpField(code *Opcode) string {
 	return fmt.Sprintf(
-		`[%d]%s%s ([idx:%d][key:%s][offset:%d])`,
+		`[%03d]%s%s ([idx:%d][key:%s][offset:%d])`,
 		code.DisplayIdx,
 		strings.Repeat("-", int(code.Indent)),
 		code.Op,
@@ -543,7 +504,7 @@ func (c *Opcode) dumpField(code *Opcode) string {
 
 func (c *Opcode) dumpKey(code *Opcode) string {
 	return fmt.Sprintf(
-		`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
+		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
 		code.DisplayIdx,
 		strings.Repeat("-", int(code.Indent)),
 		code.Op,
@@ -556,7 +517,7 @@ func (c *Opcode) dumpKey(code *Opcode) string {
 
 func (c *Opcode) dumpValue(code *Opcode) string {
 	return fmt.Sprintf(
-		`[%d]%s%s ([idx:%d][mapIter:%d])`,
+		`[%03d]%s%s ([idx:%d][mapIter:%d])`,
 		code.DisplayIdx,
 		strings.Repeat("-", int(code.Indent)),
 		code.Op,
@@ -567,7 +528,7 @@ func (c *Opcode) dumpValue(code *Opcode) string {
 
 func (c *Opcode) Dump() string {
 	codes := []string{}
-	for code := c; code.Op != OpEnd && code.Op != OpInterfaceEnd; {
+	for code := c; !code.IsEnd(); {
 		switch code.Op.CodeType() {
 		case CodeSliceHead:
 			codes = append(codes, c.dumpHead(code))
@@ -595,7 +556,7 @@ func (c *Opcode) Dump() string {
 			code = code.Next
 		default:
 			codes = append(codes, fmt.Sprintf(
-				"[%d]%s%s ([idx:%d])",
+				"[%03d]%s%s ([idx:%d])",
 				code.DisplayIdx,
 				strings.Repeat("-", int(code.Indent)),
 				code.Op,
@@ -607,44 +568,7 @@ func (c *Opcode) Dump() string {
 	return strings.Join(codes, "\n")
 }
 
-func prevField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
-	if _, exists := removedFields[code]; exists {
-		return prevField(code.PrevField, removedFields)
-	}
-	return code
-}
-
-func nextField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
-	if _, exists := removedFields[code]; exists {
-		return nextField(code.NextField, removedFields)
-	}
-	return code
-}
-
-func linkPrevToNextField(cur *Opcode, removedFields map[*Opcode]struct{}) {
-	prev := prevField(cur.PrevField, removedFields)
-	prev.NextField = nextField(cur.NextField, removedFields)
-	code := prev
-	fcode := cur
-	for {
-		var nextCode *Opcode
-		switch code.Op.CodeType() {
-		case CodeArrayElem, CodeSliceElem, CodeMapKey:
-			nextCode = code.End
-		default:
-			nextCode = code.Next
-		}
-		if nextCode == fcode {
-			code.Next = fcode.NextField
-			break
-		} else if nextCode.Op == OpEnd {
-			break
-		}
-		code = nextCode
-	}
-}
-
-func newSliceHeaderCode(ctx *compileContext) *Opcode {
+func newSliceHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
 	idx := opcodeOffset(ctx.ptrIndex)
 	ctx.incPtrIndex()
 	elemIdx := opcodeOffset(ctx.ptrIndex)
@@ -652,6 +576,7 @@ func newSliceHeaderCode(ctx *compileContext) *Opcode {
 	length := opcodeOffset(ctx.ptrIndex)
 	return &Opcode{
 		Op:         OpSlice,
+		Type:       typ,
 		Idx:        idx,
 		DisplayIdx: ctx.opcodeIndex,
 		ElemIdx:    elemIdx,
@@ -660,9 +585,10 @@ func newSliceHeaderCode(ctx *compileContext) *Opcode {
 	}
 }
 
-func newSliceElemCode(ctx *compileContext, head *Opcode, size uintptr) *Opcode {
+func newSliceElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, size uintptr) *Opcode {
 	return &Opcode{
 		Op:         OpSliceElem,
+		Type:       typ,
 		Idx:        head.Idx,
 		DisplayIdx: ctx.opcodeIndex,
 		ElemIdx:    head.ElemIdx,
@@ -672,12 +598,13 @@ func newSliceElemCode(ctx *compileContext, head *Opcode, size uintptr) *Opcode {
 	}
 }
 
-func newArrayHeaderCode(ctx *compileContext, alen int) *Opcode {
+func newArrayHeaderCode(ctx *compileContext, typ *runtime.Type, alen int) *Opcode {
 	idx := opcodeOffset(ctx.ptrIndex)
 	ctx.incPtrIndex()
 	elemIdx := opcodeOffset(ctx.ptrIndex)
 	return &Opcode{
 		Op:         OpArray,
+		Type:       typ,
 		Idx:        idx,
 		DisplayIdx: ctx.opcodeIndex,
 		ElemIdx:    elemIdx,
@@ -686,9 +613,10 @@ func newArrayHeaderCode(ctx *compileContext, alen int) *Opcode {
 	}
 }
 
-func newArrayElemCode(ctx *compileContext, head *Opcode, length int, size uintptr) *Opcode {
+func newArrayElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, length int, size uintptr) *Opcode {
 	return &Opcode{
 		Op:         OpArrayElem,
+		Type:       typ,
 		Idx:        head.Idx,
 		DisplayIdx: ctx.opcodeIndex,
 		ElemIdx:    head.ElemIdx,
@@ -698,7 +626,7 @@ func newArrayElemCode(ctx *compileContext, head *Opcode, length int, size uintpt
 	}
 }
 
-func newMapHeaderCode(ctx *compileContext) *Opcode {
+func newMapHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
 	idx := opcodeOffset(ctx.ptrIndex)
 	ctx.incPtrIndex()
 	elemIdx := opcodeOffset(ctx.ptrIndex)
@@ -708,8 +636,8 @@ func newMapHeaderCode(ctx *compileContext) *Opcode {
 	mapIter := opcodeOffset(ctx.ptrIndex)
 	return &Opcode{
 		Op:         OpMap,
+		Type:       typ,
 		Idx:        idx,
-		Type:       ctx.typ,
 		DisplayIdx: ctx.opcodeIndex,
 		ElemIdx:    elemIdx,
 		Length:     length,
@@ -718,9 +646,10 @@ func newMapHeaderCode(ctx *compileContext) *Opcode {
 	}
 }
 
-func newMapKeyCode(ctx *compileContext, head *Opcode) *Opcode {
+func newMapKeyCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
 	return &Opcode{
 		Op:         OpMapKey,
+		Type:       typ,
 		Idx:        opcodeOffset(ctx.ptrIndex),
 		DisplayIdx: ctx.opcodeIndex,
 		ElemIdx:    head.ElemIdx,
@@ -730,9 +659,10 @@ func newMapKeyCode(ctx *compileContext, head *Opcode) *Opcode {
 	}
 }
 
-func newMapValueCode(ctx *compileContext, head *Opcode) *Opcode {
+func newMapValueCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
 	return &Opcode{
 		Op:         OpMapValue,
+		Type:       typ,
 		Idx:        opcodeOffset(ctx.ptrIndex),
 		DisplayIdx: ctx.opcodeIndex,
 		ElemIdx:    head.ElemIdx,
@@ -742,14 +672,15 @@ func newMapValueCode(ctx *compileContext, head *Opcode) *Opcode {
 	}
 }
 
-func newMapEndCode(ctx *compileContext, head *Opcode) *Opcode {
+func newMapEndCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
 	mapPos := opcodeOffset(ctx.ptrIndex)
 	ctx.incPtrIndex()
 	idx := opcodeOffset(ctx.ptrIndex)
 	return &Opcode{
 		Op:         OpMapEnd,
+		Type:       typ,
 		Idx:        idx,
-		Next:       newEndOp(ctx),
+		Next:       newEndOp(ctx, typ),
 		DisplayIdx: ctx.opcodeIndex,
 		Length:     head.Length,
 		MapPos:     mapPos,
@@ -757,28 +688,12 @@ func newMapEndCode(ctx *compileContext, head *Opcode) *Opcode {
 	}
 }
 
-func newInterfaceCode(ctx *compileContext) *Opcode {
-	var flag OpFlags
-	if ctx.typ.NumMethod() > 0 {
-		flag |= NonEmptyInterfaceFlags
-	}
-	return &Opcode{
-		Op:         OpInterface,
-		Idx:        opcodeOffset(ctx.ptrIndex),
-		Next:       newEndOp(ctx),
-		Type:       ctx.typ,
-		DisplayIdx: ctx.opcodeIndex,
-		Indent:     ctx.indent,
-		Flags:      flag,
-	}
-}
-
-func newRecursiveCode(ctx *compileContext, jmp *CompiledCode) *Opcode {
+func newRecursiveCode(ctx *compileContext, typ *runtime.Type, jmp *CompiledCode) *Opcode {
 	return &Opcode{
 		Op:         OpRecursive,
+		Type:       typ,
 		Idx:        opcodeOffset(ctx.ptrIndex),
-		Next:       newEndOp(ctx),
-		Type:       ctx.typ,
+		Next:       newEndOp(ctx, typ),
 		DisplayIdx: ctx.opcodeIndex,
 		Indent:     ctx.indent,
 		Jmp:        jmp,
diff --git a/internal/encoder/optype.go b/internal/encoder/optype.go
index 335fc04..890dd9f 100644
--- a/internal/encoder/optype.go
+++ b/internal/encoder/optype.go
@@ -22,7 +22,7 @@ const (
 	CodeStructEnd   CodeType = 11
 )
 
-var opTypeStrings = [401]string{
+var opTypeStrings = [400]string{
 	"End",
 	"Interface",
 	"Ptr",
@@ -37,7 +37,6 @@ var opTypeStrings = [401]string{
 	"RecursivePtr",
 	"RecursiveEnd",
 	"InterfaceEnd",
-	"StructAnonymousEnd",
 	"Int",
 	"Uint",
 	"Float32",
@@ -443,397 +442,396 @@ const (
 	OpRecursivePtr                           OpType = 11
 	OpRecursiveEnd                           OpType = 12
 	OpInterfaceEnd                           OpType = 13
-	OpStructAnonymousEnd                     OpType = 14
-	OpInt                                    OpType = 15
-	OpUint                                   OpType = 16
-	OpFloat32                                OpType = 17
-	OpFloat64                                OpType = 18
-	OpBool                                   OpType = 19
-	OpString                                 OpType = 20
-	OpBytes                                  OpType = 21
-	OpNumber                                 OpType = 22
-	OpArray                                  OpType = 23
-	OpMap                                    OpType = 24
-	OpSlice                                  OpType = 25
-	OpStruct                                 OpType = 26
-	OpMarshalJSON                            OpType = 27
-	OpMarshalText                            OpType = 28
-	OpIntString                              OpType = 29
-	OpUintString                             OpType = 30
-	OpFloat32String                          OpType = 31
-	OpFloat64String                          OpType = 32
-	OpBoolString                             OpType = 33
-	OpStringString                           OpType = 34
-	OpNumberString                           OpType = 35
-	OpIntPtr                                 OpType = 36
-	OpUintPtr                                OpType = 37
-	OpFloat32Ptr                             OpType = 38
-	OpFloat64Ptr                             OpType = 39
-	OpBoolPtr                                OpType = 40
-	OpStringPtr                              OpType = 41
-	OpBytesPtr                               OpType = 42
-	OpNumberPtr                              OpType = 43
-	OpArrayPtr                               OpType = 44
-	OpMapPtr                                 OpType = 45
-	OpSlicePtr                               OpType = 46
-	OpMarshalJSONPtr                         OpType = 47
-	OpMarshalTextPtr                         OpType = 48
-	OpInterfacePtr                           OpType = 49
-	OpIntPtrString                           OpType = 50
-	OpUintPtrString                          OpType = 51
-	OpFloat32PtrString                       OpType = 52
-	OpFloat64PtrString                       OpType = 53
-	OpBoolPtrString                          OpType = 54
-	OpStringPtrString                        OpType = 55
-	OpNumberPtrString                        OpType = 56
-	OpStructHeadInt                          OpType = 57
-	OpStructHeadOmitEmptyInt                 OpType = 58
-	OpStructPtrHeadInt                       OpType = 59
-	OpStructPtrHeadOmitEmptyInt              OpType = 60
-	OpStructHeadUint                         OpType = 61
-	OpStructHeadOmitEmptyUint                OpType = 62
-	OpStructPtrHeadUint                      OpType = 63
-	OpStructPtrHeadOmitEmptyUint             OpType = 64
-	OpStructHeadFloat32                      OpType = 65
-	OpStructHeadOmitEmptyFloat32             OpType = 66
-	OpStructPtrHeadFloat32                   OpType = 67
-	OpStructPtrHeadOmitEmptyFloat32          OpType = 68
-	OpStructHeadFloat64                      OpType = 69
-	OpStructHeadOmitEmptyFloat64             OpType = 70
-	OpStructPtrHeadFloat64                   OpType = 71
-	OpStructPtrHeadOmitEmptyFloat64          OpType = 72
-	OpStructHeadBool                         OpType = 73
-	OpStructHeadOmitEmptyBool                OpType = 74
-	OpStructPtrHeadBool                      OpType = 75
-	OpStructPtrHeadOmitEmptyBool             OpType = 76
-	OpStructHeadString                       OpType = 77
-	OpStructHeadOmitEmptyString              OpType = 78
-	OpStructPtrHeadString                    OpType = 79
-	OpStructPtrHeadOmitEmptyString           OpType = 80
-	OpStructHeadBytes                        OpType = 81
-	OpStructHeadOmitEmptyBytes               OpType = 82
-	OpStructPtrHeadBytes                     OpType = 83
-	OpStructPtrHeadOmitEmptyBytes            OpType = 84
-	OpStructHeadNumber                       OpType = 85
-	OpStructHeadOmitEmptyNumber              OpType = 86
-	OpStructPtrHeadNumber                    OpType = 87
-	OpStructPtrHeadOmitEmptyNumber           OpType = 88
-	OpStructHeadArray                        OpType = 89
-	OpStructHeadOmitEmptyArray               OpType = 90
-	OpStructPtrHeadArray                     OpType = 91
-	OpStructPtrHeadOmitEmptyArray            OpType = 92
-	OpStructHeadMap                          OpType = 93
-	OpStructHeadOmitEmptyMap                 OpType = 94
-	OpStructPtrHeadMap                       OpType = 95
-	OpStructPtrHeadOmitEmptyMap              OpType = 96
-	OpStructHeadSlice                        OpType = 97
-	OpStructHeadOmitEmptySlice               OpType = 98
-	OpStructPtrHeadSlice                     OpType = 99
-	OpStructPtrHeadOmitEmptySlice            OpType = 100
-	OpStructHeadStruct                       OpType = 101
-	OpStructHeadOmitEmptyStruct              OpType = 102
-	OpStructPtrHeadStruct                    OpType = 103
-	OpStructPtrHeadOmitEmptyStruct           OpType = 104
-	OpStructHeadMarshalJSON                  OpType = 105
-	OpStructHeadOmitEmptyMarshalJSON         OpType = 106
-	OpStructPtrHeadMarshalJSON               OpType = 107
-	OpStructPtrHeadOmitEmptyMarshalJSON      OpType = 108
-	OpStructHeadMarshalText                  OpType = 109
-	OpStructHeadOmitEmptyMarshalText         OpType = 110
-	OpStructPtrHeadMarshalText               OpType = 111
-	OpStructPtrHeadOmitEmptyMarshalText      OpType = 112
-	OpStructHeadIntString                    OpType = 113
-	OpStructHeadOmitEmptyIntString           OpType = 114
-	OpStructPtrHeadIntString                 OpType = 115
-	OpStructPtrHeadOmitEmptyIntString        OpType = 116
-	OpStructHeadUintString                   OpType = 117
-	OpStructHeadOmitEmptyUintString          OpType = 118
-	OpStructPtrHeadUintString                OpType = 119
-	OpStructPtrHeadOmitEmptyUintString       OpType = 120
-	OpStructHeadFloat32String                OpType = 121
-	OpStructHeadOmitEmptyFloat32String       OpType = 122
-	OpStructPtrHeadFloat32String             OpType = 123
-	OpStructPtrHeadOmitEmptyFloat32String    OpType = 124
-	OpStructHeadFloat64String                OpType = 125
-	OpStructHeadOmitEmptyFloat64String       OpType = 126
-	OpStructPtrHeadFloat64String             OpType = 127
-	OpStructPtrHeadOmitEmptyFloat64String    OpType = 128
-	OpStructHeadBoolString                   OpType = 129
-	OpStructHeadOmitEmptyBoolString          OpType = 130
-	OpStructPtrHeadBoolString                OpType = 131
-	OpStructPtrHeadOmitEmptyBoolString       OpType = 132
-	OpStructHeadStringString                 OpType = 133
-	OpStructHeadOmitEmptyStringString        OpType = 134
-	OpStructPtrHeadStringString              OpType = 135
-	OpStructPtrHeadOmitEmptyStringString     OpType = 136
-	OpStructHeadNumberString                 OpType = 137
-	OpStructHeadOmitEmptyNumberString        OpType = 138
-	OpStructPtrHeadNumberString              OpType = 139
-	OpStructPtrHeadOmitEmptyNumberString     OpType = 140
-	OpStructHeadIntPtr                       OpType = 141
-	OpStructHeadOmitEmptyIntPtr              OpType = 142
-	OpStructPtrHeadIntPtr                    OpType = 143
-	OpStructPtrHeadOmitEmptyIntPtr           OpType = 144
-	OpStructHeadUintPtr                      OpType = 145
-	OpStructHeadOmitEmptyUintPtr             OpType = 146
-	OpStructPtrHeadUintPtr                   OpType = 147
-	OpStructPtrHeadOmitEmptyUintPtr          OpType = 148
-	OpStructHeadFloat32Ptr                   OpType = 149
-	OpStructHeadOmitEmptyFloat32Ptr          OpType = 150
-	OpStructPtrHeadFloat32Ptr                OpType = 151
-	OpStructPtrHeadOmitEmptyFloat32Ptr       OpType = 152
-	OpStructHeadFloat64Ptr                   OpType = 153
-	OpStructHeadOmitEmptyFloat64Ptr          OpType = 154
-	OpStructPtrHeadFloat64Ptr                OpType = 155
-	OpStructPtrHeadOmitEmptyFloat64Ptr       OpType = 156
-	OpStructHeadBoolPtr                      OpType = 157
-	OpStructHeadOmitEmptyBoolPtr             OpType = 158
-	OpStructPtrHeadBoolPtr                   OpType = 159
-	OpStructPtrHeadOmitEmptyBoolPtr          OpType = 160
-	OpStructHeadStringPtr                    OpType = 161
-	OpStructHeadOmitEmptyStringPtr           OpType = 162
-	OpStructPtrHeadStringPtr                 OpType = 163
-	OpStructPtrHeadOmitEmptyStringPtr        OpType = 164
-	OpStructHeadBytesPtr                     OpType = 165
-	OpStructHeadOmitEmptyBytesPtr            OpType = 166
-	OpStructPtrHeadBytesPtr                  OpType = 167
-	OpStructPtrHeadOmitEmptyBytesPtr         OpType = 168
-	OpStructHeadNumberPtr                    OpType = 169
-	OpStructHeadOmitEmptyNumberPtr           OpType = 170
-	OpStructPtrHeadNumberPtr                 OpType = 171
-	OpStructPtrHeadOmitEmptyNumberPtr        OpType = 172
-	OpStructHeadArrayPtr                     OpType = 173
-	OpStructHeadOmitEmptyArrayPtr            OpType = 174
-	OpStructPtrHeadArrayPtr                  OpType = 175
-	OpStructPtrHeadOmitEmptyArrayPtr         OpType = 176
-	OpStructHeadMapPtr                       OpType = 177
-	OpStructHeadOmitEmptyMapPtr              OpType = 178
-	OpStructPtrHeadMapPtr                    OpType = 179
-	OpStructPtrHeadOmitEmptyMapPtr           OpType = 180
-	OpStructHeadSlicePtr                     OpType = 181
-	OpStructHeadOmitEmptySlicePtr            OpType = 182
-	OpStructPtrHeadSlicePtr                  OpType = 183
-	OpStructPtrHeadOmitEmptySlicePtr         OpType = 184
-	OpStructHeadMarshalJSONPtr               OpType = 185
-	OpStructHeadOmitEmptyMarshalJSONPtr      OpType = 186
-	OpStructPtrHeadMarshalJSONPtr            OpType = 187
-	OpStructPtrHeadOmitEmptyMarshalJSONPtr   OpType = 188
-	OpStructHeadMarshalTextPtr               OpType = 189
-	OpStructHeadOmitEmptyMarshalTextPtr      OpType = 190
-	OpStructPtrHeadMarshalTextPtr            OpType = 191
-	OpStructPtrHeadOmitEmptyMarshalTextPtr   OpType = 192
-	OpStructHeadInterfacePtr                 OpType = 193
-	OpStructHeadOmitEmptyInterfacePtr        OpType = 194
-	OpStructPtrHeadInterfacePtr              OpType = 195
-	OpStructPtrHeadOmitEmptyInterfacePtr     OpType = 196
-	OpStructHeadIntPtrString                 OpType = 197
-	OpStructHeadOmitEmptyIntPtrString        OpType = 198
-	OpStructPtrHeadIntPtrString              OpType = 199
-	OpStructPtrHeadOmitEmptyIntPtrString     OpType = 200
-	OpStructHeadUintPtrString                OpType = 201
-	OpStructHeadOmitEmptyUintPtrString       OpType = 202
-	OpStructPtrHeadUintPtrString             OpType = 203
-	OpStructPtrHeadOmitEmptyUintPtrString    OpType = 204
-	OpStructHeadFloat32PtrString             OpType = 205
-	OpStructHeadOmitEmptyFloat32PtrString    OpType = 206
-	OpStructPtrHeadFloat32PtrString          OpType = 207
-	OpStructPtrHeadOmitEmptyFloat32PtrString OpType = 208
-	OpStructHeadFloat64PtrString             OpType = 209
-	OpStructHeadOmitEmptyFloat64PtrString    OpType = 210
-	OpStructPtrHeadFloat64PtrString          OpType = 211
-	OpStructPtrHeadOmitEmptyFloat64PtrString OpType = 212
-	OpStructHeadBoolPtrString                OpType = 213
-	OpStructHeadOmitEmptyBoolPtrString       OpType = 214
-	OpStructPtrHeadBoolPtrString             OpType = 215
-	OpStructPtrHeadOmitEmptyBoolPtrString    OpType = 216
-	OpStructHeadStringPtrString              OpType = 217
-	OpStructHeadOmitEmptyStringPtrString     OpType = 218
-	OpStructPtrHeadStringPtrString           OpType = 219
-	OpStructPtrHeadOmitEmptyStringPtrString  OpType = 220
-	OpStructHeadNumberPtrString              OpType = 221
-	OpStructHeadOmitEmptyNumberPtrString     OpType = 222
-	OpStructPtrHeadNumberPtrString           OpType = 223
-	OpStructPtrHeadOmitEmptyNumberPtrString  OpType = 224
-	OpStructHead                             OpType = 225
-	OpStructHeadOmitEmpty                    OpType = 226
-	OpStructPtrHead                          OpType = 227
-	OpStructPtrHeadOmitEmpty                 OpType = 228
-	OpStructFieldInt                         OpType = 229
-	OpStructFieldOmitEmptyInt                OpType = 230
-	OpStructEndInt                           OpType = 231
-	OpStructEndOmitEmptyInt                  OpType = 232
-	OpStructFieldUint                        OpType = 233
-	OpStructFieldOmitEmptyUint               OpType = 234
-	OpStructEndUint                          OpType = 235
-	OpStructEndOmitEmptyUint                 OpType = 236
-	OpStructFieldFloat32                     OpType = 237
-	OpStructFieldOmitEmptyFloat32            OpType = 238
-	OpStructEndFloat32                       OpType = 239
-	OpStructEndOmitEmptyFloat32              OpType = 240
-	OpStructFieldFloat64                     OpType = 241
-	OpStructFieldOmitEmptyFloat64            OpType = 242
-	OpStructEndFloat64                       OpType = 243
-	OpStructEndOmitEmptyFloat64              OpType = 244
-	OpStructFieldBool                        OpType = 245
-	OpStructFieldOmitEmptyBool               OpType = 246
-	OpStructEndBool                          OpType = 247
-	OpStructEndOmitEmptyBool                 OpType = 248
-	OpStructFieldString                      OpType = 249
-	OpStructFieldOmitEmptyString             OpType = 250
-	OpStructEndString                        OpType = 251
-	OpStructEndOmitEmptyString               OpType = 252
-	OpStructFieldBytes                       OpType = 253
-	OpStructFieldOmitEmptyBytes              OpType = 254
-	OpStructEndBytes                         OpType = 255
-	OpStructEndOmitEmptyBytes                OpType = 256
-	OpStructFieldNumber                      OpType = 257
-	OpStructFieldOmitEmptyNumber             OpType = 258
-	OpStructEndNumber                        OpType = 259
-	OpStructEndOmitEmptyNumber               OpType = 260
-	OpStructFieldArray                       OpType = 261
-	OpStructFieldOmitEmptyArray              OpType = 262
-	OpStructEndArray                         OpType = 263
-	OpStructEndOmitEmptyArray                OpType = 264
-	OpStructFieldMap                         OpType = 265
-	OpStructFieldOmitEmptyMap                OpType = 266
-	OpStructEndMap                           OpType = 267
-	OpStructEndOmitEmptyMap                  OpType = 268
-	OpStructFieldSlice                       OpType = 269
-	OpStructFieldOmitEmptySlice              OpType = 270
-	OpStructEndSlice                         OpType = 271
-	OpStructEndOmitEmptySlice                OpType = 272
-	OpStructFieldStruct                      OpType = 273
-	OpStructFieldOmitEmptyStruct             OpType = 274
-	OpStructEndStruct                        OpType = 275
-	OpStructEndOmitEmptyStruct               OpType = 276
-	OpStructFieldMarshalJSON                 OpType = 277
-	OpStructFieldOmitEmptyMarshalJSON        OpType = 278
-	OpStructEndMarshalJSON                   OpType = 279
-	OpStructEndOmitEmptyMarshalJSON          OpType = 280
-	OpStructFieldMarshalText                 OpType = 281
-	OpStructFieldOmitEmptyMarshalText        OpType = 282
-	OpStructEndMarshalText                   OpType = 283
-	OpStructEndOmitEmptyMarshalText          OpType = 284
-	OpStructFieldIntString                   OpType = 285
-	OpStructFieldOmitEmptyIntString          OpType = 286
-	OpStructEndIntString                     OpType = 287
-	OpStructEndOmitEmptyIntString            OpType = 288
-	OpStructFieldUintString                  OpType = 289
-	OpStructFieldOmitEmptyUintString         OpType = 290
-	OpStructEndUintString                    OpType = 291
-	OpStructEndOmitEmptyUintString           OpType = 292
-	OpStructFieldFloat32String               OpType = 293
-	OpStructFieldOmitEmptyFloat32String      OpType = 294
-	OpStructEndFloat32String                 OpType = 295
-	OpStructEndOmitEmptyFloat32String        OpType = 296
-	OpStructFieldFloat64String               OpType = 297
-	OpStructFieldOmitEmptyFloat64String      OpType = 298
-	OpStructEndFloat64String                 OpType = 299
-	OpStructEndOmitEmptyFloat64String        OpType = 300
-	OpStructFieldBoolString                  OpType = 301
-	OpStructFieldOmitEmptyBoolString         OpType = 302
-	OpStructEndBoolString                    OpType = 303
-	OpStructEndOmitEmptyBoolString           OpType = 304
-	OpStructFieldStringString                OpType = 305
-	OpStructFieldOmitEmptyStringString       OpType = 306
-	OpStructEndStringString                  OpType = 307
-	OpStructEndOmitEmptyStringString         OpType = 308
-	OpStructFieldNumberString                OpType = 309
-	OpStructFieldOmitEmptyNumberString       OpType = 310
-	OpStructEndNumberString                  OpType = 311
-	OpStructEndOmitEmptyNumberString         OpType = 312
-	OpStructFieldIntPtr                      OpType = 313
-	OpStructFieldOmitEmptyIntPtr             OpType = 314
-	OpStructEndIntPtr                        OpType = 315
-	OpStructEndOmitEmptyIntPtr               OpType = 316
-	OpStructFieldUintPtr                     OpType = 317
-	OpStructFieldOmitEmptyUintPtr            OpType = 318
-	OpStructEndUintPtr                       OpType = 319
-	OpStructEndOmitEmptyUintPtr              OpType = 320
-	OpStructFieldFloat32Ptr                  OpType = 321
-	OpStructFieldOmitEmptyFloat32Ptr         OpType = 322
-	OpStructEndFloat32Ptr                    OpType = 323
-	OpStructEndOmitEmptyFloat32Ptr           OpType = 324
-	OpStructFieldFloat64Ptr                  OpType = 325
-	OpStructFieldOmitEmptyFloat64Ptr         OpType = 326
-	OpStructEndFloat64Ptr                    OpType = 327
-	OpStructEndOmitEmptyFloat64Ptr           OpType = 328
-	OpStructFieldBoolPtr                     OpType = 329
-	OpStructFieldOmitEmptyBoolPtr            OpType = 330
-	OpStructEndBoolPtr                       OpType = 331
-	OpStructEndOmitEmptyBoolPtr              OpType = 332
-	OpStructFieldStringPtr                   OpType = 333
-	OpStructFieldOmitEmptyStringPtr          OpType = 334
-	OpStructEndStringPtr                     OpType = 335
-	OpStructEndOmitEmptyStringPtr            OpType = 336
-	OpStructFieldBytesPtr                    OpType = 337
-	OpStructFieldOmitEmptyBytesPtr           OpType = 338
-	OpStructEndBytesPtr                      OpType = 339
-	OpStructEndOmitEmptyBytesPtr             OpType = 340
-	OpStructFieldNumberPtr                   OpType = 341
-	OpStructFieldOmitEmptyNumberPtr          OpType = 342
-	OpStructEndNumberPtr                     OpType = 343
-	OpStructEndOmitEmptyNumberPtr            OpType = 344
-	OpStructFieldArrayPtr                    OpType = 345
-	OpStructFieldOmitEmptyArrayPtr           OpType = 346
-	OpStructEndArrayPtr                      OpType = 347
-	OpStructEndOmitEmptyArrayPtr             OpType = 348
-	OpStructFieldMapPtr                      OpType = 349
-	OpStructFieldOmitEmptyMapPtr             OpType = 350
-	OpStructEndMapPtr                        OpType = 351
-	OpStructEndOmitEmptyMapPtr               OpType = 352
-	OpStructFieldSlicePtr                    OpType = 353
-	OpStructFieldOmitEmptySlicePtr           OpType = 354
-	OpStructEndSlicePtr                      OpType = 355
-	OpStructEndOmitEmptySlicePtr             OpType = 356
-	OpStructFieldMarshalJSONPtr              OpType = 357
-	OpStructFieldOmitEmptyMarshalJSONPtr     OpType = 358
-	OpStructEndMarshalJSONPtr                OpType = 359
-	OpStructEndOmitEmptyMarshalJSONPtr       OpType = 360
-	OpStructFieldMarshalTextPtr              OpType = 361
-	OpStructFieldOmitEmptyMarshalTextPtr     OpType = 362
-	OpStructEndMarshalTextPtr                OpType = 363
-	OpStructEndOmitEmptyMarshalTextPtr       OpType = 364
-	OpStructFieldInterfacePtr                OpType = 365
-	OpStructFieldOmitEmptyInterfacePtr       OpType = 366
-	OpStructEndInterfacePtr                  OpType = 367
-	OpStructEndOmitEmptyInterfacePtr         OpType = 368
-	OpStructFieldIntPtrString                OpType = 369
-	OpStructFieldOmitEmptyIntPtrString       OpType = 370
-	OpStructEndIntPtrString                  OpType = 371
-	OpStructEndOmitEmptyIntPtrString         OpType = 372
-	OpStructFieldUintPtrString               OpType = 373
-	OpStructFieldOmitEmptyUintPtrString      OpType = 374
-	OpStructEndUintPtrString                 OpType = 375
-	OpStructEndOmitEmptyUintPtrString        OpType = 376
-	OpStructFieldFloat32PtrString            OpType = 377
-	OpStructFieldOmitEmptyFloat32PtrString   OpType = 378
-	OpStructEndFloat32PtrString              OpType = 379
-	OpStructEndOmitEmptyFloat32PtrString     OpType = 380
-	OpStructFieldFloat64PtrString            OpType = 381
-	OpStructFieldOmitEmptyFloat64PtrString   OpType = 382
-	OpStructEndFloat64PtrString              OpType = 383
-	OpStructEndOmitEmptyFloat64PtrString     OpType = 384
-	OpStructFieldBoolPtrString               OpType = 385
-	OpStructFieldOmitEmptyBoolPtrString      OpType = 386
-	OpStructEndBoolPtrString                 OpType = 387
-	OpStructEndOmitEmptyBoolPtrString        OpType = 388
-	OpStructFieldStringPtrString             OpType = 389
-	OpStructFieldOmitEmptyStringPtrString    OpType = 390
-	OpStructEndStringPtrString               OpType = 391
-	OpStructEndOmitEmptyStringPtrString      OpType = 392
-	OpStructFieldNumberPtrString             OpType = 393
-	OpStructFieldOmitEmptyNumberPtrString    OpType = 394
-	OpStructEndNumberPtrString               OpType = 395
-	OpStructEndOmitEmptyNumberPtrString      OpType = 396
-	OpStructField                            OpType = 397
-	OpStructFieldOmitEmpty                   OpType = 398
-	OpStructEnd                              OpType = 399
-	OpStructEndOmitEmpty                     OpType = 400
+	OpInt                                    OpType = 14
+	OpUint                                   OpType = 15
+	OpFloat32                                OpType = 16
+	OpFloat64                                OpType = 17
+	OpBool                                   OpType = 18
+	OpString                                 OpType = 19
+	OpBytes                                  OpType = 20
+	OpNumber                                 OpType = 21
+	OpArray                                  OpType = 22
+	OpMap                                    OpType = 23
+	OpSlice                                  OpType = 24
+	OpStruct                                 OpType = 25
+	OpMarshalJSON                            OpType = 26
+	OpMarshalText                            OpType = 27
+	OpIntString                              OpType = 28
+	OpUintString                             OpType = 29
+	OpFloat32String                          OpType = 30
+	OpFloat64String                          OpType = 31
+	OpBoolString                             OpType = 32
+	OpStringString                           OpType = 33
+	OpNumberString                           OpType = 34
+	OpIntPtr                                 OpType = 35
+	OpUintPtr                                OpType = 36
+	OpFloat32Ptr                             OpType = 37
+	OpFloat64Ptr                             OpType = 38
+	OpBoolPtr                                OpType = 39
+	OpStringPtr                              OpType = 40
+	OpBytesPtr                               OpType = 41
+	OpNumberPtr                              OpType = 42
+	OpArrayPtr                               OpType = 43
+	OpMapPtr                                 OpType = 44
+	OpSlicePtr                               OpType = 45
+	OpMarshalJSONPtr                         OpType = 46
+	OpMarshalTextPtr                         OpType = 47
+	OpInterfacePtr                           OpType = 48
+	OpIntPtrString                           OpType = 49
+	OpUintPtrString                          OpType = 50
+	OpFloat32PtrString                       OpType = 51
+	OpFloat64PtrString                       OpType = 52
+	OpBoolPtrString                          OpType = 53
+	OpStringPtrString                        OpType = 54
+	OpNumberPtrString                        OpType = 55
+	OpStructHeadInt                          OpType = 56
+	OpStructHeadOmitEmptyInt                 OpType = 57
+	OpStructPtrHeadInt                       OpType = 58
+	OpStructPtrHeadOmitEmptyInt              OpType = 59
+	OpStructHeadUint                         OpType = 60
+	OpStructHeadOmitEmptyUint                OpType = 61
+	OpStructPtrHeadUint                      OpType = 62
+	OpStructPtrHeadOmitEmptyUint             OpType = 63
+	OpStructHeadFloat32                      OpType = 64
+	OpStructHeadOmitEmptyFloat32             OpType = 65
+	OpStructPtrHeadFloat32                   OpType = 66
+	OpStructPtrHeadOmitEmptyFloat32          OpType = 67
+	OpStructHeadFloat64                      OpType = 68
+	OpStructHeadOmitEmptyFloat64             OpType = 69
+	OpStructPtrHeadFloat64                   OpType = 70
+	OpStructPtrHeadOmitEmptyFloat64          OpType = 71
+	OpStructHeadBool                         OpType = 72
+	OpStructHeadOmitEmptyBool                OpType = 73
+	OpStructPtrHeadBool                      OpType = 74
+	OpStructPtrHeadOmitEmptyBool             OpType = 75
+	OpStructHeadString                       OpType = 76
+	OpStructHeadOmitEmptyString              OpType = 77
+	OpStructPtrHeadString                    OpType = 78
+	OpStructPtrHeadOmitEmptyString           OpType = 79
+	OpStructHeadBytes                        OpType = 80
+	OpStructHeadOmitEmptyBytes               OpType = 81
+	OpStructPtrHeadBytes                     OpType = 82
+	OpStructPtrHeadOmitEmptyBytes            OpType = 83
+	OpStructHeadNumber                       OpType = 84
+	OpStructHeadOmitEmptyNumber              OpType = 85
+	OpStructPtrHeadNumber                    OpType = 86
+	OpStructPtrHeadOmitEmptyNumber           OpType = 87
+	OpStructHeadArray                        OpType = 88
+	OpStructHeadOmitEmptyArray               OpType = 89
+	OpStructPtrHeadArray                     OpType = 90
+	OpStructPtrHeadOmitEmptyArray            OpType = 91
+	OpStructHeadMap                          OpType = 92
+	OpStructHeadOmitEmptyMap                 OpType = 93
+	OpStructPtrHeadMap                       OpType = 94
+	OpStructPtrHeadOmitEmptyMap              OpType = 95
+	OpStructHeadSlice                        OpType = 96
+	OpStructHeadOmitEmptySlice               OpType = 97
+	OpStructPtrHeadSlice                     OpType = 98
+	OpStructPtrHeadOmitEmptySlice            OpType = 99
+	OpStructHeadStruct                       OpType = 100
+	OpStructHeadOmitEmptyStruct              OpType = 101
+	OpStructPtrHeadStruct                    OpType = 102
+	OpStructPtrHeadOmitEmptyStruct           OpType = 103
+	OpStructHeadMarshalJSON                  OpType = 104
+	OpStructHeadOmitEmptyMarshalJSON         OpType = 105
+	OpStructPtrHeadMarshalJSON               OpType = 106
+	OpStructPtrHeadOmitEmptyMarshalJSON      OpType = 107
+	OpStructHeadMarshalText                  OpType = 108
+	OpStructHeadOmitEmptyMarshalText         OpType = 109
+	OpStructPtrHeadMarshalText               OpType = 110
+	OpStructPtrHeadOmitEmptyMarshalText      OpType = 111
+	OpStructHeadIntString                    OpType = 112
+	OpStructHeadOmitEmptyIntString           OpType = 113
+	OpStructPtrHeadIntString                 OpType = 114
+	OpStructPtrHeadOmitEmptyIntString        OpType = 115
+	OpStructHeadUintString                   OpType = 116
+	OpStructHeadOmitEmptyUintString          OpType = 117
+	OpStructPtrHeadUintString                OpType = 118
+	OpStructPtrHeadOmitEmptyUintString       OpType = 119
+	OpStructHeadFloat32String                OpType = 120
+	OpStructHeadOmitEmptyFloat32String       OpType = 121
+	OpStructPtrHeadFloat32String             OpType = 122
+	OpStructPtrHeadOmitEmptyFloat32String    OpType = 123
+	OpStructHeadFloat64String                OpType = 124
+	OpStructHeadOmitEmptyFloat64String       OpType = 125
+	OpStructPtrHeadFloat64String             OpType = 126
+	OpStructPtrHeadOmitEmptyFloat64String    OpType = 127
+	OpStructHeadBoolString                   OpType = 128
+	OpStructHeadOmitEmptyBoolString          OpType = 129
+	OpStructPtrHeadBoolString                OpType = 130
+	OpStructPtrHeadOmitEmptyBoolString       OpType = 131
+	OpStructHeadStringString                 OpType = 132
+	OpStructHeadOmitEmptyStringString        OpType = 133
+	OpStructPtrHeadStringString              OpType = 134
+	OpStructPtrHeadOmitEmptyStringString     OpType = 135
+	OpStructHeadNumberString                 OpType = 136
+	OpStructHeadOmitEmptyNumberString        OpType = 137
+	OpStructPtrHeadNumberString              OpType = 138
+	OpStructPtrHeadOmitEmptyNumberString     OpType = 139
+	OpStructHeadIntPtr                       OpType = 140
+	OpStructHeadOmitEmptyIntPtr              OpType = 141
+	OpStructPtrHeadIntPtr                    OpType = 142
+	OpStructPtrHeadOmitEmptyIntPtr           OpType = 143
+	OpStructHeadUintPtr                      OpType = 144
+	OpStructHeadOmitEmptyUintPtr             OpType = 145
+	OpStructPtrHeadUintPtr                   OpType = 146
+	OpStructPtrHeadOmitEmptyUintPtr          OpType = 147
+	OpStructHeadFloat32Ptr                   OpType = 148
+	OpStructHeadOmitEmptyFloat32Ptr          OpType = 149
+	OpStructPtrHeadFloat32Ptr                OpType = 150
+	OpStructPtrHeadOmitEmptyFloat32Ptr       OpType = 151
+	OpStructHeadFloat64Ptr                   OpType = 152
+	OpStructHeadOmitEmptyFloat64Ptr          OpType = 153
+	OpStructPtrHeadFloat64Ptr                OpType = 154
+	OpStructPtrHeadOmitEmptyFloat64Ptr       OpType = 155
+	OpStructHeadBoolPtr                      OpType = 156
+	OpStructHeadOmitEmptyBoolPtr             OpType = 157
+	OpStructPtrHeadBoolPtr                   OpType = 158
+	OpStructPtrHeadOmitEmptyBoolPtr          OpType = 159
+	OpStructHeadStringPtr                    OpType = 160
+	OpStructHeadOmitEmptyStringPtr           OpType = 161
+	OpStructPtrHeadStringPtr                 OpType = 162
+	OpStructPtrHeadOmitEmptyStringPtr        OpType = 163
+	OpStructHeadBytesPtr                     OpType = 164
+	OpStructHeadOmitEmptyBytesPtr            OpType = 165
+	OpStructPtrHeadBytesPtr                  OpType = 166
+	OpStructPtrHeadOmitEmptyBytesPtr         OpType = 167
+	OpStructHeadNumberPtr                    OpType = 168
+	OpStructHeadOmitEmptyNumberPtr           OpType = 169
+	OpStructPtrHeadNumberPtr                 OpType = 170
+	OpStructPtrHeadOmitEmptyNumberPtr        OpType = 171
+	OpStructHeadArrayPtr                     OpType = 172
+	OpStructHeadOmitEmptyArrayPtr            OpType = 173
+	OpStructPtrHeadArrayPtr                  OpType = 174
+	OpStructPtrHeadOmitEmptyArrayPtr         OpType = 175
+	OpStructHeadMapPtr                       OpType = 176
+	OpStructHeadOmitEmptyMapPtr              OpType = 177
+	OpStructPtrHeadMapPtr                    OpType = 178
+	OpStructPtrHeadOmitEmptyMapPtr           OpType = 179
+	OpStructHeadSlicePtr                     OpType = 180
+	OpStructHeadOmitEmptySlicePtr            OpType = 181
+	OpStructPtrHeadSlicePtr                  OpType = 182
+	OpStructPtrHeadOmitEmptySlicePtr         OpType = 183
+	OpStructHeadMarshalJSONPtr               OpType = 184
+	OpStructHeadOmitEmptyMarshalJSONPtr      OpType = 185
+	OpStructPtrHeadMarshalJSONPtr            OpType = 186
+	OpStructPtrHeadOmitEmptyMarshalJSONPtr   OpType = 187
+	OpStructHeadMarshalTextPtr               OpType = 188
+	OpStructHeadOmitEmptyMarshalTextPtr      OpType = 189
+	OpStructPtrHeadMarshalTextPtr            OpType = 190
+	OpStructPtrHeadOmitEmptyMarshalTextPtr   OpType = 191
+	OpStructHeadInterfacePtr                 OpType = 192
+	OpStructHeadOmitEmptyInterfacePtr        OpType = 193
+	OpStructPtrHeadInterfacePtr              OpType = 194
+	OpStructPtrHeadOmitEmptyInterfacePtr     OpType = 195
+	OpStructHeadIntPtrString                 OpType = 196
+	OpStructHeadOmitEmptyIntPtrString        OpType = 197
+	OpStructPtrHeadIntPtrString              OpType = 198
+	OpStructPtrHeadOmitEmptyIntPtrString     OpType = 199
+	OpStructHeadUintPtrString                OpType = 200
+	OpStructHeadOmitEmptyUintPtrString       OpType = 201
+	OpStructPtrHeadUintPtrString             OpType = 202
+	OpStructPtrHeadOmitEmptyUintPtrString    OpType = 203
+	OpStructHeadFloat32PtrString             OpType = 204
+	OpStructHeadOmitEmptyFloat32PtrString    OpType = 205
+	OpStructPtrHeadFloat32PtrString          OpType = 206
+	OpStructPtrHeadOmitEmptyFloat32PtrString OpType = 207
+	OpStructHeadFloat64PtrString             OpType = 208
+	OpStructHeadOmitEmptyFloat64PtrString    OpType = 209
+	OpStructPtrHeadFloat64PtrString          OpType = 210
+	OpStructPtrHeadOmitEmptyFloat64PtrString OpType = 211
+	OpStructHeadBoolPtrString                OpType = 212
+	OpStructHeadOmitEmptyBoolPtrString       OpType = 213
+	OpStructPtrHeadBoolPtrString             OpType = 214
+	OpStructPtrHeadOmitEmptyBoolPtrString    OpType = 215
+	OpStructHeadStringPtrString              OpType = 216
+	OpStructHeadOmitEmptyStringPtrString     OpType = 217
+	OpStructPtrHeadStringPtrString           OpType = 218
+	OpStructPtrHeadOmitEmptyStringPtrString  OpType = 219
+	OpStructHeadNumberPtrString              OpType = 220
+	OpStructHeadOmitEmptyNumberPtrString     OpType = 221
+	OpStructPtrHeadNumberPtrString           OpType = 222
+	OpStructPtrHeadOmitEmptyNumberPtrString  OpType = 223
+	OpStructHead                             OpType = 224
+	OpStructHeadOmitEmpty                    OpType = 225
+	OpStructPtrHead                          OpType = 226
+	OpStructPtrHeadOmitEmpty                 OpType = 227
+	OpStructFieldInt                         OpType = 228
+	OpStructFieldOmitEmptyInt                OpType = 229
+	OpStructEndInt                           OpType = 230
+	OpStructEndOmitEmptyInt                  OpType = 231
+	OpStructFieldUint                        OpType = 232
+	OpStructFieldOmitEmptyUint               OpType = 233
+	OpStructEndUint                          OpType = 234
+	OpStructEndOmitEmptyUint                 OpType = 235
+	OpStructFieldFloat32                     OpType = 236
+	OpStructFieldOmitEmptyFloat32            OpType = 237
+	OpStructEndFloat32                       OpType = 238
+	OpStructEndOmitEmptyFloat32              OpType = 239
+	OpStructFieldFloat64                     OpType = 240
+	OpStructFieldOmitEmptyFloat64            OpType = 241
+	OpStructEndFloat64                       OpType = 242
+	OpStructEndOmitEmptyFloat64              OpType = 243
+	OpStructFieldBool                        OpType = 244
+	OpStructFieldOmitEmptyBool               OpType = 245
+	OpStructEndBool                          OpType = 246
+	OpStructEndOmitEmptyBool                 OpType = 247
+	OpStructFieldString                      OpType = 248
+	OpStructFieldOmitEmptyString             OpType = 249
+	OpStructEndString                        OpType = 250
+	OpStructEndOmitEmptyString               OpType = 251
+	OpStructFieldBytes                       OpType = 252
+	OpStructFieldOmitEmptyBytes              OpType = 253
+	OpStructEndBytes                         OpType = 254
+	OpStructEndOmitEmptyBytes                OpType = 255
+	OpStructFieldNumber                      OpType = 256
+	OpStructFieldOmitEmptyNumber             OpType = 257
+	OpStructEndNumber                        OpType = 258
+	OpStructEndOmitEmptyNumber               OpType = 259
+	OpStructFieldArray                       OpType = 260
+	OpStructFieldOmitEmptyArray              OpType = 261
+	OpStructEndArray                         OpType = 262
+	OpStructEndOmitEmptyArray                OpType = 263
+	OpStructFieldMap                         OpType = 264
+	OpStructFieldOmitEmptyMap                OpType = 265
+	OpStructEndMap                           OpType = 266
+	OpStructEndOmitEmptyMap                  OpType = 267
+	OpStructFieldSlice                       OpType = 268
+	OpStructFieldOmitEmptySlice              OpType = 269
+	OpStructEndSlice                         OpType = 270
+	OpStructEndOmitEmptySlice                OpType = 271
+	OpStructFieldStruct                      OpType = 272
+	OpStructFieldOmitEmptyStruct             OpType = 273
+	OpStructEndStruct                        OpType = 274
+	OpStructEndOmitEmptyStruct               OpType = 275
+	OpStructFieldMarshalJSON                 OpType = 276
+	OpStructFieldOmitEmptyMarshalJSON        OpType = 277
+	OpStructEndMarshalJSON                   OpType = 278
+	OpStructEndOmitEmptyMarshalJSON          OpType = 279
+	OpStructFieldMarshalText                 OpType = 280
+	OpStructFieldOmitEmptyMarshalText        OpType = 281
+	OpStructEndMarshalText                   OpType = 282
+	OpStructEndOmitEmptyMarshalText          OpType = 283
+	OpStructFieldIntString                   OpType = 284
+	OpStructFieldOmitEmptyIntString          OpType = 285
+	OpStructEndIntString                     OpType = 286
+	OpStructEndOmitEmptyIntString            OpType = 287
+	OpStructFieldUintString                  OpType = 288
+	OpStructFieldOmitEmptyUintString         OpType = 289
+	OpStructEndUintString                    OpType = 290
+	OpStructEndOmitEmptyUintString           OpType = 291
+	OpStructFieldFloat32String               OpType = 292
+	OpStructFieldOmitEmptyFloat32String      OpType = 293
+	OpStructEndFloat32String                 OpType = 294
+	OpStructEndOmitEmptyFloat32String        OpType = 295
+	OpStructFieldFloat64String               OpType = 296
+	OpStructFieldOmitEmptyFloat64String      OpType = 297
+	OpStructEndFloat64String                 OpType = 298
+	OpStructEndOmitEmptyFloat64String        OpType = 299
+	OpStructFieldBoolString                  OpType = 300
+	OpStructFieldOmitEmptyBoolString         OpType = 301
+	OpStructEndBoolString                    OpType = 302
+	OpStructEndOmitEmptyBoolString           OpType = 303
+	OpStructFieldStringString                OpType = 304
+	OpStructFieldOmitEmptyStringString       OpType = 305
+	OpStructEndStringString                  OpType = 306
+	OpStructEndOmitEmptyStringString         OpType = 307
+	OpStructFieldNumberString                OpType = 308
+	OpStructFieldOmitEmptyNumberString       OpType = 309
+	OpStructEndNumberString                  OpType = 310
+	OpStructEndOmitEmptyNumberString         OpType = 311
+	OpStructFieldIntPtr                      OpType = 312
+	OpStructFieldOmitEmptyIntPtr             OpType = 313
+	OpStructEndIntPtr                        OpType = 314
+	OpStructEndOmitEmptyIntPtr               OpType = 315
+	OpStructFieldUintPtr                     OpType = 316
+	OpStructFieldOmitEmptyUintPtr            OpType = 317
+	OpStructEndUintPtr                       OpType = 318
+	OpStructEndOmitEmptyUintPtr              OpType = 319
+	OpStructFieldFloat32Ptr                  OpType = 320
+	OpStructFieldOmitEmptyFloat32Ptr         OpType = 321
+	OpStructEndFloat32Ptr                    OpType = 322
+	OpStructEndOmitEmptyFloat32Ptr           OpType = 323
+	OpStructFieldFloat64Ptr                  OpType = 324
+	OpStructFieldOmitEmptyFloat64Ptr         OpType = 325
+	OpStructEndFloat64Ptr                    OpType = 326
+	OpStructEndOmitEmptyFloat64Ptr           OpType = 327
+	OpStructFieldBoolPtr                     OpType = 328
+	OpStructFieldOmitEmptyBoolPtr            OpType = 329
+	OpStructEndBoolPtr                       OpType = 330
+	OpStructEndOmitEmptyBoolPtr              OpType = 331
+	OpStructFieldStringPtr                   OpType = 332
+	OpStructFieldOmitEmptyStringPtr          OpType = 333
+	OpStructEndStringPtr                     OpType = 334
+	OpStructEndOmitEmptyStringPtr            OpType = 335
+	OpStructFieldBytesPtr                    OpType = 336
+	OpStructFieldOmitEmptyBytesPtr           OpType = 337
+	OpStructEndBytesPtr                      OpType = 338
+	OpStructEndOmitEmptyBytesPtr             OpType = 339
+	OpStructFieldNumberPtr                   OpType = 340
+	OpStructFieldOmitEmptyNumberPtr          OpType = 341
+	OpStructEndNumberPtr                     OpType = 342
+	OpStructEndOmitEmptyNumberPtr            OpType = 343
+	OpStructFieldArrayPtr                    OpType = 344
+	OpStructFieldOmitEmptyArrayPtr           OpType = 345
+	OpStructEndArrayPtr                      OpType = 346
+	OpStructEndOmitEmptyArrayPtr             OpType = 347
+	OpStructFieldMapPtr                      OpType = 348
+	OpStructFieldOmitEmptyMapPtr             OpType = 349
+	OpStructEndMapPtr                        OpType = 350
+	OpStructEndOmitEmptyMapPtr               OpType = 351
+	OpStructFieldSlicePtr                    OpType = 352
+	OpStructFieldOmitEmptySlicePtr           OpType = 353
+	OpStructEndSlicePtr                      OpType = 354
+	OpStructEndOmitEmptySlicePtr             OpType = 355
+	OpStructFieldMarshalJSONPtr              OpType = 356
+	OpStructFieldOmitEmptyMarshalJSONPtr     OpType = 357
+	OpStructEndMarshalJSONPtr                OpType = 358
+	OpStructEndOmitEmptyMarshalJSONPtr       OpType = 359
+	OpStructFieldMarshalTextPtr              OpType = 360
+	OpStructFieldOmitEmptyMarshalTextPtr     OpType = 361
+	OpStructEndMarshalTextPtr                OpType = 362
+	OpStructEndOmitEmptyMarshalTextPtr       OpType = 363
+	OpStructFieldInterfacePtr                OpType = 364
+	OpStructFieldOmitEmptyInterfacePtr       OpType = 365
+	OpStructEndInterfacePtr                  OpType = 366
+	OpStructEndOmitEmptyInterfacePtr         OpType = 367
+	OpStructFieldIntPtrString                OpType = 368
+	OpStructFieldOmitEmptyIntPtrString       OpType = 369
+	OpStructEndIntPtrString                  OpType = 370
+	OpStructEndOmitEmptyIntPtrString         OpType = 371
+	OpStructFieldUintPtrString               OpType = 372
+	OpStructFieldOmitEmptyUintPtrString      OpType = 373
+	OpStructEndUintPtrString                 OpType = 374
+	OpStructEndOmitEmptyUintPtrString        OpType = 375
+	OpStructFieldFloat32PtrString            OpType = 376
+	OpStructFieldOmitEmptyFloat32PtrString   OpType = 377
+	OpStructEndFloat32PtrString              OpType = 378
+	OpStructEndOmitEmptyFloat32PtrString     OpType = 379
+	OpStructFieldFloat64PtrString            OpType = 380
+	OpStructFieldOmitEmptyFloat64PtrString   OpType = 381
+	OpStructEndFloat64PtrString              OpType = 382
+	OpStructEndOmitEmptyFloat64PtrString     OpType = 383
+	OpStructFieldBoolPtrString               OpType = 384
+	OpStructFieldOmitEmptyBoolPtrString      OpType = 385
+	OpStructEndBoolPtrString                 OpType = 386
+	OpStructEndOmitEmptyBoolPtrString        OpType = 387
+	OpStructFieldStringPtrString             OpType = 388
+	OpStructFieldOmitEmptyStringPtrString    OpType = 389
+	OpStructEndStringPtrString               OpType = 390
+	OpStructEndOmitEmptyStringPtrString      OpType = 391
+	OpStructFieldNumberPtrString             OpType = 392
+	OpStructFieldOmitEmptyNumberPtrString    OpType = 393
+	OpStructEndNumberPtrString               OpType = 394
+	OpStructEndOmitEmptyNumberPtrString      OpType = 395
+	OpStructField                            OpType = 396
+	OpStructFieldOmitEmpty                   OpType = 397
+	OpStructEnd                              OpType = 398
+	OpStructEndOmitEmpty                     OpType = 399
 )
 
 func (t OpType) String() string {
-	if int(t) >= 401 {
+	if int(t) >= 400 {
 		return ""
 	}
 	return opTypeStrings[int(t)]
diff --git a/internal/encoder/vm/vm.go b/internal/encoder/vm/vm.go
index 7701501..351982a 100644
--- a/internal/encoder/vm/vm.go
+++ b/internal/encoder/vm/vm.go
@@ -4349,8 +4349,6 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
 				code = code.Next
 				store(ctxptr, code.Idx, p)
 			}
-		case encoder.OpStructAnonymousEnd:
-			code = code.Next
 		case encoder.OpStructEnd:
 			b = appendStructEndSkipLast(ctx, code, b)
 			code = code.Next
diff --git a/internal/encoder/vm_color/vm.go b/internal/encoder/vm_color/vm.go
index c90bdf6..58beb4b 100644
--- a/internal/encoder/vm_color/vm.go
+++ b/internal/encoder/vm_color/vm.go
@@ -4349,8 +4349,6 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
 				code = code.Next
 				store(ctxptr, code.Idx, p)
 			}
-		case encoder.OpStructAnonymousEnd:
-			code = code.Next
 		case encoder.OpStructEnd:
 			b = appendStructEndSkipLast(ctx, code, b)
 			code = code.Next
diff --git a/internal/encoder/vm_color_indent/vm.go b/internal/encoder/vm_color_indent/vm.go
index 5fda04b..bf4936c 100644
--- a/internal/encoder/vm_color_indent/vm.go
+++ b/internal/encoder/vm_color_indent/vm.go
@@ -4349,8 +4349,6 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
 				code = code.Next
 				store(ctxptr, code.Idx, p)
 			}
-		case encoder.OpStructAnonymousEnd:
-			code = code.Next
 		case encoder.OpStructEnd:
 			b = appendStructEndSkipLast(ctx, code, b)
 			code = code.Next
diff --git a/internal/encoder/vm_indent/vm.go b/internal/encoder/vm_indent/vm.go
index 8ca28cd..1eccc1d 100644
--- a/internal/encoder/vm_indent/vm.go
+++ b/internal/encoder/vm_indent/vm.go
@@ -4349,8 +4349,6 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
 				code = code.Next
 				store(ctxptr, code.Idx, p)
 			}
-		case encoder.OpStructAnonymousEnd:
-			code = code.Next
 		case encoder.OpStructEnd:
 			b = appendStructEndSkipLast(ctx, code, b)
 			code = code.Next


Test patch:
diff --git a/size_test.go b/size_test.go
index a57d663..3a47195 100644
--- a/size_test.go
+++ b/size_test.go
@@ -11,8 +11,8 @@ func TestOpcodeSize(t *testing.T) {
 	const uintptrSize = 4 << (^uintptr(0) >> 63)
 	if uintptrSize == 8 {
 		size := unsafe.Sizeof(encoder.Opcode{})
-		if size != 128 {
-			t.Fatalf("unexpected opcode size: expected 128bytes but got %dbytes", size)
+		if size != 120 {
+			t.Fatalf("unexpected opcode size: expected 120bytes but got %dbytes", size)
 		}
 	}
 }
diff --git a/test/cover/cover_array_test.go b/test/cover/cover_array_test.go
index 336ef39..1614078 100644
--- a/test/cover/cover_array_test.go
+++ b/test/cover/cover_array_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -1840,19 +1841,21 @@ func TestCoverArray(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_bool_test.go b/test/cover/cover_bool_test.go
index 2bc3f59..8dea174 100644
--- a/test/cover/cover_bool_test.go
+++ b/test/cover/cover_bool_test.go
@@ -3,6 +3,7 @@ package json_test
 import (
 	"bytes"
 	stdjson "encoding/json"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -2653,19 +2654,21 @@ func TestCoverBool(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_bytes_test.go b/test/cover/cover_bytes_test.go
index 49eff88..28ab9db 100644
--- a/test/cover/cover_bytes_test.go
+++ b/test/cover/cover_bytes_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -1807,19 +1808,21 @@ func TestCoverBytes(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%v,indent:%v): %v: %s", test.name, htmlEscape, indent, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%v,indent:%v): %v: %s", test.name, htmlEscape, indent, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_float32_test.go b/test/cover/cover_float32_test.go
index 961d8af..44fecdf 100644
--- a/test/cover/cover_float32_test.go
+++ b/test/cover/cover_float32_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -2334,19 +2335,21 @@ func TestCoverFloat32(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_float64_test.go b/test/cover/cover_float64_test.go
index 39235d0..c94644e 100644
--- a/test/cover/cover_float64_test.go
+++ b/test/cover/cover_float64_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -2334,19 +2335,21 @@ func TestCoverFloat64(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_int16_test.go b/test/cover/cover_int16_test.go
index f16a944..cc7867d 100644
--- a/test/cover/cover_int16_test.go
+++ b/test/cover/cover_int16_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -1811,19 +1812,21 @@ func TestCoverInt16(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_int32_test.go b/test/cover/cover_int32_test.go
index 71cd397..195301b 100644
--- a/test/cover/cover_int32_test.go
+++ b/test/cover/cover_int32_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -1788,19 +1789,21 @@ func TestCoverInt32(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_int64_test.go b/test/cover/cover_int64_test.go
index 0879cbf..612fa8e 100644
--- a/test/cover/cover_int64_test.go
+++ b/test/cover/cover_int64_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -1788,19 +1789,21 @@ func TestCoverInt64(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_int8_test.go b/test/cover/cover_int8_test.go
index 673b67b..b609e98 100644
--- a/test/cover/cover_int8_test.go
+++ b/test/cover/cover_int8_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -1788,19 +1789,21 @@ func TestCoverInt8(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_int_test.go b/test/cover/cover_int_test.go
index b21a528..c2377d4 100644
--- a/test/cover/cover_int_test.go
+++ b/test/cover/cover_int_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -2410,19 +2411,21 @@ func TestCoverInt(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_map_test.go b/test/cover/cover_map_test.go
index 2cf08d2..0b9a5fe 100644
--- a/test/cover/cover_map_test.go
+++ b/test/cover/cover_map_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -1881,19 +1882,21 @@ func TestCoverMap(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_marshal_json_test.go b/test/cover/cover_marshal_json_test.go
index 6931bf3..95d92b8 100644
--- a/test/cover/cover_marshal_json_test.go
+++ b/test/cover/cover_marshal_json_test.go
@@ -3690,19 +3690,21 @@ func TestCoverMarshalJSON(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 			for _, indent := range []bool{true, false} {
 				for _, htmlEscape := range []bool{true, false} {
-					var buf bytes.Buffer
-					enc := json.NewEncoder(&buf)
-					enc.SetEscapeHTML(htmlEscape)
-					if indent {
-						enc.SetIndent("", "  ")
-					}
-					if err := enc.Encode(test.data); err != nil {
-						t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
-					}
-					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-					if buf.String() != stdresult {
-						t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
-					}
+					t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+						var buf bytes.Buffer
+						enc := json.NewEncoder(&buf)
+						enc.SetEscapeHTML(htmlEscape)
+						if indent {
+							enc.SetIndent("", "  ")
+						}
+						if err := enc.Encode(test.data); err != nil {
+							t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
+						}
+						stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+						if buf.String() != stdresult {
+							t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
+						}
+					})
 				}
 			}
 		})
diff --git a/test/cover/cover_marshal_text_test.go b/test/cover/cover_marshal_text_test.go
index 62022ae..9cee6d4 100644
--- a/test/cover/cover_marshal_text_test.go
+++ b/test/cover/cover_marshal_text_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -3527,19 +3528,21 @@ func TestCoverMarshalText(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_number_test.go b/test/cover/cover_number_test.go
index dc4b1d3..5b98a08 100644
--- a/test/cover/cover_number_test.go
+++ b/test/cover/cover_number_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -2334,19 +2335,21 @@ func TestCoverNumber(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_slice_test.go b/test/cover/cover_slice_test.go
index eb60d94..61de9a5 100644
--- a/test/cover/cover_slice_test.go
+++ b/test/cover/cover_slice_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -2047,9 +2048,9 @@ func TestCoverSlice(t *testing.T) {
 		},
 	}
 	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			for _, indent := range []bool{true, false} {
-				for _, htmlEscape := range []bool{true, false} {
+		for _, indent := range []bool{true, false} {
+			for _, htmlEscape := range []bool{true, false} {
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
 					var buf bytes.Buffer
 					enc := json.NewEncoder(&buf)
 					enc.SetEscapeHTML(htmlEscape)
@@ -2063,8 +2064,8 @@ func TestCoverSlice(t *testing.T) {
 					if buf.String() != stdresult {
 						t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
 					}
-				}
+				})
 			}
-		})
+		}
 	}
 }
diff --git a/test/cover/cover_string_test.go b/test/cover/cover_string_test.go
index 591daaa..41904e4 100644
--- a/test/cover/cover_string_test.go
+++ b/test/cover/cover_string_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -2370,19 +2371,21 @@ func TestCoverString(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%v,indent:%v): %v: %s", test.name, htmlEscape, indent, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%v,indent:%v): %v: %s", test.name, htmlEscape, indent, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_uint16_test.go b/test/cover/cover_uint16_test.go
index f14470a..4514bb0 100644
--- a/test/cover/cover_uint16_test.go
+++ b/test/cover/cover_uint16_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -1788,19 +1789,21 @@ func TestCoverUint16(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_uint32_test.go b/test/cover/cover_uint32_test.go
index a0fb2f5..8d852b9 100644
--- a/test/cover/cover_uint32_test.go
+++ b/test/cover/cover_uint32_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -1788,19 +1789,21 @@ func TestCoverUint32(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_uint64_test.go b/test/cover/cover_uint64_test.go
index 4b56d54..bc074ce 100644
--- a/test/cover/cover_uint64_test.go
+++ b/test/cover/cover_uint64_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -1788,19 +1789,21 @@ func TestCoverUint64(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_uint8_test.go b/test/cover/cover_uint8_test.go
index 640e8f6..fab9403 100644
--- a/test/cover/cover_uint8_test.go
+++ b/test/cover/cover_uint8_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -1788,19 +1789,21 @@ func TestCoverUint8(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}
diff --git a/test/cover/cover_uint_test.go b/test/cover/cover_uint_test.go
index 35781ec..e33064a 100644
--- a/test/cover/cover_uint_test.go
+++ b/test/cover/cover_uint_test.go
@@ -2,6 +2,7 @@ package json_test
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-json"
@@ -2334,19 +2335,21 @@ func TestCoverUint(t *testing.T) {
 	for _, test := range tests {
 		for _, indent := range []bool{true, false} {
 			for _, htmlEscape := range []bool{true, false} {
-				var buf bytes.Buffer
-				enc := json.NewEncoder(&buf)
-				enc.SetEscapeHTML(htmlEscape)
-				if indent {
-					enc.SetIndent("", "  ")
-				}
-				if err := enc.Encode(test.data); err != nil {
-					t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
-				}
-				stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
-				if buf.String() != stdresult {
-					t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
-				}
+				t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
+					var buf bytes.Buffer
+					enc := json.NewEncoder(&buf)
+					enc.SetEscapeHTML(htmlEscape)
+					if indent {
+						enc.SetIndent("", "  ")
+					}
+					if err := enc.Encode(test.data); err != nil {
+						t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
+					}
+					stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
+					if buf.String() != stdresult {
+						t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
+					}
+				})
 			}
 		}
 	}