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