Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on May 7th, 2012  |  syntax: None  |  size: 7.80 KB  |  hits: 14  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /**
  2. * Author:
  3. *       Pierre Lindenbaum PhD
  4. * Contact:
  5. *       plindenbaum@yahoo.fr
  6. * WWW:
  7. *       http://plindenbaum.blogspot.com
  8. * Reference:
  9. *      
  10. * Motivation:
  11. *       Mapping a large fasta file in memory using mmap
  12. * Usage:
  13. *       g++ -Wall testmmap.cpp -lz
  14. *       ./a.out -g /path/to/genome/indexed/with/samtools-faidx/hg.fasta file.fastq
  15. */
  16. #include <cstdio>
  17. #include <cstdlib>
  18. #include <cstring>
  19. #include <climits>
  20. #include <stdint.h>
  21. #include <cassert>
  22. #include <fstream>
  23. #include <iostream>
  24. #include <algorithm>
  25. #include <map>
  26. #include <cerrno>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <fcntl.h>
  30. #include <sys/mman.h>
  31. #include <zlib.h>
  32.  
  33. using namespace std;
  34.  
  35. /* it is the structure created by samtools faidx */
  36. typedef struct faidx1_t
  37.          {
  38.          int32_t line_len, line_blen;
  39.          int64_t len;
  40.          uint64_t offset;
  41.          }faidx1_t,*FaidxPtr;
  42.  
  43.  
  44. /** returns the anti parallele base */
  45. static const char antiparralele(char c)
  46.         {
  47.         switch(c)
  48.                 {
  49.                 case 'A': return 'T';
  50.                 case 'T': return 'A';
  51.                 case 'G': return 'C';
  52.                 case 'C': return 'G';
  53.                 default:return 'N';
  54.                 }
  55.         }
  56. /**
  57.  * wrapper for a mmap, a fileid and some faidx indexes
  58.  */
  59. class IndexedGenome
  60.         {
  61.         private:
  62.                 /* maps a chromosome to the samtools faidx index */
  63.                 map<string,faidx1_t> name2index;
  64.                 /* used to get the size of the file */
  65.                 struct stat buf;
  66.                 /* genome fasta file file descriptor */
  67.                 int fd;
  68.                 /* the mmap (memory mapped) pointer */
  69.                 char *mapptr;
  70.                 /** reads an fill a string */
  71.                 bool readline(gzFile in,string& line)
  72.                         {
  73.                         if(gzeof(in)) return false;
  74.                         line.clear();
  75.                         int c=-1;
  76.                         while((c=gzgetc(in))!=EOF && c!='\n') line+=(char)c;
  77.                         return true;
  78.                         }
  79.                
  80.         public:
  81.                 /** constructor
  82.                  * @param fasta: the path to the genomic fasta file indexed with samtools faidx
  83.                  */
  84.                 IndexedGenome(const char* fasta):fd(-1),mapptr(NULL)
  85.                         {
  86.                         string faidx(fasta);
  87.                         string line;
  88.                         faidx.append(".fai");
  89.                         /* open *.fai file */
  90.                         ifstream in(faidx.c_str(),ios::in);
  91.                         if(!in.is_open())
  92.                                 {
  93.                                 cerr << "cannot open " << faidx << endl;
  94.                                 exit(EXIT_FAILURE);
  95.                                 }
  96.                         /* read indexes in fai file */
  97.                         while(getline(in,line,'\n'))
  98.                                 {
  99.                                 if(line.empty()) continue;
  100.                                 const char* p=line.c_str();
  101.                                 char* tab=(char*)strchr(p,'\t');
  102.                                 if(tab==NULL) continue;
  103.                                 string chrom(p,tab-p);
  104.                                 ++tab;
  105.                                 faidx1_t index;
  106.                                 if(sscanf(tab,"%ld\t%ld\t%d\t%d",
  107.                                         &index.len, &index.offset, &index.line_blen,&index.line_len
  108.                                         )!=4)
  109.                                         {
  110.                                         cerr << "Cannot read index in "<< line << endl;
  111.                                         exit(EXIT_FAILURE);
  112.                                         }
  113.                                 /* insert in the map(chrom,faidx) */
  114.                                 name2index.insert(make_pair(chrom,index));
  115.                                 }
  116.                         /* close index file */
  117.                         in.close();
  118.                        
  119.                         /* get the whole size of the fasta file */
  120.                         if(stat(fasta, &buf)!=0)
  121.                                 {
  122.                                 perror("Cannot stat");
  123.                                 exit(EXIT_FAILURE);
  124.                                 }
  125.                        
  126.                         /* open the fasta file */
  127.                         fd = open(fasta, O_RDONLY);
  128.                          if (fd == -1)
  129.                                 {
  130.                                 perror("Error opening file for reading");
  131.                                 exit(EXIT_FAILURE);
  132.                                 }
  133.                         /* open a memory mapped file associated to this fasta file descriptor */
  134.                         mapptr = (char*)mmap(0, buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
  135.                         if (mapptr == MAP_FAILED)
  136.                                 {
  137.                                 close(fd);
  138.                                 perror("Error mmapping the file");
  139.                                 exit(EXIT_FAILURE);
  140.                                 }
  141.                         }
  142.                 /* destructor */
  143.                 ~IndexedGenome()
  144.                         {
  145.                         /* close memory mapped map */
  146.                         if(mapptr!=NULL && munmap(mapptr,buf.st_size) == -1)
  147.                                 {
  148.                                 perror("Error un-mmapping the file");
  149.                                 }
  150.                          /* dispose fasta file descriptor */
  151.                         if(fd!=-1) close(fd);
  152.                         }
  153.                        
  154.                 /* return the base at position 'index' for the chromosome indexed by faidx */
  155.                 char at(const FaidxPtr faidx,int64_t index)
  156.                         {
  157.                         long pos= faidx->offset +
  158.                                 index / faidx->line_blen * faidx->line_len +
  159.                                 index % faidx->line_blen
  160.                                 ;
  161.                         /* here is the magic: no need to fseek/fread/ftell the file */
  162.                         return toupper(mapptr[pos]);
  163.                         }
  164.                
  165.  
  166.         private:
  167.                  /* search a DNA string in the genome
  168.                   * simple Boyer-Moore-Horspool_algorithm hacked from
  169.                   * http://en.wikipedia.org/wiki/Boyer-Moore-Horspool_algorithm
  170.                   */
  171.                 void boyerMoore(const string& search)
  172.                         {
  173.                         size_t bad_char_skip[UCHAR_MAX + 1];
  174.                        
  175.                        
  176.                         if (search.empty()) return;
  177.                  
  178.                        
  179.                         for (size_t scan = 0; scan <= UCHAR_MAX; scan++)
  180.                                 {
  181.                                 bad_char_skip[scan] = search.size();
  182.                                 }
  183.                        
  184.                         size_t last = search.size() - 1;
  185.                  
  186.                        
  187.                         for (size_t scan = 0; scan < last; scan++)
  188.                                 {
  189.                                 bad_char_skip[(size_t)search[scan]] = last - scan;
  190.                                 }
  191.                         /* loop over each chromosome */
  192.                         for(std::map<std::string,faidx1_t>::iterator r=name2index.begin();
  193.                                 r!=name2index.end();
  194.                                 ++r)
  195.                                 {
  196.                                 faidx1_t& index=r->second;
  197.                                 size_t haystack_index=0;
  198.                                
  199.                                 while (haystack_index+ search.size() <= (size_t)index.len)
  200.                                         {
  201.                                        
  202.                                         size_t scan=last;
  203.                                         while(  at(&index,haystack_index+scan) == search[scan] &&
  204.                                                 scan>0)
  205.                                                 {
  206.                                                 --scan;
  207.                                                 }
  208.                                         if(scan==0)
  209.                                                 {
  210.                                                 cout  << search << "\t"<< r->first << "\t" << haystack_index << endl;
  211.                                                 haystack_index+=search.size();
  212.                                                 }
  213.                                         else
  214.                                                 {
  215.                                                 char c=at(&index,haystack_index+last);
  216.                                                 haystack_index += bad_char_skip[c];
  217.                                                 }
  218.                                         }
  219.                                 }
  220.                         }
  221.         public:
  222.                 /** read a fastq file an searches the sequences in the genome */
  223.                 void mapReads(gzFile in)
  224.                         {
  225.                         string name;
  226.                         string seq;
  227.                         string name2;
  228.                         string qual;
  229.  
  230.                         for(;;)
  231.                                 {
  232.                                 if(!readline(in,name)) break;
  233.                                 if(!readline(in,seq)) break;
  234.                                 if(!readline(in,name2)) break;
  235.                                 if(!readline(in,qual)) break;
  236.  
  237.                                 while(!seq.empty() && seq[0]=='N')
  238.                                         {
  239.                                         seq.erase(0,1);
  240.                                         }
  241.                                 while(!seq.empty() && seq[seq.size()-1]=='N')
  242.                                         {
  243.                                         seq.erase(seq.size()-1,1);
  244.                                         }
  245.                                
  246.                                 if(seq.size()<10) continue;
  247.                                
  248.                                 std::transform(seq.begin(), seq.end(),seq.begin(), ::toupper);
  249.                                 //search
  250.                                 boyerMoore(seq);
  251.                                 //rev-comp and search
  252.                                 reverse(seq.begin(),seq.end());
  253.                                 std::transform(seq.begin(), seq.end(),seq.begin(), ::antiparralele);
  254.                                 boyerMoore(seq);
  255.                                 }
  256.                         }
  257.                 };
  258.  
  259. int main(int argc,char** argv)
  260.         {
  261.         char* genomePath=NULL;
  262.         int optind=1;
  263.         while(optind < argc)
  264.                 {
  265.                 if(strcmp(argv[optind],"-h")==0)
  266.                         {
  267.                         fprintf(stderr,"%s: Pierre Lindenbaum PHD. 2010.\n",argv[0]);
  268.                         fprintf(stderr,"Compilation: %s at %s.\n",__DATE__,__TIME__);
  269.                         fprintf(stderr," -g <path/to/genome/indexed/with/samtools/faids> FASQ[stdin|files] \n");
  270.                         exit(EXIT_FAILURE);
  271.                         }
  272.                 else if(strcmp(argv[optind],"-g")==0 && optind+1<argc)
  273.                         {
  274.                         genomePath=argv[++optind];
  275.                         }
  276.                 else if(strcmp(argv[optind],"--")==0)
  277.                         {
  278.                         ++optind;
  279.                         break;
  280.                         }
  281.                 else if(argv[optind][0]=='-')
  282.                         {
  283.                         fprintf(stderr,"unknown option '%s'\n",argv[optind]);
  284.                         exit(EXIT_FAILURE);
  285.                         }
  286.                 else
  287.                         {
  288.                         break;
  289.                         }
  290.                 ++optind;
  291.                 }
  292.         if(genomePath==NULL)
  293.                 {
  294.                 cerr << "genome path missing"<< endl;
  295.                 return EXIT_FAILURE;
  296.                 }
  297.         /* open IndexedGenome */
  298.         IndexedGenome* genome=new IndexedGenome(genomePath);
  299.         if(optind==argc)       
  300.                 {
  301.                 /* read fastqs from stdin */
  302.                 genome->mapReads(gzdopen(fileno(stdin), "r"));
  303.                 }
  304.         else
  305.                 {
  306.                 /** loop over each fastq file */
  307.                 while(optind< argc)
  308.                         {
  309.                         char* filename=argv[optind++];
  310.                         errno=0;
  311.                         gzFile in=gzopen(filename,"rb");
  312.                         if(in==NULL)
  313.                                 {
  314.                                 cerr << argv[0]
  315.                                         << ": Cannot open "<< filename
  316.                                         << ":" << strerror(errno)
  317.                                         << endl;
  318.                                 exit(EXIT_FAILURE);
  319.                                 }
  320.                         genome->mapReads(in);
  321.                         gzclose(in);
  322.                         }
  323.                 }
  324.         /* dispose genome */
  325.         delete genome;
  326.         cerr << "Done." << endl;
  327.         return 0;
  328.         }