github.com/consensys/gnark-crypto@v0.14.0/utils/unsafe/dump_slice.go (about)

     1  package unsafe
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"io"
     8  	"unsafe"
     9  )
    10  
    11  // WriteSlice writes a slice of arbitrary objects to the writer.
    12  // Use with caution, as it writes the raw memory representation of the slice;
    13  // In particular you do not want to use this with slices that contain pointers.
    14  // This architecture dependent and will not work across different architectures
    15  // (e.g. 32 vs 64 bit, big endian vs little endian).
    16  func WriteSlice[S ~[]E, E any](w io.Writer, s S) error {
    17  	var e E
    18  	size := int(unsafe.Sizeof(e))
    19  	if err := binary.Write(w, binary.LittleEndian, uint64(len(s))); err != nil {
    20  		return err
    21  	}
    22  
    23  	if len(s) == 0 {
    24  		return nil
    25  	}
    26  
    27  	data := unsafe.Slice((*byte)(unsafe.Pointer(&s[0])), size*len(s))
    28  	if _, err := w.Write(data); err != nil {
    29  		return err
    30  	}
    31  	return nil
    32  }
    33  
    34  // ReadSlice reads a slice of arbitrary objects from the reader, written by WriteSlice.
    35  func ReadSlice[S ~[]E, E any](r io.Reader, maxElements ...int) (s S, read int, err error) {
    36  	var buf [8]byte
    37  	if _, err := io.ReadFull(r, buf[:]); err != nil {
    38  		return nil, 0, err
    39  	}
    40  	read += 8
    41  
    42  	// decode length of the slice
    43  	length := binary.LittleEndian.Uint64(buf[:])
    44  
    45  	var e E
    46  	size := int(unsafe.Sizeof(e))
    47  	limit := length
    48  	if len(maxElements) == 1 && maxElements[0] > 0 && int(length) > maxElements[0] {
    49  		limit = uint64(maxElements[0])
    50  	}
    51  
    52  	if limit == 0 {
    53  		return make(S, 0), read, nil
    54  	}
    55  
    56  	toReturn := make(S, limit)
    57  
    58  	// directly read the bytes from reader into the target memory area
    59  	// (slice data)
    60  	data := unsafe.Slice((*byte)(unsafe.Pointer(&toReturn[0])), size*int(limit))
    61  	if _, err := io.ReadFull(r, data); err != nil {
    62  		return nil, read, err
    63  	}
    64  
    65  	read += size * int(limit)
    66  
    67  	// advance the reader if we had more elements than we wanted
    68  	if length > limit {
    69  		advance := int(length-limit) * size
    70  		if _, err := io.CopyN(io.Discard, r, int64(advance)); err != nil {
    71  			return nil, read, err
    72  		}
    73  		read += advance
    74  	}
    75  
    76  	return toReturn, read, nil
    77  }
    78  
    79  const marker uint64 = 0xdeadbeef
    80  
    81  // WriteMarker writes the raw memory representation of a fixed marker to the writer.
    82  // This is used to ensure that the dump was written on the same architecture.
    83  func WriteMarker(w io.Writer) error {
    84  	marker := marker
    85  	_, err := w.Write(unsafe.Slice((*byte)(unsafe.Pointer(&marker)), 8))
    86  	return err
    87  }
    88  
    89  // ReadMarker reads the raw memory representation of a fixed marker from the reader.
    90  // This is used to ensure that the dump was written on the same architecture.
    91  func ReadMarker(r io.Reader) error {
    92  	var buf [8]byte
    93  	if _, err := io.ReadFull(r, buf[:]); err != nil {
    94  		return err
    95  	}
    96  	marker := marker
    97  	d := unsafe.Slice((*byte)(unsafe.Pointer(&marker)), 8)
    98  	if !bytes.Equal(d, buf[:]) {
    99  		return errors.New("marker mismatch: dump was not written on the same architecture")
   100  	}
   101  	return nil
   102  }