github.com/aacfactory/avro@v1.2.12/internal/base/codec_fixed.go (about)

     1  package base
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"math/big"
     7  	"reflect"
     8  	"unsafe"
     9  
    10  	"github.com/modern-go/reflect2"
    11  )
    12  
    13  func createDecoderOfFixed(schema Schema, typ reflect2.Type) ValDecoder {
    14  	fixed := schema.(*FixedSchema)
    15  	switch typ.Kind() {
    16  	case reflect.Array:
    17  		arrayType := typ.(reflect2.ArrayType)
    18  		if arrayType.Elem().Kind() != reflect.Uint8 || arrayType.Len() != fixed.Size() {
    19  			break
    20  		}
    21  		return &fixedCodec{arrayType: typ.(*reflect2.UnsafeArrayType)}
    22  
    23  	case reflect.Uint64:
    24  		fixed := schema.(*FixedSchema)
    25  		if fixed.Size() != 8 {
    26  			break
    27  		}
    28  
    29  		return &fixedUint64Codec{}
    30  
    31  	case reflect.Struct:
    32  		ls := fixed.Logical()
    33  		if ls == nil {
    34  			break
    35  		}
    36  		typ1 := typ.Type1()
    37  		switch {
    38  		case typ1.ConvertibleTo(durType) && ls.Type() == Duration:
    39  			return &fixedDurationCodec{}
    40  		case typ1.ConvertibleTo(ratType) && ls.Type() == Decimal:
    41  			dec := ls.(*DecimalLogicalSchema)
    42  			return &fixedDecimalCodec{prec: dec.Precision(), scale: dec.Scale(), size: fixed.Size()}
    43  		}
    44  	default:
    45  		break
    46  	}
    47  
    48  	return &errorDecoder{
    49  		err: fmt.Errorf("avro: %s is unsupported for Avro %s, size=%d", typ.String(), schema.Type(), fixed.Size()),
    50  	}
    51  }
    52  
    53  func createEncoderOfFixed(schema Schema, typ reflect2.Type) ValEncoder {
    54  	fixed := schema.(*FixedSchema)
    55  	switch typ.Kind() {
    56  	case reflect.Array:
    57  		arrayType := typ.(reflect2.ArrayType)
    58  		fixed := schema.(*FixedSchema)
    59  		if arrayType.Elem().Kind() != reflect.Uint8 || arrayType.Len() != fixed.Size() {
    60  			break
    61  		}
    62  		return &fixedCodec{arrayType: typ.(*reflect2.UnsafeArrayType)}
    63  
    64  	case reflect.Uint64:
    65  		fixed := schema.(*FixedSchema)
    66  		if fixed.Size() != 8 {
    67  			break
    68  		}
    69  
    70  		return &fixedUint64Codec{}
    71  
    72  	case reflect.Ptr:
    73  		ptrType := typ.(*reflect2.UnsafePtrType)
    74  		elemType := ptrType.Elem()
    75  
    76  		ls := fixed.Logical()
    77  		tpy1 := elemType.Type1()
    78  		if elemType.Kind() != reflect.Struct || !tpy1.ConvertibleTo(ratType) || ls == nil ||
    79  			ls.Type() != Decimal {
    80  			break
    81  		}
    82  		dec := ls.(*DecimalLogicalSchema)
    83  		return &fixedDecimalCodec{prec: dec.Precision(), scale: dec.Scale(), size: fixed.Size()}
    84  
    85  	case reflect.Struct:
    86  		ls := fixed.Logical()
    87  		if ls == nil {
    88  			break
    89  		}
    90  		typ1 := typ.Type1()
    91  		if typ1.ConvertibleTo(durType) && ls.Type() == Duration {
    92  			return &fixedDurationCodec{}
    93  		}
    94  	default:
    95  		break
    96  	}
    97  
    98  	return &errorEncoder{
    99  		err: fmt.Errorf("avro: %s is unsupported for Avro %s, size=%d", typ.String(), schema.Type(), fixed.Size()),
   100  	}
   101  }
   102  
   103  type fixedUint64Codec [8]byte
   104  
   105  func (c *fixedUint64Codec) Decode(ptr unsafe.Pointer, r *Reader) {
   106  	buffer := c[:]
   107  	r.Read(buffer)
   108  	*(*uint64)(ptr) = binary.BigEndian.Uint64(buffer)
   109  }
   110  
   111  func (c *fixedUint64Codec) Encode(ptr unsafe.Pointer, w *Writer) {
   112  	buffer := c[:]
   113  	binary.BigEndian.PutUint64(buffer, *(*uint64)(ptr))
   114  	_, _ = w.Write(buffer)
   115  }
   116  
   117  type fixedCodec struct {
   118  	arrayType *reflect2.UnsafeArrayType
   119  }
   120  
   121  func (c *fixedCodec) Decode(ptr unsafe.Pointer, r *Reader) {
   122  	for i := 0; i < c.arrayType.Len(); i++ {
   123  		c.arrayType.UnsafeSetIndex(ptr, i, reflect2.PtrOf(r.readByte()))
   124  	}
   125  }
   126  
   127  func (c *fixedCodec) Encode(ptr unsafe.Pointer, w *Writer) {
   128  	for i := 0; i < c.arrayType.Len(); i++ {
   129  		bytePtr := c.arrayType.UnsafeGetIndex(ptr, i)
   130  		w.writeByte(*((*byte)(bytePtr)))
   131  	}
   132  }
   133  
   134  type fixedDecimalCodec struct {
   135  	prec  int
   136  	scale int
   137  	size  int
   138  }
   139  
   140  func (c *fixedDecimalCodec) Decode(ptr unsafe.Pointer, r *Reader) {
   141  	b := make([]byte, c.size)
   142  	r.Read(b)
   143  	*((*big.Rat)(ptr)) = *ratFromBytes(b, c.scale)
   144  }
   145  
   146  func (c *fixedDecimalCodec) Encode(ptr unsafe.Pointer, w *Writer) {
   147  	r := *((**big.Rat)(ptr))
   148  	scale := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(c.scale)), nil)
   149  	i := (&big.Int{}).Mul(r.Num(), scale)
   150  	i = i.Div(i, r.Denom())
   151  
   152  	var b []byte
   153  	switch i.Sign() {
   154  	case 0:
   155  		b = make([]byte, c.size)
   156  
   157  	case 1:
   158  		b = i.Bytes()
   159  		if b[0]&0x80 > 0 {
   160  			b = append([]byte{0}, b...)
   161  		}
   162  		if len(b) < c.size {
   163  			padded := make([]byte, c.size)
   164  			copy(padded[c.size-len(b):], b)
   165  			b = padded
   166  		}
   167  
   168  	case -1:
   169  		b = i.Add(i, (&big.Int{}).Lsh(one, uint(c.size*8))).Bytes()
   170  	}
   171  
   172  	_, _ = w.Write(b)
   173  }
   174  
   175  type fixedDurationCodec struct{}
   176  
   177  func (*fixedDurationCodec) Decode(ptr unsafe.Pointer, r *Reader) {
   178  	b := make([]byte, 12)
   179  	r.Read(b)
   180  	var duration LogicalDuration
   181  	duration.Months = binary.LittleEndian.Uint32(b[0:4])
   182  	duration.Days = binary.LittleEndian.Uint32(b[4:8])
   183  	duration.Milliseconds = binary.LittleEndian.Uint32(b[8:12])
   184  	*((*LogicalDuration)(ptr)) = duration
   185  }
   186  
   187  func (*fixedDurationCodec) Encode(ptr unsafe.Pointer, w *Writer) {
   188  	duration := (*LogicalDuration)(ptr)
   189  	b := make([]byte, 4)
   190  	binary.LittleEndian.PutUint32(b, duration.Months)
   191  	_, _ = w.Write(b)
   192  	binary.LittleEndian.PutUint32(b, duration.Days)
   193  	_, _ = w.Write(b)
   194  	binary.LittleEndian.PutUint32(b, duration.Milliseconds)
   195  	_, _ = w.Write(b)
   196  }