Skip to content

Commit

Permalink
Fix goccy/go-json for go1.18-beta1. (#1673)
Browse files Browse the repository at this point in the history
  • Loading branch information
efectn committed Dec 29, 2021
1 parent bd19adc commit f04ddd4
Show file tree
Hide file tree
Showing 11 changed files with 362 additions and 406 deletions.
76 changes: 25 additions & 51 deletions internal/go-json/cmd/generator/vm.go.tmpl
Expand Up @@ -403,95 +403,69 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
break
}
b = appendStructHead(ctx, b)
iter := mapiterinit(code.Type, uptr)
ctx.KeepRefs = append(ctx.KeepRefs, iter)
store(ctxptr, code.ElemIdx, 0)
store(ctxptr, code.Length, uintptr(mlen))
store(ctxptr, code.MapIter, uintptr(iter))
mapCtx := encoder.NewMapContext(mlen)
mapiterinit(code.Type, uptr, &mapCtx.Iter)
store(ctxptr, code.Idx, uintptr(unsafe.Pointer(mapCtx)))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
if (ctx.Option.Flag & encoder.UnorderedMapOption) != 0 {
b = appendMapKeyIndent(ctx, code.Next, b)
} else {
mapCtx := encoder.NewMapContext(mlen)
mapCtx.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
mapCtx.Start = len(b)
mapCtx.First = len(b)
}
key := mapiterkey(iter)
key := mapiterkey(&mapCtx.Iter)
store(ctxptr, code.Next.Idx, uintptr(key))
code = code.Next
case encoder.OpMapKey:
idx := load(ctxptr, code.ElemIdx)
length := load(ctxptr, code.Length)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(load(ctxptr, code.Idx)))
idx := mapCtx.Idx
idx++
if (ctx.Option.Flag & encoder.UnorderedMapOption) != 0 {
if idx < length {
if idx < mapCtx.Len {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
ptr := load(ctxptr, code.MapIter)
iter := ptrToUnsafePtr(ptr)
key := mapiterkey(iter)
mapCtx.Idx = int(idx)
key := mapiterkey(&mapCtx.Iter)
store(ctxptr, code.Next.Idx, uintptr(key))
code = code.Next
} else {
b = appendObjectEnd(ctx, code, b)
encoder.ReleaseMapContext(mapCtx)
code = code.End.Next
}
} else {
ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
mapCtx.Pos = append(mapCtx.Pos, len(b))
if idx < length {
ptr := load(ctxptr, code.MapIter)
iter := ptrToUnsafePtr(ptr)
store(ctxptr, code.ElemIdx, idx)
key := mapiterkey(iter)
mapCtx.Slice.Items[mapCtx.Idx].Value = b[mapCtx.Start:len(b)]
if idx < mapCtx.Len {
mapCtx.Idx = int(idx)
mapCtx.Start = len(b)
key := mapiterkey(&mapCtx.Iter)
store(ctxptr, code.Next.Idx, uintptr(key))
code = code.Next
} else {
code = code.End
}
}
case encoder.OpMapValue:
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(load(ctxptr, code.Idx)))
if (ctx.Option.Flag & encoder.UnorderedMapOption) != 0 {
b = appendColon(ctx, b)
} else {
ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
mapCtx.Pos = append(mapCtx.Pos, len(b))
mapCtx.Slice.Items[mapCtx.Idx].Key = b[mapCtx.Start:len(b)]
mapCtx.Start = len(b)
}
ptr := load(ctxptr, code.MapIter)
iter := ptrToUnsafePtr(ptr)
value := mapitervalue(iter)
value := mapitervalue(&mapCtx.Iter)
store(ctxptr, code.Next.Idx, uintptr(value))
mapiternext(iter)
mapiternext(&mapCtx.Iter)
code = code.Next
case encoder.OpMapEnd:
// this operation only used by sorted map.
length := int(load(ctxptr, code.Length))
ptr := load(ctxptr, code.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
pos := mapCtx.Pos
for i := 0; i < length; i++ {
startKey := pos[i*2]
startValue := pos[i*2+1]
var endValue int
if i+1 < length {
endValue = pos[i*2+2]
} else {
endValue = len(b)
}
mapCtx.Slice.Items = append(mapCtx.Slice.Items, encoder.MapItem{
Key: b[startKey:startValue],
Value: b[startValue:endValue],
})
}
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(load(ctxptr, code.Idx)))
sort.Sort(mapCtx.Slice)
buf := mapCtx.Buf
for _, item := range mapCtx.Slice.Items {
buf = appendMapKeyValue(ctx, code, buf, item.Key, item.Value)
}
buf = appendMapEnd(ctx, code, buf)
b = b[:pos[0]]
b = b[:mapCtx.First]
b = append(b, buf...)
mapCtx.Buf = buf
encoder.ReleaseMapContext(mapCtx)
Expand Down
127 changes: 127 additions & 0 deletions internal/go-json/encoder/decode_rune.go
@@ -0,0 +1,127 @@
package encoder

import "unicode/utf8"

const (
// The default lowest and highest continuation byte.
locb = 128 //0b10000000
hicb = 191 //0b10111111

// These names of these constants are chosen to give nice alignment in the
// table below. The first nibble is an index into acceptRanges or F for
// special one-byte cases. The second nibble is the Rune length or the
// Status for the special one-byte case.
xx = 0xF1 // invalid: size 1
as = 0xF0 // ASCII: size 1
s1 = 0x02 // accept 0, size 2
s2 = 0x13 // accept 1, size 3
s3 = 0x03 // accept 0, size 3
s4 = 0x23 // accept 2, size 3
s5 = 0x34 // accept 3, size 4
s6 = 0x04 // accept 0, size 4
s7 = 0x44 // accept 4, size 4
)

// first is information about the first byte in a UTF-8 sequence.
var first = [256]uint8{
// 1 2 3 4 5 6 7 8 9 A B C D E F
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x00-0x0F
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x10-0x1F
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x20-0x2F
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x30-0x3F
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x40-0x4F
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x50-0x5F
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x60-0x6F
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x70-0x7F
// 1 2 3 4 5 6 7 8 9 A B C D E F
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF
xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF
s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF
s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF
s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF
}

// acceptRange gives the range of valid values for the second byte in a UTF-8
// sequence.
type acceptRange struct {
lo uint8 // lowest value for second byte.
hi uint8 // highest value for second byte.
}

const (
lineSep = byte(168) //'\u2028'
paragraphSep = byte(169) //'\u2029'
)

type decodeRuneState int

const (
validUTF8State decodeRuneState = iota
runeErrorState
lineSepState
paragraphSepState
)

func decodeRuneInString(s string) (decodeRuneState, int) {
n := len(s)
s0 := s[0]
x := first[s0]
if x >= as {
// The following code simulates an additional check for x == xx and
// handling the ASCII and invalid cases accordingly. This mask-and-or
// approach prevents an additional branch.
mask := rune(x) << 31 >> 31 // Create 0x0000 or 0xFFFF.
if rune(s[0])&^mask|utf8.RuneError&mask == utf8.RuneError {
return runeErrorState, 1
}
return validUTF8State, 1
}
sz := int(x & 7)
var accept acceptRange
switch x >> 4 {
case 0:
accept = acceptRange{locb, hicb}
case 1:
accept = acceptRange{0xA0, hicb}
case 2:
accept = acceptRange{locb, 0x9F}
case 3:
accept = acceptRange{0x90, hicb}
case 4:
accept = acceptRange{locb, 0x8F}
}
if n < sz {
return runeErrorState, 1
}
s1 := s[1]
if s1 < accept.lo || accept.hi < s1 {
return runeErrorState, 1
}
if sz <= 2 {
return validUTF8State, 2
}
s2 := s[2]
if s2 < locb || hicb < s2 {
return runeErrorState, 1
}
if sz <= 3 {
// separator character prefixes: [2]byte{226, 128}
if s0 == 226 && s1 == 128 {
switch s2 {
case lineSep:
return lineSepState, 3
case paragraphSep:
return paragraphSepState, 3
}
}
return validUTF8State, 3
}
s3 := s[3]
if s3 < locb || hicb < s3 {
return runeErrorState, 1
}
return validUTF8State, 4
}
53 changes: 37 additions & 16 deletions internal/go-json/encoder/encoder.go
Expand Up @@ -222,51 +222,72 @@ func (m *Mapslice) Swap(i, j int) {
m.Items[i], m.Items[j] = m.Items[j], m.Items[i]
}

//nolint:structcheck,unused
type mapIter struct {
key unsafe.Pointer
elem unsafe.Pointer
t unsafe.Pointer
h unsafe.Pointer
buckets unsafe.Pointer
bptr unsafe.Pointer
overflow unsafe.Pointer
oldoverflow unsafe.Pointer
startBucket uintptr
offset uint8
wrapped bool
B uint8
i uint8
bucket uintptr
checkBucket uintptr
}

type MapContext struct {
Pos []int
Start int
First int
Idx int
Slice *Mapslice
Buf []byte
Len int
Iter mapIter
}

var mapContextPool = sync.Pool{
New: func() interface{} {
return &MapContext{}
return &MapContext{
Slice: &Mapslice{},
}
},
}

func NewMapContext(mapLen int) *MapContext {
ctx := mapContextPool.Get().(*MapContext)
if ctx.Slice == nil {
ctx.Slice = &Mapslice{
Items: make([]MapItem, 0, mapLen),
}
}
if cap(ctx.Pos) < (mapLen*2 + 1) {
ctx.Pos = make([]int, 0, mapLen*2+1)
ctx.Slice.Items = make([]MapItem, 0, mapLen)
if len(ctx.Slice.Items) < mapLen {
ctx.Slice.Items = make([]MapItem, mapLen)
} else {
ctx.Pos = ctx.Pos[:0]
ctx.Slice.Items = ctx.Slice.Items[:0]
ctx.Slice.Items = ctx.Slice.Items[:mapLen]
}
ctx.Buf = ctx.Buf[:0]
ctx.Iter = mapIter{}
ctx.Idx = 0
ctx.Len = mapLen
return ctx
}

func ReleaseMapContext(c *MapContext) {
mapContextPool.Put(c)
}

//go:linkname MapIterInit reflect.mapiterinit
//go:linkname MapIterInit runtime.mapiterinit
//go:noescape
func MapIterInit(mapType *runtime.Type, m unsafe.Pointer) unsafe.Pointer
func MapIterInit(mapType *runtime.Type, m unsafe.Pointer, it *mapIter)

//go:linkname MapIterKey reflect.mapiterkey
//go:noescape
func MapIterKey(it unsafe.Pointer) unsafe.Pointer
func MapIterKey(it *mapIter) unsafe.Pointer

//go:linkname MapIterNext reflect.mapiternext
//go:noescape
func MapIterNext(it unsafe.Pointer)
func MapIterNext(it *mapIter)

//go:linkname MapLen reflect.maplen
//go:noescape
Expand Down
3 changes: 2 additions & 1 deletion internal/go-json/encoder/map112.go
@@ -1,8 +1,9 @@
//go:build !go1.13
// +build !go1.13

package encoder

import "unsafe"

//go:linkname MapIterValue reflect.mapitervalue
func MapIterValue(it unsafe.Pointer) unsafe.Pointer
func MapIterValue(it *mapIter) unsafe.Pointer
3 changes: 2 additions & 1 deletion internal/go-json/encoder/map113.go
@@ -1,8 +1,9 @@
//go:build go1.13
// +build go1.13

package encoder

import "unsafe"

//go:linkname MapIterValue reflect.mapiterelem
func MapIterValue(it unsafe.Pointer) unsafe.Pointer
func MapIterValue(it *mapIter) unsafe.Pointer

0 comments on commit f04ddd4

Please sign in to comment.