github.com/Schaudge/hts@v0.0.0-20240223063651-737b4d69d68c/csi/csi_write.go (about)

     1  // Copyright ©2015 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 csi
     6  
     7  import (
     8  	"encoding/binary"
     9  	"fmt"
    10  	"io"
    11  
    12  	"github.com/Schaudge/hts/bgzf"
    13  	"github.com/Schaudge/hts/bgzf/index"
    14  )
    15  
    16  // WriteTo writes the CSI index to the given io.Writer. Note that
    17  // the csi specification states that the index is stored as BGZF, but
    18  // WriteTo does not perform compression.
    19  func WriteTo(w io.Writer, idx *Index) error {
    20  	idx.sort()
    21  	err := binary.Write(w, binary.LittleEndian, csiMagic)
    22  	if err != nil {
    23  		return err
    24  	}
    25  	_, err = w.Write([]byte{idx.Version})
    26  	if err != nil {
    27  		return err
    28  	}
    29  	err = binary.Write(w, binary.LittleEndian, int32(idx.minShift))
    30  	if err != nil {
    31  		return err
    32  	}
    33  	err = binary.Write(w, binary.LittleEndian, int32(idx.depth))
    34  	if err != nil {
    35  		return err
    36  	}
    37  	err = binary.Write(w, binary.LittleEndian, int32(len(idx.Auxilliary)))
    38  	if err != nil {
    39  		return err
    40  	}
    41  	_, err = w.Write(idx.Auxilliary)
    42  	if err != nil {
    43  		return err
    44  	}
    45  	binLimit := uint32(((1 << ((idx.depth + 1) * nextBinShift)) - 1) / 7)
    46  	err = writeIndices(w, idx.Version, idx.refs, binLimit)
    47  	if err != nil {
    48  		return err
    49  	}
    50  	if idx.unmapped != nil {
    51  		err = binary.Write(w, binary.LittleEndian, idx.unmapped)
    52  	}
    53  	return err
    54  }
    55  
    56  func writeIndices(w io.Writer, version byte, idx []refIndex, binLimit uint32) error {
    57  	err := binary.Write(w, binary.LittleEndian, int32(len(idx)))
    58  	if err != nil {
    59  		return err
    60  	}
    61  	for i := range idx {
    62  		err = writeBins(w, version, idx[i].bins, idx[i].stats, binLimit)
    63  		if err != nil {
    64  			return err
    65  		}
    66  	}
    67  	return nil
    68  }
    69  
    70  func writeBins(w io.Writer, version byte, bins []bin, stats *index.ReferenceStats, binLimit uint32) error {
    71  	n := int32(len(bins))
    72  	if stats != nil {
    73  		n++
    74  	}
    75  	err := binary.Write(w, binary.LittleEndian, &n)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	for _, b := range bins {
    80  		err = binary.Write(w, binary.LittleEndian, b.bin)
    81  		if err != nil {
    82  			return fmt.Errorf("csi: failed to write bin number: %v", err)
    83  		}
    84  		err = binary.Write(w, binary.LittleEndian, vOffset(b.left))
    85  		if err != nil {
    86  			return fmt.Errorf("csi: failed to write left virtual offset: %v", err)
    87  		}
    88  		if version == 0x2 {
    89  			err = binary.Write(w, binary.LittleEndian, b.records)
    90  			if err != nil {
    91  				return fmt.Errorf("csi: failed to write record count: %v", err)
    92  			}
    93  		}
    94  		err = writeChunks(w, b.chunks)
    95  		if err != nil {
    96  			return err
    97  		}
    98  	}
    99  	if stats != nil {
   100  		return writeStats(w, version, stats, binLimit)
   101  	}
   102  	return nil
   103  }
   104  
   105  func writeChunks(w io.Writer, chunks []bgzf.Chunk) error {
   106  	err := binary.Write(w, binary.LittleEndian, int32(len(chunks)))
   107  	if err != nil {
   108  		return fmt.Errorf("csi: failed to write bin count: %v", err)
   109  	}
   110  	for _, c := range chunks {
   111  		err = binary.Write(w, binary.LittleEndian, vOffset(c.Begin))
   112  		if err != nil {
   113  			return fmt.Errorf("csi: failed to write chunk begin virtual offset: %v", err)
   114  		}
   115  		err = binary.Write(w, binary.LittleEndian, vOffset(c.End))
   116  		if err != nil {
   117  			return fmt.Errorf("csi: failed to write chunk end virtual offset: %v", err)
   118  		}
   119  	}
   120  	return nil
   121  }
   122  
   123  func writeStats(w io.Writer, version byte, stats *index.ReferenceStats, binLimit uint32) error {
   124  	var err error
   125  	statsDummyBin := binLimit + 1
   126  	switch version {
   127  	case 0x1:
   128  		err = binary.Write(w, binary.LittleEndian, [4]uint32{statsDummyBin, 0, 0, 2})
   129  	case 0x2:
   130  		err = binary.Write(w, binary.LittleEndian, [6]uint32{statsDummyBin, 0, 0, 0, 0, 2})
   131  	}
   132  	if err != nil {
   133  		return fmt.Errorf("csi: failed to write stats bin header: %v", err)
   134  	}
   135  	err = binary.Write(w, binary.LittleEndian, vOffset(stats.Chunk.Begin))
   136  	if err != nil {
   137  		return fmt.Errorf("csi: failed to write index stats chunk begin virtual offset: %v", err)
   138  	}
   139  	err = binary.Write(w, binary.LittleEndian, vOffset(stats.Chunk.End))
   140  	if err != nil {
   141  		return fmt.Errorf("csi: failed to write index stats chunk end virtual offset: %v", err)
   142  	}
   143  	err = binary.Write(w, binary.LittleEndian, stats.Mapped)
   144  	if err != nil {
   145  		return fmt.Errorf("csi: failed to write index stats mapped count: %v", err)
   146  	}
   147  	err = binary.Write(w, binary.LittleEndian, stats.Unmapped)
   148  	if err != nil {
   149  		return fmt.Errorf("csi: failed to write index stats unmapped count: %v", err)
   150  	}
   151  	return nil
   152  }