mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2024-12-23 19:23:12 +00:00
98263a7de6
* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
533 lines
12 KiB
Go
533 lines
12 KiB
Go
package encoder
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"unsafe"
|
|
|
|
"github.com/goccy/go-json/internal/errors"
|
|
"github.com/goccy/go-json/internal/runtime"
|
|
)
|
|
|
|
type Option int
|
|
|
|
const (
|
|
HTMLEscapeOption Option = 1 << iota
|
|
IndentOption
|
|
UnorderedMapOption
|
|
)
|
|
|
|
func (t OpType) IsMultipleOpHead() bool {
|
|
switch t {
|
|
case OpStructHead:
|
|
return true
|
|
case OpStructHeadSlice:
|
|
return true
|
|
case OpStructHeadArray:
|
|
return true
|
|
case OpStructHeadMap:
|
|
return true
|
|
case OpStructHeadStruct:
|
|
return true
|
|
case OpStructHeadOmitEmpty:
|
|
return true
|
|
case OpStructHeadOmitEmptySlice:
|
|
return true
|
|
case OpStructHeadOmitEmptyArray:
|
|
return true
|
|
case OpStructHeadOmitEmptyMap:
|
|
return true
|
|
case OpStructHeadOmitEmptyStruct:
|
|
return true
|
|
case OpStructHeadSlicePtr:
|
|
return true
|
|
case OpStructHeadOmitEmptySlicePtr:
|
|
return true
|
|
case OpStructHeadArrayPtr:
|
|
return true
|
|
case OpStructHeadOmitEmptyArrayPtr:
|
|
return true
|
|
case OpStructHeadMapPtr:
|
|
return true
|
|
case OpStructHeadOmitEmptyMapPtr:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t OpType) IsMultipleOpField() bool {
|
|
switch t {
|
|
case OpStructField:
|
|
return true
|
|
case OpStructFieldSlice:
|
|
return true
|
|
case OpStructFieldArray:
|
|
return true
|
|
case OpStructFieldMap:
|
|
return true
|
|
case OpStructFieldStruct:
|
|
return true
|
|
case OpStructFieldOmitEmpty:
|
|
return true
|
|
case OpStructFieldOmitEmptySlice:
|
|
return true
|
|
case OpStructFieldOmitEmptyArray:
|
|
return true
|
|
case OpStructFieldOmitEmptyMap:
|
|
return true
|
|
case OpStructFieldOmitEmptyStruct:
|
|
return true
|
|
case OpStructFieldSlicePtr:
|
|
return true
|
|
case OpStructFieldOmitEmptySlicePtr:
|
|
return true
|
|
case OpStructFieldArrayPtr:
|
|
return true
|
|
case OpStructFieldOmitEmptyArrayPtr:
|
|
return true
|
|
case OpStructFieldMapPtr:
|
|
return true
|
|
case OpStructFieldOmitEmptyMapPtr:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
type OpcodeSet struct {
|
|
Type *runtime.Type
|
|
Code *Opcode
|
|
CodeLength int
|
|
}
|
|
|
|
type CompiledCode struct {
|
|
Code *Opcode
|
|
Linked bool // whether recursive code already have linked
|
|
CurLen uintptr
|
|
NextLen uintptr
|
|
}
|
|
|
|
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 LoadNPtr(base uintptr, idx uintptr, ptrNum int) uintptr {
|
|
addr := base + idx
|
|
p := **(**uintptr)(unsafe.Pointer(&addr))
|
|
if p == 0 {
|
|
return 0
|
|
}
|
|
return PtrToPtr(p)
|
|
/*
|
|
for i := 0; i < ptrNum; i++ {
|
|
if p == 0 {
|
|
return p
|
|
}
|
|
p = PtrToPtr(p)
|
|
}
|
|
return 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) json.Number { return **(**json.Number)(unsafe.Pointer(&p)) }
|
|
func PtrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) }
|
|
func PtrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader)(unsafe.Pointer(&p)) }
|
|
func PtrToPtr(p uintptr) uintptr {
|
|
return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&p)))
|
|
}
|
|
func PtrToNPtr(p uintptr, ptrNum int) uintptr {
|
|
for i := 0; i < ptrNum; i++ {
|
|
if p == 0 {
|
|
return 0
|
|
}
|
|
p = PtrToPtr(p)
|
|
}
|
|
return 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.Type,
|
|
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
|
|
}))
|
|
}
|
|
|
|
func ErrUnsupportedValue(code *Opcode, ptr uintptr) *errors.UnsupportedValueError {
|
|
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
|
typ: code.Type,
|
|
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&ptr)),
|
|
}))
|
|
return &errors.UnsupportedValueError{
|
|
Value: reflect.ValueOf(v),
|
|
Str: fmt.Sprintf("encountered a cycle via %s", code.Type),
|
|
}
|
|
}
|
|
|
|
func ErrUnsupportedFloat(v float64) *errors.UnsupportedValueError {
|
|
return &errors.UnsupportedValueError{
|
|
Value: reflect.ValueOf(v),
|
|
Str: strconv.FormatFloat(v, 'g', -1, 64),
|
|
}
|
|
}
|
|
|
|
func ErrMarshalerWithCode(code *Opcode, err error) *errors.MarshalerError {
|
|
return &errors.MarshalerError{
|
|
Type: runtime.RType2Type(code.Type),
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
type emptyInterface struct {
|
|
typ *runtime.Type
|
|
ptr unsafe.Pointer
|
|
}
|
|
|
|
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 MapContext struct {
|
|
Pos []int
|
|
Slice *Mapslice
|
|
Buf []byte
|
|
}
|
|
|
|
var mapContextPool = sync.Pool{
|
|
New: func() interface{} {
|
|
return &MapContext{}
|
|
},
|
|
}
|
|
|
|
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)
|
|
} else {
|
|
ctx.Pos = ctx.Pos[:0]
|
|
ctx.Slice.Items = ctx.Slice.Items[:0]
|
|
}
|
|
ctx.Buf = ctx.Buf[:0]
|
|
return ctx
|
|
}
|
|
|
|
func ReleaseMapContext(c *MapContext) {
|
|
mapContextPool.Put(c)
|
|
}
|
|
|
|
//go:linkname MapIterInit reflect.mapiterinit
|
|
//go:noescape
|
|
func MapIterInit(mapType *runtime.Type, m unsafe.Pointer) unsafe.Pointer
|
|
|
|
//go:linkname MapIterKey reflect.mapiterkey
|
|
//go:noescape
|
|
func MapIterKey(it unsafe.Pointer) unsafe.Pointer
|
|
|
|
//go:linkname MapIterNext reflect.mapiternext
|
|
//go:noescape
|
|
func MapIterNext(it unsafe.Pointer)
|
|
|
|
//go:linkname MapLen reflect.maplen
|
|
//go:noescape
|
|
func MapLen(m unsafe.Pointer) int
|
|
|
|
func AppendByteSlice(b []byte, src []byte) []byte {
|
|
if src == nil {
|
|
return append(b, `null`...)
|
|
}
|
|
encodedLen := base64.StdEncoding.EncodedLen(len(src))
|
|
b = append(b, '"')
|
|
pos := len(b)
|
|
remainLen := cap(b[pos:])
|
|
var buf []byte
|
|
if remainLen > encodedLen {
|
|
buf = b[pos : pos+encodedLen]
|
|
} else {
|
|
buf = make([]byte, encodedLen)
|
|
}
|
|
base64.StdEncoding.Encode(buf, src)
|
|
return append(append(b, buf...), '"')
|
|
}
|
|
|
|
func AppendFloat32(b []byte, v float32) []byte {
|
|
f64 := float64(v)
|
|
abs := math.Abs(f64)
|
|
fmt := byte('f')
|
|
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
|
if abs != 0 {
|
|
f32 := float32(abs)
|
|
if f32 < 1e-6 || f32 >= 1e21 {
|
|
fmt = 'e'
|
|
}
|
|
}
|
|
return strconv.AppendFloat(b, f64, fmt, -1, 32)
|
|
}
|
|
|
|
func AppendFloat64(b []byte, v float64) []byte {
|
|
abs := math.Abs(v)
|
|
fmt := byte('f')
|
|
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
|
if abs != 0 {
|
|
if abs < 1e-6 || abs >= 1e21 {
|
|
fmt = 'e'
|
|
}
|
|
}
|
|
return strconv.AppendFloat(b, v, fmt, -1, 64)
|
|
}
|
|
|
|
func AppendBool(b []byte, v bool) []byte {
|
|
if v {
|
|
return append(b, "true"...)
|
|
}
|
|
return append(b, "false"...)
|
|
}
|
|
|
|
var (
|
|
floatTable = [256]bool{
|
|
'0': true,
|
|
'1': true,
|
|
'2': true,
|
|
'3': true,
|
|
'4': true,
|
|
'5': true,
|
|
'6': true,
|
|
'7': true,
|
|
'8': true,
|
|
'9': true,
|
|
'.': true,
|
|
'e': true,
|
|
'E': true,
|
|
'+': true,
|
|
'-': true,
|
|
}
|
|
)
|
|
|
|
func AppendNumber(b []byte, n json.Number) ([]byte, error) {
|
|
if len(n) == 0 {
|
|
return append(b, '0'), nil
|
|
}
|
|
for i := 0; i < len(n); i++ {
|
|
if !floatTable[n[i]] {
|
|
return nil, fmt.Errorf("json: invalid number literal %q", n)
|
|
}
|
|
}
|
|
b = append(b, n...)
|
|
return b, nil
|
|
}
|
|
|
|
func AppendMarshalJSON(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
|
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
|
if code.AddrForMarshaler {
|
|
if rv.CanAddr() {
|
|
rv = rv.Addr()
|
|
} else {
|
|
newV := reflect.New(rv.Type())
|
|
newV.Elem().Set(rv)
|
|
rv = newV
|
|
}
|
|
}
|
|
v = rv.Interface()
|
|
marshaler, ok := v.(json.Marshaler)
|
|
if !ok {
|
|
return AppendNull(b), nil
|
|
}
|
|
bb, err := marshaler.MarshalJSON()
|
|
if err != nil {
|
|
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
|
}
|
|
marshalBuf := ctx.MarshalBuf[:0]
|
|
marshalBuf = append(append(marshalBuf, bb...), nul)
|
|
compactedBuf, err := compact(b, marshalBuf, escape)
|
|
if err != nil {
|
|
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
|
}
|
|
ctx.MarshalBuf = marshalBuf
|
|
return compactedBuf, nil
|
|
}
|
|
|
|
func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
|
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
|
if code.AddrForMarshaler {
|
|
if rv.CanAddr() {
|
|
rv = rv.Addr()
|
|
} else {
|
|
newV := reflect.New(rv.Type())
|
|
newV.Elem().Set(rv)
|
|
rv = newV
|
|
}
|
|
}
|
|
v = rv.Interface()
|
|
marshaler, ok := v.(json.Marshaler)
|
|
if !ok {
|
|
return AppendNull(b), nil
|
|
}
|
|
bb, err := marshaler.MarshalJSON()
|
|
if err != nil {
|
|
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
|
}
|
|
marshalBuf := ctx.MarshalBuf[:0]
|
|
marshalBuf = append(append(marshalBuf, bb...), nul)
|
|
indentedBuf, err := doIndent(
|
|
b,
|
|
marshalBuf,
|
|
string(ctx.Prefix)+strings.Repeat(string(ctx.IndentStr), ctx.BaseIndent+code.Indent),
|
|
string(ctx.IndentStr),
|
|
escape,
|
|
)
|
|
if err != nil {
|
|
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
|
}
|
|
ctx.MarshalBuf = marshalBuf
|
|
return indentedBuf, nil
|
|
}
|
|
|
|
func AppendMarshalText(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
|
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
|
if code.AddrForMarshaler {
|
|
if rv.CanAddr() {
|
|
rv = rv.Addr()
|
|
} else {
|
|
newV := reflect.New(rv.Type())
|
|
newV.Elem().Set(rv)
|
|
rv = newV
|
|
}
|
|
}
|
|
v = rv.Interface()
|
|
marshaler, ok := v.(encoding.TextMarshaler)
|
|
if !ok {
|
|
return AppendNull(b), nil
|
|
}
|
|
bytes, err := marshaler.MarshalText()
|
|
if err != nil {
|
|
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
|
}
|
|
if escape {
|
|
return AppendEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
|
}
|
|
return AppendString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
|
}
|
|
|
|
func AppendMarshalTextIndent(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
|
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
|
if code.AddrForMarshaler {
|
|
if rv.CanAddr() {
|
|
rv = rv.Addr()
|
|
} else {
|
|
newV := reflect.New(rv.Type())
|
|
newV.Elem().Set(rv)
|
|
rv = newV
|
|
}
|
|
}
|
|
v = rv.Interface()
|
|
marshaler, ok := v.(encoding.TextMarshaler)
|
|
if !ok {
|
|
return AppendNull(b), nil
|
|
}
|
|
bytes, err := marshaler.MarshalText()
|
|
if err != nil {
|
|
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
|
}
|
|
if escape {
|
|
return AppendEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
|
}
|
|
return AppendString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
|
}
|
|
|
|
func AppendNull(b []byte) []byte {
|
|
return append(b, "null"...)
|
|
}
|
|
|
|
func AppendComma(b []byte) []byte {
|
|
return append(b, ',')
|
|
}
|
|
|
|
func AppendCommaIndent(b []byte) []byte {
|
|
return append(b, ',', '\n')
|
|
}
|
|
|
|
func AppendStructEnd(b []byte) []byte {
|
|
return append(b, '}', ',')
|
|
}
|
|
|
|
func AppendStructEndIndent(ctx *RuntimeContext, code *Opcode, b []byte) []byte {
|
|
b = append(b, '\n')
|
|
b = append(b, ctx.Prefix...)
|
|
indentNum := ctx.BaseIndent + code.Indent - 1
|
|
for i := 0; i < indentNum; i++ {
|
|
b = append(b, ctx.IndentStr...)
|
|
}
|
|
return append(b, '}', ',', '\n')
|
|
}
|
|
|
|
func AppendIndent(ctx *RuntimeContext, b []byte, indent int) []byte {
|
|
b = append(b, ctx.Prefix...)
|
|
indentNum := ctx.BaseIndent + indent
|
|
for i := 0; i < indentNum; i++ {
|
|
b = append(b, ctx.IndentStr...)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func IsNilForMarshaler(v interface{}) bool {
|
|
rv := reflect.ValueOf(v)
|
|
switch rv.Kind() {
|
|
case reflect.Bool:
|
|
return !rv.Bool()
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
return rv.Int() == 0
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
return rv.Uint() == 0
|
|
case reflect.Float32, reflect.Float64:
|
|
return math.Float64bits(rv.Float()) == 0
|
|
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Func:
|
|
return rv.IsNil()
|
|
case reflect.Slice:
|
|
return rv.IsNil() || rv.Len() == 0
|
|
}
|
|
return false
|
|
}
|