Remove unnecessary files

This commit is contained in:
Masaaki Goshima 2021-03-18 15:53:22 +09:00
parent 9cbe7b3991
commit 4db967f28d
19 changed files with 17 additions and 4290 deletions

View File

@ -12,8 +12,6 @@ const (
) )
var ( var (
cachedOpcodeSets []*opcodeSet
cachedOpcodeMap unsafe.Pointer // map[uintptr]*opcodeSet
cachedDecoder []decoder cachedDecoder []decoder
cachedDecoderMap unsafe.Pointer // map[uintptr]decoder cachedDecoderMap unsafe.Pointer // map[uintptr]decoder
baseTypeAddr uintptr baseTypeAddr uintptr
@ -66,7 +64,6 @@ func setupCodec() error {
if addrRange > maxAcceptableTypeAddrRange { if addrRange > maxAcceptableTypeAddrRange {
return fmt.Errorf("too big address range %d", addrRange) return fmt.Errorf("too big address range %d", addrRange)
} }
cachedOpcodeSets = make([]*opcodeSet, addrRange)
cachedDecoder = make([]decoder, addrRange) cachedDecoder = make([]decoder, addrRange)
baseTypeAddr = min baseTypeAddr = min
maxTypeAddr = max maxTypeAddr = max
@ -77,22 +74,6 @@ func init() {
_ = setupCodec() _ = setupCodec()
} }
func loadOpcodeMap() map[uintptr]*opcodeSet {
p := atomic.LoadPointer(&cachedOpcodeMap)
return *(*map[uintptr]*opcodeSet)(unsafe.Pointer(&p))
}
func storeOpcodeSet(typ uintptr, set *opcodeSet, m map[uintptr]*opcodeSet) {
newOpcodeMap := make(map[uintptr]*opcodeSet, len(m)+1)
newOpcodeMap[typ] = set
for k, v := range m {
newOpcodeMap[k] = v
}
atomic.StorePointer(&cachedOpcodeMap, *(*unsafe.Pointer)(unsafe.Pointer(&newOpcodeMap)))
}
func loadDecoderMap() map[uintptr]decoder { func loadDecoderMap() map[uintptr]decoder {
p := atomic.LoadPointer(&cachedDecoderMap) p := atomic.LoadPointer(&cachedDecoderMap)
return *(*map[uintptr]decoder)(unsafe.Pointer(&p)) return *(*map[uintptr]decoder)(unsafe.Pointer(&p))

View File

@ -1,54 +0,0 @@
package json
import (
"bytes"
)
func compact(dst *bytes.Buffer, src []byte, escape bool) error {
if len(src) == 0 {
return errUnexpectedEndOfJSON("", 0)
}
length := len(src)
for cursor := 0; cursor < length; cursor++ {
c := src[cursor]
switch c {
case ' ', '\t', '\n', '\r':
continue
case '"':
if err := dst.WriteByte(c); err != nil {
return err
}
for {
cursor++
c := src[cursor]
if escape && (c == '<' || c == '>' || c == '&') {
if _, err := dst.WriteString(`\u00`); err != nil {
return err
}
if _, err := dst.Write([]byte{hex[c>>4], hex[c&0xF]}); err != nil {
return err
}
} else if err := dst.WriteByte(c); err != nil {
return err
}
switch c {
case '\\':
cursor++
if err := dst.WriteByte(src[cursor]); err != nil {
return err
}
case '"':
goto LOOP_END
case nul:
return errUnexpectedEndOfJSON("string", int64(length))
}
}
default:
if err := dst.WriteByte(c); err != nil {
return err
}
}
LOOP_END:
}
return nil
}

View File

@ -1,6 +1,7 @@
package json package json
import ( import (
"encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
@ -8,6 +9,10 @@ import (
"unsafe" "unsafe"
) )
var (
jsonNumberType = reflect.TypeOf(json.Number(""))
)
func decodeCompileToGetDecoderSlowPath(typeptr uintptr, typ *rtype) (decoder, error) { func decodeCompileToGetDecoderSlowPath(typeptr uintptr, typ *rtype) (decoder, error) {
decoderMap := loadDecoderMap() decoderMap := loadDecoderMap()
if dec, exists := decoderMap[typeptr]; exists { if dec, exists := decoderMap[typeptr]; exists {

File diff suppressed because it is too large Load Diff

View File

@ -1,34 +0,0 @@
// +build !race
package json
import "unsafe"
func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
if typeptr > maxTypeAddr {
return encodeCompileToGetCodeSetSlowPath(typeptr)
}
index := typeptr - baseTypeAddr
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
return codeSet, nil
}
// noescape trick for header.typ ( reflect.*rtype )
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
code, err := encodeCompileHead(&encodeCompileContext{
typ: copiedType,
structTypeToCompiledCode: map[uintptr]*compiledCode{},
})
if err != nil {
return nil, err
}
code = copyOpcode(code)
codeLength := code.totalLength()
codeSet := &opcodeSet{
code: code,
codeLength: codeLength,
}
cachedOpcodeSets[index] = codeSet
return codeSet, nil
}

View File

@ -1,44 +0,0 @@
// +build race
package json
import (
"sync"
"unsafe"
)
var setsMu sync.RWMutex
func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
if typeptr > maxTypeAddr {
return encodeCompileToGetCodeSetSlowPath(typeptr)
}
index := typeptr - baseTypeAddr
setsMu.RLock()
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
setsMu.RUnlock()
return codeSet, nil
}
setsMu.RUnlock()
// noescape trick for header.typ ( reflect.*rtype )
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
code, err := encodeCompileHead(&encodeCompileContext{
typ: copiedType,
structTypeToCompiledCode: map[uintptr]*compiledCode{},
})
if err != nil {
return nil, err
}
code = copyOpcode(code)
codeLength := code.totalLength()
codeSet := &opcodeSet{
code: code,
codeLength: codeLength,
}
setsMu.Lock()
cachedOpcodeSets[index] = codeSet
setsMu.Unlock()
return codeSet, nil
}

View File

@ -1,164 +0,0 @@
package json
import (
"bytes"
"sync"
"unsafe"
)
type mapItem struct {
key []byte
value []byte
}
type mapslice struct {
items []mapItem
}
func (m *mapslice) Len() int {
return len(m.items)
}
func (m *mapslice) Less(i, j int) bool {
return bytes.Compare(m.items[i].key, m.items[j].key) < 0
}
func (m *mapslice) Swap(i, j int) {
m.items[i], m.items[j] = m.items[j], m.items[i]
}
type encodeMapContext struct {
pos []int
slice *mapslice
buf []byte
}
var mapContextPool = sync.Pool{
New: func() interface{} {
return &encodeMapContext{}
},
}
func newMapContext(mapLen int) *encodeMapContext {
ctx := mapContextPool.Get().(*encodeMapContext)
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)
} else {
ctx.pos = ctx.pos[:0]
ctx.slice.items = ctx.slice.items[:0]
}
ctx.buf = ctx.buf[:0]
return ctx
}
func releaseMapContext(c *encodeMapContext) {
mapContextPool.Put(c)
}
type encodeCompileContext struct {
typ *rtype
opcodeIndex int
ptrIndex int
indent int
structTypeToCompiledCode map[uintptr]*compiledCode
parent *encodeCompileContext
}
func (c *encodeCompileContext) context() *encodeCompileContext {
return &encodeCompileContext{
typ: c.typ,
opcodeIndex: c.opcodeIndex,
ptrIndex: c.ptrIndex,
indent: c.indent,
structTypeToCompiledCode: c.structTypeToCompiledCode,
parent: c,
}
}
func (c *encodeCompileContext) withType(typ *rtype) *encodeCompileContext {
ctx := c.context()
ctx.typ = typ
return ctx
}
func (c *encodeCompileContext) incIndent() *encodeCompileContext {
ctx := c.context()
ctx.indent++
return ctx
}
func (c *encodeCompileContext) decIndent() *encodeCompileContext {
ctx := c.context()
ctx.indent--
return ctx
}
func (c *encodeCompileContext) incIndex() {
c.incOpcodeIndex()
c.incPtrIndex()
}
func (c *encodeCompileContext) decIndex() {
c.decOpcodeIndex()
c.decPtrIndex()
}
func (c *encodeCompileContext) incOpcodeIndex() {
c.opcodeIndex++
if c.parent != nil {
c.parent.incOpcodeIndex()
}
}
func (c *encodeCompileContext) decOpcodeIndex() {
c.opcodeIndex--
if c.parent != nil {
c.parent.decOpcodeIndex()
}
}
func (c *encodeCompileContext) incPtrIndex() {
c.ptrIndex++
if c.parent != nil {
c.parent.incPtrIndex()
}
}
func (c *encodeCompileContext) decPtrIndex() {
c.ptrIndex--
if c.parent != nil {
c.parent.decPtrIndex()
}
}
type encodeRuntimeContext struct {
buf []byte
ptrs []uintptr
keepRefs []unsafe.Pointer
seenPtr []uintptr
baseIndent int
prefix []byte
indentStr []byte
}
func (c *encodeRuntimeContext) init(p uintptr, codelen int) {
if len(c.ptrs) < codelen {
c.ptrs = make([]uintptr, codelen)
}
c.ptrs[0] = p
c.keepRefs = c.keepRefs[:0]
c.seenPtr = c.seenPtr[:0]
c.baseIndent = 0
}
func (c *encodeRuntimeContext) ptr() uintptr {
header := (*sliceHeader)(unsafe.Pointer(&c.ptrs))
return uintptr(header.data)
}

View File

@ -1,124 +0,0 @@
package json
import (
"unsafe"
)
var endianness int
func init() {
var b [2]byte
*(*uint16)(unsafe.Pointer(&b)) = uint16(0xABCD)
switch b[0] {
case 0xCD:
endianness = 0 // LE
case 0xAB:
endianness = 1 // BE
default:
panic("could not determine endianness")
}
}
// "00010203...96979899" cast to []uint16
var intLELookup = [100]uint16{
0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, 0x3630, 0x3730, 0x3830, 0x3930,
0x3031, 0x3131, 0x3231, 0x3331, 0x3431, 0x3531, 0x3631, 0x3731, 0x3831, 0x3931,
0x3032, 0x3132, 0x3232, 0x3332, 0x3432, 0x3532, 0x3632, 0x3732, 0x3832, 0x3932,
0x3033, 0x3133, 0x3233, 0x3333, 0x3433, 0x3533, 0x3633, 0x3733, 0x3833, 0x3933,
0x3034, 0x3134, 0x3234, 0x3334, 0x3434, 0x3534, 0x3634, 0x3734, 0x3834, 0x3934,
0x3035, 0x3135, 0x3235, 0x3335, 0x3435, 0x3535, 0x3635, 0x3735, 0x3835, 0x3935,
0x3036, 0x3136, 0x3236, 0x3336, 0x3436, 0x3536, 0x3636, 0x3736, 0x3836, 0x3936,
0x3037, 0x3137, 0x3237, 0x3337, 0x3437, 0x3537, 0x3637, 0x3737, 0x3837, 0x3937,
0x3038, 0x3138, 0x3238, 0x3338, 0x3438, 0x3538, 0x3638, 0x3738, 0x3838, 0x3938,
0x3039, 0x3139, 0x3239, 0x3339, 0x3439, 0x3539, 0x3639, 0x3739, 0x3839, 0x3939,
}
var intBELookup = [100]uint16{
0x3030, 0x3031, 0x3032, 0x3033, 0x3034, 0x3035, 0x3036, 0x3037, 0x3038, 0x3039,
0x3130, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3138, 0x3139,
0x3230, 0x3231, 0x3232, 0x3233, 0x3234, 0x3235, 0x3236, 0x3237, 0x3238, 0x3239,
0x3330, 0x3331, 0x3332, 0x3333, 0x3334, 0x3335, 0x3336, 0x3337, 0x3338, 0x3339,
0x3430, 0x3431, 0x3432, 0x3433, 0x3434, 0x3435, 0x3436, 0x3437, 0x3438, 0x3439,
0x3530, 0x3531, 0x3532, 0x3533, 0x3534, 0x3535, 0x3536, 0x3537, 0x3538, 0x3539,
0x3630, 0x3631, 0x3632, 0x3633, 0x3634, 0x3635, 0x3636, 0x3637, 0x3638, 0x3639,
0x3730, 0x3731, 0x3732, 0x3733, 0x3734, 0x3735, 0x3736, 0x3737, 0x3738, 0x3739,
0x3830, 0x3831, 0x3832, 0x3833, 0x3834, 0x3835, 0x3836, 0x3837, 0x3838, 0x3839,
0x3930, 0x3931, 0x3932, 0x3933, 0x3934, 0x3935, 0x3936, 0x3937, 0x3938, 0x3939,
}
var intLookup = [2]*[100]uint16{&intLELookup, &intBELookup}
func appendInt(out []byte, u64 uint64, code *opcode) []byte {
n := u64 & code.mask
negative := (u64>>code.rshiftNum)&1 == 1
if !negative {
if n < 10 {
return append(out, byte(n+'0'))
} else if n < 100 {
u := intLELookup[n]
return append(out, byte(u), byte(u>>8))
}
} else {
n = -n & code.mask
}
lookup := intLookup[endianness]
var b [22]byte
u := (*[11]uint16)(unsafe.Pointer(&b))
i := 11
for n >= 100 {
j := n % 100
n /= 100
i--
u[i] = lookup[j]
}
i--
u[i] = lookup[n]
i *= 2 // convert to byte index
if n < 10 {
i++ // remove leading zero
}
if negative {
i--
b[i] = '-'
}
return append(out, b[i:]...)
}
func appendUint(out []byte, u64 uint64, code *opcode) []byte {
n := u64 & code.mask
if n < 10 {
return append(out, byte(n+'0'))
} else if n < 100 {
u := intLELookup[n]
return append(out, byte(u), byte(u>>8))
}
lookup := intLookup[endianness]
var b [22]byte
u := (*[11]uint16)(unsafe.Pointer(&b))
i := 11
for n >= 100 {
j := n % 100
n /= 100
i--
u[i] = lookup[j]
}
i--
u[i] = lookup[n]
i *= 2 // convert to byte index
if n < 10 {
i++ // remove leading zero
}
return append(out, b[i:]...)
}

View File

@ -1,8 +0,0 @@
// +build !go1.13
package json
import "unsafe"
//go:linkname mapitervalue reflect.mapitervalue
func mapitervalue(it unsafe.Pointer) unsafe.Pointer

View File

@ -1,8 +0,0 @@
// +build go1.13
package json
import "unsafe"
//go:linkname mapitervalue reflect.mapiterelem
func mapitervalue(it unsafe.Pointer) unsafe.Pointer

View File

@ -1,512 +0,0 @@
package json
import (
"fmt"
"strings"
"unsafe"
)
const uintptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
type opcode struct {
op opType // operation type
typ *rtype // go type
displayIdx int // opcode index
key []byte // struct field key
escapedKey []byte // struct field key ( HTML escaped )
ptrNum int // pointer number: e.g. double pointer is 2.
displayKey string // key text to display
isTaggedKey bool // whether tagged key
anonymousKey bool // whether anonymous key
anonymousHead bool // whether anonymous head or not
indirect bool // whether indirect or not
nilcheck bool // whether needs to nilcheck or not
addrForMarshaler bool // whether needs to addr for marshaler or not
rshiftNum uint8 // use to take bit for judging whether negative integer or not
mask uint64 // mask for number
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
mapPos uintptr // offset to access position list for sorted map
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
prevField *opcode // prev struct field
nextField *opcode // next struct field
next *opcode // next opcode
jmp *compiledCode // for recursive call
}
func newOpCode(ctx *encodeCompileContext, op opType) *opcode {
return newOpCodeWithNext(ctx, op, newEndOp(ctx))
}
func opcodeOffset(idx int) uintptr {
return uintptr(idx) * uintptrSize
}
func copyOpcode(code *opcode) *opcode {
codeMap := map[uintptr]*opcode{}
return code.copy(codeMap)
}
func newOpCodeWithNext(ctx *encodeCompileContext, op opType, next *opcode) *opcode {
return &opcode{
op: op,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
indent: ctx.indent,
idx: opcodeOffset(ctx.ptrIndex),
next: next,
}
}
func newEndOp(ctx *encodeCompileContext) *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,
typ: c.typ,
displayIdx: c.displayIdx,
key: c.key,
escapedKey: c.escapedKey,
displayKey: c.displayKey,
ptrNum: c.ptrNum,
mask: c.mask,
rshiftNum: c.rshiftNum,
isTaggedKey: c.isTaggedKey,
anonymousKey: c.anonymousKey,
anonymousHead: c.anonymousHead,
indirect: c.indirect,
nilcheck: c.nilcheck,
addrForMarshaler: c.addrForMarshaler,
indent: c.indent,
idx: c.idx,
headIdx: c.headIdx,
elemIdx: c.elemIdx,
length: c.length,
mapIter: c.mapIter,
mapPos: c.mapPos,
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.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 (c *opcode) totalLength() int {
var idx int
for code := c; code.op != opEnd; {
idx = int(code.idx / uintptrSize)
if code.op == opStructFieldRecursiveEnd {
break
}
switch code.op.codeType() {
case codeArrayElem, codeSliceElem, codeMapKey:
code = code.end
default:
code = code.next
}
}
return idx + 2 // opEnd + 1
}
func (c *opcode) decOpcodeIndex() {
for code := c; code.op != opEnd; {
code.displayIdx--
code.idx -= uintptrSize
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
}
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 uintptr
if code.op.codeType() == codeArrayHead {
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,
)
}
func (c *opcode) dumpMapEnd(code *opcode) string {
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][mapPos:%d][length:%d])`,
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
code.idx/uintptrSize,
code.mapPos/uintptrSize,
code.length/uintptrSize,
)
}
func (c *opcode) dumpElem(code *opcode) string {
var length uintptr
if code.op.codeType() == codeArrayElem {
length = code.length
} else {
length = code.length / uintptrSize
}
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d][size:%d])`,
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
code.idx/uintptrSize,
code.headIdx/uintptrSize,
code.elemIdx/uintptrSize,
length,
code.size,
)
}
func (c *opcode) dumpField(code *opcode) string {
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][key:%s][offset:%d][headIdx:%d])`,
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
code.idx/uintptrSize,
code.displayKey,
code.offset,
code.headIdx/uintptrSize,
)
}
func (c *opcode) dumpKey(code *opcode) string {
return fmt.Sprintf(
`[%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])`,
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
code.idx/uintptrSize,
code.mapIter/uintptrSize,
)
}
func (c *opcode) dump() string {
codes := []string{}
for code := c; code.op != opEnd; {
switch code.op.codeType() {
case codeSliceHead:
codes = append(codes, c.dumpHead(code))
code = code.next
case codeMapHead:
codes = append(codes, c.dumpMapHead(code))
code = code.next
case codeArrayElem, codeSliceElem:
codes = append(codes, c.dumpElem(code))
code = code.end
case codeMapKey:
codes = append(codes, c.dumpKey(code))
code = code.end
case codeMapValue:
codes = append(codes, c.dumpValue(code))
code = code.next
case codeMapEnd:
codes = append(codes, c.dumpMapEnd(code))
code = code.next
case codeStructField:
codes = append(codes, c.dumpField(code))
code = code.next
case codeStructEnd:
codes = append(codes, c.dumpField(code))
code = code.next
default:
codes = append(codes, fmt.Sprintf(
"[%d]%s%s ([idx:%d])",
code.displayIdx,
strings.Repeat("-", code.indent),
code.op,
code.idx/uintptrSize,
))
code = code.next
}
}
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 encodeLinkPrevToNextField(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.next
break
} else if nextCode.op == opEnd {
break
}
code = nextCode
}
}
func newSliceHeaderCode(ctx *encodeCompileContext) *opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
length := opcodeOffset(ctx.ptrIndex)
return &opcode{
op: opSlice,
displayIdx: ctx.opcodeIndex,
idx: idx,
headIdx: idx,
elemIdx: elemIdx,
length: length,
indent: ctx.indent,
}
}
func newSliceElemCode(ctx *encodeCompileContext, head *opcode, size uintptr) *opcode {
return &opcode{
op: opSliceElem,
displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.ptrIndex),
headIdx: head.idx,
elemIdx: head.elemIdx,
length: head.length,
indent: ctx.indent,
size: size,
}
}
func newArrayHeaderCode(ctx *encodeCompileContext, alen int) *opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
return &opcode{
op: opArray,
displayIdx: ctx.opcodeIndex,
idx: idx,
headIdx: idx,
elemIdx: elemIdx,
indent: ctx.indent,
length: uintptr(alen),
}
}
func newArrayElemCode(ctx *encodeCompileContext, head *opcode, length int, size uintptr) *opcode {
return &opcode{
op: opArrayElem,
displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.ptrIndex),
elemIdx: head.elemIdx,
headIdx: head.headIdx,
length: uintptr(length),
indent: ctx.indent,
size: size,
}
}
func newMapHeaderCode(ctx *encodeCompileContext) *opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
length := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
mapIter := opcodeOffset(ctx.ptrIndex)
return &opcode{
op: opMap,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
idx: idx,
elemIdx: elemIdx,
length: length,
mapIter: mapIter,
indent: ctx.indent,
}
}
func newMapKeyCode(ctx *encodeCompileContext, head *opcode) *opcode {
return &opcode{
op: opMapKey,
displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.ptrIndex),
elemIdx: head.elemIdx,
length: head.length,
mapIter: head.mapIter,
indent: ctx.indent,
}
}
func newMapValueCode(ctx *encodeCompileContext, head *opcode) *opcode {
return &opcode{
op: opMapValue,
displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.ptrIndex),
elemIdx: head.elemIdx,
length: head.length,
mapIter: head.mapIter,
indent: ctx.indent,
}
}
func newMapEndCode(ctx *encodeCompileContext, head *opcode) *opcode {
mapPos := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
idx := opcodeOffset(ctx.ptrIndex)
return &opcode{
op: opMapEnd,
displayIdx: ctx.opcodeIndex,
idx: idx,
length: head.length,
mapPos: mapPos,
indent: ctx.indent,
next: newEndOp(ctx),
}
}
func newInterfaceCode(ctx *encodeCompileContext) *opcode {
return &opcode{
op: opInterface,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.ptrIndex),
indent: ctx.indent,
next: newEndOp(ctx),
}
}
func newRecursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
return &opcode{
op: opStructFieldRecursive,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.ptrIndex),
indent: ctx.indent,
next: newEndOp(ctx),
jmp: jmp,
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,637 +0,0 @@
package json
import (
"math/bits"
"reflect"
"unicode/utf8"
"unsafe"
)
const (
lsb = 0x0101010101010101
msb = 0x8080808080808080
)
var needEscapeWithHTML = [256]bool{
'"': true,
'&': true,
'<': true,
'>': true,
'\\': true,
0x00: true,
0x01: true,
0x02: true,
0x03: true,
0x04: true,
0x05: true,
0x06: true,
0x07: true,
0x08: true,
0x09: true,
0x0a: true,
0x0b: true,
0x0c: true,
0x0d: true,
0x0e: true,
0x0f: true,
0x10: true,
0x11: true,
0x12: true,
0x13: true,
0x14: true,
0x15: true,
0x16: true,
0x17: true,
0x18: true,
0x19: true,
0x1a: true,
0x1b: true,
0x1c: true,
0x1d: true,
0x1e: true,
0x1f: true,
/* 0x20 - 0x7f */
0x80: true,
0x81: true,
0x82: true,
0x83: true,
0x84: true,
0x85: true,
0x86: true,
0x87: true,
0x88: true,
0x89: true,
0x8a: true,
0x8b: true,
0x8c: true,
0x8d: true,
0x8e: true,
0x8f: true,
0x90: true,
0x91: true,
0x92: true,
0x93: true,
0x94: true,
0x95: true,
0x96: true,
0x97: true,
0x98: true,
0x99: true,
0x9a: true,
0x9b: true,
0x9c: true,
0x9d: true,
0x9e: true,
0x9f: true,
0xa0: true,
0xa1: true,
0xa2: true,
0xa3: true,
0xa4: true,
0xa5: true,
0xa6: true,
0xa7: true,
0xa8: true,
0xa9: true,
0xaa: true,
0xab: true,
0xac: true,
0xad: true,
0xae: true,
0xaf: true,
0xb0: true,
0xb1: true,
0xb2: true,
0xb3: true,
0xb4: true,
0xb5: true,
0xb6: true,
0xb7: true,
0xb8: true,
0xb9: true,
0xba: true,
0xbb: true,
0xbc: true,
0xbd: true,
0xbe: true,
0xbf: true,
0xc0: true,
0xc1: true,
0xc2: true,
0xc3: true,
0xc4: true,
0xc5: true,
0xc6: true,
0xc7: true,
0xc8: true,
0xc9: true,
0xca: true,
0xcb: true,
0xcc: true,
0xcd: true,
0xce: true,
0xcf: true,
0xd0: true,
0xd1: true,
0xd2: true,
0xd3: true,
0xd4: true,
0xd5: true,
0xd6: true,
0xd7: true,
0xd8: true,
0xd9: true,
0xda: true,
0xdb: true,
0xdc: true,
0xdd: true,
0xde: true,
0xdf: true,
0xe0: true,
0xe1: true,
0xe2: true,
0xe3: true,
0xe4: true,
0xe5: true,
0xe6: true,
0xe7: true,
0xe8: true,
0xe9: true,
0xea: true,
0xeb: true,
0xec: true,
0xed: true,
0xee: true,
0xef: true,
0xf0: true,
0xf1: true,
0xf2: true,
0xf3: true,
0xf4: true,
0xf5: true,
0xf6: true,
0xf7: true,
0xf8: true,
0xf9: true,
0xfa: true,
0xfb: true,
0xfc: true,
0xfd: true,
0xfe: true,
0xff: true,
}
var needEscape = [256]bool{
'"': true,
'\\': true,
0x00: true,
0x01: true,
0x02: true,
0x03: true,
0x04: true,
0x05: true,
0x06: true,
0x07: true,
0x08: true,
0x09: true,
0x0a: true,
0x0b: true,
0x0c: true,
0x0d: true,
0x0e: true,
0x0f: true,
0x10: true,
0x11: true,
0x12: true,
0x13: true,
0x14: true,
0x15: true,
0x16: true,
0x17: true,
0x18: true,
0x19: true,
0x1a: true,
0x1b: true,
0x1c: true,
0x1d: true,
0x1e: true,
0x1f: true,
/* 0x20 - 0x7f */
0x80: true,
0x81: true,
0x82: true,
0x83: true,
0x84: true,
0x85: true,
0x86: true,
0x87: true,
0x88: true,
0x89: true,
0x8a: true,
0x8b: true,
0x8c: true,
0x8d: true,
0x8e: true,
0x8f: true,
0x90: true,
0x91: true,
0x92: true,
0x93: true,
0x94: true,
0x95: true,
0x96: true,
0x97: true,
0x98: true,
0x99: true,
0x9a: true,
0x9b: true,
0x9c: true,
0x9d: true,
0x9e: true,
0x9f: true,
0xa0: true,
0xa1: true,
0xa2: true,
0xa3: true,
0xa4: true,
0xa5: true,
0xa6: true,
0xa7: true,
0xa8: true,
0xa9: true,
0xaa: true,
0xab: true,
0xac: true,
0xad: true,
0xae: true,
0xaf: true,
0xb0: true,
0xb1: true,
0xb2: true,
0xb3: true,
0xb4: true,
0xb5: true,
0xb6: true,
0xb7: true,
0xb8: true,
0xb9: true,
0xba: true,
0xbb: true,
0xbc: true,
0xbd: true,
0xbe: true,
0xbf: true,
0xc0: true,
0xc1: true,
0xc2: true,
0xc3: true,
0xc4: true,
0xc5: true,
0xc6: true,
0xc7: true,
0xc8: true,
0xc9: true,
0xca: true,
0xcb: true,
0xcc: true,
0xcd: true,
0xce: true,
0xcf: true,
0xd0: true,
0xd1: true,
0xd2: true,
0xd3: true,
0xd4: true,
0xd5: true,
0xd6: true,
0xd7: true,
0xd8: true,
0xd9: true,
0xda: true,
0xdb: true,
0xdc: true,
0xdd: true,
0xde: true,
0xdf: true,
0xe0: true,
0xe1: true,
0xe2: true,
0xe3: true,
0xe4: true,
0xe5: true,
0xe6: true,
0xe7: true,
0xe8: true,
0xe9: true,
0xea: true,
0xeb: true,
0xec: true,
0xed: true,
0xee: true,
0xef: true,
0xf0: true,
0xf1: true,
0xf2: true,
0xf3: true,
0xf4: true,
0xf5: true,
0xf6: true,
0xf7: true,
0xf8: true,
0xf9: true,
0xfa: true,
0xfb: true,
0xfc: true,
0xfd: true,
0xfe: true,
0xff: true,
}
var hex = "0123456789abcdef"
// escapeIndex finds the index of the first char in `s` that requires escaping.
// A char requires escaping if it's outside of the range of [0x20, 0x7F] or if
// it includes a double quote or backslash.
// If no chars in `s` require escaping, the return value is -1.
func escapeIndex(s string) int {
chunks := stringToUint64Slice(s)
for _, n := range chunks {
// combine masks before checking for the MSB of each byte. We include
// `n` in the mask to check whether any of the *input* byte MSBs were
// set (i.e. the byte was outside the ASCII range).
mask := n | below(n, 0x20) | contains(n, '"') | contains(n, '\\')
if (mask & msb) != 0 {
return bits.TrailingZeros64(mask&msb) / 8
}
}
valLen := len(s)
for i := len(chunks) * 8; i < valLen; i++ {
if needEscape[s[i]] {
return i
}
}
return -1
}
// below return a mask that can be used to determine if any of the bytes
// in `n` are below `b`. If a byte's MSB is set in the mask then that byte was
// below `b`. The result is only valid if `b`, and each byte in `n`, is below
// 0x80.
func below(n uint64, b byte) uint64 {
return n - expand(b)
}
// contains returns a mask that can be used to determine if any of the
// bytes in `n` are equal to `b`. If a byte's MSB is set in the mask then
// that byte is equal to `b`. The result is only valid if `b`, and each
// byte in `n`, is below 0x80.
func contains(n uint64, b byte) uint64 {
return (n ^ expand(b)) - lsb
}
// expand puts the specified byte into each of the 8 bytes of a uint64.
func expand(b byte) uint64 {
return lsb * uint64(b)
}
//nolint:govet
func stringToUint64Slice(s string) []uint64 {
return *(*[]uint64)(unsafe.Pointer(&reflect.SliceHeader{
Data: ((*reflect.StringHeader)(unsafe.Pointer(&s))).Data,
Len: len(s) / 8,
Cap: len(s) / 8,
}))
}
func encodeEscapedString(buf []byte, s string) []byte {
valLen := len(s)
if valLen == 0 {
return append(buf, `""`...)
}
buf = append(buf, '"')
var (
i, j int
)
if valLen >= 8 {
chunks := stringToUint64Slice(s)
for _, n := range chunks {
// combine masks before checking for the MSB of each byte. We include
// `n` in the mask to check whether any of the *input* byte MSBs were
// set (i.e. the byte was outside the ASCII range).
mask := n | (n - (lsb * 0x20)) |
((n ^ (lsb * '"')) - lsb) |
((n ^ (lsb * '\\')) - lsb) |
((n ^ (lsb * '<')) - lsb) |
((n ^ (lsb * '>')) - lsb) |
((n ^ (lsb * '&')) - lsb)
if (mask & msb) != 0 {
j = bits.TrailingZeros64(mask&msb) / 8
goto ESCAPE_END
}
}
for i := len(chunks) * 8; i < valLen; i++ {
if needEscapeWithHTML[s[i]] {
j = i
goto ESCAPE_END
}
}
// no found any escape characters.
return append(append(buf, s...), '"')
}
ESCAPE_END:
for j < valLen {
c := s[j]
if !needEscapeWithHTML[c] {
// fast path: most of the time, printable ascii characters are used
j++
continue
}
switch c {
case '\\', '"':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', c)
i = j + 1
j = j + 1
continue
case '\n':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 'n')
i = j + 1
j = j + 1
continue
case '\r':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 'r')
i = j + 1
j = j + 1
continue
case '\t':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 't')
i = j + 1
j = j + 1
continue
case '<', '>', '&':
buf = append(buf, s[i:j]...)
buf = append(buf, `\u00`...)
buf = append(buf, hex[c>>4], hex[c&0xF])
i = j + 1
j = j + 1
continue
}
// This encodes bytes < 0x20 except for \t, \n and \r.
if c < 0x20 {
buf = append(buf, s[i:j]...)
buf = append(buf, `\u00`...)
buf = append(buf, hex[c>>4], hex[c&0xF])
i = j + 1
j = j + 1
continue
}
r, size := utf8.DecodeRuneInString(s[j:])
if r == utf8.RuneError && size == 1 {
buf = append(buf, s[i:j]...)
buf = append(buf, `\ufffd`...)
i = j + size
j = j + size
continue
}
switch r {
case '\u2028', '\u2029':
// U+2028 is LINE SEPARATOR.
// U+2029 is PARAGRAPH SEPARATOR.
// They are both technically valid characters in JSON strings,
// but don't work in JSONP, which has to be evaluated as JavaScript,
// and can lead to security holes there. It is valid JSON to
// escape them, so we do so unconditionally.
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
buf = append(buf, s[i:j]...)
buf = append(buf, `\u202`...)
buf = append(buf, hex[r&0xF])
i = j + size
j = j + size
continue
}
j += size
}
return append(append(buf, s[i:]...), '"')
}
func encodeNoEscapedString(buf []byte, s string) []byte {
valLen := len(s)
if valLen == 0 {
return append(buf, `""`...)
}
buf = append(buf, '"')
var escapeIdx int
if valLen >= 8 {
if escapeIdx = escapeIndex(s); escapeIdx < 0 {
return append(append(buf, s...), '"')
}
}
i := 0
j := escapeIdx
for j < valLen {
c := s[j]
if c >= 0x20 && c <= 0x7f && c != '\\' && c != '"' {
// fast path: most of the time, printable ascii characters are used
j++
continue
}
switch c {
case '\\', '"':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', c)
i = j + 1
j = j + 1
continue
case '\n':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 'n')
i = j + 1
j = j + 1
continue
case '\r':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 'r')
i = j + 1
j = j + 1
continue
case '\t':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 't')
i = j + 1
j = j + 1
continue
case '<', '>', '&':
buf = append(buf, s[i:j]...)
buf = append(buf, `\u00`...)
buf = append(buf, hex[c>>4], hex[c&0xF])
i = j + 1
j = j + 1
continue
}
// This encodes bytes < 0x20 except for \t, \n and \r.
if c < 0x20 {
buf = append(buf, s[i:j]...)
buf = append(buf, `\u00`...)
buf = append(buf, hex[c>>4], hex[c&0xF])
i = j + 1
j = j + 1
continue
}
r, size := utf8.DecodeRuneInString(s[j:])
if r == utf8.RuneError && size == 1 {
buf = append(buf, s[i:j]...)
buf = append(buf, `\ufffd`...)
i = j + size
j = j + size
continue
}
switch r {
case '\u2028', '\u2029':
// U+2028 is LINE SEPARATOR.
// U+2029 is PARAGRAPH SEPARATOR.
// They are both technically valid characters in JSON strings,
// but don't work in JSONP, which has to be evaluated as JavaScript,
// and can lead to security holes there. It is valid JSON to
// escape them, so we do so unconditionally.
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
buf = append(buf, s[i:j]...)
buf = append(buf, `\u202`...)
buf = append(buf, hex[r&0xF])
i = j + size
j = j + size
continue
}
j += size
}
return append(append(buf, s[i:]...), '"')
}

View File

@ -1,66 +0,0 @@
package json
import (
"fmt"
"reflect"
"strconv"
"unsafe"
)
const startDetectingCyclesAfter = 1000
func load(base uintptr, idx uintptr) uintptr {
addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr))
}
func store(base uintptr, idx uintptr, p uintptr) {
addr := base + idx
**(**uintptr)(unsafe.Pointer(&addr)) = p
}
func ptrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
func ptrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) }
func ptrToNumber(p uintptr) Number { return **(**Number)(unsafe.Pointer(&p)) }
func ptrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) }
func ptrToSlice(p uintptr) *sliceHeader { return *(**sliceHeader)(unsafe.Pointer(&p)) }
func ptrToPtr(p uintptr) uintptr {
return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&p)))
}
func ptrToUnsafePtr(p uintptr) unsafe.Pointer {
return *(*unsafe.Pointer)(unsafe.Pointer(&p))
}
func ptrToInterface(code *opcode, p uintptr) interface{} {
return *(*interface{})(unsafe.Pointer(&emptyInterface{
typ: code.typ,
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
}))
}
func errUnsupportedValue(code *opcode, ptr uintptr) *UnsupportedValueError {
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
typ: code.typ,
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&ptr)),
}))
return &UnsupportedValueError{
Value: reflect.ValueOf(v),
Str: fmt.Sprintf("encountered a cycle via %s", code.typ),
}
}
func errUnsupportedFloat(v float64) *UnsupportedValueError {
return &UnsupportedValueError{
Value: reflect.ValueOf(v),
Str: strconv.FormatFloat(v, 'g', -1, 64),
}
}
func errMarshalerWithCode(code *opcode, err error) *MarshalerError {
return &MarshalerError{
Type: rtype2type(code.typ),
Err: err,
}
}

View File

@ -6,7 +6,7 @@ import (
"github.com/goccy/go-json/internal/errors" "github.com/goccy/go-json/internal/errors"
) )
func compact(dst *bytes.Buffer, src []byte, escape bool) error { func Compact(dst *bytes.Buffer, src []byte, escape bool) error {
if len(src) == 0 { if len(src) == 0 {
return errors.ErrUnexpectedEndOfJSON("", 0) return errors.ErrUnexpectedEndOfJSON("", 0)
} }

View File

@ -1,4 +1,4 @@
package json package encoder
import ( import (
"testing" "testing"
@ -10,9 +10,9 @@ func TestDumpOpcode(t *testing.T) {
header := (*emptyInterface)(unsafe.Pointer(&v)) header := (*emptyInterface)(unsafe.Pointer(&v))
typ := header.typ typ := header.typ
typeptr := uintptr(unsafe.Pointer(typ)) typeptr := uintptr(unsafe.Pointer(typ))
codeSet, err := encodeCompileToGetCodeSet(typeptr) codeSet, err := CompileToGetCodeSet(typeptr)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
codeSet.code.dump() codeSet.Code.Dump()
} }

View File

@ -434,7 +434,7 @@ func AppendMarshalJSON(code *Opcode, b []byte, v interface{}, escape bool) ([]by
} }
buf := bytes.NewBuffer(b) buf := bytes.NewBuffer(b)
// TODO: we should validate buffer with `compact` // TODO: we should validate buffer with `compact`
if err := compact(buf, bb, escape); err != nil { if err := Compact(buf, bb, escape); err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err} return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
} }
return buf.Bytes(), nil return buf.Bytes(), nil
@ -461,11 +461,11 @@ func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v inte
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err} return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
} }
var compactBuf bytes.Buffer var compactBuf bytes.Buffer
if err := compact(&compactBuf, bb, escape); err != nil { if err := Compact(&compactBuf, bb, escape); err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err} return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
} }
var indentBuf bytes.Buffer var indentBuf bytes.Buffer
if err := encodeIndent( if err := Indent(
&indentBuf, &indentBuf,
compactBuf.Bytes(), compactBuf.Bytes(),
string(ctx.Prefix)+strings.Repeat(string(ctx.IndentStr), ctx.BaseIndent+indent+1), string(ctx.Prefix)+strings.Repeat(string(ctx.IndentStr), ctx.BaseIndent+indent+1),

View File

@ -6,7 +6,7 @@ import (
"github.com/goccy/go-json/internal/errors" "github.com/goccy/go-json/internal/errors"
) )
func encodeIndent(dst *bytes.Buffer, src []byte, prefix, indentStr string) error { func Indent(dst *bytes.Buffer, src []byte, prefix, indentStr string) error {
length := int64(len(src)) length := int64(len(src))
indentNum := 0 indentNum := 0
indentBytes := []byte(indentStr) indentBytes := []byte(indentStr)

View File

@ -4,6 +4,8 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/goccy/go-json/internal/encoder"
) )
// Marshaler is the interface implemented by types that // Marshaler is the interface implemented by types that
@ -308,7 +310,7 @@ func (m *RawMessage) UnmarshalJSON(data []byte) error {
// Compact appends to dst the JSON-encoded src with // Compact appends to dst the JSON-encoded src with
// insignificant space characters elided. // insignificant space characters elided.
func Compact(dst *bytes.Buffer, src []byte) error { func Compact(dst *bytes.Buffer, src []byte) error {
return compact(dst, src, false) return encoder.Compact(dst, src, false)
} }
// Indent appends to dst an indented form of the JSON-encoded src. // Indent appends to dst an indented form of the JSON-encoded src.
@ -323,7 +325,7 @@ func Compact(dst *bytes.Buffer, src []byte) error {
// For example, if src has no trailing spaces, neither will dst; // For example, if src has no trailing spaces, neither will dst;
// if src ends in a trailing newline, so will dst. // if src ends in a trailing newline, so will dst.
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
return encodeWithIndent(dst, src, prefix, indent) return encoder.Indent(dst, src, prefix, indent)
} }
// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 // HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029