github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/jsoni/config.go (about)

     1  package jsoni
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"io"
     7  	"reflect"
     8  	"sync"
     9  	"unsafe"
    10  
    11  	"github.com/bingoohuang/gg/pkg/ss"
    12  	"github.com/modern-go/concurrent"
    13  	"github.com/modern-go/reflect2"
    14  )
    15  
    16  // Config customize how the API should behave.
    17  // The API is created from Config by Froze.
    18  type Config struct {
    19  	IndentionStep                 int
    20  	MarshalFloatWith6Digits       bool
    21  	EscapeHTML                    bool
    22  	SortMapKeys                   bool
    23  	OmitEmptyStructField          bool // omit empty struct field.
    24  	OmitEmptyMapKeys              bool // omit keys whose value is empty.
    25  	UseNumber                     bool
    26  	DisallowUnknownFields         bool
    27  	TagKey                        string
    28  	OnlyTaggedField               bool
    29  	ValidateJsonRawMessage        bool
    30  	ObjectFieldMustBeSimpleString bool
    31  	CaseSensitive                 bool
    32  	Int64AsString                 bool
    33  	NilAsEmpty                    bool
    34  	ClearQuotes                   bool
    35  }
    36  
    37  // API the public interface of this package.
    38  // Primary Marshal and Unmarshal.
    39  type API interface {
    40  	IteratorPool
    41  	StreamPool
    42  	MarshalToString(ctx context.Context, v interface{}) (string, error)
    43  	Marshal(ctx context.Context, v interface{}) ([]byte, error)
    44  	MarshalIndent(ctx context.Context, v interface{}, prefix, indent string) ([]byte, error)
    45  	UnmarshalFromString(ctx context.Context, str string, v interface{}) error
    46  	Unmarshal(ctx context.Context, data []byte, v interface{}) error
    47  	Get(data []byte, path ...interface{}) Any
    48  	NewEncoder(writer io.Writer) *Encoder
    49  	NewDecoder(reader io.Reader) *Decoder
    50  	Valid(ctx context.Context, data []byte) bool
    51  	RegisterExtension(extension Extension)
    52  	DecoderOf(typ reflect2.Type) ValDecoder
    53  	EncoderOf(typ reflect2.Type) ValEncoder
    54  
    55  	RegisterTypeEncoder(typ string, encoder ValEncoder)
    56  	RegisterTypeDecoder(typ string, decoder ValDecoder)
    57  	RegisterTypeEncoderFunc(typ string, fun EncoderFunc, isEmptyFunc IsEmptyFn)
    58  	RegisterTypeDecoderFunc(typ string, fun DecoderFunc)
    59  	RegisterFieldEncoder(typ string, field string, encoder ValEncoder)
    60  	RegisterFieldDecoder(typ string, field string, decoder ValDecoder)
    61  }
    62  
    63  // ConfigDefault the default API
    64  var ConfigDefault = Config{
    65  	EscapeHTML: true,
    66  }.Froze()
    67  
    68  // ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior
    69  var ConfigCompatibleWithStandardLibrary = Config{
    70  	EscapeHTML:             true,
    71  	SortMapKeys:            true,
    72  	ValidateJsonRawMessage: true,
    73  }.Froze()
    74  
    75  // ConfigFastest marshals float with only 6 digits precision
    76  var ConfigFastest = Config{
    77  	EscapeHTML:                    false,
    78  	MarshalFloatWith6Digits:       true, // will lose precession
    79  	ObjectFieldMustBeSimpleString: true, // do not unescape object field
    80  }.Froze()
    81  
    82  type frozenConfig struct {
    83  	configBeforeFrozen Config
    84  
    85  	indentionStep        int
    86  	sortMapKeys          bool
    87  	omitEmptyStructField bool // omit empty struct field
    88  	omitEmptyMapKeys     bool // omit empty keys whose value is empty
    89  
    90  	objectFieldMustBeSimpleString bool
    91  
    92  	onlyTaggedField       bool
    93  	disallowUnknownFields bool
    94  
    95  	caseSensitive bool
    96  	int64AsString bool
    97  	nilAsEmpty    bool
    98  	clearQuotes   bool // clear Valid JSONObject/JSONArray string without quotes
    99  
   100  	decoderCache     *concurrent.Map
   101  	encoderCache     *concurrent.Map
   102  	encoderExtension Extension
   103  	decoderExtension Extension
   104  	extensions       Extensions
   105  	streamPool       *sync.Pool
   106  	iteratorPool     *sync.Pool
   107  
   108  	typeDecoders  map[string]ValDecoder
   109  	fieldDecoders map[string]ValDecoder
   110  	typeEncoders  map[string]ValEncoder
   111  	fieldEncoders map[string]ValEncoder
   112  }
   113  
   114  func (c *frozenConfig) initCache() {
   115  	c.decoderCache = concurrent.NewMap()
   116  	c.encoderCache = concurrent.NewMap()
   117  }
   118  
   119  func (c *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) {
   120  	c.decoderCache.Store(cacheKey, decoder)
   121  }
   122  
   123  func (c *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) {
   124  	c.encoderCache.Store(cacheKey, encoder)
   125  }
   126  
   127  func (c *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder {
   128  	if decoder, ok := c.decoderCache.Load(cacheKey); ok {
   129  		return decoder.(ValDecoder)
   130  	}
   131  	return nil
   132  }
   133  
   134  func (c *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder {
   135  	encoder, found := c.encoderCache.Load(cacheKey)
   136  	if found {
   137  		return encoder.(ValEncoder)
   138  	}
   139  	return nil
   140  }
   141  
   142  var cfgCache = concurrent.NewMap()
   143  
   144  func getFrozenConfigFromCache(cfg Config) *frozenConfig {
   145  	if obj, found := cfgCache.Load(cfg); found {
   146  		return obj.(*frozenConfig)
   147  	}
   148  	return nil
   149  }
   150  
   151  func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
   152  	cfgCache.Store(cfg, frozenConfig)
   153  }
   154  
   155  // Froze forge API from config
   156  func (c Config) Froze() API {
   157  	api := &frozenConfig{
   158  		sortMapKeys:                   c.SortMapKeys,
   159  		omitEmptyStructField:          c.OmitEmptyStructField,
   160  		omitEmptyMapKeys:              c.OmitEmptyMapKeys,
   161  		indentionStep:                 c.IndentionStep,
   162  		objectFieldMustBeSimpleString: c.ObjectFieldMustBeSimpleString,
   163  		onlyTaggedField:               c.OnlyTaggedField,
   164  		disallowUnknownFields:         c.DisallowUnknownFields,
   165  		caseSensitive:                 c.CaseSensitive,
   166  		int64AsString:                 c.Int64AsString,
   167  		nilAsEmpty:                    c.NilAsEmpty,
   168  		clearQuotes:                   c.ClearQuotes,
   169  
   170  		typeDecoders:  map[string]ValDecoder{},
   171  		fieldDecoders: map[string]ValDecoder{},
   172  		typeEncoders:  map[string]ValEncoder{},
   173  		fieldEncoders: map[string]ValEncoder{},
   174  	}
   175  	api.streamPool = &sync.Pool{New: func() interface{} { return NewStream(api, nil, 512) }}
   176  	api.iteratorPool = &sync.Pool{New: func() interface{} { return NewIterator(api) }}
   177  	api.initCache()
   178  	encoderExtension := EncoderExtension{}
   179  	decoderExtension := DecoderExtension{}
   180  	if c.MarshalFloatWith6Digits {
   181  		api.marshalFloatWith6Digits(encoderExtension)
   182  	}
   183  	if c.EscapeHTML {
   184  		api.escapeHTML(encoderExtension)
   185  	}
   186  	if c.UseNumber {
   187  		api.useNumber(decoderExtension)
   188  	}
   189  	if c.ValidateJsonRawMessage {
   190  		api.validateJsonRawMessage(encoderExtension)
   191  	}
   192  	api.encoderExtension = encoderExtension
   193  	api.decoderExtension = decoderExtension
   194  	api.configBeforeFrozen = c
   195  	return api
   196  }
   197  
   198  func (c Config) frozeWithCacheReuse(extraExtensions []Extension) *frozenConfig {
   199  	api := getFrozenConfigFromCache(c)
   200  	if api != nil {
   201  		return api
   202  	}
   203  	api = c.Froze().(*frozenConfig)
   204  	for _, extension := range extraExtensions {
   205  		api.RegisterExtension(extension)
   206  	}
   207  	addFrozenConfigToCache(c, api)
   208  	return api
   209  }
   210  
   211  func (c *frozenConfig) validateJsonRawMessage(extension EncoderExtension) {
   212  	encoder := &funcEncoder{fn: func(ctx context.Context, ptr unsafe.Pointer, stream *Stream) {
   213  		rawMessage := *(*json.RawMessage)(ptr)
   214  		iter := c.BorrowIterator(rawMessage)
   215  		defer c.ReturnIterator(iter)
   216  		iter.Read(ctx)
   217  		if iter.Error != nil && iter.Error != io.EOF {
   218  			stream.WriteRaw("null")
   219  		} else {
   220  			stream.WriteRaw(string(rawMessage))
   221  		}
   222  	}, isEmptyFn: func(ctx context.Context, ptr unsafe.Pointer, checkZero bool) bool {
   223  		return len(*((*json.RawMessage)(ptr))) == 0
   224  	}}
   225  	extension[PtrElem((*json.RawMessage)(nil))] = encoder
   226  	extension[PtrElem((*RawMessage)(nil))] = encoder
   227  }
   228  
   229  func PtrElem(obj interface{}) reflect2.Type { return reflect2.TypeOfPtr(obj).Elem() }
   230  
   231  func (c *frozenConfig) useNumber(extension DecoderExtension) {
   232  	extension[PtrElem((*interface{})(nil))] = &funcDecoder{fun: func(ctx context.Context, ptr unsafe.Pointer, iter *Iterator) {
   233  		exitingValue := *((*interface{})(ptr))
   234  		if exitingValue != nil && reflect.TypeOf(exitingValue).Kind() == reflect.Ptr {
   235  			iter.ReadVal(ctx, exitingValue)
   236  			return
   237  		}
   238  		if iter.WhatIsNext() == NumberValue {
   239  			*((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
   240  		} else {
   241  			*((*interface{})(ptr)) = iter.Read(ctx)
   242  		}
   243  	}}
   244  }
   245  func (c *frozenConfig) getTagKey() string { return ss.Or(c.configBeforeFrozen.TagKey, "json") }
   246  
   247  func (c *frozenConfig) RegisterExtension(extension Extension) {
   248  	c.extensions = append(c.extensions, extension)
   249  }
   250  
   251  type lossyFloat32Encoder struct{}
   252  
   253  func (e *lossyFloat32Encoder) Encode(_ context.Context, ptr unsafe.Pointer, stream *Stream) {
   254  	stream.WriteFloat32Lossy(*((*float32)(ptr)))
   255  }
   256  
   257  func (e *lossyFloat32Encoder) IsEmpty(_ context.Context, ptr unsafe.Pointer, _ bool) bool {
   258  	return *((*float32)(ptr)) == 0
   259  }
   260  
   261  type lossyFloat64Encoder struct{}
   262  
   263  func (e *lossyFloat64Encoder) Encode(_ context.Context, ptr unsafe.Pointer, stream *Stream) {
   264  	stream.WriteFloat64Lossy(*((*float64)(ptr)))
   265  }
   266  
   267  func (e *lossyFloat64Encoder) IsEmpty(_ context.Context, ptr unsafe.Pointer, _ bool) bool {
   268  	return *((*float64)(ptr)) == 0
   269  }
   270  
   271  // EnableLossyFloatMarshalling keeps 10**(-6) precision
   272  // for float variables for better performance.
   273  func (c *frozenConfig) marshalFloatWith6Digits(extension EncoderExtension) {
   274  	// for better performance
   275  	extension[PtrElem((*float32)(nil))] = &lossyFloat32Encoder{}
   276  	extension[PtrElem((*float64)(nil))] = &lossyFloat64Encoder{}
   277  }
   278  
   279  type htmlEscapedStringEncoder struct{}
   280  
   281  func (e *htmlEscapedStringEncoder) Encode(ctx context.Context, ptr unsafe.Pointer, stream *Stream) {
   282  	s := *((*string)(ptr))
   283  
   284  	if writeRawBytesIfClearQuotes(ctx, s, stream) {
   285  		return
   286  	}
   287  
   288  	stream.WriteStringWithHTMLEscaped(s)
   289  }
   290  
   291  func (e *htmlEscapedStringEncoder) IsEmpty(_ context.Context, ptr unsafe.Pointer, _ bool) bool {
   292  	return *((*string)(ptr)) == ""
   293  }
   294  
   295  func (c *frozenConfig) escapeHTML(encoderExtension EncoderExtension) {
   296  	encoderExtension[PtrElem((*string)(nil))] = &htmlEscapedStringEncoder{}
   297  }
   298  
   299  func (c *frozenConfig) MarshalToString(parent context.Context, v interface{}) (string, error) {
   300  	stream := c.BorrowStream(nil)
   301  	defer c.ReturnStream(stream)
   302  	stream.WriteVal(createCfgContext(parent, c), v)
   303  	if stream.Error != nil {
   304  		return "", stream.Error
   305  	}
   306  	return string(stream.Buffer()), nil
   307  }
   308  
   309  func (c *frozenConfig) Marshal(parent context.Context, v interface{}) ([]byte, error) {
   310  	stream := c.BorrowStream(nil)
   311  	defer c.ReturnStream(stream)
   312  	stream.WriteVal(createCfgContext(parent, c), v)
   313  	if stream.Error != nil {
   314  		return nil, stream.Error
   315  	}
   316  	result := stream.Buffer()
   317  	copied := make([]byte, len(result))
   318  	copy(copied, result)
   319  	return copied, nil
   320  }
   321  
   322  func createCfgContext(parent context.Context, c *frozenConfig) context.Context {
   323  	if parent == nil {
   324  		parent = context.Background()
   325  	}
   326  	return context.WithValue(parent, ContextCfg, c)
   327  }
   328  
   329  func (c *frozenConfig) MarshalIndent(parent context.Context, v interface{}, prefix, indent string) ([]byte, error) {
   330  	if prefix != "" {
   331  		panic("prefix is not supported")
   332  	}
   333  	for _, r := range indent {
   334  		if r != ' ' {
   335  			panic("indent can only be space")
   336  		}
   337  	}
   338  	newCfg := c.configBeforeFrozen
   339  	newCfg.IndentionStep = len(indent)
   340  	return newCfg.frozeWithCacheReuse(c.extensions).Marshal(createCfgContext(parent, c), v)
   341  }
   342  
   343  func (c *frozenConfig) UnmarshalFromString(parent context.Context, str string, v interface{}) error {
   344  	data := []byte(str)
   345  	iter := c.BorrowIterator(data)
   346  	defer c.ReturnIterator(iter)
   347  	iter.ReadVal(createCfgContext(parent, c), v)
   348  	if t := iter.nextToken(); t == 0 {
   349  		if iter.Error == io.EOF {
   350  			return nil
   351  		}
   352  		return iter.Error
   353  	}
   354  	iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
   355  	return iter.Error
   356  }
   357  
   358  func (c *frozenConfig) Get(data []byte, path ...interface{}) Any {
   359  	iter := c.BorrowIterator(data)
   360  	defer c.ReturnIterator(iter)
   361  	return locatePath(iter, path)
   362  }
   363  
   364  func (c *frozenConfig) Unmarshal(parent context.Context, data []byte, v interface{}) error {
   365  	iter := c.BorrowIterator(data)
   366  	defer c.ReturnIterator(iter)
   367  	iter.ReadVal(createCfgContext(parent, c), v)
   368  	if t := iter.nextToken(); t == 0 {
   369  		if iter.Error == io.EOF {
   370  			return nil
   371  		}
   372  		return iter.Error
   373  	}
   374  	iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
   375  	return iter.Error
   376  }
   377  
   378  func (c *frozenConfig) NewEncoder(writer io.Writer) *Encoder {
   379  	return &Encoder{stream: NewStream(c, writer, 512)}
   380  }
   381  
   382  func (c *frozenConfig) NewDecoder(reader io.Reader) *Decoder {
   383  	return &Decoder{iter: Parse(c, reader, 512)}
   384  }
   385  
   386  func (c *frozenConfig) Valid(_ context.Context, data []byte) bool {
   387  	iter := c.BorrowIterator(data)
   388  	defer c.ReturnIterator(iter)
   389  	iter.Skip()
   390  	return iter.Error == nil
   391  }