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 }