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  }