github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/cache/binary_write.go (about) 1 package cache 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "io" 7 "math/big" 8 "reflect" 9 ) 10 11 // write writes bytes in a correct byte order 12 func write(writer io.Writer, value any) (err error) { 13 return binary.Write(writer, binary.LittleEndian, value) 14 } 15 16 // WriteValue writes binary representation of fixed-size values, strings, 17 // big.Int, Marshaler and slices of these values. Contrary to 18 // ReadValue, it doesn't support versioning, because it is expected that 19 // only the most recent data format is used when writing. 20 func WriteValue(writer io.Writer, value any) (err error) { 21 switch v := value.(type) { 22 case Marshaler: 23 err = v.MarshalCache(writer) 24 25 // binary.Write takes care of slices of fixed-size types, e.g. []uint8, 26 // so we only have to support []string, []big.Int and []Marshaler 27 case []string: 28 err = writeSlice(writer, v) 29 case []big.Int: 30 err = writeSlice(writer, v) 31 case []Marshaler: 32 err = writeSlice(writer, v) 33 case string: 34 err = writeString(writer, &v) 35 case *big.Int: 36 err = writeBigInt(writer, v) 37 default: 38 if rf := reflect.ValueOf(value); rf.Kind() == reflect.Slice { 39 return writeSliceReflect(writer, &rf) 40 } 41 42 err = write(writer, value) 43 } 44 return err 45 } 46 47 // writeSlice reads binary representation of slice of T. WriteValue is called for each 48 // item, so slice items can be of any type supported by WriteValue. 49 func writeSlice[T any](writer io.Writer, slice []T) (err error) { 50 buffer := new(bytes.Buffer) 51 sliceLen := uint64(len(slice)) 52 53 if err = write(buffer, sliceLen); err != nil { 54 return 55 } 56 for _, sliceItem := range slice { 57 if err = WriteValue(buffer, sliceItem); err != nil { 58 return 59 } 60 } 61 if _, err = buffer.WriteTo(writer); err != nil { 62 return err 63 } 64 65 return 66 } 67 68 func writeSliceReflect(writer io.Writer, sliceValue *reflect.Value) (err error) { 69 buffer := new(bytes.Buffer) 70 sliceLen := sliceValue.Len() 71 72 if err = write(buffer, uint64(sliceLen)); err != nil { 73 return 74 } 75 for i := 0; i < sliceLen; i++ { 76 item := sliceValue.Index(i) 77 78 cm, ok := item.Addr().Interface().(Marshaler) 79 if !ok { 80 if err = WriteValue(buffer, item.Interface()); err != nil { 81 return 82 } 83 } else { 84 if err = WriteValue(buffer, cm); err != nil { 85 return 86 } 87 } 88 } 89 if _, err = buffer.WriteTo(writer); err != nil { 90 return err 91 } 92 93 return 94 } 95 96 func writeString(writer io.Writer, str *string) (err error) { 97 if err = write(writer, uint64(len(*str))); err != nil { 98 return 99 } 100 return write(writer, []byte(*str)) 101 } 102 103 func writeBigInt(writer io.Writer, value *big.Int) (err error) { 104 data, err := value.GobEncode() 105 if err != nil { 106 return 107 } 108 // write length of data, so readBigInt knows how many bytes to read 109 if err = write(writer, uint64(len(data))); err != nil { 110 return err 111 } 112 113 // write the data 114 _, err = writer.Write(data) 115 return 116 }