gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

encode.go (9015B)


      1 package json
      2 
      3 import (
      4 	"context"
      5 	"io"
      6 	"os"
      7 	"unsafe"
      8 
      9 	"github.com/goccy/go-json/internal/encoder"
     10 	"github.com/goccy/go-json/internal/encoder/vm"
     11 	"github.com/goccy/go-json/internal/encoder/vm_color"
     12 	"github.com/goccy/go-json/internal/encoder/vm_color_indent"
     13 	"github.com/goccy/go-json/internal/encoder/vm_indent"
     14 )
     15 
     16 // An Encoder writes JSON values to an output stream.
     17 type Encoder struct {
     18 	w                 io.Writer
     19 	enabledIndent     bool
     20 	enabledHTMLEscape bool
     21 	prefix            string
     22 	indentStr         string
     23 }
     24 
     25 // NewEncoder returns a new encoder that writes to w.
     26 func NewEncoder(w io.Writer) *Encoder {
     27 	return &Encoder{w: w, enabledHTMLEscape: true}
     28 }
     29 
     30 // Encode writes the JSON encoding of v to the stream, followed by a newline character.
     31 //
     32 // See the documentation for Marshal for details about the conversion of Go values to JSON.
     33 func (e *Encoder) Encode(v interface{}) error {
     34 	return e.EncodeWithOption(v)
     35 }
     36 
     37 // EncodeWithOption call Encode with EncodeOption.
     38 func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc) error {
     39 	ctx := encoder.TakeRuntimeContext()
     40 	ctx.Option.Flag = 0
     41 
     42 	err := e.encodeWithOption(ctx, v, optFuncs...)
     43 
     44 	encoder.ReleaseRuntimeContext(ctx)
     45 	return err
     46 }
     47 
     48 // EncodeContext call Encode with context.Context and EncodeOption.
     49 func (e *Encoder) EncodeContext(ctx context.Context, v interface{}, optFuncs ...EncodeOptionFunc) error {
     50 	rctx := encoder.TakeRuntimeContext()
     51 	rctx.Option.Flag = 0
     52 	rctx.Option.Flag |= encoder.ContextOption
     53 	rctx.Option.Context = ctx
     54 
     55 	err := e.encodeWithOption(rctx, v, optFuncs...)
     56 
     57 	encoder.ReleaseRuntimeContext(rctx)
     58 	return err
     59 }
     60 
     61 func (e *Encoder) encodeWithOption(ctx *encoder.RuntimeContext, v interface{}, optFuncs ...EncodeOptionFunc) error {
     62 	if e.enabledHTMLEscape {
     63 		ctx.Option.Flag |= encoder.HTMLEscapeOption
     64 	}
     65 	ctx.Option.Flag |= encoder.NormalizeUTF8Option
     66 	ctx.Option.DebugOut = os.Stdout
     67 	for _, optFunc := range optFuncs {
     68 		optFunc(ctx.Option)
     69 	}
     70 	var (
     71 		buf []byte
     72 		err error
     73 	)
     74 	if e.enabledIndent {
     75 		buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr)
     76 	} else {
     77 		buf, err = encode(ctx, v)
     78 	}
     79 	if err != nil {
     80 		return err
     81 	}
     82 	if e.enabledIndent {
     83 		buf = buf[:len(buf)-2]
     84 	} else {
     85 		buf = buf[:len(buf)-1]
     86 	}
     87 	buf = append(buf, '\n')
     88 	if _, err := e.w.Write(buf); err != nil {
     89 		return err
     90 	}
     91 	return nil
     92 }
     93 
     94 // SetEscapeHTML specifies whether problematic HTML characters should be escaped inside JSON quoted strings.
     95 // The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e to avoid certain safety problems that can arise when embedding JSON in HTML.
     96 //
     97 // In non-HTML settings where the escaping interferes with the readability of the output, SetEscapeHTML(false) disables this behavior.
     98 func (e *Encoder) SetEscapeHTML(on bool) {
     99 	e.enabledHTMLEscape = on
    100 }
    101 
    102 // SetIndent instructs the encoder to format each subsequent encoded value as if indented by the package-level function Indent(dst, src, prefix, indent).
    103 // Calling SetIndent("", "") disables indentation.
    104 func (e *Encoder) SetIndent(prefix, indent string) {
    105 	if prefix == "" && indent == "" {
    106 		e.enabledIndent = false
    107 		return
    108 	}
    109 	e.prefix = prefix
    110 	e.indentStr = indent
    111 	e.enabledIndent = true
    112 }
    113 
    114 func marshalContext(ctx context.Context, v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
    115 	rctx := encoder.TakeRuntimeContext()
    116 	rctx.Option.Flag = 0
    117 	rctx.Option.Flag = encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option | encoder.ContextOption
    118 	rctx.Option.Context = ctx
    119 	for _, optFunc := range optFuncs {
    120 		optFunc(rctx.Option)
    121 	}
    122 
    123 	buf, err := encode(rctx, v)
    124 	if err != nil {
    125 		encoder.ReleaseRuntimeContext(rctx)
    126 		return nil, err
    127 	}
    128 
    129 	// this line exists to escape call of `runtime.makeslicecopy` .
    130 	// if use `make([]byte, len(buf)-1)` and `copy(copied, buf)`,
    131 	// dst buffer size and src buffer size are differrent.
    132 	// in this case, compiler uses `runtime.makeslicecopy`, but it is slow.
    133 	buf = buf[:len(buf)-1]
    134 	copied := make([]byte, len(buf))
    135 	copy(copied, buf)
    136 
    137 	encoder.ReleaseRuntimeContext(rctx)
    138 	return copied, nil
    139 }
    140 
    141 func marshal(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
    142 	ctx := encoder.TakeRuntimeContext()
    143 
    144 	ctx.Option.Flag = 0
    145 	ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option)
    146 	for _, optFunc := range optFuncs {
    147 		optFunc(ctx.Option)
    148 	}
    149 
    150 	buf, err := encode(ctx, v)
    151 	if err != nil {
    152 		encoder.ReleaseRuntimeContext(ctx)
    153 		return nil, err
    154 	}
    155 
    156 	// this line exists to escape call of `runtime.makeslicecopy` .
    157 	// if use `make([]byte, len(buf)-1)` and `copy(copied, buf)`,
    158 	// dst buffer size and src buffer size are differrent.
    159 	// in this case, compiler uses `runtime.makeslicecopy`, but it is slow.
    160 	buf = buf[:len(buf)-1]
    161 	copied := make([]byte, len(buf))
    162 	copy(copied, buf)
    163 
    164 	encoder.ReleaseRuntimeContext(ctx)
    165 	return copied, nil
    166 }
    167 
    168 func marshalNoEscape(v interface{}) ([]byte, error) {
    169 	ctx := encoder.TakeRuntimeContext()
    170 
    171 	ctx.Option.Flag = 0
    172 	ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option)
    173 
    174 	buf, err := encodeNoEscape(ctx, v)
    175 	if err != nil {
    176 		encoder.ReleaseRuntimeContext(ctx)
    177 		return nil, err
    178 	}
    179 
    180 	// this line exists to escape call of `runtime.makeslicecopy` .
    181 	// if use `make([]byte, len(buf)-1)` and `copy(copied, buf)`,
    182 	// dst buffer size and src buffer size are differrent.
    183 	// in this case, compiler uses `runtime.makeslicecopy`, but it is slow.
    184 	buf = buf[:len(buf)-1]
    185 	copied := make([]byte, len(buf))
    186 	copy(copied, buf)
    187 
    188 	encoder.ReleaseRuntimeContext(ctx)
    189 	return copied, nil
    190 }
    191 
    192 func marshalIndent(v interface{}, prefix, indent string, optFuncs ...EncodeOptionFunc) ([]byte, error) {
    193 	ctx := encoder.TakeRuntimeContext()
    194 
    195 	ctx.Option.Flag = 0
    196 	ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option | encoder.IndentOption)
    197 	for _, optFunc := range optFuncs {
    198 		optFunc(ctx.Option)
    199 	}
    200 
    201 	buf, err := encodeIndent(ctx, v, prefix, indent)
    202 	if err != nil {
    203 		encoder.ReleaseRuntimeContext(ctx)
    204 		return nil, err
    205 	}
    206 
    207 	buf = buf[:len(buf)-2]
    208 	copied := make([]byte, len(buf))
    209 	copy(copied, buf)
    210 
    211 	encoder.ReleaseRuntimeContext(ctx)
    212 	return copied, nil
    213 }
    214 
    215 func encode(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error) {
    216 	b := ctx.Buf[:0]
    217 	if v == nil {
    218 		b = encoder.AppendNull(ctx, b)
    219 		b = encoder.AppendComma(ctx, b)
    220 		return b, nil
    221 	}
    222 	header := (*emptyInterface)(unsafe.Pointer(&v))
    223 	typ := header.typ
    224 
    225 	typeptr := uintptr(unsafe.Pointer(typ))
    226 	codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
    227 	if err != nil {
    228 		return nil, err
    229 	}
    230 
    231 	p := uintptr(header.ptr)
    232 	ctx.Init(p, codeSet.CodeLength)
    233 	ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
    234 
    235 	buf, err := encodeRunCode(ctx, b, codeSet)
    236 	if err != nil {
    237 		return nil, err
    238 	}
    239 	ctx.Buf = buf
    240 	return buf, nil
    241 }
    242 
    243 func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error) {
    244 	b := ctx.Buf[:0]
    245 	if v == nil {
    246 		b = encoder.AppendNull(ctx, b)
    247 		b = encoder.AppendComma(ctx, b)
    248 		return b, nil
    249 	}
    250 	header := (*emptyInterface)(unsafe.Pointer(&v))
    251 	typ := header.typ
    252 
    253 	typeptr := uintptr(unsafe.Pointer(typ))
    254 	codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
    255 	if err != nil {
    256 		return nil, err
    257 	}
    258 
    259 	p := uintptr(header.ptr)
    260 	ctx.Init(p, codeSet.CodeLength)
    261 	buf, err := encodeRunCode(ctx, b, codeSet)
    262 	if err != nil {
    263 		return nil, err
    264 	}
    265 
    266 	ctx.Buf = buf
    267 	return buf, nil
    268 }
    269 
    270 func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent string) ([]byte, error) {
    271 	b := ctx.Buf[:0]
    272 	if v == nil {
    273 		b = encoder.AppendNull(ctx, b)
    274 		b = encoder.AppendCommaIndent(ctx, b)
    275 		return b, nil
    276 	}
    277 	header := (*emptyInterface)(unsafe.Pointer(&v))
    278 	typ := header.typ
    279 
    280 	typeptr := uintptr(unsafe.Pointer(typ))
    281 	codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
    282 	if err != nil {
    283 		return nil, err
    284 	}
    285 
    286 	p := uintptr(header.ptr)
    287 	ctx.Init(p, codeSet.CodeLength)
    288 	buf, err := encodeRunIndentCode(ctx, b, codeSet, prefix, indent)
    289 
    290 	ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
    291 
    292 	if err != nil {
    293 		return nil, err
    294 	}
    295 
    296 	ctx.Buf = buf
    297 	return buf, nil
    298 }
    299 
    300 func encodeRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
    301 	if (ctx.Option.Flag & encoder.DebugOption) != 0 {
    302 		if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
    303 			return vm_color.DebugRun(ctx, b, codeSet)
    304 		}
    305 		return vm.DebugRun(ctx, b, codeSet)
    306 	}
    307 	if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
    308 		return vm_color.Run(ctx, b, codeSet)
    309 	}
    310 	return vm.Run(ctx, b, codeSet)
    311 }
    312 
    313 func encodeRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, prefix, indent string) ([]byte, error) {
    314 	ctx.Prefix = []byte(prefix)
    315 	ctx.IndentStr = []byte(indent)
    316 	if (ctx.Option.Flag & encoder.DebugOption) != 0 {
    317 		if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
    318 			return vm_color_indent.DebugRun(ctx, b, codeSet)
    319 		}
    320 		return vm_indent.DebugRun(ctx, b, codeSet)
    321 	}
    322 	if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
    323 		return vm_color_indent.Run(ctx, b, codeSet)
    324 	}
    325 	return vm_indent.Run(ctx, b, codeSet)
    326 }