]> git.donarmstrong.com Git - bamtools.git/blob - src/api/BamReader.cpp
change printf's to fprint(stderr,
[bamtools.git] / src / api / BamReader.cpp
1 // ***************************************************************************\r
2 // BamReader.cpp (c) 2009 Derek Barnett, Michael Str�mberg\r
3 // Marth Lab, Department of Biology, Boston College\r
4 // All rights reserved.\r
5 // ---------------------------------------------------------------------------\r
6 // Last modified: 15 July 2010 (DB)\r
7 // ---------------------------------------------------------------------------\r
8 // Uses BGZF routines were adapted from the bgzf.c code developed at the Broad\r
9 // Institute.\r
10 // ---------------------------------------------------------------------------\r
11 // Provides the basic functionality for reading BAM files\r
12 // ***************************************************************************\r
13 \r
14 // C++ includes\r
15 #include <algorithm>\r
16 #include <iterator>\r
17 #include <string>\r
18 #include <vector>\r
19 #include <iostream>\r
20 \r
21 // BamTools includes\r
22 #include "BGZF.h"\r
23 #include "BamReader.h"\r
24 #include "BamIndex.h"\r
25 using namespace BamTools;\r
26 using namespace std;\r
27 \r
28 struct BamReader::BamReaderPrivate {\r
29 \r
30     // -------------------------------\r
31     // structs, enums, typedefs\r
32     // -------------------------------\r
33     enum RegionState { BEFORE_REGION = 0\r
34                      , WITHIN_REGION\r
35                      , AFTER_REGION\r
36                      };\r
37 \r
38     // -------------------------------\r
39     // data members\r
40     // -------------------------------\r
41 \r
42     // general file data\r
43     BgzfData  mBGZF;\r
44     string    HeaderText;\r
45     //BamIndex  Index;\r
46     BamIndex* NewIndex;\r
47     RefVector References;\r
48     bool      IsIndexLoaded;\r
49     int64_t   AlignmentsBeginOffset;\r
50     string    Filename;\r
51     string    IndexFilename;\r
52     \r
53     // system data\r
54     bool IsBigEndian;\r
55 \r
56     // user-specified region values\r
57     BamRegion Region;\r
58     bool IsLeftBoundSpecified;\r
59     bool IsRightBoundSpecified;\r
60     \r
61     bool IsRegionSpecified;\r
62     int  CurrentRefID;\r
63     int  CurrentLeft;\r
64 \r
65     // parent BamReader\r
66     BamReader* Parent;\r
67     \r
68     // BAM character constants\r
69     const char* DNA_LOOKUP;\r
70     const char* CIGAR_LOOKUP;\r
71 \r
72     // -------------------------------\r
73     // constructor & destructor\r
74     // -------------------------------\r
75     BamReaderPrivate(BamReader* parent);\r
76     ~BamReaderPrivate(void);\r
77 \r
78     // -------------------------------\r
79     // "public" interface\r
80     // -------------------------------\r
81 \r
82     // file operations\r
83     void Close(void);\r
84     bool Jump(int refID, int position = 0);\r
85     bool Open(const string& filename, const string& indexFilename = "");\r
86     bool Rewind(void);\r
87     bool SetRegion(const BamRegion& region);\r
88 \r
89     // access alignment data\r
90     bool GetNextAlignment(BamAlignment& bAlignment);\r
91     bool GetNextAlignmentCore(BamAlignment& bAlignment);\r
92 \r
93     // access auxiliary data\r
94     int GetReferenceID(const string& refName) const;\r
95 \r
96     // index operations\r
97     bool CreateIndex(bool useDefaultIndex);\r
98 \r
99     // -------------------------------\r
100     // internal methods\r
101     // -------------------------------\r
102 \r
103     // *** reading alignments and auxiliary data *** //\r
104 \r
105     // fills out character data for BamAlignment data\r
106     bool BuildCharData(BamAlignment& bAlignment);\r
107     // checks to see if alignment overlaps current region\r
108     RegionState IsOverlap(BamAlignment& bAlignment);\r
109     // retrieves header text from BAM file\r
110     void LoadHeaderData(void);\r
111     // retrieves BAM alignment under file pointer\r
112     bool LoadNextAlignment(BamAlignment& bAlignment);\r
113     // builds reference data structure from BAM file\r
114     void LoadReferenceData(void);\r
115 \r
116     // *** index file handling *** //\r
117 \r
118     // clear out inernal index data structure\r
119     void ClearIndex(void);\r
120     // loads index from BAM index file\r
121     bool LoadIndex(void);\r
122 };\r
123 \r
124 // -----------------------------------------------------\r
125 // BamReader implementation (wrapper around BRPrivate)\r
126 // -----------------------------------------------------\r
127 // constructor\r
128 BamReader::BamReader(void) {\r
129     d = new BamReaderPrivate(this);\r
130 }\r
131 \r
132 // destructor\r
133 BamReader::~BamReader(void) {\r
134     delete d;\r
135     d = 0;\r
136 }\r
137 \r
138 // file operations\r
139 void BamReader::Close(void) { d->Close(); }\r
140 bool BamReader::IsOpen(void) const { return d->mBGZF.IsOpen; }\r
141 bool BamReader::Jump(int refID, int position) { \r
142     d->Region.LeftRefID = refID;\r
143     d->Region.LeftPosition = position;\r
144     d->IsLeftBoundSpecified = true;\r
145     d->IsRightBoundSpecified = false;\r
146     return d->Jump(refID, position); \r
147 }\r
148 bool BamReader::Open(const string& filename, const string& indexFilename) { return d->Open(filename, indexFilename); }\r
149 bool BamReader::Rewind(void) { return d->Rewind(); }\r
150 bool BamReader::SetRegion(const BamRegion& region) { return d->SetRegion(region); }\r
151 bool BamReader::SetRegion(const int& leftRefID, const int& leftBound, const int& rightRefID, const int& rightBound) {\r
152     return d->SetRegion( BamRegion(leftRefID, leftBound, rightRefID, rightBound) );\r
153 }\r
154 \r
155 // access alignment data\r
156 bool BamReader::GetNextAlignment(BamAlignment& bAlignment) { return d->GetNextAlignment(bAlignment); }\r
157 bool BamReader::GetNextAlignmentCore(BamAlignment& bAlignment) { return d->GetNextAlignmentCore(bAlignment); }\r
158 \r
159 // access auxiliary data\r
160 const string BamReader::GetHeaderText(void) const { return d->HeaderText; }\r
161 int BamReader::GetReferenceCount(void) const { return d->References.size(); }\r
162 const RefVector& BamReader::GetReferenceData(void) const { return d->References; }\r
163 int BamReader::GetReferenceID(const string& refName) const { return d->GetReferenceID(refName); }\r
164 const std::string BamReader::GetFilename(void) const { return d->Filename; }\r
165 \r
166 // index operations\r
167 bool BamReader::CreateIndex(bool useDefaultIndex) { return d->CreateIndex(useDefaultIndex); }\r
168 \r
169 // -----------------------------------------------------\r
170 // BamReaderPrivate implementation\r
171 // -----------------------------------------------------\r
172 \r
173 // constructor\r
174 BamReader::BamReaderPrivate::BamReaderPrivate(BamReader* parent)\r
175     : NewIndex(0)\r
176     , IsIndexLoaded(false)\r
177     , AlignmentsBeginOffset(0)\r
178     , IsLeftBoundSpecified(false)\r
179     , IsRightBoundSpecified(false)\r
180     , IsRegionSpecified(false)\r
181     , CurrentRefID(0)\r
182     , CurrentLeft(0)\r
183     , Parent(parent)\r
184     , DNA_LOOKUP("=ACMGRSVTWYHKDBN")\r
185     , CIGAR_LOOKUP("MIDNSHP")\r
186\r
187     IsBigEndian = SystemIsBigEndian();\r
188 }\r
189 \r
190 // destructor\r
191 BamReader::BamReaderPrivate::~BamReaderPrivate(void) {\r
192     Close();\r
193 }\r
194 \r
195 bool BamReader::BamReaderPrivate::BuildCharData(BamAlignment& bAlignment) {\r
196   \r
197     // calculate character lengths/offsets\r
198     const unsigned int dataLength      = bAlignment.SupportData.BlockLength - BAM_CORE_SIZE;\r
199     const unsigned int cigarDataOffset = bAlignment.SupportData.QueryNameLength;\r
200     const unsigned int seqDataOffset   = bAlignment.SupportData.QueryNameLength + (bAlignment.SupportData.NumCigarOperations * 4);\r
201     const unsigned int qualDataOffset  = seqDataOffset + (bAlignment.SupportData.QuerySequenceLength+1)/2;\r
202     const unsigned int tagDataOffset   = qualDataOffset + bAlignment.SupportData.QuerySequenceLength;\r
203     const unsigned int tagDataLength   = dataLength - tagDataOffset;\r
204       \r
205     // set up char buffers\r
206     const char*     allCharData = bAlignment.SupportData.AllCharData.data();\r
207           uint32_t* cigarData   = (uint32_t*)(allCharData + cigarDataOffset);\r
208     const char*     seqData     = ((const char*)allCharData) + seqDataOffset;\r
209     const char*     qualData    = ((const char*)allCharData) + qualDataOffset;\r
210           char*     tagData     = ((char*)allCharData) + tagDataOffset;\r
211   \r
212     // store alignment name (depends on null char as terminator)\r
213     bAlignment.Name.assign((const char*)(allCharData));    \r
214           \r
215     // save CigarOps \r
216     CigarOp op;\r
217     bAlignment.CigarData.clear();\r
218     bAlignment.CigarData.reserve(bAlignment.SupportData.NumCigarOperations);\r
219     for (unsigned int i = 0; i < bAlignment.SupportData.NumCigarOperations; ++i) {\r
220 \r
221         // swap if necessary\r
222         if ( IsBigEndian ) { SwapEndian_32(cigarData[i]); }\r
223       \r
224         // build CigarOp structure\r
225         op.Length = (cigarData[i] >> BAM_CIGAR_SHIFT);\r
226         op.Type   = CIGAR_LOOKUP[ (cigarData[i] & BAM_CIGAR_MASK) ];\r
227 \r
228         // save CigarOp\r
229         bAlignment.CigarData.push_back(op);\r
230     }\r
231           \r
232           \r
233     // save query sequence\r
234     bAlignment.QueryBases.clear();\r
235     bAlignment.QueryBases.reserve(bAlignment.SupportData.QuerySequenceLength);\r
236     for (unsigned int i = 0; i < bAlignment.SupportData.QuerySequenceLength; ++i) {\r
237         char singleBase = DNA_LOOKUP[ ( ( seqData[(i/2)] >> (4*(1-(i%2)))) & 0xf ) ];\r
238         bAlignment.QueryBases.append(1, singleBase);\r
239     }\r
240   \r
241     // save qualities, converting from numeric QV to 'FASTQ-style' ASCII character\r
242     bAlignment.Qualities.clear();\r
243     bAlignment.Qualities.reserve(bAlignment.SupportData.QuerySequenceLength);\r
244     for (unsigned int i = 0; i < bAlignment.SupportData.QuerySequenceLength; ++i) {\r
245         char singleQuality = (char)(qualData[i]+33);\r
246         bAlignment.Qualities.append(1, singleQuality);\r
247     }\r
248     \r
249     // if QueryBases is empty (and this is a allowed case)\r
250     if ( bAlignment.QueryBases.empty() ) \r
251         bAlignment.AlignedBases = bAlignment.QueryBases;\r
252     \r
253     // if QueryBases contains data, then build AlignedBases using CIGAR data\r
254     else {\r
255     \r
256         // resize AlignedBases\r
257         bAlignment.AlignedBases.clear();\r
258         bAlignment.AlignedBases.reserve(bAlignment.SupportData.QuerySequenceLength);\r
259       \r
260         // iterate over CigarOps\r
261         int k = 0;\r
262         vector<CigarOp>::const_iterator cigarIter = bAlignment.CigarData.begin();\r
263         vector<CigarOp>::const_iterator cigarEnd  = bAlignment.CigarData.end();\r
264         for ( ; cigarIter != cigarEnd; ++cigarIter ) {\r
265             \r
266             const CigarOp& op = (*cigarIter);\r
267             switch(op.Type) {\r
268               \r
269                 case ('M') :\r
270                 case ('I') :\r
271                     bAlignment.AlignedBases.append(bAlignment.QueryBases.substr(k, op.Length)); // for 'M', 'I' - write bases\r
272                     // fall through\r
273                 \r
274                 case ('S') :\r
275                     k += op.Length;                                     // for 'S' - soft clip, skip over query bases\r
276                     break;\r
277                     \r
278                 case ('D') :\r
279                     bAlignment.AlignedBases.append(op.Length, '-');     // for 'D' - write gap character\r
280                     break;\r
281                     \r
282                 case ('P') :\r
283                     bAlignment.AlignedBases.append( op.Length, '*' );   // for 'P' - write padding character\r
284                     break;\r
285                     \r
286                 case ('N') :\r
287                     bAlignment.AlignedBases.append( op.Length, 'N' );  // for 'N' - write N's, skip bases in original query sequence\r
288                     break;\r
289                     \r
290                 case ('H') :\r
291                     break;  // for 'H' - hard clip, do nothing to AlignedBases, move to next op\r
292                     \r
293                 default:\r
294                     fprintf(stderr, "ERROR: Invalid Cigar op type\n"); // shouldn't get here\r
295                     exit(1);\r
296             }\r
297         }\r
298     }\r
299  \r
300     // -----------------------\r
301     // Added: 3-25-2010 DB\r
302     // Fixed: endian-correctness for tag data\r
303     // -----------------------\r
304     if ( IsBigEndian ) {\r
305         int i = 0;\r
306         while ( (unsigned int)i < tagDataLength ) {\r
307           \r
308             i += 2; // skip tag type (e.g. "RG", "NM", etc)\r
309             uint8_t type = toupper(tagData[i]);     // lower & upper case letters have same meaning \r
310             ++i;                                    // skip value type\r
311     \r
312             switch (type) {\r
313                 \r
314                 case('A') :\r
315                 case('C') : \r
316                     ++i;\r
317                     break;\r
318 \r
319                 case('S') : \r
320                     SwapEndian_16p(&tagData[i]); \r
321                     i += sizeof(uint16_t);\r
322                     break;\r
323                     \r
324                 case('F') :\r
325                 case('I') : \r
326                     SwapEndian_32p(&tagData[i]);\r
327                     i += sizeof(uint32_t);\r
328                     break;\r
329                 \r
330                 case('D') : \r
331                     SwapEndian_64p(&tagData[i]);\r
332                     i += sizeof(uint64_t);\r
333                     break;\r
334                 \r
335                 case('H') :\r
336                 case('Z') : \r
337                     while (tagData[i]) { ++i; }\r
338                     ++i; // increment one more for null terminator\r
339                     break;\r
340                 \r
341                 default : \r
342                     fprintf(stderr, "ERROR: Invalid tag value type\n"); // shouldn't get here\r
343                     exit(1);\r
344             }\r
345         }\r
346     }\r
347     \r
348     // store TagData\r
349     bAlignment.TagData.clear();\r
350     bAlignment.TagData.resize(tagDataLength);\r
351     memcpy((char*)bAlignment.TagData.data(), tagData, tagDataLength);\r
352     \r
353     // clear the core-only flag\r
354     bAlignment.SupportData.HasCoreOnly = false;\r
355     \r
356     // return success\r
357     return true;\r
358 }\r
359 \r
360 // clear index data structure\r
361 void BamReader::BamReaderPrivate::ClearIndex(void) {\r
362     delete NewIndex;\r
363     NewIndex = 0;\r
364 }\r
365 \r
366 // closes the BAM file\r
367 void BamReader::BamReaderPrivate::Close(void) {\r
368     \r
369     // close BGZF file stream\r
370     mBGZF.Close();\r
371     \r
372     // clear out index data\r
373     ClearIndex();\r
374     \r
375     // clear out header data\r
376     HeaderText.clear();\r
377     \r
378     // clear out region flags\r
379     IsLeftBoundSpecified  = false;\r
380     IsRightBoundSpecified = false;\r
381     IsRegionSpecified     = false;\r
382 }\r
383 \r
384 // create BAM index from BAM file (keep structure in memory) and write to default index output file\r
385 bool BamReader::BamReaderPrivate::CreateIndex(bool useDefaultIndex) {\r
386 \r
387     // clear out prior index data\r
388     ClearIndex();\r
389     \r
390     // create default index\r
391     if ( useDefaultIndex )\r
392         NewIndex = new BamDefaultIndex(&mBGZF, Parent, IsBigEndian);\r
393     // create BamTools 'custom' index\r
394     else\r
395         NewIndex = new BamToolsIndex(&mBGZF, Parent, IsBigEndian);\r
396     \r
397     bool ok = true;\r
398     ok &= NewIndex->Build();\r
399     ok &= NewIndex->Write(Filename); \r
400     \r
401     // return success/fail\r
402     return ok;\r
403 }\r
404 \r
405 // get next alignment (from specified region, if given)\r
406 bool BamReader::BamReaderPrivate::GetNextAlignment(BamAlignment& bAlignment) {\r
407 \r
408     // if valid alignment found, attempt to parse char data, and return success/failure\r
409     if ( GetNextAlignmentCore(bAlignment) )\r
410         return BuildCharData(bAlignment);\r
411     \r
412     // no valid alignment found\r
413     else\r
414         return false;\r
415 }\r
416 \r
417 // retrieves next available alignment core data (returns success/fail)\r
418 // ** DOES NOT parse any character data (read name, bases, qualities, tag data)\r
419 //    these can be accessed, if necessary, from the supportData \r
420 // useful for operations requiring ONLY positional or other alignment-related information\r
421 bool BamReader::BamReaderPrivate::GetNextAlignmentCore(BamAlignment& bAlignment) {\r
422 \r
423     // if valid alignment available\r
424     if ( LoadNextAlignment(bAlignment) ) {\r
425 \r
426         // set core-only flag\r
427         bAlignment.SupportData.HasCoreOnly = true;\r
428       \r
429         // if region not specified, return success\r
430         if ( !IsLeftBoundSpecified ) return true;\r
431 \r
432         // determine region state (before, within, after)\r
433         BamReader::BamReaderPrivate::RegionState state = IsOverlap(bAlignment);\r
434       \r
435         // if alignment lies after region, return false\r
436         if ( state == AFTER_REGION ) \r
437             return false;\r
438 \r
439         while ( state != WITHIN_REGION ) {\r
440             // if no valid alignment available (likely EOF) return failure\r
441             if ( !LoadNextAlignment(bAlignment) ) return false;\r
442             // if alignment lies after region, return false (no available read within region)\r
443             state = IsOverlap(bAlignment);\r
444             if ( state == AFTER_REGION) return false;\r
445             \r
446         }\r
447 \r
448         // return success (alignment found that overlaps region)\r
449         return true;\r
450     }\r
451 \r
452     // no valid alignment\r
453     else\r
454         return false;\r
455 }\r
456 \r
457 // returns RefID for given RefName (returns References.size() if not found)\r
458 int BamReader::BamReaderPrivate::GetReferenceID(const string& refName) const {\r
459 \r
460     // retrieve names from reference data\r
461     vector<string> refNames;\r
462     RefVector::const_iterator refIter = References.begin();\r
463     RefVector::const_iterator refEnd  = References.end();\r
464     for ( ; refIter != refEnd; ++refIter) {\r
465         refNames.push_back( (*refIter).RefName );\r
466     }\r
467 \r
468     // return 'index-of' refName ( if not found, returns refNames.size() )\r
469     return distance(refNames.begin(), find(refNames.begin(), refNames.end(), refName));\r
470 }\r
471 \r
472 // returns region state - whether alignment ends before, overlaps, or starts after currently specified region\r
473 // this *internal* method should ONLY called when (at least) IsLeftBoundSpecified == true\r
474 BamReader::BamReaderPrivate::RegionState BamReader::BamReaderPrivate::IsOverlap(BamAlignment& bAlignment) {\r
475     \r
476     // --------------------------------------------------\r
477     // check alignment start against right bound cutoff\r
478     \r
479     // if full region of interest was given\r
480     if ( IsRightBoundSpecified ) {\r
481       \r
482         // read starts on right bound reference, but AFTER right bound position\r
483         if ( bAlignment.RefID == Region.RightRefID && bAlignment.Position > Region.RightPosition )\r
484             return AFTER_REGION;\r
485       \r
486         // if read starts on reference AFTER right bound, return false\r
487         if ( bAlignment.RefID > Region.RightRefID ) \r
488             return AFTER_REGION;\r
489     }\r
490   \r
491     // --------------------------------------------------------\r
492     // no right bound given OR read starts before right bound\r
493     // so, check if it overlaps left bound \r
494   \r
495     // if read starts on left bound reference AND after left boundary, return success\r
496     if ( bAlignment.RefID == Region.LeftRefID && bAlignment.Position >= Region.LeftPosition)\r
497         return WITHIN_REGION;\r
498   \r
499     // if read is on any reference sequence before left bound, return false\r
500     if ( bAlignment.RefID < Region.LeftRefID )\r
501         return BEFORE_REGION;\r
502 \r
503     // --------------------------------------------------------\r
504     // read is on left bound reference, but starts before left bound position\r
505 \r
506     // if it overlaps, return WITHIN_REGION\r
507     if ( bAlignment.GetEndPosition() >= Region.LeftPosition )\r
508         return WITHIN_REGION;\r
509     // else begins before left bound position\r
510     else\r
511         return BEFORE_REGION;\r
512 }\r
513 \r
514 // jumps to specified region(refID, leftBound) in BAM file, returns success/fail\r
515 bool BamReader::BamReaderPrivate::Jump(int refID, int position) {\r
516 \r
517     // -----------------------------------------------------------------------\r
518     // check for existing index \r
519     if ( NewIndex == 0 ) return false; \r
520     // see if reference has alignments\r
521     if ( !NewIndex->HasAlignments(refID) ) return false; \r
522     // make sure position is valid\r
523     if ( position > References.at(refID).RefLength ) return false;\r
524     \r
525     // determine possible offsets\r
526     vector<int64_t> offsets;\r
527     if ( !NewIndex->GetOffsets(Region, IsRightBoundSpecified, offsets) ) {\r
528         fprintf(stderr, "ERROR: Could not jump: unable to calculate offset for specified region.\n");\r
529         return false;\r
530     }\r
531       \r
532     // iterate through offsets\r
533     BamAlignment bAlignment;\r
534     bool result = true;\r
535     for ( vector<int64_t>::const_iterator o = offsets.begin(); o != offsets.end(); ++o) {\r
536         \r
537         // attempt seek & load first available alignment\r
538         result &= mBGZF.Seek(*o);\r
539         LoadNextAlignment(bAlignment);\r
540         \r
541         // if this alignment corresponds to desired position\r
542         // return success of seeking back to 'current offset'\r
543         if ( (bAlignment.RefID == refID && bAlignment.Position + bAlignment.Length > position) || (bAlignment.RefID > refID) ) {\r
544             if ( o != offsets.begin() ) --o;\r
545             return mBGZF.Seek(*o);\r
546         }\r
547     }\r
548     \r
549     return result;\r
550 }\r
551 \r
552 // load BAM header data\r
553 void BamReader::BamReaderPrivate::LoadHeaderData(void) {\r
554 \r
555     // check to see if proper BAM header\r
556     char buffer[4];\r
557     if (mBGZF.Read(buffer, 4) != 4) {\r
558         fprintf(stderr, "Could not read header type\n");\r
559         exit(1);\r
560     }\r
561 \r
562     if (strncmp(buffer, "BAM\001", 4)) {\r
563         fprintf(stderr, "wrong header type!\n");\r
564         exit(1);\r
565     }\r
566 \r
567     // get BAM header text length\r
568     mBGZF.Read(buffer, 4);\r
569     unsigned int headerTextLength = BgzfData::UnpackUnsignedInt(buffer);\r
570     if ( IsBigEndian ) { SwapEndian_32(headerTextLength); }\r
571     \r
572     // get BAM header text\r
573     char* headerText = (char*)calloc(headerTextLength + 1, 1);\r
574     mBGZF.Read(headerText, headerTextLength);\r
575     HeaderText = (string)((const char*)headerText);\r
576 \r
577     // clean up calloc-ed temp variable\r
578     free(headerText);\r
579 }\r
580 \r
581 // load existing index data from BAM index file (".bai"), return success/fail\r
582 bool BamReader::BamReaderPrivate::LoadIndex(void) {\r
583 \r
584     // clear out any existing index data\r
585     ClearIndex();\r
586 \r
587     // skip if index file empty\r
588     if ( IndexFilename.empty() )\r
589         return false;\r
590 \r
591     // check supplied filename for index type\r
592     size_t defaultExtensionFound = IndexFilename.find(".bai");\r
593     size_t customExtensionFound  = IndexFilename.find(".bti");\r
594     \r
595     // if SAM/BAM default (".bai")\r
596     if ( defaultExtensionFound != string::npos )\r
597         NewIndex = new BamDefaultIndex(&mBGZF, Parent, IsBigEndian);\r
598     \r
599     // if BamTools custom index (".bti")\r
600     else if ( customExtensionFound != string::npos )\r
601         NewIndex = new BamToolsIndex(&mBGZF, Parent, IsBigEndian);\r
602     \r
603     // else unknown\r
604     else {\r
605         fprintf(stderr, "ERROR: Unknown index file extension.\n");\r
606         return false;\r
607     }\r
608     \r
609     // return success of loading index data\r
610     return NewIndex->Load(IndexFilename);\r
611 }\r
612 \r
613 // populates BamAlignment with alignment data under file pointer, returns success/fail\r
614 bool BamReader::BamReaderPrivate::LoadNextAlignment(BamAlignment& bAlignment) {\r
615 \r
616     // read in the 'block length' value, make sure it's not zero\r
617     char buffer[4];\r
618     mBGZF.Read(buffer, 4);\r
619     bAlignment.SupportData.BlockLength = BgzfData::UnpackUnsignedInt(buffer);\r
620     if ( IsBigEndian ) { SwapEndian_32(bAlignment.SupportData.BlockLength); }\r
621     if ( bAlignment.SupportData.BlockLength == 0 ) { return false; }\r
622 \r
623     // read in core alignment data, make sure the right size of data was read\r
624     char x[BAM_CORE_SIZE];\r
625     if ( mBGZF.Read(x, BAM_CORE_SIZE) != BAM_CORE_SIZE ) { return false; }\r
626 \r
627     if ( IsBigEndian ) {\r
628         for ( int i = 0; i < BAM_CORE_SIZE; i+=sizeof(uint32_t) ) { \r
629           SwapEndian_32p(&x[i]); \r
630         }\r
631     }\r
632     \r
633     // set BamAlignment 'core' and 'support' data\r
634     bAlignment.RefID    = BgzfData::UnpackSignedInt(&x[0]);  \r
635     bAlignment.Position = BgzfData::UnpackSignedInt(&x[4]);\r
636     \r
637     unsigned int tempValue = BgzfData::UnpackUnsignedInt(&x[8]);\r
638     bAlignment.Bin        = tempValue >> 16;\r
639     bAlignment.MapQuality = tempValue >> 8 & 0xff;\r
640     bAlignment.SupportData.QueryNameLength = tempValue & 0xff;\r
641 \r
642     tempValue = BgzfData::UnpackUnsignedInt(&x[12]);\r
643     bAlignment.AlignmentFlag = tempValue >> 16;\r
644     bAlignment.SupportData.NumCigarOperations = tempValue & 0xffff;\r
645 \r
646     bAlignment.SupportData.QuerySequenceLength = BgzfData::UnpackUnsignedInt(&x[16]);\r
647     bAlignment.MateRefID    = BgzfData::UnpackSignedInt(&x[20]);\r
648     bAlignment.MatePosition = BgzfData::UnpackSignedInt(&x[24]);\r
649     bAlignment.InsertSize   = BgzfData::UnpackSignedInt(&x[28]);\r
650     \r
651     // set BamAlignment length\r
652     bAlignment.Length = bAlignment.SupportData.QuerySequenceLength;\r
653     \r
654     // read in character data - make sure proper data size was read\r
655     bool readCharDataOK = false;\r
656     const unsigned int dataLength = bAlignment.SupportData.BlockLength - BAM_CORE_SIZE;\r
657     char* allCharData = (char*)calloc(sizeof(char), dataLength);\r
658     \r
659     if ( mBGZF.Read(allCharData, dataLength) == (signed int)dataLength) { \r
660       \r
661         // store 'allCharData' in supportData structure\r
662         bAlignment.SupportData.AllCharData.assign((const char*)allCharData, dataLength);\r
663         \r
664         // set success flag\r
665         readCharDataOK = true;\r
666     }\r
667 \r
668     free(allCharData);\r
669     return readCharDataOK;\r
670 }\r
671 \r
672 // loads reference data from BAM file\r
673 void BamReader::BamReaderPrivate::LoadReferenceData(void) {\r
674 \r
675     // get number of reference sequences\r
676     char buffer[4];\r
677     mBGZF.Read(buffer, 4);\r
678     unsigned int numberRefSeqs = BgzfData::UnpackUnsignedInt(buffer);\r
679     if ( IsBigEndian ) { SwapEndian_32(numberRefSeqs); }\r
680     if (numberRefSeqs == 0) { return; }\r
681     References.reserve((int)numberRefSeqs);\r
682 \r
683     // iterate over all references in header\r
684     for (unsigned int i = 0; i != numberRefSeqs; ++i) {\r
685 \r
686         // get length of reference name\r
687         mBGZF.Read(buffer, 4);\r
688         unsigned int refNameLength = BgzfData::UnpackUnsignedInt(buffer);\r
689         if ( IsBigEndian ) { SwapEndian_32(refNameLength); }\r
690         char* refName = (char*)calloc(refNameLength, 1);\r
691 \r
692         // get reference name and reference sequence length\r
693         mBGZF.Read(refName, refNameLength);\r
694         mBGZF.Read(buffer, 4);\r
695         int refLength = BgzfData::UnpackSignedInt(buffer);\r
696         if ( IsBigEndian ) { SwapEndian_32(refLength); }\r
697 \r
698         // store data for reference\r
699         RefData aReference;\r
700         aReference.RefName   = (string)((const char*)refName);\r
701         aReference.RefLength = refLength;\r
702         References.push_back(aReference);\r
703 \r
704         // clean up calloc-ed temp variable\r
705         free(refName);\r
706     }\r
707 }\r
708 \r
709 // opens BAM file (and index)\r
710 bool BamReader::BamReaderPrivate::Open(const string& filename, const string& indexFilename) {\r
711 \r
712     Filename = filename;\r
713     IndexFilename = indexFilename;\r
714 \r
715     // open the BGZF file for reading, return false on failure\r
716     if ( !mBGZF.Open(filename, "rb") ) \r
717         return false;\r
718     \r
719     // retrieve header text & reference data\r
720     LoadHeaderData();\r
721     LoadReferenceData();\r
722 \r
723     // store file offset of first alignment\r
724     AlignmentsBeginOffset = mBGZF.Tell();\r
725 \r
726     // open index file & load index data (if exists)\r
727     if ( !IndexFilename.empty() )\r
728         LoadIndex();\r
729     \r
730     // return success\r
731     return true;\r
732 }\r
733 \r
734 // returns BAM file pointer to beginning of alignment data\r
735 bool BamReader::BamReaderPrivate::Rewind(void) {\r
736    \r
737     // rewind to first alignment\r
738     if ( !mBGZF.Seek(AlignmentsBeginOffset) ) return false;\r
739   \r
740     // retrieve first alignment data\r
741     BamAlignment al;\r
742     if ( !LoadNextAlignment(al) ) return false;\r
743       \r
744     // reset default region info using first alignment in file\r
745     Region.LeftRefID      = al.RefID;\r
746     Region.LeftPosition   = al.Position;\r
747     Region.RightRefID     = -1;\r
748     Region.RightPosition  = -1;\r
749     IsLeftBoundSpecified  = false;\r
750     IsRightBoundSpecified = false; \r
751 \r
752     // rewind back to before first alignment\r
753     // return success/fail of seek\r
754     return mBGZF.Seek(AlignmentsBeginOffset);\r
755 }\r
756 \r
757 // sets a region of interest (with left & right bound reference/position)\r
758 // attempts a Jump() to left bound as well\r
759 // returns success/failure of Jump()\r
760 bool BamReader::BamReaderPrivate::SetRegion(const BamRegion& region) {\r
761     \r
762     // save region of interest\r
763     Region = region;\r
764     \r
765     // set flags\r
766     if ( region.LeftRefID >= 0 && region.LeftPosition >= 0 ) \r
767         IsLeftBoundSpecified = true;\r
768     if ( region.RightRefID >= 0 && region.RightPosition >= 0 ) \r
769         IsRightBoundSpecified = true;\r
770     \r
771     // attempt jump to beginning of region, return success/fail of Jump()\r
772     return Jump( Region.LeftRefID, Region.LeftPosition );\r
773 }\r