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 }