github.com/vertgenlab/gonomics@v1.0.0/fileio/magic.go (about)

     1  package fileio
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"os"
     7  
     8  	"github.com/vertgenlab/gonomics/exception"
     9  )
    10  
    11  // magic number that identifies a gzip file.
    12  var magicGzip = []byte{0x1f, 0x8b}
    13  
    14  // IsGzip checks if the magic gzip number is
    15  // present at the start of the input io.ReadSeeker.
    16  // After check for the magic number, IsGzip
    17  // seeks back to the initial position.
    18  func IsGzip(r io.ReadSeeker) bool {
    19  	return checkMagic(r, magicGzip)
    20  }
    21  
    22  // checkMagic returns true if the input io.ReadSeeker
    23  // begins with the bytes in b. Seeks back to initial
    24  // position after checking for b.
    25  func checkMagic(r io.ReadSeeker, b []byte) bool {
    26  	// get current pos
    27  	initialPos, err := r.Seek(0, io.SeekCurrent)
    28  	exception.PanicOnErr(err)
    29  
    30  	// seek to start
    31  	_, err = r.Seek(0, io.SeekStart)
    32  	exception.PanicOnErr(err)
    33  
    34  	readBytes := make([]byte, len(b))
    35  	r.Read(readBytes)
    36  
    37  	// seek back to initialPos
    38  	_, err = r.Seek(initialPos, io.SeekStart)
    39  	exception.PanicOnErr(err)
    40  
    41  	// check readBytes against b
    42  	return bytes.Equal(readBytes, b)
    43  }
    44  
    45  // newStdinMagicReader creates a reader with an internal buffer of len(magic) that
    46  // serves the read magic bytes to a function calling Read, then serves the rest of
    47  // os.Stdin without passing the read bytes through the internal buffer.
    48  //
    49  // This basically allows the function to peek at the first bytes of the stdin stream
    50  // to see if they match the input magic bytes.
    51  func newStdinMagicReader(magic []byte) (reader *stdinMagicReader, hasMagic bool) {
    52  	// create reader
    53  	reader = &stdinMagicReader{
    54  		in:             os.Stdin,
    55  		readMagicBytes: make([]byte, len(magic)),
    56  	}
    57  
    58  	// read len(magic) bytes into internal buffer
    59  	// and truncate internal buffer if necessary
    60  	n, _ := reader.in.Read(reader.readMagicBytes)
    61  	reader.readMagicBytes = reader.readMagicBytes[:n]
    62  
    63  	// check if internal buffer matches input magic bytes
    64  	hasMagic = true
    65  	if n != len(magic) {
    66  		hasMagic = false
    67  	} else {
    68  		hasMagic = bytes.Equal(magic, reader.readMagicBytes)
    69  	}
    70  
    71  	return
    72  }
    73  
    74  // stdinMagicReader is designed to have a small internal buffer which
    75  // is filled at creation and served on the first call to Read with
    76  // further calls to Read reading from os.Stdin directly.
    77  type stdinMagicReader struct {
    78  	in             *os.File
    79  	readMagicBytes []byte
    80  }
    81  
    82  // Read will serve the bytes in readMagicBytes before serving the rest
    83  // of os.Stdin directly.
    84  func (r *stdinMagicReader) Read(b []byte) (n int, err error) {
    85  	if r.readMagicBytes == nil {
    86  		return r.in.Read(b)
    87  	}
    88  
    89  	mLen := len(r.readMagicBytes)
    90  	switch {
    91  	case len(b) > mLen:
    92  		copy(b[0:mLen], r.readMagicBytes)
    93  		n, err = r.in.Read(b[mLen:])
    94  		n += mLen
    95  		r.readMagicBytes = nil
    96  		return
    97  
    98  	case len(b) < mLen:
    99  		copy(b, r.readMagicBytes)
   100  		n = len(b)
   101  		r.readMagicBytes = r.readMagicBytes[len(b):]
   102  		return
   103  
   104  	default: // len(b) == mLen
   105  		copy(b, r.readMagicBytes)
   106  		r.readMagicBytes = nil
   107  		return
   108  	}
   109  }