github.com/hamba/avro/v2@v2.22.1-0.20240518180522-aff3955acf7d/config.go (about) 1 package avro 2 3 import ( 4 "errors" 5 "io" 6 "sync" 7 8 "github.com/modern-go/reflect2" 9 ) 10 11 const ( 12 defaultMaxByteSliceSize = 1_048_576 // 1 MiB 13 ) 14 15 // DefaultConfig is the default API. 16 var DefaultConfig = Config{}.Freeze() 17 18 // Config customises how the codec should behave. 19 type Config struct { 20 // TagKey is the struct tag key used when en/decoding structs. 21 // This defaults to "avro". 22 TagKey string 23 24 // BlockLength is the length of blocks for maps and arrays. 25 // This defaults to 100. 26 BlockLength int 27 28 // DisableBlockSizeHeader disables encoding of an array/map size in bytes. 29 // Encoded array/map will be prefixed with only the number of elements in 30 // contrast with default behavior which prefixes them with the number of elements 31 // and the total number of bytes in the array/map. Both approaches are valid according to the 32 // Avro specification, however not all decoders support the latter. 33 DisableBlockSizeHeader bool 34 35 // UnionResolutionError determines if an error will be returned 36 // when a type cannot be resolved while decoding a union. 37 UnionResolutionError bool 38 39 // PartialUnionTypeResolution dictates if the union type resolution 40 // should be attempted even when not all union types are registered. 41 // When enabled, the underlying type will get resolved if it is registered 42 // even if other types of the union are not. If resolution fails, logic 43 // falls back to default union resolution behavior based on the value of 44 // UnionResolutionError. 45 PartialUnionTypeResolution bool 46 47 // Disable caching layer for encoders and decoders, forcing them to get rebuilt on every 48 // call to Marshal() and Unmarshal() 49 DisableCaching bool 50 51 // MaxByteSliceSize is the maximum size of `bytes` or `string` types the Reader will create, defaulting to 1MiB. 52 // If this size is exceeded, the Reader returns an error. This can be disabled by setting a negative number. 53 MaxByteSliceSize int 54 55 // MaxSliceAllocSize is the maximum size that the decoder will allocate, set to the max heap 56 // allocation size by default. 57 // If this size is exceeded, the decoder returns an error. 58 MaxSliceAllocSize int 59 } 60 61 // Freeze makes the configuration immutable. 62 func (c Config) Freeze() API { 63 api := &frozenConfig{ 64 config: c, 65 resolver: NewTypeResolver(), 66 } 67 68 api.readerPool = &sync.Pool{ 69 New: func() any { 70 return &Reader{ 71 cfg: api, 72 reader: nil, 73 buf: nil, 74 head: 0, 75 tail: 0, 76 } 77 }, 78 } 79 api.writerPool = &sync.Pool{ 80 New: func() any { 81 return &Writer{ 82 cfg: api, 83 out: nil, 84 buf: make([]byte, 0, 512), 85 Error: nil, 86 } 87 }, 88 } 89 90 return api 91 } 92 93 // API represents a frozen Config. 94 type API interface { 95 // Marshal returns the Avro encoding of v. 96 Marshal(schema Schema, v any) ([]byte, error) 97 98 // Unmarshal parses the Avro encoded data and stores the result in the value pointed to by v. 99 // If v is nil or not a pointer, Unmarshal returns an error. 100 Unmarshal(schema Schema, data []byte, v any) error 101 102 // NewEncoder returns a new encoder that writes to w using schema. 103 NewEncoder(schema Schema, w io.Writer) *Encoder 104 105 // NewDecoder returns a new decoder that reads from reader r using schema. 106 NewDecoder(schema Schema, r io.Reader) *Decoder 107 108 // DecoderOf returns the value decoder for a given schema and type. 109 DecoderOf(schema Schema, typ reflect2.Type) ValDecoder 110 111 // EncoderOf returns the value encoder for a given schema and type. 112 EncoderOf(schema Schema, tpy reflect2.Type) ValEncoder 113 114 // Register registers names to their types for resolution. All primitive types are pre-registered. 115 Register(name string, obj any) 116 } 117 118 type frozenConfig struct { 119 config Config 120 121 decoderCache sync.Map // map[cacheKey]ValDecoder 122 encoderCache sync.Map // map[cacheKey]ValEncoder 123 124 readerPool *sync.Pool 125 writerPool *sync.Pool 126 127 resolver *TypeResolver 128 } 129 130 func (c *frozenConfig) Marshal(schema Schema, v any) ([]byte, error) { 131 writer := c.borrowWriter() 132 133 writer.WriteVal(schema, v) 134 if err := writer.Error; err != nil { 135 c.returnWriter(writer) 136 return nil, err 137 } 138 139 result := writer.Buffer() 140 copied := make([]byte, len(result)) 141 copy(copied, result) 142 143 c.returnWriter(writer) 144 return copied, nil 145 } 146 147 func (c *frozenConfig) borrowWriter() *Writer { 148 writer := c.writerPool.Get().(*Writer) 149 writer.Reset(nil) 150 return writer 151 } 152 153 func (c *frozenConfig) returnWriter(writer *Writer) { 154 writer.out = nil 155 writer.Error = nil 156 157 c.writerPool.Put(writer) 158 } 159 160 func (c *frozenConfig) Unmarshal(schema Schema, data []byte, v any) error { 161 reader := c.borrowReader(data) 162 163 reader.ReadVal(schema, v) 164 err := reader.Error 165 c.returnReader(reader) 166 167 if errors.Is(err, io.EOF) { 168 return nil 169 } 170 171 return err 172 } 173 174 func (c *frozenConfig) borrowReader(data []byte) *Reader { 175 reader := c.readerPool.Get().(*Reader) 176 reader.Reset(data) 177 return reader 178 } 179 180 func (c *frozenConfig) returnReader(reader *Reader) { 181 reader.Error = nil 182 c.readerPool.Put(reader) 183 } 184 185 func (c *frozenConfig) NewEncoder(schema Schema, w io.Writer) *Encoder { 186 writer, ok := w.(*Writer) 187 if !ok { 188 writer = NewWriter(w, 512, WithWriterConfig(c)) 189 } 190 return &Encoder{ 191 s: schema, 192 w: writer, 193 } 194 } 195 196 func (c *frozenConfig) NewDecoder(schema Schema, r io.Reader) *Decoder { 197 reader := NewReader(r, 512, WithReaderConfig(c)) 198 return &Decoder{ 199 s: schema, 200 r: reader, 201 } 202 } 203 204 func (c *frozenConfig) Register(name string, obj any) { 205 c.resolver.Register(name, obj) 206 } 207 208 type cacheKey struct { 209 fingerprint [32]byte 210 rtype uintptr 211 } 212 213 func (c *frozenConfig) addDecoderToCache(fingerprint [32]byte, rtype uintptr, dec ValDecoder) { 214 if c.config.DisableCaching { 215 return 216 } 217 key := cacheKey{fingerprint: fingerprint, rtype: rtype} 218 c.decoderCache.Store(key, dec) 219 } 220 221 func (c *frozenConfig) getDecoderFromCache(fingerprint [32]byte, rtype uintptr) ValDecoder { 222 if c.config.DisableCaching { 223 return nil 224 } 225 key := cacheKey{fingerprint: fingerprint, rtype: rtype} 226 if dec, ok := c.decoderCache.Load(key); ok { 227 return dec.(ValDecoder) 228 } 229 230 return nil 231 } 232 233 func (c *frozenConfig) addEncoderToCache(fingerprint [32]byte, rtype uintptr, enc ValEncoder) { 234 if c.config.DisableCaching { 235 return 236 } 237 key := cacheKey{fingerprint: fingerprint, rtype: rtype} 238 c.encoderCache.Store(key, enc) 239 } 240 241 func (c *frozenConfig) getEncoderFromCache(fingerprint [32]byte, rtype uintptr) ValEncoder { 242 if c.config.DisableCaching { 243 return nil 244 } 245 key := cacheKey{fingerprint: fingerprint, rtype: rtype} 246 if enc, ok := c.encoderCache.Load(key); ok { 247 return enc.(ValEncoder) 248 } 249 250 return nil 251 } 252 253 func (c *frozenConfig) getTagKey() string { 254 tagKey := c.config.TagKey 255 if tagKey == "" { 256 return "avro" 257 } 258 return tagKey 259 } 260 261 func (c *frozenConfig) getBlockLength() int { 262 blockSize := c.config.BlockLength 263 if blockSize <= 0 { 264 return 100 265 } 266 return blockSize 267 } 268 269 func (c *frozenConfig) getMaxByteSliceSize() int { 270 size := c.config.MaxByteSliceSize 271 if size == 0 { 272 return defaultMaxByteSliceSize 273 } 274 return size 275 } 276 277 func (c *frozenConfig) getMaxSliceAllocSize() int { 278 size := c.config.MaxSliceAllocSize 279 if size > maxAllocSize || size <= 0 { 280 return maxAllocSize 281 } 282 return size 283 }