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  }