]> git.donarmstrong.com Git - bamtools.git/blob - src/api/BamIndex.cpp
Major update to BamTools version 1.0
[bamtools.git] / src / api / BamIndex.cpp
1 // ***************************************************************************
2 // BamIndex.cpp (c) 2009 Derek Barnett
3 // Marth Lab, Department of Biology, Boston College
4 // All rights reserved.
5 // ---------------------------------------------------------------------------
6 // Last modified: 23 March 2011 (DB)
7 // ---------------------------------------------------------------------------
8 // Provides index functionality - both for the default (standardized) BAM 
9 // index format (.bai) as well as a BamTools-specific (nonstandard) index 
10 // format (.bti).
11 // ***************************************************************************
12
13 #include <api/BamIndex.h>
14 #include <api/BamReader.h>
15 #include <api/internal/BamStandardIndex_p.h>
16 #include <api/internal/BamToolsIndex_p.h>
17 using namespace BamTools;
18 using namespace BamTools::Internal;
19
20 #include <cstdio>
21 #include <cstdlib>
22 #include <algorithm>
23 #include <iostream>
24 #include <map>
25 using namespace std;
26
27 /*! \class BamTools::BamIndex
28     \brief Provides methods for generating & loading BAM index files.
29
30     This class straddles the line between public API and internal
31     implementation detail. Most client code should never have to use this
32     class directly.
33
34     It is exposed to the public API to allow advanced users to implement
35     their own custom indexing schemes.
36
37     N.B. - Please note that if you wish to derive your own subclass, you are
38            entering waters that are not well-documented at the moment and are
39            likely to be changing soon anyway. Implementing a custom index is
40            technically do-able at the moment, but the learning curve is (at the
41            moment) overly steep. Changes will be coming soon to alleviate some
42            of this headache.
43 */
44
45 // ctor
46 BamIndex::BamIndex(void)
47     : m_indexStream(0)
48     , m_indexFilename("")
49     , m_cacheMode(BamIndex::LimitedIndexCaching)
50 { }
51
52 // dtor
53 BamIndex::~BamIndex(void) {
54     if ( IsOpen() ) fclose(m_indexStream);
55     m_indexFilename = "";
56 }
57
58 // return true if FILE* is open
59 bool BamIndex::IsOpen(void) const {
60     return ( m_indexStream != 0 );
61 }
62
63 // loads existing data from file into memory
64 bool BamIndex::Load(const string& filename)  {
65
66     // open index file, abort on error
67     if ( !OpenIndexFile(filename, "rb") ) {
68         fprintf(stderr, "BamIndex ERROR: unable to open the BAM index file %s for reading.\n", filename.c_str());
69         return false;
70     }
71
72     // check magic number
73     if ( !LoadHeader() ) {
74         fclose(m_indexStream);
75         return false;
76     }
77
78     // load reference data (but only keep in memory if full caching requested)
79     bool saveInitialLoad = ( m_cacheMode == BamIndex::FullIndexCaching );
80     if ( !LoadAllReferences(saveInitialLoad) ) {
81         fclose(m_indexStream);
82         return false;
83     }
84
85     // update index cache based on selected mode
86     UpdateCache();
87
88     // return success
89     return true;
90 }
91
92 // opens index file for reading/writing, return true if opened OK
93 bool BamIndex::OpenIndexFile(const string& filename, const string& mode) {
94
95     // attempt to open file, return false if error
96     m_indexStream = fopen(filename.c_str(), mode.c_str());
97     if ( m_indexStream == 0 )  return false;
98
99     // otherwise save filename & return sucess
100     m_indexFilename = filename;
101     return true;
102 }
103
104 // rewind index file to beginning of index data, return true if rewound OK
105 bool BamIndex::Rewind(void) {
106     return ( fseek64(m_indexStream, DataBeginOffset(), SEEK_SET) == 0 );
107 }
108
109 // change the index caching behavior
110 void BamIndex::SetCacheMode(const BamIndex::IndexCacheMode& mode) {
111     if ( mode != m_cacheMode ) {
112         m_cacheMode = mode;
113         UpdateCache();
114     }
115 }
116
117 // updates in-memory cache of index data, depending on current cache mode
118 void BamIndex::UpdateCache(void) {
119
120     // skip if file not open
121     if ( !IsOpen() ) return;
122
123     // reflect requested cache mode behavior
124     switch ( m_cacheMode ) {
125
126         case (BamIndex::FullIndexCaching) :
127             Rewind();
128             LoadAllReferences(true);
129             break;
130
131         case (BamIndex::LimitedIndexCaching) :
132             if ( HasFullDataCache() )
133                 KeepOnlyFirstReferenceOffsets();
134             else {
135                 ClearAllData();
136                 SkipToFirstReference();
137                 LoadFirstReference(true);
138             }
139             break;
140
141         case(BamIndex::NoIndexCaching) :
142             ClearAllData();
143             break;
144
145         default :
146             // unreachable
147             ;
148     }
149 }
150
151 // writes in-memory index data out to file
152 bool BamIndex::Write(const string& bamFilename) {
153
154     // open index file for writing
155     string indexFilename = bamFilename + Extension();
156     if ( !OpenIndexFile(indexFilename, "wb") ) {
157         fprintf(stderr, "BamIndex ERROR: could not open file to save index data.\n");
158         return false;
159     }
160
161     // write index header data
162     if ( !WriteHeader() ) {
163         fprintf(stderr, "BamIndex ERROR: there was a problem writing index metadata to the new index file.\n");
164         fflush(m_indexStream);
165         fclose(m_indexStream);
166         exit(1);
167     }
168
169     // write main index data
170     if ( !WriteAllReferences() ) {
171         fprintf(stderr, "BamIndex ERROR: there was a problem writing index data to the new index file.\n");
172         fflush(m_indexStream);
173         fclose(m_indexStream);
174         exit(1);
175     }
176
177     // flush any remaining output
178     fflush(m_indexStream);
179     fclose(m_indexStream);
180
181     // re-open index file for later reading
182     if ( !OpenIndexFile(indexFilename, "rb") ) {
183         fprintf(stderr, "BamIndex ERROR: could not open newly created index file for reading.\n");
184         return false;
185     }
186
187     // save index filename & return success
188     m_indexFilename = indexFilename;
189     return true;
190 }