github.com/hamba/avro@v1.8.0/config.go (about)

     1  package avro
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"sync"
     7  
     8  	"github.com/modern-go/concurrent"
     9  	"github.com/modern-go/reflect2"
    10  )
    11  
    12  // DefaultConfig is the default API.
    13  var DefaultConfig = Config{}.Freeze()
    14  
    15  // Config customises how the codec should behave.
    16  type Config struct {
    17  	// TagKey is the struct tag key used when en/decoding structs.
    18  	// This defaults to "avro".
    19  	TagKey string
    20  
    21  	// BlockLength is the length of blocks for maps and arrays.
    22  	// This defaults to 100.
    23  	BlockLength int
    24  
    25  	// UnionResolutionError determines if an error will be returned
    26  	// when a type cannot be resolved while decoding a union.
    27  	UnionResolutionError bool
    28  }
    29  
    30  // Freeze makes the configuration immutable.
    31  func (c Config) Freeze() API {
    32  	api := &frozenConfig{
    33  		config:       c,
    34  		decoderCache: concurrent.NewMap(),
    35  		encoderCache: concurrent.NewMap(),
    36  		resolver:     NewTypeResolver(),
    37  	}
    38  
    39  	api.readerPool = &sync.Pool{
    40  		New: func() interface{} {
    41  			return &Reader{
    42  				cfg:    api,
    43  				reader: nil,
    44  				buf:    nil,
    45  				head:   0,
    46  				tail:   0,
    47  			}
    48  		},
    49  	}
    50  	api.writerPool = &sync.Pool{
    51  		New: func() interface{} {
    52  			return &Writer{
    53  				cfg:   api,
    54  				out:   nil,
    55  				buf:   make([]byte, 0, 512),
    56  				Error: nil,
    57  			}
    58  		},
    59  	}
    60  
    61  	return api
    62  }
    63  
    64  // API represents a frozen Config.
    65  type API interface {
    66  	// Marshal returns the Avro encoding of v.
    67  	Marshal(schema Schema, v interface{}) ([]byte, error)
    68  
    69  	// Unmarshal parses the Avro encoded data and stores the result in the value pointed to by v.
    70  	// If v is nil or not a pointer, Unmarshal returns an error.
    71  	Unmarshal(schema Schema, data []byte, v interface{}) error
    72  
    73  	// NewEncoder returns a new encoder that writes to w using schema.
    74  	NewEncoder(schema Schema, w io.Writer) *Encoder
    75  
    76  	// NewDecoder returns a new decoder that reads from reader r using schema.
    77  	NewDecoder(schema Schema, r io.Reader) *Decoder
    78  
    79  	// DecoderOf returns the value decoder for a given schema and type.
    80  	DecoderOf(schema Schema, typ reflect2.Type) ValDecoder
    81  
    82  	// EncoderOf returns the value encoder for a given schema and type.
    83  	EncoderOf(schema Schema, tpy reflect2.Type) ValEncoder
    84  
    85  	// Register registers names to their types for resolution. All primitive types are pre-registered.
    86  	Register(name string, obj interface{})
    87  }
    88  
    89  type frozenConfig struct {
    90  	config Config
    91  
    92  	decoderCache *concurrent.Map // map[cacheKey]ValDecoder
    93  	encoderCache *concurrent.Map // map[cacheKey]ValEncoder
    94  
    95  	readerPool *sync.Pool
    96  	writerPool *sync.Pool
    97  
    98  	resolver *TypeResolver
    99  }
   100  
   101  func (c *frozenConfig) Marshal(schema Schema, v interface{}) ([]byte, error) {
   102  	writer := c.borrowWriter()
   103  
   104  	writer.WriteVal(schema, v)
   105  	if err := writer.Error; err != nil {
   106  		c.returnWriter(writer)
   107  		return nil, err
   108  	}
   109  
   110  	result := writer.Buffer()
   111  	copied := make([]byte, len(result))
   112  	copy(copied, result)
   113  
   114  	c.returnWriter(writer)
   115  	return copied, nil
   116  }
   117  
   118  func (c *frozenConfig) borrowWriter() *Writer {
   119  	writer := c.writerPool.Get().(*Writer)
   120  	writer.Reset(nil)
   121  	return writer
   122  }
   123  
   124  func (c *frozenConfig) returnWriter(writer *Writer) {
   125  	writer.out = nil
   126  	writer.Error = nil
   127  
   128  	c.writerPool.Put(writer)
   129  }
   130  
   131  func (c *frozenConfig) Unmarshal(schema Schema, data []byte, v interface{}) error {
   132  	reader := c.borrowReader(data)
   133  
   134  	reader.ReadVal(schema, v)
   135  	err := reader.Error
   136  	c.returnReader(reader)
   137  
   138  	if errors.Is(err, io.EOF) {
   139  		return nil
   140  	}
   141  
   142  	return err
   143  }
   144  
   145  func (c *frozenConfig) borrowReader(data []byte) *Reader {
   146  	reader := c.readerPool.Get().(*Reader)
   147  	reader.Reset(data)
   148  	return reader
   149  }
   150  
   151  func (c *frozenConfig) returnReader(reader *Reader) {
   152  	reader.Error = nil
   153  	c.readerPool.Put(reader)
   154  }
   155  
   156  func (c *frozenConfig) NewEncoder(schema Schema, w io.Writer) *Encoder {
   157  	writer := NewWriter(w, 512, WithWriterConfig(c))
   158  	return &Encoder{
   159  		s: schema,
   160  		w: writer,
   161  	}
   162  }
   163  
   164  func (c *frozenConfig) NewDecoder(schema Schema, r io.Reader) *Decoder {
   165  	reader := NewReader(r, 512, WithReaderConfig(c))
   166  	return &Decoder{
   167  		s: schema,
   168  		r: reader,
   169  	}
   170  }
   171  
   172  func (c *frozenConfig) Register(name string, obj interface{}) {
   173  	c.resolver.Register(name, obj)
   174  }
   175  
   176  type cacheKey struct {
   177  	fingerprint [32]byte
   178  	rtype       uintptr
   179  }
   180  
   181  func (c *frozenConfig) addDecoderToCache(fingerprint [32]byte, rtype uintptr, dec ValDecoder) {
   182  	key := cacheKey{fingerprint: fingerprint, rtype: rtype}
   183  	c.decoderCache.Store(key, dec)
   184  }
   185  
   186  func (c *frozenConfig) getDecoderFromCache(fingerprint [32]byte, rtype uintptr) ValDecoder {
   187  	key := cacheKey{fingerprint: fingerprint, rtype: rtype}
   188  	if dec, ok := c.decoderCache.Load(key); ok {
   189  		return dec.(ValDecoder)
   190  	}
   191  
   192  	return nil
   193  }
   194  
   195  func (c *frozenConfig) addEncoderToCache(fingerprint [32]byte, rtype uintptr, enc ValEncoder) {
   196  	key := cacheKey{fingerprint: fingerprint, rtype: rtype}
   197  	c.encoderCache.Store(key, enc)
   198  }
   199  
   200  func (c *frozenConfig) getEncoderFromCache(fingerprint [32]byte, rtype uintptr) ValEncoder {
   201  	key := cacheKey{fingerprint: fingerprint, rtype: rtype}
   202  	if enc, ok := c.encoderCache.Load(key); ok {
   203  		return enc.(ValEncoder)
   204  	}
   205  
   206  	return nil
   207  }
   208  
   209  func (c *frozenConfig) getTagKey() string {
   210  	tagKey := c.config.TagKey
   211  	if tagKey == "" {
   212  		return "avro"
   213  	}
   214  	return tagKey
   215  }
   216  
   217  func (c *frozenConfig) getBlockLength() int {
   218  	blockSize := c.config.BlockLength
   219  	if blockSize <= 0 {
   220  		return 100
   221  	}
   222  	return blockSize
   223  }