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 }