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

     1  package avro
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"reflect"
     7  	"unsafe"
     8  
     9  	"github.com/modern-go/reflect2"
    10  )
    11  
    12  func createDecoderOfFixed(schema Schema, typ reflect2.Type) ValDecoder {
    13  	fixed := schema.(*FixedSchema)
    14  	switch typ.Kind() {
    15  	case reflect.Array:
    16  		arrayType := typ.(reflect2.ArrayType)
    17  		if arrayType.Elem().Kind() != reflect.Uint8 || arrayType.Len() != fixed.Size() {
    18  			break
    19  		}
    20  		return &fixedCodec{arrayType: typ.(*reflect2.UnsafeArrayType)}
    21  
    22  	case reflect.Struct:
    23  		ls := fixed.Logical()
    24  		if typ.RType() != ratRType || ls == nil || ls.Type() != Decimal {
    25  			break
    26  		}
    27  		dec := ls.(*DecimalLogicalSchema)
    28  		return &fixedDecimalCodec{prec: dec.Precision(), scale: dec.Scale(), size: fixed.Size()}
    29  	}
    30  
    31  	return &errorDecoder{err: fmt.Errorf("avro: %s is unsupported for Avro %s", typ.String(), schema.Type())}
    32  }
    33  
    34  func createEncoderOfFixed(schema Schema, typ reflect2.Type) ValEncoder {
    35  	fixed := schema.(*FixedSchema)
    36  	switch typ.Kind() {
    37  	case reflect.Array:
    38  		arrayType := typ.(reflect2.ArrayType)
    39  		fixed := schema.(*FixedSchema)
    40  		if arrayType.Elem().Kind() != reflect.Uint8 || arrayType.Len() != fixed.Size() {
    41  			break
    42  		}
    43  		return &fixedCodec{arrayType: typ.(*reflect2.UnsafeArrayType)}
    44  
    45  	case reflect.Ptr:
    46  		ptrType := typ.(*reflect2.UnsafePtrType)
    47  		elemType := ptrType.Elem()
    48  
    49  		ls := fixed.Logical()
    50  		if elemType.Kind() != reflect.Struct || elemType.RType() != ratRType || ls == nil || ls.Type() != Decimal {
    51  			break
    52  		}
    53  		dec := ls.(*DecimalLogicalSchema)
    54  		return &fixedDecimalCodec{prec: dec.Precision(), scale: dec.Scale(), size: fixed.Size()}
    55  	}
    56  
    57  	return &errorEncoder{err: fmt.Errorf("avro: %s is unsupported for Avro %s", typ.String(), schema.Type())}
    58  }
    59  
    60  type fixedCodec struct {
    61  	arrayType *reflect2.UnsafeArrayType
    62  }
    63  
    64  func (c *fixedCodec) Decode(ptr unsafe.Pointer, r *Reader) {
    65  	for i := 0; i < c.arrayType.Len(); i++ {
    66  		c.arrayType.UnsafeSetIndex(ptr, i, reflect2.PtrOf(r.readByte()))
    67  	}
    68  }
    69  
    70  func (c *fixedCodec) Encode(ptr unsafe.Pointer, w *Writer) {
    71  	for i := 0; i < c.arrayType.Len(); i++ {
    72  		bytePtr := c.arrayType.UnsafeGetIndex(ptr, i)
    73  		w.writeByte(*((*byte)(bytePtr)))
    74  	}
    75  }
    76  
    77  type fixedDecimalCodec struct {
    78  	prec  int
    79  	scale int
    80  	size  int
    81  }
    82  
    83  func (c *fixedDecimalCodec) Decode(ptr unsafe.Pointer, r *Reader) {
    84  	b := make([]byte, c.size)
    85  	r.Read(b)
    86  	*((*big.Rat)(ptr)) = *ratFromBytes(b, c.scale)
    87  }
    88  
    89  func (c *fixedDecimalCodec) Encode(ptr unsafe.Pointer, w *Writer) {
    90  	r := *((**big.Rat)(ptr))
    91  	scale := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(c.scale)), nil)
    92  	i := (&big.Int{}).Mul(r.Num(), scale)
    93  	i = i.Div(i, r.Denom())
    94  
    95  	var b []byte
    96  	switch i.Sign() {
    97  	case 0:
    98  		b = make([]byte, c.size)
    99  
   100  	case 1:
   101  		b = i.Bytes()
   102  		if b[0]&0x80 > 0 {
   103  			b = append([]byte{0}, b...)
   104  		}
   105  		if len(b) < c.size {
   106  			padded := make([]byte, c.size)
   107  			copy(padded[c.size-len(b):], b)
   108  			b = padded
   109  		}
   110  
   111  	case -1:
   112  		b = i.Add(i, (&big.Int{}).Lsh(one, uint(c.size*8))).Bytes()
   113  	}
   114  
   115  	w.Write(b)
   116  }