]> git.donarmstrong.com Git - bamtools.git/blob - BamAux.h
Fixed: off by 1 in BamWriter, variable tag parsing in BamAux, endian-correctness
[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: 29 March 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 \r
69     // Queries against alignment flag\r
70     public:\r
71         // Returns true if this read is a PCR duplicate (determined by external app)\r
72         bool IsDuplicate(void) const { return ( (AlignmentFlag & DUPLICATE) != 0 ); }\r
73         // Returns true if this read failed quality control (determined by external app)\r
74         bool IsFailedQC(void) const { return ( (AlignmentFlag & QC_FAILED) != 0 ); }\r
75         // Returns true if alignment is first mate on read\r
76         bool IsFirstMate(void) const { return ( (AlignmentFlag & READ_1) != 0 ); }\r
77         // Returns true if alignment is mapped\r
78         bool IsMapped(void) const { return ( (AlignmentFlag & UNMAPPED) == 0 ); }\r
79         // Returns true if alignment's mate is mapped\r
80         bool IsMateMapped(void) const { return ( (AlignmentFlag & MATE_UNMAPPED) == 0 ); }\r
81         // Returns true if alignment's mate mapped to reverse strand\r
82         bool IsMateReverseStrand(void) const { return ( (AlignmentFlag & MATE_REVERSE)  != 0 ); }\r
83         // Returns true if alignment part of paired-end read\r
84         bool IsPaired(void) const { return ( (AlignmentFlag & PAIRED) != 0 ); }\r
85         // Returns true if this position is primary alignment (determined by external app)\r
86         bool IsPrimaryAlignment(void) const  { return ( (AlignmentFlag & SECONDARY) == 0 ); }\r
87         // Returns true if alignment is part of read that satisfied paired-end resolution (determined by external app)\r
88         bool IsProperPair(void) const { return ( (AlignmentFlag & PROPER_PAIR) != 0 ); }\r
89         // Returns true if alignment mapped to reverse strand\r
90         bool IsReverseStrand(void) const { return ( (AlignmentFlag & REVERSE) != 0 ); }\r
91         // Returns true if alignment is second mate on read\r
92         bool IsSecondMate(void) const { return ( (AlignmentFlag & READ_2) != 0 ); }\r
93 \r
94     // Manipulate alignment flag\r
95     public:\r
96         // Sets "PCR duplicate" bit \r
97         void SetIsDuplicate(bool ok) { if (ok) AlignmentFlag |= DUPLICATE; else AlignmentFlag &= ~DUPLICATE; }\r
98         // Sets "failed quality control" bit\r
99         void SetIsFailedQC(bool ok) { if (ok) AlignmentFlag |= QC_FAILED; else AlignmentFlag &= ~QC_FAILED; }\r
100         // Sets "alignment is first mate" bit\r
101         void SetIsFirstMate(bool ok) { if (ok) AlignmentFlag |= READ_1; else AlignmentFlag &= ~READ_1; }\r
102         // Sets "alignment's mate is mapped" bit\r
103         void SetIsMateUnmapped(bool ok) { if (ok) AlignmentFlag |= MATE_UNMAPPED; else AlignmentFlag &= ~MATE_UNMAPPED; }\r
104         // Sets "alignment's mate mapped to reverse strand" bit\r
105         void SetIsMateReverseStrand(bool ok) { if (ok) AlignmentFlag |= MATE_REVERSE; else AlignmentFlag &= ~MATE_REVERSE; }\r
106         // Sets "alignment part of paired-end read" bit\r
107         void SetIsPaired(bool ok) { if (ok) AlignmentFlag |= PAIRED; else AlignmentFlag &= ~PAIRED; }\r
108         // Sets "alignment is part of read that satisfied paired-end resolution" bit\r
109         void SetIsProperPair(bool ok) { if (ok) AlignmentFlag |= PROPER_PAIR; else AlignmentFlag &= ~PROPER_PAIR; }\r
110         // Sets "alignment mapped to reverse strand" bit\r
111         void SetIsReverseStrand(bool ok) { if (ok) AlignmentFlag |= REVERSE; else AlignmentFlag &= ~REVERSE; }\r
112         // Sets "position is primary alignment (determined by external app)"\r
113         void SetIsSecondaryAlignment(bool ok)  { if (ok) AlignmentFlag |= SECONDARY; else AlignmentFlag &= ~SECONDARY; }\r
114         // Sets "alignment is second mate on read" bit\r
115         void SetIsSecondMate(bool ok) { if (ok) AlignmentFlag |= READ_2; else AlignmentFlag &= ~READ_2; }\r
116         // Sets "alignment is mapped" bit\r
117         void SetIsUnmapped(bool ok) { if (ok) AlignmentFlag |= UNMAPPED; else AlignmentFlag &= ~UNMAPPED; }\r
118 \r
119     public:\r
120 \r
121         // get "RG" tag data\r
122         bool GetReadGroup(std::string& readGroup) const {\r
123 \r
124             if ( TagData.empty() ) { return false; }\r
125 \r
126             // localize the tag data\r
127             char* pTagData = (char*)TagData.data();\r
128             const unsigned int tagDataLen = TagData.size();\r
129             unsigned int numBytesParsed = 0;\r
130 \r
131             bool foundReadGroupTag = false;\r
132             while( numBytesParsed < tagDataLen ) {\r
133 \r
134                 const char* pTagType = pTagData;\r
135                 const char* pTagStorageType = pTagData + 2;\r
136                 pTagData       += 3;\r
137                 numBytesParsed += 3;\r
138 \r
139                 // check the current tag\r
140                 if ( std::strncmp(pTagType, "RG", 2) == 0 ) {\r
141                     foundReadGroupTag = true;\r
142                     break;\r
143                 }\r
144 \r
145                 // get the storage class and find the next tag\r
146                 SkipToNextTag( *pTagStorageType, pTagData, numBytesParsed );\r
147             }\r
148 \r
149             // return if the read group tag was not present\r
150             if ( !foundReadGroupTag ) { return false; }\r
151 \r
152             // assign the read group\r
153             const unsigned int readGroupLen = std::strlen(pTagData);\r
154             readGroup.resize(readGroupLen);\r
155             std::memcpy( (char*)readGroup.data(), pTagData, readGroupLen );\r
156             return true;\r
157         }\r
158 \r
159         // get "NM" tag data - contributed by Aaron Quinlan\r
160         bool GetEditDistance(uint8_t& editDistance) const {\r
161 \r
162             if ( TagData.empty() ) { return false; }\r
163 \r
164             // localize the tag data\r
165             char* pTagData = (char*)TagData.data();\r
166             const unsigned int tagDataLen = TagData.size();\r
167             unsigned int numBytesParsed = 0;\r
168 \r
169             bool foundEditDistanceTag = false;\r
170             while( numBytesParsed < tagDataLen ) {\r
171 \r
172                 const char* pTagType = pTagData;\r
173                 const char* pTagStorageType = pTagData + 2;\r
174                 pTagData       += 3;\r
175                 numBytesParsed += 3;\r
176 \r
177                 // check the current tag\r
178                 if ( strncmp(pTagType, "NM", 2) == 0 ) {\r
179                     foundEditDistanceTag = true;\r
180                     break;\r
181                 }\r
182 \r
183                 // get the storage class and find the next tag\r
184                 SkipToNextTag( *pTagStorageType, pTagData, numBytesParsed );\r
185             }\r
186             // return if the edit distance tag was not present\r
187             if ( !foundEditDistanceTag ) { return false; }\r
188 \r
189             // assign the editDistance value\r
190             std::memcpy(&editDistance, pTagData, 1);\r
191             return true;\r
192         }\r
193 \r
194     private:\r
195         static void SkipToNextTag(const char storageType, char* &pTagData, unsigned int& numBytesParsed) {\r
196             switch(storageType) {\r
197 \r
198                 case 'A':\r
199                 case 'c':\r
200                 case 'C':\r
201                         ++numBytesParsed;\r
202                         ++pTagData;\r
203                         break;\r
204 \r
205                 case 's':\r
206                 case 'S':\r
207                 case 'f':\r
208                         numBytesParsed += 2;\r
209                         pTagData       += 2;\r
210                         break;\r
211 \r
212                 case 'i':\r
213                 case 'I':\r
214                         numBytesParsed += 4;\r
215                         pTagData       += 4;\r
216                         break;\r
217 \r
218                 case 'Z':\r
219                 case 'H':\r
220                         while(*pTagData) {\r
221                             ++numBytesParsed;\r
222                             ++pTagData;\r
223                         }\r
224                         // ---------------------------\r
225                         // Added: 3-25-2010 DWB\r
226                         // Contributed: ARQ\r
227                         // Fixed: error parsing variable length tag data\r
228                         ++pTagData;\r
229                         // ---------------------------\r
230                         break;\r
231 \r
232                 default:\r
233                         printf("ERROR: Unknown tag storage class encountered: [%c]\n", *pTagData);\r
234                         exit(1);\r
235             }\r
236         }\r
237 \r
238     // Data members\r
239     public:\r
240         std::string  Name;              // Read name\r
241         int32_t      Length;            // Query length\r
242         std::string  QueryBases;        // 'Original' sequence (as reported from sequencing machine)\r
243         std::string  AlignedBases;      // 'Aligned' sequence (includes any indels, padding, clipping)\r
244         std::string  Qualities;         // FASTQ qualities (ASCII characters, not numeric values)\r
245         std::string  TagData;           // Tag data (accessor methods will pull the requested information out)\r
246         int32_t      RefID;             // ID number for reference sequence\r
247         int32_t      Position;          // Position (0-based) where alignment starts\r
248         uint16_t     Bin;               // Bin in BAM file where this alignment resides\r
249         uint16_t     MapQuality;        // Mapping quality score\r
250         uint32_t     AlignmentFlag;     // Alignment bit-flag - see Is<something>() methods to query this value, SetIs<something>() methods to manipulate \r
251         std::vector<CigarOp> CigarData; // CIGAR operations for this alignment\r
252         int32_t      MateRefID;         // ID number for reference sequence where alignment's mate was aligned\r
253         int32_t      MatePosition;      // Position (0-based) where alignment's mate starts\r
254         int32_t      InsertSize;        // Mate-pair insert size\r
255 \r
256     // Alignment flag query constants\r
257     private:\r
258         enum { PAIRED        = 1,\r
259                PROPER_PAIR   = 2,\r
260                UNMAPPED      = 4,\r
261                MATE_UNMAPPED = 8,\r
262                REVERSE       = 16,\r
263                MATE_REVERSE  = 32,\r
264                READ_1        = 64,\r
265                READ_2        = 128,\r
266                SECONDARY     = 256,\r
267                QC_FAILED     = 512,\r
268                DUPLICATE     = 1024\r
269              };\r
270 };\r
271 \r
272 // ----------------------------------------------------------------\r
273 // Auxiliary data structs & typedefs\r
274 \r
275 struct CigarOp {\r
276     char     Type;   // Operation type (MIDNSHP)\r
277     uint32_t Length; // Operation length (number of bases)\r
278 };\r
279 \r
280 struct RefData {\r
281     // data members\r
282     std::string RefName;          // Name of reference sequence\r
283     int32_t     RefLength;        // Length of reference sequence\r
284     bool        RefHasAlignments; // True if BAM file contains alignments mapped to reference sequence\r
285     // constructor\r
286     RefData(void)\r
287         : RefLength(0)\r
288         , RefHasAlignments(false)\r
289     { }\r
290 };\r
291 \r
292 typedef std::vector<RefData>      RefVector;\r
293 typedef std::vector<BamAlignment> BamAlignmentVector;\r
294 \r
295 // ----------------------------------------------------------------\r
296 // Indexing structs & typedefs\r
297 \r
298 struct Chunk {\r
299     // data members\r
300     uint64_t Start;\r
301     uint64_t Stop;\r
302     // constructor\r
303     Chunk(const uint64_t& start = 0, const uint64_t& stop = 0)\r
304         : Start(start)\r
305         , Stop(stop)\r
306     { }\r
307 };\r
308 \r
309 inline\r
310 bool ChunkLessThan(const Chunk& lhs, const Chunk& rhs) {\r
311     return lhs.Start < rhs.Start;\r
312 }\r
313 \r
314 typedef std::vector<Chunk> ChunkVector;\r
315 typedef std::map<uint32_t, ChunkVector> BamBinMap;\r
316 typedef std::vector<uint64_t> LinearOffsetVector;\r
317 \r
318 struct ReferenceIndex {\r
319     // data members\r
320     BamBinMap Bins;\r
321     LinearOffsetVector Offsets;\r
322     // constructor\r
323     ReferenceIndex(const BamBinMap& binMap = BamBinMap(),\r
324                    const LinearOffsetVector& offsets = LinearOffsetVector())\r
325         : Bins(binMap)\r
326         , Offsets(offsets)\r
327     { }\r
328 };\r
329 \r
330 typedef std::vector<ReferenceIndex> BamIndex;\r
331 \r
332 // ----------------------------------------------------------------\r
333 // Added: 3-35-2010 DWB\r
334 // Fixed: Routines to provide endian-correctness\r
335 // ----------------------------------------------------------------\r
336 \r
337 // returns true if system is big endian\r
338 inline bool SystemIsBigEndian(void) {\r
339    const uint16_t one = 0x0001;\r
340    return ((*(char*) &one) == 0 );\r
341 }\r
342 \r
343 // swaps endianness of 16-bit value 'in place'\r
344 inline void SwapEndian_16(uint16_t& x) {\r
345     x = ((x >> 8) | (x << 8));\r
346 }\r
347 \r
348 // swaps endianness of 32-bit value 'in-place'\r
349 inline void SwapEndian_32(uint32_t& value) {\r
350     x = ( (x >> 24) | \r
351          ((x << 8) & 0x00FF0000) | \r
352          ((x >> 8) & 0x0000FF00) | \r
353           (x << 24)\r
354         );\r
355 }\r
356 \r
357 // swaps endianness of 64-bit value 'in-place'\r
358 inline void SwapEndian_64(uint64_t& value) {\r
359     x = ( (x >> 56) | \r
360          ((x << 40) & 0x00FF000000000000) |\r
361          ((x << 24) & 0x0000FF0000000000) |\r
362          ((x << 8)  & 0x000000FF00000000) |\r
363          ((x >> 8)  & 0x00000000FF000000) |\r
364          ((x >> 24) & 0x0000000000FF0000) |\r
365          ((x >> 40) & 0x000000000000FF00) |\r
366           (x << 56)\r
367         );\r
368 }\r
369 \r
370 inline void SwapEndian_16p(char* data) {\r
371     uint16_t& value = (uint16_t&)*data; \r
372     SwapEndian_16(value);\r
373 }\r
374 \r
375 inline void SwapEndian_32p(char* data) {\r
376     uint32_t& value = (uint32_t&)*data; \r
377     SwapEndian_32(value);\r
378 }\r
379 \r
380 inline void SwapEndian_64p(char* data) {\r
381     uint64_t& value = (uint64_t&)*data; \r
382     SwapEndian_64(value);\r
383 }\r
384 \r
385 } // namespace BamTools\r
386 \r
387 #endif // BAMAUX_H\r