github.com/Schaudge/hts@v0.0.0-20240223063651-737b4d69d68c/bgzf/bgzf.go (about) 1 // Copyright ©2012 The bíogo 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 // Package bgzf implements BGZF format reading and writing according to the 6 // SAM specification. 7 // 8 // The specification is available at https://github.com/samtools/hts-specs. 9 package bgzf 10 11 import ( 12 "errors" 13 "io" 14 "os" 15 "time" 16 ) 17 18 const ( 19 BlockSize = 0x0ff00 // The maximum size of an uncompressed input data block. 20 MaxBlockSize = 0x10000 // The maximum size of a compressed output block. 21 ) 22 23 const ( 24 bgzfExtra = "BC\x02\x00\x00\x00" 25 minFrame = 20 + len(bgzfExtra) // Minimum bgzf header+footer length. 26 27 // Magic EOF block. 28 magicBlock = "\x1f\x8b\x08\x04\x00\x00\x00\x00\x00\xff\x06\x00\x42\x43\x02\x00\x1b\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00" 29 ) 30 31 var ( 32 bgzfExtraPrefix = []byte(bgzfExtra[:4]) 33 unixEpoch = time.Unix(0, 0) 34 ) 35 36 func compressBound(srcLen int) int { 37 return srcLen + srcLen>>12 + srcLen>>14 + srcLen>>25 + 13 + minFrame 38 } 39 40 func init() { 41 if compressBound(BlockSize) > MaxBlockSize { 42 panic("bam: BlockSize too large") 43 } 44 } 45 46 var ( 47 ErrClosed = errors.New("bgzf: use of closed writer") 48 ErrCorrupt = errors.New("bgzf: corrupt block") 49 ErrBlockOverflow = errors.New("bgzf: block overflow") 50 ErrWrongFileType = errors.New("bgzf: file is a directory") 51 ErrNoEnd = errors.New("bgzf: cannot determine offset from end") 52 ErrNotASeeker = errors.New("bgzf: not a seeker") 53 ErrContaminatedCache = errors.New("bgzf: cache owner mismatch") 54 ErrNoBlockSize = errors.New("bgzf: could not determine block size") 55 ErrBlockSizeMismatch = errors.New("bgzf: unexpected block size") 56 ) 57 58 // HasEOF checks for the presence of a BGZF magic EOF block. 59 // The magic block is defined in the SAM specification. A magic block 60 // is written by a Writer on calling Close. The ReaderAt must provide 61 // some method for determining valid ReadAt offsets. 62 func HasEOF(r io.ReaderAt) (bool, error) { 63 type sizer interface { 64 Size() int64 65 } 66 type stater interface { 67 Stat() (os.FileInfo, error) 68 } 69 type lenSeeker interface { 70 io.Seeker 71 Len() int 72 } 73 var size int64 74 switch r := r.(type) { 75 case sizer: 76 size = r.Size() 77 case stater: 78 fi, err := r.Stat() 79 if err != nil { 80 return false, err 81 } 82 size = fi.Size() 83 case lenSeeker: 84 var err error 85 size, err = r.Seek(0, 1) 86 if err != nil { 87 return false, err 88 } 89 size += int64(r.Len()) 90 default: 91 return false, ErrNoEnd 92 } 93 94 b := make([]byte, len(magicBlock)) 95 _, err := r.ReadAt(b, size-int64(len(magicBlock))) 96 if err != nil { 97 return false, err 98 } 99 for i, c := range b { 100 if c != magicBlock[i] { 101 return false, nil 102 } 103 } 104 return true, nil 105 }