github.com/ethersphere/bee/v2@v2.2.0/pkg/util/testutil/pseudorand/reader.go (about) 1 // Copyright 2023 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // this is a pseudorandom reader that generates a deterministic 6 // sequence of bytes based on the seed. It is used in tests to 7 // enable large volumes of pseudorandom data to be generated 8 // and compared without having to store the data in memory. 9 package pseudorand 10 11 import ( 12 "bytes" 13 "crypto/rand" 14 "encoding/binary" 15 "errors" 16 "fmt" 17 "io" 18 19 "github.com/ethersphere/bee/v2/pkg/swarm" 20 ) 21 22 const bufSize = 4096 23 24 // Reader is a pseudorandom reader that generates a deterministic 25 // sequence of bytes based on the seed. 26 type Reader struct { 27 cur int 28 len int 29 seg [40]byte 30 buf [bufSize]byte 31 } 32 33 // NewSeed creates a new seed. 34 func NewSeed() ([]byte, error) { 35 seed := make([]byte, 32) 36 _, err := io.ReadFull(rand.Reader, seed) 37 return seed, err 38 } 39 40 // New creates a new pseudorandom reader seeded with the given seed. 41 func NewReader(seed []byte, l int) *Reader { 42 r := &Reader{len: l} 43 _ = copy(r.buf[8:], seed) 44 r.fill() 45 return r 46 } 47 48 // Size returns the size of the reader. 49 func (r *Reader) Size() int { 50 return r.len 51 } 52 53 // Read reads len(buf) bytes into buf. It returns the number of bytes read (0 <= n <= len(buf)) and any error encountered. Even if Read returns n < len(buf), it may use all of buf as scratch space during the call. If some data is available but not len(buf) bytes, Read conventionally returns what is available instead of waiting for more. 54 func (r *Reader) Read(buf []byte) (n int, err error) { 55 cur := r.cur % bufSize 56 toRead := min(bufSize-cur, r.len-r.cur) 57 if toRead < len(buf) { 58 buf = buf[:toRead] 59 } 60 n = copy(buf, r.buf[cur:]) 61 r.cur += n 62 if r.cur == r.len { 63 return n, io.EOF 64 } 65 if r.cur%bufSize == 0 { 66 r.fill() 67 } 68 return n, nil 69 } 70 71 // Equal compares the contents of the reader with the contents of 72 // the given reader. It returns true if the contents are equal upto n bytes 73 func (r1 *Reader) Equal(r2 io.Reader) (bool, error) { 74 ok, err := r1.Match(r2, r1.len) 75 if err != nil { 76 return false, err 77 } 78 if !ok { 79 return false, nil 80 } 81 n, err := io.ReadFull(r2, make([]byte, 1)) 82 if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { 83 return n == 0, nil 84 } 85 return false, err 86 } 87 88 // Match compares the contents of the reader with the contents of 89 // the given reader. It returns true if the contents are equal upto n bytes 90 func (r1 *Reader) Match(r2 io.Reader, l int) (bool, error) { 91 92 read := func(r io.Reader, buf []byte) (n int, err error) { 93 for n < len(buf) && err == nil { 94 i, e := r.Read(buf[n:]) 95 if e == nil && i == 0 { 96 return n, nil 97 } 98 err = e 99 n += i 100 } 101 if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { 102 err = nil 103 } 104 return n, err 105 } 106 107 buf1 := make([]byte, bufSize) 108 buf2 := make([]byte, bufSize) 109 for l > 0 { 110 if l <= len(buf1) { 111 buf1 = buf1[:l] 112 buf2 = buf2[:l] 113 } 114 115 n1, err := read(r1, buf1) 116 if err != nil { 117 return false, err 118 } 119 n2, err := read(r2, buf2) 120 if err != nil { 121 return false, err 122 } 123 if n1 != n2 { 124 return false, nil 125 } 126 if !bytes.Equal(buf1[:n1], buf2[:n2]) { 127 return false, nil 128 } 129 l -= n1 130 } 131 return true, nil 132 } 133 134 // Seek sets the offset for the next Read to offset, interpreted 135 // according to whence: 0 means relative to the start of the file, 136 // 1 means relative to the current offset, and 2 means relative to 137 // the end. It returns the new offset and an error, if any. 138 func (r *Reader) Seek(offset int64, whence int) (int64, error) { 139 switch whence { 140 case 0: 141 r.cur = int(offset) 142 case 1: 143 r.cur += int(offset) 144 case 2: 145 r.cur = r.len - int(offset) 146 } 147 if r.cur < 0 || r.cur > r.len { 148 return 0, fmt.Errorf("seek: invalid offset %d", r.cur) 149 } 150 r.fill() 151 return int64(r.cur), nil 152 } 153 154 // Offset returns the current offset of the reader. 155 func (r *Reader) Offset() int64 { 156 return int64(r.cur) 157 } 158 159 // ReadAt reads len(buf) bytes into buf starting at offset off. 160 func (r *Reader) ReadAt(buf []byte, off int64) (n int, err error) { 161 if _, err := r.Seek(off, io.SeekStart); err != nil { 162 return 0, err 163 } 164 return r.Read(buf) 165 } 166 167 // fill fills the buffer with the hash of the current segment. 168 func (r *Reader) fill() { 169 if r.cur >= r.len { 170 return 171 } 172 bufSegments := bufSize / 32 173 start := r.cur / bufSegments 174 rem := (r.cur % bufSize) / 32 175 h := swarm.NewHasher() 176 for i := 32 * rem; i < len(r.buf); i += 32 { 177 binary.BigEndian.PutUint64(r.seg[:], uint64((start+i)/32)) 178 h.Reset() 179 _, _ = h.Write(r.seg[:]) 180 copy(r.buf[i:], h.Sum(nil)) 181 } 182 }