github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/cache/binary_read.go (about) 1 package cache 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "io" 7 "math/big" 8 "reflect" 9 ) 10 11 // read reads bytes in a correct byte order 12 func read(reader io.Reader, value any) (err error) { 13 return binary.Read(reader, binary.LittleEndian, value) 14 } 15 16 // ReadValue reads binary representation of fixed-size values, strings, 17 // big.Int, Unmarshaler and slices of these values. Version number 18 // is passed to any Unmarshaler to ease reading older formats. 19 func ReadValue(reader io.Reader, value any, version uint64) (err error) { 20 switch v := value.(type) { 21 case Unmarshaler: 22 err = v.UnmarshalCache(version, reader) 23 case *[]string: 24 err = readSlice(reader, v, version) 25 case *[]big.Int: 26 err = readSlice(reader, v, version) 27 case *string: 28 err = readString(reader, v) 29 case *big.Int: 30 err = readBigInt(reader, v) 31 default: 32 // Reading []Unmarshaler is a bit more complex. The type switch won't work and 33 // we'll end up here. If value is a pointer to a slice, then it may contain Unmarshalers 34 reflectedValue := reflect.ValueOf(value) 35 if reflectedValue.Kind() == reflect.Pointer { 36 if reflectedValue.Elem().Kind() == reflect.Slice { 37 // It is what we want, so let's try to read. If we get an error, we'll ignore it and still 38 err = readSliceReflect(reader, reflect.TypeOf(value).Elem(), reflectedValue.Elem(), version) 39 if err == nil { 40 return 41 } 42 } 43 } 44 45 err = read(reader, value) 46 } 47 return 48 } 49 50 // readSlice reads binary representation of slice of T. ReadValue is called for each 51 // item, so slice items can be of any type supported by ReadValue. 52 func readSlice[T any](reader io.Reader, slice *[]T, version uint64) (err error) { 53 var itemCount uint64 = 0 54 if err = read(reader, &itemCount); err != nil { 55 return 56 } 57 58 // make target large enough 59 *slice = make([]T, 0, itemCount) 60 for i := 0; uint64(i) < itemCount; i++ { 61 item := new(T) 62 if err = ReadValue(reader, item, version); err != nil { 63 return 64 } 65 66 *slice = append(*slice, *item) 67 } 68 return nil 69 } 70 71 // readSliceReflect uses reflection to read a slice (typically of Unmarshaler) 72 func readSliceReflect(reader io.Reader, slice reflect.Type, destPointer reflect.Value, version uint64) (err error) { 73 var itemCount uint64 = 0 74 if err = read(reader, &itemCount); err != nil { 75 return 76 } 77 78 sliceItemType := slice.Elem() 79 if !reflect.New(sliceItemType).CanInterface() { 80 return errors.New("cannot cast value to interface") 81 } 82 sliceOfType := reflect.SliceOf(sliceItemType) 83 84 // Make sure we return a correct zero value for empty arrays 85 if itemCount == 0 { 86 destPointer.Set(reflect.Zero(sliceOfType)) 87 return 88 } 89 90 result := reflect.MakeSlice(sliceOfType, 0, int(itemCount)) 91 for i := 0; uint64(i) < itemCount; i++ { 92 item := reflect.New(sliceItemType) 93 94 unmarshaler, ok := item.Interface().(Unmarshaler) 95 if !ok { 96 // If it's not Unmarshaler, it can be a simpler type, which we can read into 97 if err = ReadValue(reader, item.Interface(), version); err != nil { 98 return 99 } 100 } else { 101 if err = ReadValue(reader, unmarshaler, version); err != nil { 102 return 103 } 104 } 105 result = reflect.Append(result, item.Elem()) 106 } 107 108 destPointer.Set(result) 109 110 return nil 111 } 112 113 func readString(reader io.Reader, str *string) (err error) { 114 var size uint64 115 if err = read(reader, &size); err != nil { 116 return 117 } 118 119 content := make([]byte, size) 120 if err = ReadValue(reader, content, 0); err != nil { 121 return err 122 } 123 *str = string(content) 124 return 125 } 126 127 func readBigInt(reader io.Reader, target *big.Int) (err error) { 128 // we first need to learn how many bytes we should read 129 // (GobEncode that we use to write big.Int produces 130 // variable-length []byte) 131 var size uint64 132 if err = read(reader, &size); err != nil { 133 return 134 } 135 136 // now we read as many bytes as we need 137 data := make([]byte, size) 138 if err = read(reader, data); err != nil { 139 return 140 } 141 // and use big.Int.GobDecode to decode the value 142 err = target.GobDecode(data) 143 return 144 }