]> git.donarmstrong.com Git - bamtools.git/blob - BamAux.h
2ce273307b4c52542616c6e12cb88ecedd46b4fb
[bamtools.git] / BamAux.h
1 // ***************************************************************************\r
2 // BamAux.h (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: 14 April 2010 (DB)\r
7 // ---------------------------------------------------------------------------\r
8 // Provides the basic constants, data structures, etc. for using BAM files\r
9 // ***************************************************************************\r
10 \r
11 #ifndef BAMAUX_H\r
12 #define BAMAUX_H\r
13 \r
14 // C inclues\r
15 #include <cstdio>\r
16 #include <cstdlib>\r
17 #include <cstring>\r
18 \r
19 // C++ includes\r
20 #include <exception>\r
21 #include <map>\r
22 #include <string>\r
23 #include <utility>\r
24 #include <vector>\r
25 \r
26 // Platform-specific type definitions\r
27 #ifndef BAMTOOLS_TYPES\r
28 #define BAMTOOLS_TYPES\r
29     #ifdef _MSC_VER\r
30         typedef char                 int8_t;\r
31         typedef unsigned char       uint8_t;\r
32         typedef short               int16_t;\r
33         typedef unsigned short     uint16_t;\r
34         typedef int                 int32_t;\r
35         typedef unsigned int       uint32_t;\r
36         typedef long long           int64_t;\r
37         typedef unsigned long long uint64_t;\r
38     #else\r
39         #include <stdint.h>\r
40     #endif\r
41 #endif // BAMTOOLS_TYPES\r
42 \r
43 namespace BamTools {\r
44 \r
45 // BAM constants\r
46 const int BAM_CORE_SIZE   = 32;\r
47 const int BAM_CMATCH      = 0;\r
48 const int BAM_CINS        = 1;\r
49 const int BAM_CDEL        = 2;\r
50 const int BAM_CREF_SKIP   = 3;\r
51 const int BAM_CSOFT_CLIP  = 4;\r
52 const int BAM_CHARD_CLIP  = 5;\r
53 const int BAM_CPAD        = 6;\r
54 const int BAM_CIGAR_SHIFT = 4;\r
55 const int BAM_CIGAR_MASK  = ((1 << BAM_CIGAR_SHIFT) - 1);\r
56 \r
57 // BAM index constants\r
58 const int MAX_BIN           = 37450;    // =(8^6-1)/7+1\r
59 const int BAM_MIN_CHUNK_GAP = 32768;\r
60 const int BAM_LIDX_SHIFT    = 14;\r
61 \r
62 // Explicit variable sizes\r
63 const int BT_SIZEOF_INT = 4;\r
64 \r
65 struct CigarOp;\r
66 \r
67 struct BamAlignment {\r
68
69     // constructors & destructor
70     public:
71         BamAlignment(void);
72         BamAlignment(const BamAlignment& other);
73         ~BamAlignment(void);
74 \r
75     // Queries against alignment flags\r
76     public:        \r
77         bool IsDuplicate(void) const;           // Returns true if this read is a PCR duplicate       \r
78         bool IsFailedQC(void) const;            // Returns true if this read failed quality control      \r
79         bool IsFirstMate(void) const;           // Returns true if alignment is first mate on read        \r
80         bool IsMapped(void) const;              // Returns true if alignment is mapped        \r
81         bool IsMateMapped(void) const;          // Returns true if alignment's mate is mapped        \r
82         bool IsMateReverseStrand(void) const;   // Returns true if alignment's mate mapped to reverse strand        \r
83         bool IsPaired(void) const;              // Returns true if alignment part of paired-end read        \r
84         bool IsPrimaryAlignment(void) const;    // Returns true if reported position is primary alignment       \r
85         bool IsProperPair(void) const;          // Returns true if alignment is part of read that satisfied paired-end resolution     \r
86         bool IsReverseStrand(void) const;       // Returns true if alignment mapped to reverse strand\r
87         bool IsSecondMate(void) const;          // Returns true if alignment is second mate on read\r
88 \r
89     // Manipulate alignment flags\r
90     public:        \r
91         void SetIsDuplicate(bool ok);           // Sets "PCR duplicate" flag        \r
92         void SetIsFailedQC(bool ok);            // Sets "failed quality control" flag        \r
93         void SetIsFirstMate(bool ok);           // Sets "alignment is first mate" flag        \r
94         void SetIsMateUnmapped(bool ok);        // Sets "alignment's mate is mapped" flag        \r
95         void SetIsMateReverseStrand(bool ok);   // Sets "alignment's mate mapped to reverse strand" flag        \r
96         void SetIsPaired(bool ok);              // Sets "alignment part of paired-end read" flag        \r
97         void SetIsProperPair(bool ok);          // Sets "alignment is part of read that satisfied paired-end resolution" flag        \r
98         void SetIsReverseStrand(bool ok);       // Sets "alignment mapped to reverse strand" flag        \r
99         void SetIsSecondaryAlignment(bool ok);  // Sets "position is primary alignment" flag        \r
100         void SetIsSecondMate(bool ok);          // Sets "alignment is second mate on read" flag        \r
101         void SetIsUnmapped(bool ok);            // Sets "alignment is mapped" flag\r
102
103     // Tag data access methods
104     public:\r
105         bool GetEditDistance(uint8_t& editDistance) const;      // get "NM" tag data - contributed by Aaron Quinlan\r
106         bool GetReadGroup(std::string& readGroup) const;        // get "RG" tag data\r
107
108     // Additional data access methods\r
109     public:\r
110         int GetEndPosition(bool usePadded = false) const;       // calculates alignment end position, based on starting position and CIGAR operations
111
112     // 'internal' utility methods \r
113     private:\r
114         static void SkipToNextTag(const char storageType, char* &pTagData, unsigned int& numBytesParsed);\r
115 \r
116     // Data members\r
117     public:\r
118         std::string  Name;              // Read name\r
119         int32_t      Length;            // Query length\r
120         std::string  QueryBases;        // 'Original' sequence (as reported from sequencing machine)\r
121         std::string  AlignedBases;      // 'Aligned' sequence (includes any indels, padding, clipping)\r
122         std::string  Qualities;         // FASTQ qualities (ASCII characters, not numeric values)\r
123         std::string  TagData;           // Tag data (accessor methods will pull the requested information out)\r
124         int32_t      RefID;             // ID number for reference sequence\r
125         int32_t      Position;          // Position (0-based) where alignment starts\r
126         uint16_t     Bin;               // Bin in BAM file where this alignment resides\r
127         uint16_t     MapQuality;        // Mapping quality score\r
128         uint32_t     AlignmentFlag;     // Alignment bit-flag - see Is<something>() methods to query this value, SetIs<something>() methods to manipulate \r
129         std::vector<CigarOp> CigarData; // CIGAR operations for this alignment\r
130         int32_t      MateRefID;         // ID number for reference sequence where alignment's mate was aligned\r
131         int32_t      MatePosition;      // Position (0-based) where alignment's mate starts\r
132         int32_t      InsertSize;        // Mate-pair insert size\r
133 \r
134     // Alignment flag query constants
135     // Use the get/set methods above instead\r
136     private:\r
137         enum { PAIRED        = 1\r
138              , PROPER_PAIR   = 2\r
139              , UNMAPPED      = 4\r
140              , MATE_UNMAPPED = 8\r
141              , REVERSE       = 16\r
142              , MATE_REVERSE  = 32\r
143              , READ_1        = 64\r
144              , READ_2        = 128\r
145              , SECONDARY     = 256\r
146              , QC_FAILED     = 512\r
147              , DUPLICATE     = 1024 \r
148              };\r
149 };\r
150 \r
151 // ----------------------------------------------------------------\r
152 // Auxiliary data structs & typedefs\r
153 \r
154 struct CigarOp {\r
155     char     Type;   // Operation type (MIDNSHP)\r
156     uint32_t Length; // Operation length (number of bases)\r
157 };\r
158 \r
159 struct RefData {\r
160    
161     // data members\r
162     std::string RefName;          // Name of reference sequence\r
163     int32_t     RefLength;        // Length of reference sequence\r
164     bool        RefHasAlignments; // True if BAM file contains alignments mapped to reference sequence\r
165     
166     // constructor\r
167     RefData(const int32_t& length = 0, 
168             bool ok = false)\r
169         : RefLength(length)\r
170         , RefHasAlignments(ok)\r
171     { }\r
172 };\r
173 \r
174 typedef std::vector<RefData>      RefVector;\r
175 typedef std::vector<BamAlignment> BamAlignmentVector;\r
176 \r
177 // ----------------------------------------------------------------\r
178 // Indexing structs & typedefs\r
179 \r
180 struct Chunk {
181 \r
182     // data members\r
183     uint64_t Start;\r
184     uint64_t Stop;\r
185
186     // constructor\r
187     Chunk(const uint64_t& start = 0, 
188           const uint64_t& stop = 0)\r
189         : Start(start)\r
190         , Stop(stop)\r
191     { }\r
192 };\r
193 \r
194 inline\r
195 bool ChunkLessThan(const Chunk& lhs, const Chunk& rhs) {\r
196     return lhs.Start < rhs.Start;\r
197 }\r
198 \r
199 typedef std::vector<Chunk> ChunkVector;\r
200 typedef std::map<uint32_t, ChunkVector> BamBinMap;\r
201 typedef std::vector<uint64_t> LinearOffsetVector;\r
202 \r
203 struct ReferenceIndex {\r
204     // data members\r
205     BamBinMap Bins;\r
206     LinearOffsetVector Offsets;\r
207     // constructor\r
208     ReferenceIndex(const BamBinMap& binMap = BamBinMap(),\r
209                    const LinearOffsetVector& offsets = LinearOffsetVector())\r
210         : Bins(binMap)\r
211         , Offsets(offsets)\r
212     { }\r
213 };\r
214 \r
215 typedef std::vector<ReferenceIndex> BamIndex;\r
216
217 // ----------------------------------------------------------------
218 // BamAlignment member methods
219
220 // constructors & destructor
221 inline 
222 BamAlignment::BamAlignment(void) { }
223
224 inline 
225 BamAlignment::BamAlignment(const BamAlignment& other)
226     : Name(other.Name)
227     , Length(other.Length)
228     , QueryBases(other.QueryBases)
229     , AlignedBases(other.AlignedBases)
230     , Qualities(other.Qualities)
231     , TagData(other.TagData)
232     , RefID(other.RefID)
233     , Position(other.Position)
234     , Bin(other.Bin)
235     , MapQuality(other.MapQuality)
236     , AlignmentFlag(other.AlignmentFlag)
237     , CigarData(other.CigarData)
238     , MateRefID(other.MateRefID)
239     , MatePosition(other.MatePosition)
240     , InsertSize(other.InsertSize)
241 { }
242
243 inline 
244 BamAlignment::~BamAlignment(void) { }
245
246 // Queries against alignment flags\r
247 inline bool BamAlignment::IsDuplicate(void) const         { return ( (AlignmentFlag & DUPLICATE)     != 0 ); }\r
248 inline bool BamAlignment::IsFailedQC(void) const          { return ( (AlignmentFlag & QC_FAILED)     != 0 ); }\r
249 inline bool BamAlignment::IsFirstMate(void) const         { return ( (AlignmentFlag & READ_1)        != 0 ); }\r
250 inline bool BamAlignment::IsMapped(void) const            { return ( (AlignmentFlag & UNMAPPED)      == 0 ); }\r
251 inline bool BamAlignment::IsMateMapped(void) const        { return ( (AlignmentFlag & MATE_UNMAPPED) == 0 ); }\r
252 inline bool BamAlignment::IsMateReverseStrand(void) const { return ( (AlignmentFlag & MATE_REVERSE)  != 0 ); }\r
253 inline bool BamAlignment::IsPaired(void) const            { return ( (AlignmentFlag & PAIRED)        != 0 ); }\r
254 inline bool BamAlignment::IsPrimaryAlignment(void) const  { return ( (AlignmentFlag & SECONDARY)     == 0 ); }\r
255 inline bool BamAlignment::IsProperPair(void) const        { return ( (AlignmentFlag & PROPER_PAIR)   != 0 ); }\r
256 inline bool BamAlignment::IsReverseStrand(void) const     { return ( (AlignmentFlag & REVERSE)       != 0 ); }\r
257 inline bool BamAlignment::IsSecondMate(void) const        { return ( (AlignmentFlag & READ_2)        != 0 ); }
258
259 // Manipulate alignment flags \r
260 inline void BamAlignment::SetIsDuplicate(bool ok)          { if (ok) AlignmentFlag |= DUPLICATE;     else AlignmentFlag &= ~DUPLICATE; }\r
261 inline void BamAlignment::SetIsFailedQC(bool ok)           { if (ok) AlignmentFlag |= QC_FAILED;     else AlignmentFlag &= ~QC_FAILED; }\r
262 inline void BamAlignment::SetIsFirstMate(bool ok)          { if (ok) AlignmentFlag |= READ_1;        else AlignmentFlag &= ~READ_1; }\r
263 inline void BamAlignment::SetIsMateUnmapped(bool ok)       { if (ok) AlignmentFlag |= MATE_UNMAPPED; else AlignmentFlag &= ~MATE_UNMAPPED; }\r
264 inline void BamAlignment::SetIsMateReverseStrand(bool ok)  { if (ok) AlignmentFlag |= MATE_REVERSE;  else AlignmentFlag &= ~MATE_REVERSE; }\r
265 inline void BamAlignment::SetIsPaired(bool ok)             { if (ok) AlignmentFlag |= PAIRED;        else AlignmentFlag &= ~PAIRED; }\r
266 inline void BamAlignment::SetIsProperPair(bool ok)         { if (ok) AlignmentFlag |= PROPER_PAIR;   else AlignmentFlag &= ~PROPER_PAIR; }\r
267 inline void BamAlignment::SetIsReverseStrand(bool ok)      { if (ok) AlignmentFlag |= REVERSE;       else AlignmentFlag &= ~REVERSE; }\r
268 inline void BamAlignment::SetIsSecondaryAlignment(bool ok) { if (ok) AlignmentFlag |= SECONDARY;     else AlignmentFlag &= ~SECONDARY; }\r
269 inline void BamAlignment::SetIsSecondMate(bool ok)         { if (ok) AlignmentFlag |= READ_2;        else AlignmentFlag &= ~READ_2; }\r
270 inline void BamAlignment::SetIsUnmapped(bool ok)           { if (ok) AlignmentFlag |= UNMAPPED;      else AlignmentFlag &= ~UNMAPPED; }
271
272 // calculates alignment end position, based on starting position and CIGAR operations
273 inline \r
274 int BamAlignment::GetEndPosition(bool usePadded) const {\r
275 \r
276     // initialize alignment end to starting position\r
277     int alignEnd = Position;\r
278 \r
279     // iterate over cigar operations\r
280     std::vector<CigarOp>::const_iterator cigarIter = CigarData.begin();\r
281     std::vector<CigarOp>::const_iterator cigarEnd  = CigarData.end();\r
282     for ( ; cigarIter != cigarEnd; ++cigarIter) {\r
283         const char cigarType = (*cigarIter).Type;\r
284         if ( cigarType == 'M' || cigarType == 'D' || cigarType == 'N' ) {\r
285             alignEnd += (*cigarIter).Length;\r
286         } 
287         else if ( usePadded && cigarType == 'I' ) {
288             alignEnd += (*cigarIter).Length;
289         }\r
290     }\r
291     return alignEnd;\r
292 }\r
293 \r
294 // get "NM" tag data - contributed by Aaron Quinlan
295 // stores data in 'editDistance', returns success/fail
296 inline \r
297 bool BamAlignment::GetEditDistance(uint8_t& editDistance) const {\r
298 \r
299     if ( TagData.empty() ) { return false; }\r
300 \r
301     // localize the tag data\r
302     char* pTagData = (char*)TagData.data();\r
303     const unsigned int tagDataLen = TagData.size();\r
304     unsigned int numBytesParsed = 0;\r
305 \r
306     bool foundEditDistanceTag = false;\r
307     while( numBytesParsed < tagDataLen ) {\r
308 \r
309         const char* pTagType = pTagData;\r
310         const char* pTagStorageType = pTagData + 2;\r
311         pTagData       += 3;\r
312         numBytesParsed += 3;\r
313 \r
314         // check the current tag\r
315         if ( strncmp(pTagType, "NM", 2) == 0 ) {\r
316             foundEditDistanceTag = true;\r
317             break;\r
318         }\r
319 \r
320         // get the storage class and find the next tag\r
321         if (*pTagStorageType == '\0') { return false; }\r
322         SkipToNextTag( *pTagStorageType, pTagData, numBytesParsed );\r
323         if (*pTagData == '\0') { return false; }\r
324     }\r
325     // return if the edit distance tag was not present\r
326     if ( !foundEditDistanceTag ) { return false; }\r
327 \r
328     // assign the editDistance value\r
329     std::memcpy(&editDistance, pTagData, 1);\r
330     return true;\r
331 }
332
333 // get "RG" tag data
334 // stores data in 'readGroup', returns success/fail
335 inline \r
336 bool BamAlignment::GetReadGroup(std::string& readGroup) const {\r
337 \r
338     if ( TagData.empty() ) { return false; }\r
339 \r
340     // localize the tag data\r
341     char* pTagData = (char*)TagData.data();\r
342     const unsigned int tagDataLen = TagData.size();\r
343     unsigned int numBytesParsed = 0;\r
344 \r
345     bool foundReadGroupTag = false;\r
346     while( numBytesParsed < tagDataLen ) {\r
347 \r
348         const char* pTagType = pTagData;\r
349         const char* pTagStorageType = pTagData + 2;\r
350         pTagData       += 3;\r
351         numBytesParsed += 3;\r
352 \r
353         // check the current tag\r
354         if ( std::strncmp(pTagType, "RG", 2) == 0 ) {\r
355             foundReadGroupTag = true;\r
356             break;\r
357         }\r
358 \r
359         // get the storage class and find the next tag\r
360         if (*pTagStorageType == '\0') { return false; }\r
361         SkipToNextTag( *pTagStorageType, pTagData, numBytesParsed );\r
362         if (*pTagData == '\0') { return false; }\r
363     }\r
364 \r
365     // return if the read group tag was not present\r
366     if ( !foundReadGroupTag ) { return false; }\r
367 \r
368     // assign the read group\r
369     const unsigned int readGroupLen = std::strlen(pTagData);\r
370     readGroup.resize(readGroupLen);\r
371     std::memcpy( (char*)readGroup.data(), pTagData, readGroupLen );\r
372     return true;\r
373 }
374
375 inline
376 void BamAlignment::SkipToNextTag(const char storageType, char* &pTagData, unsigned int& numBytesParsed) {\r
377     
378     switch(storageType) {\r
379 \r
380         case 'A':\r
381         case 'c':\r
382         case 'C':\r
383             ++numBytesParsed;\r
384             ++pTagData;\r
385             break;\r
386 \r
387         case 's':\r
388         case 'S':\r
389             numBytesParsed += 2;\r
390             pTagData       += 2;\r
391             break;\r
392 \r
393         case 'f':\r
394         case 'i':\r
395         case 'I':\r
396             numBytesParsed += 4;\r
397             pTagData       += 4;\r
398             break;\r
399 \r
400         case 'Z':\r
401         case 'H':\r
402             while(*pTagData) {\r
403                 ++numBytesParsed;\r
404                 ++pTagData;\r
405             }\r
406         // ---------------------------\r
407         // Added: 3-25-2010 DWB\r
408         // Contributed: ARQ\r
409         // Fixed: error parsing variable length tag data\r
410             ++pTagData;\r
411         // ---------------------------\r
412             break;\r
413 \r
414         default:\r
415             printf("ERROR: Unknown tag storage class encountered: [%c]\n", *pTagData);\r
416             exit(1);\r
417     }\r
418 }
419 \r
420 // ----------------------------------------------------------------\r
421 // Added: 3-35-2010 DWB\r
422 // Fixed: Routines to provide endian-correctness\r
423 // ----------------------------------------------------------------\r
424 \r
425 // returns true if system is big endian\r
426 inline bool SystemIsBigEndian(void) {\r
427    const uint16_t one = 0x0001;\r
428    return ((*(char*) &one) == 0 );\r
429 }\r
430 \r
431 // swaps endianness of 16-bit value 'in place'\r
432 inline void SwapEndian_16(int16_t& x) {\r
433     x = ((x >> 8) | (x << 8));\r
434 }
435 \r
436 inline void SwapEndian_16(uint16_t& x) {\r
437     x = ((x >> 8) | (x << 8));\r
438 }\r
439 \r
440 // swaps endianness of 32-bit value 'in-place'\r
441 inline void SwapEndian_32(int32_t& x) {\r
442     x = ( (x >> 24) | \r
443          ((x << 8) & 0x00FF0000) | \r
444          ((x >> 8) & 0x0000FF00) | \r
445           (x << 24)\r
446         );\r
447 }
448 \r
449 inline void SwapEndian_32(uint32_t& x) {\r
450     x = ( (x >> 24) | \r
451          ((x << 8) & 0x00FF0000) | \r
452          ((x >> 8) & 0x0000FF00) | \r
453           (x << 24)\r
454         );\r
455 }\r
456 \r
457 // swaps endianness of 64-bit value 'in-place'\r
458 inline void SwapEndian_64(int64_t& x) {\r
459     x = ( (x >> 56) | \r
460          ((x << 40) & 0x00FF000000000000ll) |\r
461          ((x << 24) & 0x0000FF0000000000ll) |\r
462          ((x << 8)  & 0x000000FF00000000ll) |\r
463          ((x >> 8)  & 0x00000000FF000000ll) |\r
464          ((x >> 24) & 0x0000000000FF0000ll) |\r
465          ((x >> 40) & 0x000000000000FF00ll) |\r
466           (x << 56)\r
467         );\r
468 }\r
469 \r
470 inline void SwapEndian_64(uint64_t& x) {\r
471     x = ( (x >> 56) | \r
472          ((x << 40) & 0x00FF000000000000ll) |\r
473          ((x << 24) & 0x0000FF0000000000ll) |\r
474          ((x << 8)  & 0x000000FF00000000ll) |\r
475          ((x >> 8)  & 0x00000000FF000000ll) |\r
476          ((x >> 24) & 0x0000000000FF0000ll) |\r
477          ((x >> 40) & 0x000000000000FF00ll) |\r
478           (x << 56)\r
479         );\r
480 }\r
481
482 // swaps endianness of 'next 2 bytes' in a char buffer (in-place)\r
483 inline void SwapEndian_16p(char* data) {\r
484     uint16_t& value = (uint16_t&)*data; \r
485     SwapEndian_16(value);\r
486 }\r
487
488 // swaps endianness of 'next 4 bytes' in a char buffer (in-place)\r
489 inline void SwapEndian_32p(char* data) {\r
490     uint32_t& value = (uint32_t&)*data; \r
491     SwapEndian_32(value);\r
492 }\r
493
494 // swaps endianness of 'next 8 bytes' in a char buffer (in-place)\r
495 inline void SwapEndian_64p(char* data) {\r
496     uint64_t& value = (uint64_t&)*data; \r
497     SwapEndian_64(value);\r
498 }\r
499 \r
500 } // namespace BamTools\r
501 \r
502 #endif // BAMAUX_H\r