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  }