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  }