github.com/night-codes/go-json@v0.9.15/internal/encoder/opcode.go (about)

     1  package encoder
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"unsafe"
     7  
     8  	"github.com/night-codes/go-json/internal/runtime"
     9  )
    10  
    11  const uintptrSize = 4 << (^uintptr(0) >> 63)
    12  
    13  type OpFlags uint16
    14  
    15  const (
    16  	AnonymousHeadFlags     OpFlags = 1 << 0
    17  	AnonymousKeyFlags      OpFlags = 1 << 1
    18  	IndirectFlags          OpFlags = 1 << 2
    19  	IsTaggedKeyFlags       OpFlags = 1 << 3
    20  	NilCheckFlags          OpFlags = 1 << 4
    21  	AddrForMarshalerFlags  OpFlags = 1 << 5
    22  	IsNextOpPtrTypeFlags   OpFlags = 1 << 6
    23  	IsNilableTypeFlags     OpFlags = 1 << 7
    24  	MarshalerContextFlags  OpFlags = 1 << 8
    25  	NonEmptyInterfaceFlags OpFlags = 1 << 9
    26  )
    27  
    28  type Opcode struct {
    29  	Op         OpType  // operation type
    30  	Idx        uint32  // offset to access ptr
    31  	Next       *Opcode // next opcode
    32  	End        *Opcode // array/slice/struct/map end
    33  	NextField  *Opcode // next struct field
    34  	Key        string  // struct field key
    35  	Offset     uint32  // offset size from struct header
    36  	PtrNum     uint8   // pointer number: e.g. double pointer is 2.
    37  	NumBitSize uint8
    38  	Flags      OpFlags
    39  
    40  	Type       *runtime.Type // go type
    41  	Jmp        *CompiledCode // for recursive call
    42  	FieldQuery *FieldQuery   // field query for Interface / MarshalJSON / MarshalText
    43  	ElemIdx    uint32        // offset to access array/slice elem
    44  	Length     uint32        // offset to access slice length or array length
    45  	Indent     uint32        // indent number
    46  	Size       uint32        // array/slice elem size
    47  	DisplayIdx uint32        // opcode index
    48  	DisplayKey string        // key text to display
    49  }
    50  
    51  func (c *Opcode) Validate() error {
    52  	var prevIdx uint32
    53  	for code := c; !code.IsEnd(); {
    54  		if prevIdx != 0 {
    55  			if code.DisplayIdx != prevIdx+1 {
    56  				return fmt.Errorf(
    57  					"invalid index. previous display index is %d but next is %d. dump = %s",
    58  					prevIdx, code.DisplayIdx, c.Dump(),
    59  				)
    60  			}
    61  		}
    62  		prevIdx = code.DisplayIdx
    63  		code = code.IterNext()
    64  	}
    65  	return nil
    66  }
    67  
    68  func (c *Opcode) IterNext() *Opcode {
    69  	if c == nil {
    70  		return nil
    71  	}
    72  	switch c.Op.CodeType() {
    73  	case CodeArrayElem, CodeSliceElem, CodeMapKey:
    74  		return c.End
    75  	default:
    76  		return c.Next
    77  	}
    78  }
    79  
    80  func (c *Opcode) IsEnd() bool {
    81  	if c == nil {
    82  		return true
    83  	}
    84  	return c.Op == OpEnd || c.Op == OpInterfaceEnd || c.Op == OpRecursiveEnd
    85  }
    86  
    87  func (c *Opcode) MaxIdx() uint32 {
    88  	max := uint32(0)
    89  	for _, value := range []uint32{
    90  		c.Idx,
    91  		c.ElemIdx,
    92  		c.Length,
    93  		c.Size,
    94  	} {
    95  		if max < value {
    96  			max = value
    97  		}
    98  	}
    99  	return max
   100  }
   101  
   102  func (c *Opcode) ToHeaderType(isString bool) OpType {
   103  	switch c.Op {
   104  	case OpInt:
   105  		if isString {
   106  			return OpStructHeadIntString
   107  		}
   108  		return OpStructHeadInt
   109  	case OpIntPtr:
   110  		if isString {
   111  			return OpStructHeadIntPtrString
   112  		}
   113  		return OpStructHeadIntPtr
   114  	case OpUint:
   115  		if isString {
   116  			return OpStructHeadUintString
   117  		}
   118  		return OpStructHeadUint
   119  	case OpUintPtr:
   120  		if isString {
   121  			return OpStructHeadUintPtrString
   122  		}
   123  		return OpStructHeadUintPtr
   124  	case OpFloat32:
   125  		if isString {
   126  			return OpStructHeadFloat32String
   127  		}
   128  		return OpStructHeadFloat32
   129  	case OpFloat32Ptr:
   130  		if isString {
   131  			return OpStructHeadFloat32PtrString
   132  		}
   133  		return OpStructHeadFloat32Ptr
   134  	case OpFloat64:
   135  		if isString {
   136  			return OpStructHeadFloat64String
   137  		}
   138  		return OpStructHeadFloat64
   139  	case OpFloat64Ptr:
   140  		if isString {
   141  			return OpStructHeadFloat64PtrString
   142  		}
   143  		return OpStructHeadFloat64Ptr
   144  	case OpString:
   145  		if isString {
   146  			return OpStructHeadStringString
   147  		}
   148  		return OpStructHeadString
   149  	case OpStringPtr:
   150  		if isString {
   151  			return OpStructHeadStringPtrString
   152  		}
   153  		return OpStructHeadStringPtr
   154  	case OpNumber:
   155  		if isString {
   156  			return OpStructHeadNumberString
   157  		}
   158  		return OpStructHeadNumber
   159  	case OpNumberPtr:
   160  		if isString {
   161  			return OpStructHeadNumberPtrString
   162  		}
   163  		return OpStructHeadNumberPtr
   164  	case OpBool:
   165  		if isString {
   166  			return OpStructHeadBoolString
   167  		}
   168  		return OpStructHeadBool
   169  	case OpBoolPtr:
   170  		if isString {
   171  			return OpStructHeadBoolPtrString
   172  		}
   173  		return OpStructHeadBoolPtr
   174  	case OpBytes:
   175  		return OpStructHeadBytes
   176  	case OpBytesPtr:
   177  		return OpStructHeadBytesPtr
   178  	case OpMap:
   179  		return OpStructHeadMap
   180  	case OpMapPtr:
   181  		c.Op = OpMap
   182  		return OpStructHeadMapPtr
   183  	case OpArray:
   184  		return OpStructHeadArray
   185  	case OpArrayPtr:
   186  		c.Op = OpArray
   187  		return OpStructHeadArrayPtr
   188  	case OpSlice:
   189  		return OpStructHeadSlice
   190  	case OpSlicePtr:
   191  		c.Op = OpSlice
   192  		return OpStructHeadSlicePtr
   193  	case OpMarshalJSON:
   194  		return OpStructHeadMarshalJSON
   195  	case OpMarshalJSONPtr:
   196  		return OpStructHeadMarshalJSONPtr
   197  	case OpMarshalText:
   198  		return OpStructHeadMarshalText
   199  	case OpMarshalTextPtr:
   200  		return OpStructHeadMarshalTextPtr
   201  	}
   202  	return OpStructHead
   203  }
   204  
   205  func (c *Opcode) ToFieldType(isString bool) OpType {
   206  	switch c.Op {
   207  	case OpInt:
   208  		if isString {
   209  			return OpStructFieldIntString
   210  		}
   211  		return OpStructFieldInt
   212  	case OpIntPtr:
   213  		if isString {
   214  			return OpStructFieldIntPtrString
   215  		}
   216  		return OpStructFieldIntPtr
   217  	case OpUint:
   218  		if isString {
   219  			return OpStructFieldUintString
   220  		}
   221  		return OpStructFieldUint
   222  	case OpUintPtr:
   223  		if isString {
   224  			return OpStructFieldUintPtrString
   225  		}
   226  		return OpStructFieldUintPtr
   227  	case OpFloat32:
   228  		if isString {
   229  			return OpStructFieldFloat32String
   230  		}
   231  		return OpStructFieldFloat32
   232  	case OpFloat32Ptr:
   233  		if isString {
   234  			return OpStructFieldFloat32PtrString
   235  		}
   236  		return OpStructFieldFloat32Ptr
   237  	case OpFloat64:
   238  		if isString {
   239  			return OpStructFieldFloat64String
   240  		}
   241  		return OpStructFieldFloat64
   242  	case OpFloat64Ptr:
   243  		if isString {
   244  			return OpStructFieldFloat64PtrString
   245  		}
   246  		return OpStructFieldFloat64Ptr
   247  	case OpString:
   248  		if isString {
   249  			return OpStructFieldStringString
   250  		}
   251  		return OpStructFieldString
   252  	case OpStringPtr:
   253  		if isString {
   254  			return OpStructFieldStringPtrString
   255  		}
   256  		return OpStructFieldStringPtr
   257  	case OpNumber:
   258  		if isString {
   259  			return OpStructFieldNumberString
   260  		}
   261  		return OpStructFieldNumber
   262  	case OpNumberPtr:
   263  		if isString {
   264  			return OpStructFieldNumberPtrString
   265  		}
   266  		return OpStructFieldNumberPtr
   267  	case OpBool:
   268  		if isString {
   269  			return OpStructFieldBoolString
   270  		}
   271  		return OpStructFieldBool
   272  	case OpBoolPtr:
   273  		if isString {
   274  			return OpStructFieldBoolPtrString
   275  		}
   276  		return OpStructFieldBoolPtr
   277  	case OpBytes:
   278  		return OpStructFieldBytes
   279  	case OpBytesPtr:
   280  		return OpStructFieldBytesPtr
   281  	case OpMap:
   282  		return OpStructFieldMap
   283  	case OpMapPtr:
   284  		c.Op = OpMap
   285  		return OpStructFieldMapPtr
   286  	case OpArray:
   287  		return OpStructFieldArray
   288  	case OpArrayPtr:
   289  		c.Op = OpArray
   290  		return OpStructFieldArrayPtr
   291  	case OpSlice:
   292  		return OpStructFieldSlice
   293  	case OpSlicePtr:
   294  		c.Op = OpSlice
   295  		return OpStructFieldSlicePtr
   296  	case OpMarshalJSON:
   297  		return OpStructFieldMarshalJSON
   298  	case OpMarshalJSONPtr:
   299  		return OpStructFieldMarshalJSONPtr
   300  	case OpMarshalText:
   301  		return OpStructFieldMarshalText
   302  	case OpMarshalTextPtr:
   303  		return OpStructFieldMarshalTextPtr
   304  	}
   305  	return OpStructField
   306  }
   307  
   308  func newOpCode(ctx *compileContext, typ *runtime.Type, op OpType) *Opcode {
   309  	return newOpCodeWithNext(ctx, typ, op, newEndOp(ctx, typ))
   310  }
   311  
   312  func opcodeOffset(idx int) uint32 {
   313  	return uint32(idx) * uintptrSize
   314  }
   315  
   316  func getCodeAddrByIdx(head *Opcode, idx uint32) *Opcode {
   317  	addr := uintptr(unsafe.Pointer(head)) + uintptr(idx)*unsafe.Sizeof(Opcode{})
   318  	return *(**Opcode)(unsafe.Pointer(&addr))
   319  }
   320  
   321  func copyOpcode(code *Opcode) *Opcode {
   322  	codeNum := ToEndCode(code).DisplayIdx + 1
   323  	codeSlice := make([]Opcode, codeNum)
   324  	head := (*Opcode)((*runtime.SliceHeader)(unsafe.Pointer(&codeSlice)).Data)
   325  	ptr := head
   326  	c := code
   327  	for {
   328  		*ptr = Opcode{
   329  			Op:         c.Op,
   330  			Key:        c.Key,
   331  			PtrNum:     c.PtrNum,
   332  			NumBitSize: c.NumBitSize,
   333  			Flags:      c.Flags,
   334  			Idx:        c.Idx,
   335  			Offset:     c.Offset,
   336  			Type:       c.Type,
   337  			FieldQuery: c.FieldQuery,
   338  			DisplayIdx: c.DisplayIdx,
   339  			DisplayKey: c.DisplayKey,
   340  			ElemIdx:    c.ElemIdx,
   341  			Length:     c.Length,
   342  			Size:       c.Size,
   343  			Indent:     c.Indent,
   344  			Jmp:        c.Jmp,
   345  		}
   346  		if c.End != nil {
   347  			ptr.End = getCodeAddrByIdx(head, c.End.DisplayIdx)
   348  		}
   349  		if c.NextField != nil {
   350  			ptr.NextField = getCodeAddrByIdx(head, c.NextField.DisplayIdx)
   351  		}
   352  		if c.Next != nil {
   353  			ptr.Next = getCodeAddrByIdx(head, c.Next.DisplayIdx)
   354  		}
   355  		if c.IsEnd() {
   356  			break
   357  		}
   358  		ptr = getCodeAddrByIdx(head, c.DisplayIdx+1)
   359  		c = c.IterNext()
   360  	}
   361  	return head
   362  }
   363  
   364  func setTotalLengthToInterfaceOp(code *Opcode) {
   365  	for c := code; !c.IsEnd(); {
   366  		if c.Op == OpInterface || c.Op == OpInterfacePtr {
   367  			c.Length = uint32(code.TotalLength())
   368  		}
   369  		c = c.IterNext()
   370  	}
   371  }
   372  
   373  func ToEndCode(code *Opcode) *Opcode {
   374  	c := code
   375  	for !c.IsEnd() {
   376  		c = c.IterNext()
   377  	}
   378  	return c
   379  }
   380  
   381  func copyToInterfaceOpcode(code *Opcode) *Opcode {
   382  	copied := copyOpcode(code)
   383  	c := copied
   384  	c = ToEndCode(c)
   385  	c.Idx += uintptrSize
   386  	c.ElemIdx = c.Idx + uintptrSize
   387  	c.Length = c.Idx + 2*uintptrSize
   388  	c.Op = OpInterfaceEnd
   389  	return copied
   390  }
   391  
   392  func newOpCodeWithNext(ctx *compileContext, typ *runtime.Type, op OpType, next *Opcode) *Opcode {
   393  	return &Opcode{
   394  		Op:         op,
   395  		Idx:        opcodeOffset(ctx.ptrIndex),
   396  		Next:       next,
   397  		Type:       typ,
   398  		DisplayIdx: ctx.opcodeIndex,
   399  		Indent:     ctx.indent,
   400  	}
   401  }
   402  
   403  func newEndOp(ctx *compileContext, typ *runtime.Type) *Opcode {
   404  	return newOpCodeWithNext(ctx, typ, OpEnd, nil)
   405  }
   406  
   407  func (c *Opcode) TotalLength() int {
   408  	var idx int
   409  	code := c
   410  	for !code.IsEnd() {
   411  		maxIdx := int(code.MaxIdx() / uintptrSize)
   412  		if idx < maxIdx {
   413  			idx = maxIdx
   414  		}
   415  		if code.Op == OpRecursiveEnd {
   416  			break
   417  		}
   418  		code = code.IterNext()
   419  	}
   420  	maxIdx := int(code.MaxIdx() / uintptrSize)
   421  	if idx < maxIdx {
   422  		idx = maxIdx
   423  	}
   424  	return idx + 1
   425  }
   426  
   427  func (c *Opcode) dumpHead(code *Opcode) string {
   428  	var length uint32
   429  	if code.Op.CodeType() == CodeArrayHead {
   430  		length = code.Length
   431  	} else {
   432  		length = code.Length / uintptrSize
   433  	}
   434  	return fmt.Sprintf(
   435  		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d])`,
   436  		code.DisplayIdx,
   437  		strings.Repeat("-", int(code.Indent)),
   438  		code.Op,
   439  		code.Idx/uintptrSize,
   440  		code.ElemIdx/uintptrSize,
   441  		length,
   442  	)
   443  }
   444  
   445  func (c *Opcode) dumpMapHead(code *Opcode) string {
   446  	return fmt.Sprintf(
   447  		`[%03d]%s%s ([idx:%d])`,
   448  		code.DisplayIdx,
   449  		strings.Repeat("-", int(code.Indent)),
   450  		code.Op,
   451  		code.Idx/uintptrSize,
   452  	)
   453  }
   454  
   455  func (c *Opcode) dumpMapEnd(code *Opcode) string {
   456  	return fmt.Sprintf(
   457  		`[%03d]%s%s ([idx:%d])`,
   458  		code.DisplayIdx,
   459  		strings.Repeat("-", int(code.Indent)),
   460  		code.Op,
   461  		code.Idx/uintptrSize,
   462  	)
   463  }
   464  
   465  func (c *Opcode) dumpElem(code *Opcode) string {
   466  	var length uint32
   467  	if code.Op.CodeType() == CodeArrayElem {
   468  		length = code.Length
   469  	} else {
   470  		length = code.Length / uintptrSize
   471  	}
   472  	return fmt.Sprintf(
   473  		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d][size:%d])`,
   474  		code.DisplayIdx,
   475  		strings.Repeat("-", int(code.Indent)),
   476  		code.Op,
   477  		code.Idx/uintptrSize,
   478  		code.ElemIdx/uintptrSize,
   479  		length,
   480  		code.Size,
   481  	)
   482  }
   483  
   484  func (c *Opcode) dumpField(code *Opcode) string {
   485  	return fmt.Sprintf(
   486  		`[%03d]%s%s ([idx:%d][key:%s][offset:%d])`,
   487  		code.DisplayIdx,
   488  		strings.Repeat("-", int(code.Indent)),
   489  		code.Op,
   490  		code.Idx/uintptrSize,
   491  		code.DisplayKey,
   492  		code.Offset,
   493  	)
   494  }
   495  
   496  func (c *Opcode) dumpKey(code *Opcode) string {
   497  	return fmt.Sprintf(
   498  		`[%03d]%s%s ([idx:%d])`,
   499  		code.DisplayIdx,
   500  		strings.Repeat("-", int(code.Indent)),
   501  		code.Op,
   502  		code.Idx/uintptrSize,
   503  	)
   504  }
   505  
   506  func (c *Opcode) dumpValue(code *Opcode) string {
   507  	return fmt.Sprintf(
   508  		`[%03d]%s%s ([idx:%d])`,
   509  		code.DisplayIdx,
   510  		strings.Repeat("-", int(code.Indent)),
   511  		code.Op,
   512  		code.Idx/uintptrSize,
   513  	)
   514  }
   515  
   516  func (c *Opcode) Dump() string {
   517  	codes := []string{}
   518  	for code := c; !code.IsEnd(); {
   519  		switch code.Op.CodeType() {
   520  		case CodeSliceHead:
   521  			codes = append(codes, c.dumpHead(code))
   522  			code = code.Next
   523  		case CodeMapHead:
   524  			codes = append(codes, c.dumpMapHead(code))
   525  			code = code.Next
   526  		case CodeArrayElem, CodeSliceElem:
   527  			codes = append(codes, c.dumpElem(code))
   528  			code = code.End
   529  		case CodeMapKey:
   530  			codes = append(codes, c.dumpKey(code))
   531  			code = code.End
   532  		case CodeMapValue:
   533  			codes = append(codes, c.dumpValue(code))
   534  			code = code.Next
   535  		case CodeMapEnd:
   536  			codes = append(codes, c.dumpMapEnd(code))
   537  			code = code.Next
   538  		case CodeStructField:
   539  			codes = append(codes, c.dumpField(code))
   540  			code = code.Next
   541  		case CodeStructEnd:
   542  			codes = append(codes, c.dumpField(code))
   543  			code = code.Next
   544  		default:
   545  			codes = append(codes, fmt.Sprintf(
   546  				"[%03d]%s%s ([idx:%d])",
   547  				code.DisplayIdx,
   548  				strings.Repeat("-", int(code.Indent)),
   549  				code.Op,
   550  				code.Idx/uintptrSize,
   551  			))
   552  			code = code.Next
   553  		}
   554  	}
   555  	return strings.Join(codes, "\n")
   556  }
   557  
   558  func newSliceHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
   559  	idx := opcodeOffset(ctx.ptrIndex)
   560  	ctx.incPtrIndex()
   561  	elemIdx := opcodeOffset(ctx.ptrIndex)
   562  	ctx.incPtrIndex()
   563  	length := opcodeOffset(ctx.ptrIndex)
   564  	return &Opcode{
   565  		Op:         OpSlice,
   566  		Type:       typ,
   567  		Idx:        idx,
   568  		DisplayIdx: ctx.opcodeIndex,
   569  		ElemIdx:    elemIdx,
   570  		Length:     length,
   571  		Indent:     ctx.indent,
   572  	}
   573  }
   574  
   575  func newSliceElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, size uintptr) *Opcode {
   576  	return &Opcode{
   577  		Op:         OpSliceElem,
   578  		Type:       typ,
   579  		Idx:        head.Idx,
   580  		DisplayIdx: ctx.opcodeIndex,
   581  		ElemIdx:    head.ElemIdx,
   582  		Length:     head.Length,
   583  		Indent:     ctx.indent,
   584  		Size:       uint32(size),
   585  	}
   586  }
   587  
   588  func newArrayHeaderCode(ctx *compileContext, typ *runtime.Type, alen int) *Opcode {
   589  	idx := opcodeOffset(ctx.ptrIndex)
   590  	ctx.incPtrIndex()
   591  	elemIdx := opcodeOffset(ctx.ptrIndex)
   592  	return &Opcode{
   593  		Op:         OpArray,
   594  		Type:       typ,
   595  		Idx:        idx,
   596  		DisplayIdx: ctx.opcodeIndex,
   597  		ElemIdx:    elemIdx,
   598  		Indent:     ctx.indent,
   599  		Length:     uint32(alen),
   600  	}
   601  }
   602  
   603  func newArrayElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, length int, size uintptr) *Opcode {
   604  	return &Opcode{
   605  		Op:         OpArrayElem,
   606  		Type:       typ,
   607  		Idx:        head.Idx,
   608  		DisplayIdx: ctx.opcodeIndex,
   609  		ElemIdx:    head.ElemIdx,
   610  		Length:     uint32(length),
   611  		Indent:     ctx.indent,
   612  		Size:       uint32(size),
   613  	}
   614  }
   615  
   616  func newMapHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
   617  	idx := opcodeOffset(ctx.ptrIndex)
   618  	ctx.incPtrIndex()
   619  	return &Opcode{
   620  		Op:         OpMap,
   621  		Type:       typ,
   622  		Idx:        idx,
   623  		DisplayIdx: ctx.opcodeIndex,
   624  		Indent:     ctx.indent,
   625  	}
   626  }
   627  
   628  func newMapKeyCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
   629  	return &Opcode{
   630  		Op:         OpMapKey,
   631  		Type:       typ,
   632  		Idx:        head.Idx,
   633  		DisplayIdx: ctx.opcodeIndex,
   634  		Indent:     ctx.indent,
   635  	}
   636  }
   637  
   638  func newMapValueCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
   639  	return &Opcode{
   640  		Op:         OpMapValue,
   641  		Type:       typ,
   642  		Idx:        head.Idx,
   643  		DisplayIdx: ctx.opcodeIndex,
   644  		Indent:     ctx.indent,
   645  	}
   646  }
   647  
   648  func newMapEndCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
   649  	return &Opcode{
   650  		Op:         OpMapEnd,
   651  		Type:       typ,
   652  		Idx:        head.Idx,
   653  		DisplayIdx: ctx.opcodeIndex,
   654  		Indent:     ctx.indent,
   655  		Next:       newEndOp(ctx, typ),
   656  	}
   657  }
   658  
   659  func newRecursiveCode(ctx *compileContext, typ *runtime.Type, jmp *CompiledCode) *Opcode {
   660  	return &Opcode{
   661  		Op:         OpRecursive,
   662  		Type:       typ,
   663  		Idx:        opcodeOffset(ctx.ptrIndex),
   664  		Next:       newEndOp(ctx, typ),
   665  		DisplayIdx: ctx.opcodeIndex,
   666  		Indent:     ctx.indent,
   667  		Jmp:        jmp,
   668  	}
   669  }