...

Source file src/edge-infra.dev/pkg/lib/filesystem/compare.go

Documentation: edge-infra.dev/pkg/lib/filesystem

     1  package filesystem
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"io"
     7  	"os"
     8  )
     9  
    10  const defaultBufferSize = 4096
    11  
    12  // FileEqualsTarget will read in the file from path and compare its contents
    13  // to the target bytes.
    14  func FileEqualsTarget(path string, targetBytes []byte) (bool, error) {
    15  	fileInfo, err := os.Stat(path)
    16  	if err != nil {
    17  		return false, err
    18  	}
    19  	if fileInfo.Size() != int64(len(targetBytes)) {
    20  		return false, nil
    21  	}
    22  
    23  	file, closeFn, err := newReader(path)
    24  	if err != nil {
    25  		return false, err
    26  	}
    27  	defer closeFn() //nolint:errcheck
    28  	bytesBuffer := bytes.NewReader(targetBytes)
    29  	return Equal(file, bytesBuffer, defaultBufferSize)
    30  }
    31  
    32  // FilesEqual will read in two file from path1 and path2 and compare the contents
    33  // of each file and return true if they are equal.
    34  func FilesEqual(path1, path2 string) (bool, error) {
    35  	file1Info, err := os.Stat(path1)
    36  	if err != nil {
    37  		return false, err
    38  	}
    39  
    40  	file2Info, err := os.Stat(path2)
    41  	if err != nil {
    42  		return false, err
    43  	}
    44  
    45  	if file1Info.Size() != file2Info.Size() {
    46  		return false, nil
    47  	}
    48  
    49  	file1, closeFn1, err := newReader(path1)
    50  	if err != nil {
    51  		return false, err
    52  	}
    53  	defer closeFn1() //nolint:errcheck
    54  
    55  	file2, closeFn2, err := newReader(path2)
    56  	if err != nil {
    57  		return false, err
    58  	}
    59  	defer closeFn2() //nolint:errcheck
    60  	return Equal(file1, file2, defaultBufferSize)
    61  }
    62  
    63  // Equal iterates through each reader chunk by chunk, evaluating
    64  // if each chunk is equal between the readers.
    65  func Equal(r1, r2 io.Reader, bufferSize int) (bool, error) {
    66  	if bufferSize <= 0 {
    67  		bufferSize = defaultBufferSize
    68  	}
    69  	buf1 := make([]byte, bufferSize)
    70  	buf2 := make([]byte, bufferSize)
    71  
    72  	for {
    73  		n1, err1 := readFull(r1, buf1)
    74  		n2, err2 := readFull(r2, buf2)
    75  
    76  		// Check for non-EOF errors first
    77  		if err1 != nil && err1 != io.EOF {
    78  			return false, err1
    79  		}
    80  		if err2 != nil && err2 != io.EOF {
    81  			return false, err2
    82  		}
    83  
    84  		// Compare the read data
    85  		if n1 != n2 || !bytes.Equal(buf1[:n1], buf2[:n2]) {
    86  			return false, nil
    87  		}
    88  
    89  		// Handle EOF cases
    90  		if err1 == io.EOF && err2 == io.EOF {
    91  			return true, nil // Both readers reached EOF simultaneously
    92  		}
    93  		if err1 == io.EOF || err2 == io.EOF {
    94  			return false, nil // One reader reached EOF before the other
    95  		}
    96  	}
    97  }
    98  
    99  // readFull reads from r into buf, handling short reads
   100  func readFull(r io.Reader, buf []byte) (int, error) {
   101  	n, err := io.ReadFull(r, buf)
   102  	if err == io.ErrUnexpectedEOF {
   103  		return n, io.EOF
   104  	}
   105  	return n, err
   106  }
   107  
   108  func newReader(path string) (io.Reader, func() error, error) {
   109  	base, err := os.Open(path)
   110  	if err != nil {
   111  		return nil, nil, err
   112  	}
   113  	baseBuffer := bufio.NewReader(base)
   114  	return baseBuffer, base.Close, nil
   115  }
   116  

View as plain text