go-json/encode_opcode.go

476 lines
11 KiB
Go
Raw Normal View History

2020-04-29 18:31:50 +03:00
package json
import (
"fmt"
"strings"
"unsafe"
)
2020-08-31 15:59:22 +03:00
var uintptrSize = unsafe.Sizeof(uintptr(0))
2020-08-09 11:48:28 +03:00
2020-04-29 18:31:50 +03:00
type opcode struct {
2020-08-31 15:59:22 +03:00
op opType // operation type
typ *rtype // go type
displayIdx int // opcode index
key []byte // struct field key
2020-09-16 19:26:39 +03:00
escapedKey []byte // struct field key ( HTML escaped )
2020-08-31 15:59:22 +03:00
displayKey string // key text to display
isTaggedKey bool // whether tagged key
anonymousKey bool // whether anonymous key
root bool // whether root
indent int // indent number
idx uintptr // offset to access ptr
headIdx uintptr // offset to access slice/struct head
elemIdx uintptr // offset to access array/slice/map elem
length uintptr // offset to access slice/map length or array length
mapIter uintptr // offset to access map iterator
2020-09-16 08:51:37 +03:00
mapPos uintptr // offset to access position list for sorted map
2020-08-31 15:59:22 +03:00
offset uintptr // offset size from struct header
size uintptr // array/slice elem size
mapKey *opcode // map key
mapValue *opcode // map value
elem *opcode // array/slice elem
end *opcode // array/slice/struct/map end
nextField *opcode // next struct field
next *opcode // next opcode
jmp *compiledCode // for recursive call
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:35:03 +03:00
func newOpCode(ctx *encodeCompileContext, op opType) *opcode {
2020-08-30 11:32:26 +03:00
return newOpCodeWithNext(ctx, op, newEndOp(ctx))
2020-08-29 09:35:03 +03:00
}
2020-08-31 15:59:22 +03:00
func opcodeOffset(idx int) uintptr {
return uintptr(idx) * uintptrSize
}
2020-09-15 14:47:41 +03:00
func copyOpcode(code *opcode) *opcode {
codeMap := map[uintptr]*opcode{}
return code.copy(codeMap)
}
2020-08-29 09:35:03 +03:00
func newOpCodeWithNext(ctx *encodeCompileContext, op opType, next *opcode) *opcode {
return &opcode{
2020-08-31 15:59:22 +03:00
op: op,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
indent: ctx.indent,
2020-09-01 16:26:26 +03:00
idx: opcodeOffset(ctx.ptrIndex),
2020-08-31 15:59:22 +03:00
next: next,
2020-04-29 18:31:50 +03:00
}
}
2020-08-29 09:35:03 +03:00
func newEndOp(ctx *encodeCompileContext) *opcode {
return newOpCodeWithNext(ctx, opEnd, nil)
2020-04-29 18:31:50 +03:00
}
2020-09-15 14:47:41 +03:00
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,
typ: c.typ,
displayIdx: c.displayIdx,
key: c.key,
2020-09-16 19:26:39 +03:00
escapedKey: c.escapedKey,
2020-09-15 14:47:41 +03:00
displayKey: c.displayKey,
isTaggedKey: c.isTaggedKey,
anonymousKey: c.anonymousKey,
root: c.root,
indent: c.indent,
idx: c.idx,
headIdx: c.headIdx,
elemIdx: c.elemIdx,
length: c.length,
mapIter: c.mapIter,
2020-09-16 08:51:37 +03:00
mapPos: c.mapPos,
2020-09-15 14:47:41 +03:00
offset: c.offset,
size: c.size,
}
codeMap[addr] = copied
copied.mapKey = c.mapKey.copy(codeMap)
copied.mapValue = c.mapValue.copy(codeMap)
copied.elem = c.elem.copy(codeMap)
copied.end = c.end.copy(codeMap)
copied.nextField = c.nextField.copy(codeMap)
copied.next = c.next.copy(codeMap)
copied.jmp = c.jmp
return copied
}
2020-04-29 18:31:50 +03:00
func (c *opcode) beforeLastCode() *opcode {
code := c
for {
var nextCode *opcode
2020-08-22 06:58:34 +03:00
switch code.op.codeType() {
2020-08-31 15:59:22 +03:00
case codeArrayElem, codeSliceElem, codeMapKey:
nextCode = code.end
default:
2020-04-29 18:31:50 +03:00
nextCode = code.next
}
if nextCode.op == opEnd {
return code
}
code = nextCode
}
return nil
}
2020-08-31 15:59:22 +03:00
func (c *opcode) totalLength() int {
2020-08-30 11:52:59 +03:00
var idx int
for code := c; code.op != opEnd; {
2020-09-03 09:36:11 +03:00
idx = int(code.idx / uintptrSize)
2020-09-04 07:48:21 +03:00
if code.op == opInterfaceEnd || code.op == opStructFieldRecursiveEnd {
2020-09-03 09:36:11 +03:00
break
}
2020-08-30 11:52:59 +03:00
switch code.op.codeType() {
2020-08-31 15:59:22 +03:00
case codeArrayElem, codeSliceElem, codeMapKey:
code = code.end
2020-08-30 11:52:59 +03:00
default:
code = code.next
}
}
2020-09-03 09:36:11 +03:00
return idx + 2 // opEnd + 1
2020-08-30 11:52:59 +03:00
}
2020-08-30 11:32:26 +03:00
func (c *opcode) decOpcodeIndex() {
for code := c; code.op != opEnd; {
2020-08-30 21:14:37 +03:00
code.displayIdx--
2020-09-01 16:26:26 +03:00
code.idx -= uintptrSize
2020-09-01 17:23:07 +03:00
if code.headIdx > 0 {
code.headIdx -= 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
}
2020-08-30 11:32:26 +03:00
switch code.op.codeType() {
2020-08-31 15:59:22 +03:00
case codeArrayElem, codeSliceElem, codeMapKey:
code = code.end
2020-08-30 11:32:26 +03:00
default:
code = code.next
}
}
}
2020-09-01 16:26:26 +03:00
func (c *opcode) dumpHead(code *opcode) string {
var length uintptr
2020-09-01 17:23:07 +03:00
if code.op.codeType() == codeArrayHead {
2020-09-01 16:26:26 +03:00
length = code.length
} else {
length = code.length / uintptrSize
}
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d])`,
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
code.idx/uintptrSize,
code.headIdx/uintptrSize,
code.elemIdx/uintptrSize,
length,
)
}
func (c *opcode) dumpMapHead(code *opcode) string {
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
code.idx/uintptrSize,
code.headIdx/uintptrSize,
code.elemIdx/uintptrSize,
code.length/uintptrSize,
code.mapIter/uintptrSize,
)
}
2020-09-16 08:51:37 +03:00
func (c *opcode) dumpMapEnd(code *opcode) string {
return fmt.Sprintf(
2020-09-16 12:17:17 +03:00
`[%d]%s%s ([idx:%d][mapPos:%d][length:%d])`,
2020-09-16 08:51:37 +03:00
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
code.idx/uintptrSize,
code.mapPos/uintptrSize,
code.length/uintptrSize,
)
}
2020-08-31 15:59:22 +03:00
func (c *opcode) dumpElem(code *opcode) string {
2020-09-01 16:26:26 +03:00
var length uintptr
if code.op.codeType() == codeArrayElem {
length = code.length
} else {
length = code.length / uintptrSize
}
2020-08-31 15:59:22 +03:00
return fmt.Sprintf(
2020-09-01 16:26:26 +03:00
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d][size:%d])`,
2020-08-31 15:59:22 +03:00
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
2020-09-01 16:26:26 +03:00
code.idx/uintptrSize,
code.headIdx/uintptrSize,
code.elemIdx/uintptrSize,
length,
2020-08-31 15:59:22 +03:00
code.size,
)
}
func (c *opcode) dumpField(code *opcode) string {
return fmt.Sprintf(
2020-09-01 16:26:26 +03:00
`[%d]%s%s ([idx:%d][key:%s][offset:%d][headIdx:%d])`,
2020-08-31 15:59:22 +03:00
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
2020-09-01 16:26:26 +03:00
code.idx/uintptrSize,
2020-08-31 15:59:22 +03:00
code.displayKey,
code.offset,
2020-09-01 16:26:26 +03:00
code.headIdx/uintptrSize,
2020-08-31 15:59:22 +03:00
)
}
func (c *opcode) dumpKey(code *opcode) string {
return fmt.Sprintf(
2020-09-01 16:26:26 +03:00
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
code.idx/uintptrSize,
code.elemIdx/uintptrSize,
code.length/uintptrSize,
code.mapIter/uintptrSize,
)
}
func (c *opcode) dumpValue(code *opcode) string {
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][mapIter:%d])`,
2020-08-31 15:59:22 +03:00
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
2020-09-01 16:26:26 +03:00
code.idx/uintptrSize,
code.mapIter/uintptrSize,
2020-08-31 15:59:22 +03:00
)
2020-08-09 11:48:28 +03:00
}
2020-04-29 18:31:50 +03:00
func (c *opcode) dump() string {
codes := []string{}
for code := c; code.op != opEnd; {
2020-08-22 06:58:34 +03:00
switch code.op.codeType() {
2020-09-01 16:26:26 +03:00
case codeSliceHead:
codes = append(codes, c.dumpHead(code))
code = code.next
case codeMapHead:
codes = append(codes, c.dumpMapHead(code))
code = code.next
2020-08-31 15:59:22 +03:00
case codeArrayElem, codeSliceElem:
codes = append(codes, c.dumpElem(code))
code = code.end
2020-08-22 06:58:34 +03:00
case codeMapKey:
2020-08-31 15:59:22 +03:00
codes = append(codes, c.dumpKey(code))
code = code.end
2020-09-01 16:26:26 +03:00
case codeMapValue:
codes = append(codes, c.dumpValue(code))
code = code.next
2020-09-16 08:51:37 +03:00
case codeMapEnd:
codes = append(codes, c.dumpMapEnd(code))
code = code.next
2020-08-22 12:13:44 +03:00
case codeStructField:
2020-08-31 15:59:22 +03:00
codes = append(codes, c.dumpField(code))
2020-08-22 12:13:44 +03:00
code = code.next
default:
2020-08-31 15:59:22 +03:00
codes = append(codes, fmt.Sprintf(
2020-09-01 16:26:26 +03:00
"[%d]%s%s ([idx:%d])",
2020-08-31 15:59:22 +03:00
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
2020-09-01 16:26:26 +03:00
code.idx/uintptrSize,
2020-08-31 15:59:22 +03:00
))
2020-04-29 18:31:50 +03:00
code = code.next
}
}
return strings.Join(codes, "\n")
}
2020-08-31 15:59:22 +03:00
func linkPrevToNextField(prev, cur *opcode) {
2020-08-22 06:58:34 +03:00
prev.nextField = cur.nextField
2020-08-31 15:59:22 +03:00
code := prev
fcode := cur
2020-08-22 06:58:34 +03:00
for {
var nextCode *opcode
switch code.op.codeType() {
2020-08-31 15:59:22 +03:00
case codeArrayElem, codeSliceElem, codeMapKey:
nextCode = code.end
2020-08-22 06:58:34 +03:00
default:
nextCode = code.next
}
if nextCode == fcode {
code.next = fcode.next
break
} else if nextCode.op == opEnd {
break
}
code = nextCode
}
}
2020-08-31 15:59:22 +03:00
func newSliceHeaderCode(ctx *encodeCompileContext) *opcode {
2020-09-01 16:26:26 +03:00
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
length := opcodeOffset(ctx.ptrIndex)
2020-08-31 15:59:22 +03:00
return &opcode{
op: opSliceHead,
displayIdx: ctx.opcodeIndex,
2020-09-01 16:26:26 +03:00
idx: idx,
headIdx: idx,
elemIdx: elemIdx,
length: length,
2020-08-31 15:59:22 +03:00
indent: ctx.indent,
2020-08-09 11:48:28 +03:00
}
}
2020-09-01 16:26:26 +03:00
func newSliceElemCode(ctx *encodeCompileContext, head *opcode, size uintptr) *opcode {
2020-08-31 15:59:22 +03:00
return &opcode{
op: opSliceElem,
displayIdx: ctx.opcodeIndex,
2020-09-01 16:26:26 +03:00
idx: opcodeOffset(ctx.ptrIndex),
headIdx: head.idx,
elemIdx: head.elemIdx,
length: head.length,
2020-08-31 15:59:22 +03:00
indent: ctx.indent,
size: size,
2020-08-09 11:48:28 +03:00
}
}
2020-08-31 15:59:22 +03:00
func newArrayHeaderCode(ctx *encodeCompileContext, alen int) *opcode {
2020-09-01 16:26:26 +03:00
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
2020-08-31 15:59:22 +03:00
return &opcode{
op: opArrayHead,
displayIdx: ctx.opcodeIndex,
2020-09-01 16:26:26 +03:00
idx: idx,
headIdx: idx,
elemIdx: elemIdx,
2020-08-31 15:59:22 +03:00
indent: ctx.indent,
length: uintptr(alen),
2020-08-09 11:48:28 +03:00
}
}
2020-09-01 16:26:26 +03:00
func newArrayElemCode(ctx *encodeCompileContext, head *opcode, length int, size uintptr) *opcode {
2020-08-31 15:59:22 +03:00
return &opcode{
op: opArrayElem,
displayIdx: ctx.opcodeIndex,
2020-09-01 16:26:26 +03:00
idx: opcodeOffset(ctx.ptrIndex),
elemIdx: head.elemIdx,
headIdx: head.headIdx,
length: uintptr(length),
2020-08-31 15:59:22 +03:00
size: size,
2020-08-09 11:48:28 +03:00
}
}
2020-08-31 15:59:22 +03:00
func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode {
var op opType
if withLoad {
op = opMapHeadLoad
} else {
op = opMapHead
}
2020-09-01 16:26:26 +03:00
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
length := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
mapIter := opcodeOffset(ctx.ptrIndex)
2020-08-31 15:59:22 +03:00
return &opcode{
op: op,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
2020-09-01 16:26:26 +03:00
idx: idx,
elemIdx: elemIdx,
length: length,
mapIter: mapIter,
2020-08-31 15:59:22 +03:00
indent: ctx.indent,
}
}
2020-09-01 16:26:26 +03:00
func newMapKeyCode(ctx *encodeCompileContext, head *opcode) *opcode {
2020-08-31 15:59:22 +03:00
return &opcode{
op: opMapKey,
2020-08-31 15:59:22 +03:00
displayIdx: ctx.opcodeIndex,
2020-09-01 16:26:26 +03:00
idx: opcodeOffset(ctx.ptrIndex),
elemIdx: head.elemIdx,
length: head.length,
mapIter: head.mapIter,
2020-08-31 15:59:22 +03:00
indent: ctx.indent,
}
}
2020-09-01 16:26:26 +03:00
func newMapValueCode(ctx *encodeCompileContext, head *opcode) *opcode {
2020-08-31 15:59:22 +03:00
return &opcode{
op: opMapValue,
2020-08-31 15:59:22 +03:00
displayIdx: ctx.opcodeIndex,
2020-09-01 16:26:26 +03:00
idx: opcodeOffset(ctx.ptrIndex),
elemIdx: head.elemIdx,
length: head.length,
mapIter: head.mapIter,
2020-08-31 15:59:22 +03:00
indent: ctx.indent,
}
}
2020-08-12 12:42:29 +03:00
2020-09-16 08:51:37 +03:00
func newMapEndCode(ctx *encodeCompileContext, head *opcode) *opcode {
mapPos := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
idx := opcodeOffset(ctx.ptrIndex)
return &opcode{
op: opMapEnd,
2020-09-16 08:51:37 +03:00
displayIdx: ctx.opcodeIndex,
idx: idx,
length: head.length,
mapPos: mapPos,
indent: ctx.indent,
next: newEndOp(ctx),
}
}
2020-08-31 15:59:22 +03:00
func newInterfaceCode(ctx *encodeCompileContext) *opcode {
return &opcode{
op: opInterface,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
2020-09-01 16:26:26 +03:00
idx: opcodeOffset(ctx.ptrIndex),
2020-08-31 15:59:22 +03:00
indent: ctx.indent,
root: ctx.root,
2020-09-03 09:36:11 +03:00
next: newEndOp(ctx),
2020-08-12 12:42:29 +03:00
}
}
2020-08-31 15:59:22 +03:00
func newRecursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
return &opcode{
op: opStructFieldRecursive,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
2020-09-01 16:26:26 +03:00
idx: opcodeOffset(ctx.ptrIndex),
2020-08-31 15:59:22 +03:00
indent: ctx.indent,
next: newEndOp(ctx),
jmp: jmp,
2020-08-12 12:42:29 +03:00
}
}