Discussion:
[med-svn] [freebayes] branch master created (now 0bfee02)
Brad Chapman
2014-02-02 15:53:37 UTC
Permalink
This is an automated email from the git hooks/post-receive script.

chapmanb-guest pushed a change to branch master
in repository freebayes.

at 0bfee02 Add patches for intial clean and cmake dependency

This branch includes the following new commits:

new 55cd62b Imported Upstream version 0.9.10.11
new 70a2413 Add debian build files and patches
new 0bfee02 Add patches for intial clean and cmake dependency

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "adds" were already present in the repository and have only
been added to this reference.
--
Alioth's /git/debian-med/git-commit-notice on /srv/git.debian.org/git/debian-med/freebayes.git
Brad Chapman
2014-02-02 15:53:38 UTC
Permalink
This is an automated email from the git hooks/post-receive script.

chapmanb-guest pushed a commit to branch master
in repository freebayes.

commit 55cd62b94eb9de77abb213c863de8cefc05848d8
Author: chapmanb <***@50mail.com>
Date: Sun Feb 2 06:02:32 2014 -0500

Imported Upstream version 0.9.10.11
---
.gitignore | 11 +
.gitmodules | 9 +
LICENSE | 19 +
Makefile | 17 +
README.md | 422 +
bamtools/CMakeLists.txt | 65 +
bamtools/LICENSE | 22 +
bamtools/README | 60 +
bamtools/docs/Doxyfile | 1605 +
bamtools/src/CMakeLists.txt | 16 +
bamtools/src/ExportHeader.cmake | 27 +
bamtools/src/api/BamAlgorithms.h | 21 +
bamtools/src/api/BamAlignment.cpp | 1083 +
bamtools/src/api/BamAlignment.h | 638 +
bamtools/src/api/BamAux.h | 468 +
bamtools/src/api/BamConstants.h | 282 +
bamtools/src/api/BamIndex.h | 90 +
bamtools/src/api/BamMultiReader.cpp | 421 +
bamtools/src/api/BamMultiReader.h | 127 +
bamtools/src/api/BamReader.cpp | 383 +
bamtools/src/api/BamReader.h | 119 +
bamtools/src/api/BamWriter.cpp | 152 +
bamtools/src/api/BamWriter.h | 69 +
bamtools/src/api/CMakeLists.txt | 83 +
bamtools/src/api/IBamIODevice.h | 98 +
bamtools/src/api/SamConstants.h | 97 +
bamtools/src/api/SamHeader.cpp | 236 +
bamtools/src/api/SamHeader.h | 74 +
bamtools/src/api/SamProgram.cpp | 139 +
bamtools/src/api/SamProgram.h | 61 +
bamtools/src/api/SamProgramChain.cpp | 354 +
bamtools/src/api/SamProgramChain.h | 85 +
bamtools/src/api/SamReadGroup.cpp | 221 +
bamtools/src/api/SamReadGroup.h | 68 +
bamtools/src/api/SamReadGroupDictionary.cpp | 297 +
bamtools/src/api/SamReadGroupDictionary.h | 85 +
bamtools/src/api/SamSequence.cpp | 161 +
bamtools/src/api/SamSequence.h | 60 +
bamtools/src/api/SamSequenceDictionary.cpp | 301 +
bamtools/src/api/SamSequenceDictionary.h | 86 +
bamtools/src/api/algorithms/Sort.h | 335 +
bamtools/src/api/api_global.h | 21 +
bamtools/src/api/internal/CMakeLists.txt | 25 +
bamtools/src/api/internal/bam/BamHeader_p.cpp | 125 +
bamtools/src/api/internal/bam/BamHeader_p.h | 71 +
bamtools/src/api/internal/bam/BamMultiMerger_p.h | 266 +
bamtools/src/api/internal/bam/BamMultiReader_p.cpp | 872 +
bamtools/src/api/internal/bam/BamMultiReader_p.h | 105 +
.../internal/bam/BamRandomAccessController_p.cpp | 289 +
.../api/internal/bam/BamRandomAccessController_p.h | 94 +
bamtools/src/api/internal/bam/BamReader_p.cpp | 470 +
bamtools/src/api/internal/bam/BamReader_p.h | 119 +
bamtools/src/api/internal/bam/BamWriter_p.cpp | 475 +
bamtools/src/api/internal/bam/BamWriter_p.h | 73 +
bamtools/src/api/internal/bam/CMakeLists.txt | 19 +
.../src/api/internal/index/BamIndexFactory_p.cpp | 107 +
.../src/api/internal/index/BamIndexFactory_p.h | 49 +
.../src/api/internal/index/BamStandardIndex_p.cpp | 965 +
.../src/api/internal/index/BamStandardIndex_p.h | 237 +
.../src/api/internal/index/BamToolsIndex_p.cpp | 642 +
bamtools/src/api/internal/index/BamToolsIndex_p.h | 186 +
bamtools/src/api/internal/index/CMakeLists.txt | 17 +
.../src/api/internal/io/BamDeviceFactory_p.cpp | 37 +
bamtools/src/api/internal/io/BamDeviceFactory_p.h | 37 +
bamtools/src/api/internal/io/BamFile_p.cpp | 69 +
bamtools/src/api/internal/io/BamFile_p.h | 51 +
bamtools/src/api/internal/io/BamFtp_p.cpp | 490 +
bamtools/src/api/internal/io/BamFtp_p.h | 91 +
bamtools/src/api/internal/io/BamHttp_p.cpp | 544 +
bamtools/src/api/internal/io/BamHttp_p.h | 91 +
bamtools/src/api/internal/io/BamPipe_p.cpp | 69 +
bamtools/src/api/internal/io/BamPipe_p.h | 46 +
bamtools/src/api/internal/io/BgzfStream_p.cpp | 469 +
bamtools/src/api/internal/io/BgzfStream_p.h | 93 +
bamtools/src/api/internal/io/ByteArray_p.cpp | 111 +
bamtools/src/api/internal/io/ByteArray_p.h | 69 +
bamtools/src/api/internal/io/CMakeLists.txt | 48 +
bamtools/src/api/internal/io/HostAddress_p.cpp | 396 +
bamtools/src/api/internal/io/HostAddress_p.h | 100 +
bamtools/src/api/internal/io/HostInfo_p.cpp | 224 +
bamtools/src/api/internal/io/HostInfo_p.h | 76 +
bamtools/src/api/internal/io/HttpHeader_p.cpp | 395 +
bamtools/src/api/internal/io/HttpHeader_p.h | 132 +
bamtools/src/api/internal/io/ILocalIODevice_p.cpp | 56 +
bamtools/src/api/internal/io/ILocalIODevice_p.h | 50 +
bamtools/src/api/internal/io/NetUnix_p.h | 39 +
bamtools/src/api/internal/io/NetWin_p.h | 60 +
bamtools/src/api/internal/io/RollingBuffer_p.cpp | 314 +
bamtools/src/api/internal/io/RollingBuffer_p.h | 87 +
bamtools/src/api/internal/io/TcpSocketEngine_p.cpp | 196 +
bamtools/src/api/internal/io/TcpSocketEngine_p.h | 103 +
.../src/api/internal/io/TcpSocketEngine_unix_p.cpp | 216 +
.../src/api/internal/io/TcpSocketEngine_win_p.cpp | 241 +
bamtools/src/api/internal/io/TcpSocket_p.cpp | 430 +
bamtools/src/api/internal/io/TcpSocket_p.h | 128 +
bamtools/src/api/internal/sam/CMakeLists.txt | 17 +
.../src/api/internal/sam/SamFormatParser_p.cpp | 202 +
bamtools/src/api/internal/sam/SamFormatParser_p.h | 61 +
.../src/api/internal/sam/SamFormatPrinter_p.cpp | 219 +
bamtools/src/api/internal/sam/SamFormatPrinter_p.h | 59 +
.../src/api/internal/sam/SamHeaderValidator_p.cpp | 524 +
.../src/api/internal/sam/SamHeaderValidator_p.h | 105 +
bamtools/src/api/internal/sam/SamHeaderVersion_p.h | 134 +
bamtools/src/api/internal/utils/BamException_p.cpp | 15 +
bamtools/src/api/internal/utils/BamException_p.h | 51 +
bamtools/src/api/internal/utils/CMakeLists.txt | 15 +
bamtools/src/shared/bamtools_global.h | 97 +
bamtools/src/third_party/CMakeLists.txt | 9 +
bamtools/src/third_party/gtest-1.6.0/CHANGES | 130 +
.../src/third_party/gtest-1.6.0/CMakeLists.txt | 240 +
bamtools/src/third_party/gtest-1.6.0/CONTRIBUTORS | 37 +
bamtools/src/third_party/gtest-1.6.0/COPYING | 28 +
bamtools/src/third_party/gtest-1.6.0/README | 424 +
.../gtest-1.6.0/cmake/internal_utils.cmake | 216 +
.../gtest-1.6.0/fused-src/gtest/gtest-all.cc | 9118 ++
.../gtest-1.6.0/fused-src/gtest/gtest.h | 19537 ++++
.../gtest-1.6.0/fused-src/gtest/gtest_main.cc | 39 +
.../src/third_party/gtest-1.6.0/src/gtest-all.cc | 48 +
.../gtest-1.6.0/src/gtest-death-test.cc | 1234 +
.../third_party/gtest-1.6.0/src/gtest-filepath.cc | 380 +
.../gtest-1.6.0/src/gtest-internal-inl.h | 1038 +
.../src/third_party/gtest-1.6.0/src/gtest-port.cc | 746 +
.../third_party/gtest-1.6.0/src/gtest-printers.cc | 356 +
.../third_party/gtest-1.6.0/src/gtest-test-part.cc | 110 +
.../gtest-1.6.0/src/gtest-typed-test.cc | 110 +
bamtools/src/third_party/gtest-1.6.0/src/gtest.cc | 4898 +
.../src/third_party/gtest-1.6.0/src/gtest_main.cc | 39 +
bamtools/src/third_party/jsoncpp/CMakeLists.txt | 23 +
bamtools/src/third_party/jsoncpp/LICENSE | 55 +
bamtools/src/third_party/jsoncpp/json.h | 15 +
.../src/third_party/jsoncpp/json_batchallocator.h | 130 +
bamtools/src/third_party/jsoncpp/json_config.h | 42 +
bamtools/src/third_party/jsoncpp/json_features.h | 47 +
bamtools/src/third_party/jsoncpp/json_forwards.h | 42 +
.../src/third_party/jsoncpp/json_internalarray.inl | 453 +
.../src/third_party/jsoncpp/json_internalmap.inl | 612 +
bamtools/src/third_party/jsoncpp/json_reader.cpp | 870 +
bamtools/src/third_party/jsoncpp/json_reader.h | 201 +
bamtools/src/third_party/jsoncpp/json_tool.h | 93 +
bamtools/src/third_party/jsoncpp/json_value.cpp | 1701 +
bamtools/src/third_party/jsoncpp/json_value.h | 1059 +
.../src/third_party/jsoncpp/json_valueiterator.inl | 297 +
bamtools/src/third_party/jsoncpp/json_writer.cpp | 819 +
bamtools/src/third_party/jsoncpp/json_writer.h | 179 +
bamtools/src/toolkit/CMakeLists.txt | 44 +
bamtools/src/toolkit/bamtools.cpp | 163 +
bamtools/src/toolkit/bamtools_convert.cpp | 919 +
bamtools/src/toolkit/bamtools_convert.h | 37 +
bamtools/src/toolkit/bamtools_count.cpp | 210 +
bamtools/src/toolkit/bamtools_count.h | 37 +
bamtools/src/toolkit/bamtools_coverage.cpp | 197 +
bamtools/src/toolkit/bamtools_coverage.h | 37 +
bamtools/src/toolkit/bamtools_filter.cpp | 955 +
bamtools/src/toolkit/bamtools_filter.h | 37 +
bamtools/src/toolkit/bamtools_header.cpp | 141 +
bamtools/src/toolkit/bamtools_header.h | 38 +
bamtools/src/toolkit/bamtools_index.cpp | 126 +
bamtools/src/toolkit/bamtools_index.h | 37 +
bamtools/src/toolkit/bamtools_merge.cpp | 241 +
bamtools/src/toolkit/bamtools_merge.h | 37 +
bamtools/src/toolkit/bamtools_random.cpp | 285 +
bamtools/src/toolkit/bamtools_random.h | 37 +
bamtools/src/toolkit/bamtools_resolve.cpp | 1414 +
bamtools/src/toolkit/bamtools_resolve.h | 42 +
bamtools/src/toolkit/bamtools_revert.cpp | 194 +
bamtools/src/toolkit/bamtools_revert.h | 37 +
bamtools/src/toolkit/bamtools_sort.cpp | 375 +
bamtools/src/toolkit/bamtools_sort.h | 37 +
bamtools/src/toolkit/bamtools_split.cpp | 600 +
bamtools/src/toolkit/bamtools_split.h | 38 +
bamtools/src/toolkit/bamtools_stats.cpp | 307 +
bamtools/src/toolkit/bamtools_stats.h | 37 +
bamtools/src/toolkit/bamtools_tool.h | 35 +
bamtools/src/toolkit/bamtools_version.h.in | 20 +
bamtools/src/utils/CMakeLists.txt | 30 +
bamtools/src/utils/bamtools_fasta.cpp | 623 +
bamtools/src/utils/bamtools_fasta.h | 47 +
bamtools/src/utils/bamtools_filter_engine.h | 552 +
bamtools/src/utils/bamtools_filter_properties.h | 195 +
bamtools/src/utils/bamtools_filter_ruleparser.h | 319 +
bamtools/src/utils/bamtools_options.cpp | 287 +
bamtools/src/utils/bamtools_options.h | 213 +
bamtools/src/utils/bamtools_pileup_engine.cpp | 346 +
bamtools/src/utils/bamtools_pileup_engine.h | 95 +
bamtools/src/utils/bamtools_utilities.cpp | 333 +
bamtools/src/utils/bamtools_utilities.h | 65 +
bamtools/src/utils/bamtools_variant.h | 128 +
bamtools/src/utils/utils_global.h | 21 +
bin/.keep | 0
examples/pipeline.sh | 29 +
intervaltree/.gitignore | 1 +
intervaltree/IntervalTree.h | 215 +
intervaltree/LICENSE | 19 +
intervaltree/Makefile | 7 +
intervaltree/README | 41 +
intervaltree/interval_tree_test.cpp | 81 +
python/.gitignore | 1 +
python/README | 1 +
python/allelebayes.py | 373 +
python/dirichlet.py | 58 +
python/factorialln.py | 11 +
python/hwe.py | 64 +
python/logsumexp.py | 23 +
python/multiset.py | 140 +
python/phred.py | 17 +
scripts/fasta_generate_regions.py | 31 +
scripts/generate_freebayes_region_scripts.sh | 25 +
scripts/sam_add_rg.pl | 36 +
scripts/samples.cnv | 9 +
src/.gitignore | 4 +
src/Allele.cpp | 1529 +
src/Allele.h | 393 +
src/AlleleParser.cpp | 3986 +
src/AlleleParser.h | 351 +
src/BGZF.cpp | 398 +
src/BGZF.h | 320 +
src/BedReader.cpp | 75 +
src/BedReader.h | 75 +
src/Bias.cpp | 56 +
src/Bias.h | 30 +
src/CNV.cpp | 57 +
src/CNV.h | 33 +
src/Contamination.cpp | 76 +
src/Contamination.h | 39 +
src/DataLikelihood.cpp | 209 +
src/DataLikelihood.h | 47 +
src/Dirichlet.cpp | 62 +
src/Dirichlet.h | 8 +
src/Ewens.cpp | 51 +
src/Ewens.h | 25 +
src/Fasta.cpp | 308 +
src/Fasta.h | 73 +
src/Genotype.cpp | 1847 +
src/Genotype.h | 451 +
src/GenotypePriors.cpp | 181 +
src/GenotypePriors.h | 54 +
src/IndelAllele.cpp | 46 +
src/IndelAllele.h | 35 +
src/LargeFileSupport.h | 15 +
src/LeftAlign.cpp | 380 +
src/LeftAlign.h | 37 +
src/Makefile | 264 +
src/Marginals.cpp | 196 +
src/Marginals.h | 26 +
src/Multinomial.cpp | 39 +
src/Multinomial.h | 11 +
src/Parameters.cpp | 1069 +
src/Parameters.h | 139 +
src/Product.h | 19 +
src/Result.cpp | 6 +
src/Result.h | 25 +
src/ResultData.cpp | 612 +
src/ResultData.h | 69 +
src/Sample.cpp | 472 +
src/Sample.h | 137 +
src/SegfaultHandler.cpp | 17 +
src/SegfaultHandler.h | 11 +
src/Sum.h | 15 +
src/TryCatch.h | 16 +
src/Utility.cpp | 100758 ++++++++++++++++++
src/Utility.h | 154 +
src/Version.h | 2 +
src/alleles.cpp | 92 +
src/bamfiltertech.cpp | 109 +
src/bamleftalign.cpp | 197 +
src/convert.h | 22 +
src/dummy.cpp | 46 +
src/fastlz.c | 559 +
src/fastlz.h | 100 +
src/freebayes.cpp | 725 +
src/join.h | 24 +
src/levenshtein.cpp | 93 +
src/multichoose.h | 79 +
src/multipermute.h | 185 +
src/split.cpp | 33 +
src/split.h | 20 +
src/version_git.h | 4 +
src/version_release.txt | 5 +
ttmath/COPYRIGHT | 28 +
ttmath/ttmath.h | 2853 +
ttmath/ttmathbig.h | 6045 ++
ttmath/ttmathdec.h | 419 +
ttmath/ttmathint.h | 1922 +
ttmath/ttmathmisc.h | 250 +
ttmath/ttmathobjects.h | 809 +
ttmath/ttmathparser.h | 2777 +
ttmath/ttmaththreads.h | 250 +
ttmath/ttmathtypes.h | 676 +
ttmath/ttmathuint.h | 4165 +
ttmath/ttmathuint_noasm.h | 1017 +
ttmath/ttmathuint_x86.h | 1602 +
ttmath/ttmathuint_x86_64.h | 1146 +
ttmath/ttmathuint_x86_64_msvc.asm | 548 +
vcflib/.gitignore | 107 +
vcflib/.gitmodules | 21 +
vcflib/LICENSE | 19 +
vcflib/Makefile | 154 +
vcflib/README.md | 639 +
vcflib/bin/plot_roc.r | 153 +
vcflib/bin/vcf2sqlite.py | 130 +
vcflib/bin/vcf_strip_extra_headers | 18 +
vcflib/bin/vcfbiallelic | 20 +
vcflib/bin/vcfclearid | 12 +
vcflib/bin/vcfclearinfo | 12 +
vcflib/bin/vcfcomplex | 26 +
vcflib/bin/vcfgtcompare.sh | 16 +
vcflib/bin/vcfindelproximity | 82 +
vcflib/bin/vcfindels | 26 +
vcflib/bin/vcfmultiallelic | 20 +
vcflib/bin/vcfmultiway | 20 +
vcflib/bin/vcfmultiwayscripts | 30 +
vcflib/bin/vcfnobiallelicsnps | 25 +
vcflib/bin/vcfnoindels | 25 +
vcflib/bin/vcfnosnps | 25 +
vcflib/bin/vcfnulldotslashdot | 22 +
vcflib/bin/vcfplotaltdiscrepancy.r | 511 +
vcflib/bin/vcfplotaltdiscrepancy.sh | 12 +
vcflib/bin/vcfplotsitediscrepancy.r | 99 +
vcflib/bin/vcfplottstv.sh | 13 +
vcflib/bin/vcfprintaltdiscrepancy.r | 37 +
vcflib/bin/vcfprintaltdiscrepancy.sh | 18 +
vcflib/bin/vcfqualfilter | 32 +
vcflib/bin/vcfregionreduce | 29 +
vcflib/bin/vcfregionreduce_and_cut | 32 +
vcflib/bin/vcfregionreduce_pipe | 29 +
vcflib/bin/vcfregionreduce_uncompressed | 29 +
vcflib/bin/vcfsnps | 26 +
vcflib/bin/vcfsort | 3 +
vcflib/bin/vcfvarstats | 225 +
vcflib/fastahack/.gitignore | 2 +
vcflib/fastahack/Fasta.cpp | 317 +
vcflib/fastahack/Fasta.h | 82 +
vcflib/fastahack/FastaHack.cpp | 179 +
vcflib/fastahack/LargeFileSupport.h | 16 +
vcflib/fastahack/Makefile | 19 +
vcflib/fastahack/README | 63 +
vcflib/fastahack/Region.h | 51 +
vcflib/fastahack/disorder.c | 192 +
vcflib/fastahack/disorder.h | 62 +
vcflib/fastahack/libdisorder.LICENSE | 339 +
vcflib/fastahack/split.cpp | 33 +
vcflib/fastahack/split.h | 20 +
vcflib/fastahack/tests/correct.fasta | 30 +
vcflib/fastahack/tests/embedded_newline.fasta | 35 +
vcflib/fastahack/tests/mismatched_lines.fasta | 30 +
vcflib/fastahack/tests/trailing_newlines.fasta | 34 +
vcflib/filevercmp/Makefile | 13 +
vcflib/filevercmp/README.md | 39 +
vcflib/filevercmp/filevercmp.c | 180 +
vcflib/filevercmp/filevercmp.h | 42 +
vcflib/filevercmp/main.c | 15 +
vcflib/fsom/Makefile | 5 +
vcflib/fsom/README | 0
vcflib/fsom/convert.h | 22 +
vcflib/fsom/example.c | 121 +
vcflib/fsom/fsom.c | 988 +
vcflib/fsom/fsom.h | 87 +
vcflib/fsom/main.cpp | 272 +
vcflib/fsom/split.cpp | 33 +
vcflib/fsom/split.h | 20 +
vcflib/intervaltree/.gitignore | 1 +
vcflib/intervaltree/IntervalTree.h | 214 +
vcflib/intervaltree/Makefile | 7 +
vcflib/intervaltree/interval_tree_test.cpp | 81 +
vcflib/multichoose/.gitignore | 5 +
vcflib/multichoose/Makefile | 17 +
vcflib/multichoose/README | 40 +
vcflib/multichoose/multichoose.c | 53 +
vcflib/multichoose/multichoose.cpp | 66 +
vcflib/multichoose/multichoose.h | 79 +
vcflib/multichoose/multichoose.py | 55 +
vcflib/multichoose/multipermute.cpp | 66 +
vcflib/multichoose/multipermute.h | 132 +
vcflib/multichoose/multipermute.py | 98 +
vcflib/samples/sample.vcf | 31 +
vcflib/smithwaterman/.gitignore | 3 +
vcflib/smithwaterman/BandedSmithWaterman.cpp | 670 +
vcflib/smithwaterman/BandedSmithWaterman.h | 111 +
vcflib/smithwaterman/IndelAllele.cpp | 62 +
vcflib/smithwaterman/IndelAllele.h | 37 +
vcflib/smithwaterman/LeftAlign.cpp | 853 +
vcflib/smithwaterman/LeftAlign.h | 32 +
vcflib/smithwaterman/Makefile | 34 +
vcflib/smithwaterman/Mosaik.h | 73 +
vcflib/smithwaterman/Repeats.cpp | 69 +
vcflib/smithwaterman/Repeats.h | 8 +
vcflib/smithwaterman/SWMain.cpp | 126 +
vcflib/smithwaterman/SmithWatermanGotoh.cpp | 741 +
vcflib/smithwaterman/SmithWatermanGotoh.h | 101 +
vcflib/smithwaterman/convert.h | 22 +
vcflib/smithwaterman/disorder.c | 192 +
vcflib/smithwaterman/disorder.h | 62 +
vcflib/smithwaterman/examples.txt | 76 +
vcflib/smithwaterman/libdisorder.LICENSE | 339 +
vcflib/smithwaterman/smithwaterman.cpp | 246 +
vcflib/src/BedReader.h | 176 +
vcflib/src/Variant.cpp | 2199 +
vcflib/src/Variant.h | 495 +
vcflib/src/convert.h | 22 +
vcflib/src/join.h | 36 +
vcflib/src/mt19937ar.h | 192 +
vcflib/src/split.cpp | 23 +
vcflib/src/split.h | 53 +
vcflib/src/ssw.c | 834 +
vcflib/src/ssw.h | 129 +
vcflib/src/ssw_cpp.cpp | 399 +
vcflib/src/ssw_cpp.h | 216 +
vcflib/src/vcf2fasta.cpp | 264 +
vcflib/src/vcf2tsv.cpp | 241 +
vcflib/src/vcfaddinfo.cpp | 111 +
vcflib/src/vcfafpath.cpp | 52 +
vcflib/src/vcfallelicprimitives.cpp | 341 +
vcflib/src/vcfaltcount.cpp | 50 +
vcflib/src/vcfannotate.cpp | 126 +
vcflib/src/vcfannotategenotypes.cpp | 215 +
vcflib/src/vcfbreakmulti.cpp | 114 +
vcflib/src/vcfcat.cpp | 34 +
vcflib/src/vcfcheck.cpp | 139 +
vcflib/src/vcfclassify.cpp | 162 +
vcflib/src/vcfcleancomplex.cpp | 71 +
vcflib/src/vcfcombine.cpp | 179 +
vcflib/src/vcfcommonsamples.cpp | 85 +
vcflib/src/vcfcountalleles.cpp | 33 +
vcflib/src/vcfcreatemulti.cpp | 195 +
vcflib/src/vcfdistance.cpp | 85 +
vcflib/src/vcfecho.cpp | 31 +
vcflib/src/vcfentropy.cpp | 142 +
vcflib/src/vcfevenregions.cpp | 202 +
vcflib/src/vcffilter.cpp | 389 +
vcflib/src/vcffixup.cpp | 115 +
vcflib/src/vcfflatten.cpp | 178 +
vcflib/src/vcfgeno2alleles.cpp | 54 +
vcflib/src/vcfgeno2haplo.cpp | 391 +
vcflib/src/vcfgenosamplenames.cpp | 39 +
vcflib/src/vcfgenosummarize.cpp | 107 +
vcflib/src/vcfgenotypecompare.cpp | 327 +
vcflib/src/vcfgenotypes.cpp | 62 +
vcflib/src/vcfglxgt.cpp | 171 +
vcflib/src/vcfhetcount.cpp | 67 +
vcflib/src/vcfhethomratio.cpp | 66 +
vcflib/src/vcfintersect.cpp | 569 +
vcflib/src/vcfkeepgeno.cpp | 62 +
vcflib/src/vcfkeepinfo.cpp | 62 +
vcflib/src/vcfkeepsamples.cpp | 54 +
vcflib/src/vcfleftalign.cpp | 779 +
vcflib/src/vcflength.cpp | 39 +
vcflib/src/vcfnumalt.cpp | 55 +
vcflib/src/vcfoverlay.cpp | 115 +
vcflib/src/vcfparsealts.cpp | 42 +
vcflib/src/vcfprimers.cpp | 140 +
vcflib/src/vcfrandom.cpp | 70 +
vcflib/src/vcfrandomsample.cpp | 174 +
vcflib/src/vcfremap.cpp | 350 +
vcflib/src/vcfremoveaberrantgenotypes.cpp | 75 +
vcflib/src/vcfremovesamples.cpp | 76 +
vcflib/src/vcfroc.cpp | 469 +
vcflib/src/vcfsamplediff.cpp | 200 +
vcflib/src/vcfsamplenames.cpp | 29 +
vcflib/src/vcfsamplestats.cpp | 193 +
vcflib/src/vcfsitesummarize.cpp | 94 +
vcflib/src/vcfsom.cpp | 626 +
vcflib/src/vcfstats.cpp | 570 +
vcflib/src/vcfstreamsort.cpp | 143 +
vcflib/src/vcfuniq.cpp | 49 +
vcflib/src/vcfuniqalleles.cpp | 54 +
vcflib/tabixpp/ChangeLog | 593 +
vcflib/tabixpp/Makefile | 71 +
vcflib/tabixpp/NEWS | 126 +
vcflib/tabixpp/README | 6 +
vcflib/tabixpp/TabixReader.java | 395 +
vcflib/tabixpp/bam_endian.h | 42 +
vcflib/tabixpp/bedidx.c | 156 +
vcflib/tabixpp/bgzf.c | 714 +
vcflib/tabixpp/bgzf.h | 157 +
vcflib/tabixpp/bgzip.c | 206 +
vcflib/tabixpp/example.gtf.gz | Bin 0 -> 3778 bytes
vcflib/tabixpp/example.gtf.gz.tbi | Bin 0 -> 259 bytes
vcflib/tabixpp/index.c | 998 +
vcflib/tabixpp/khash.h | 486 +
vcflib/tabixpp/knetfile.c | 632 +
vcflib/tabixpp/knetfile.h | 75 +
vcflib/tabixpp/kseq.h | 227 +
vcflib/tabixpp/ksort.h | 271 +
vcflib/tabixpp/kstring.c | 165 +
vcflib/tabixpp/kstring.h | 68 +
vcflib/tabixpp/main.c | 290 +
vcflib/tabixpp/main.cpp | 47 +
vcflib/tabixpp/perl/MANIFEST | 8 +
vcflib/tabixpp/perl/Makefile.PL | 8 +
vcflib/tabixpp/perl/Tabix.pm | 76 +
vcflib/tabixpp/perl/Tabix.xs | 71 +
vcflib/tabixpp/perl/TabixIterator.pm | 41 +
vcflib/tabixpp/perl/t/01local.t | 28 +
vcflib/tabixpp/perl/t/02remote.t | 28 +
vcflib/tabixpp/perl/typemap | 3 +
vcflib/tabixpp/python/setup.py | 55 +
vcflib/tabixpp/python/tabixmodule.c | 408 +
vcflib/tabixpp/python/test.py | 91 +
vcflib/tabixpp/tabix.1 | 132 +
vcflib/tabixpp/tabix.cpp | 89 +
vcflib/tabixpp/tabix.h | 145 +
vcflib/tabixpp/tabix.hpp | 31 +
vcflib/tabixpp/tabix.py | 87 +
vcflib/tabixpp/tabix.tex | 121 +
504 files changed, 252572 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a68208e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+test/
+bugs/
+*.bak
+*.patch
+*~
+.Rhistory
+simulation
+src/libbamtools.a
+performance
+math
+.DS_Store
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..8b1bc04
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,9 @@
+[submodule "vcflib"]
+ path = vcflib
+ url = git://github.com/ekg/vcflib.git
+[submodule "bamtools"]
+ path = bamtools
+ url = git://github.com/pezmaster31/bamtools.git
+[submodule "intervaltree"]
+ path = intervaltree
+ url = git://github.com/ekg/intervaltree.git
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9e3322e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010 Erik Garrison, Gabor Marth
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c6d18b9
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+all:
+ cd src && $(MAKE)
+
+debug:
+ cd src && $(MAKE) debug
+
+install:
+ cp bin/freebayes bin/bamleftalign /usr/local/bin/
+
+uninstall:
+ rm /usr/local/bin/freebayes /usr/local/bin/bamleftalign
+
+clean:
+ cd src && $(MAKE) clean
+ rm -f bin/*
+
+.PHONY: all install uninstall clean
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3208718
--- /dev/null
+++ b/README.md
@@ -0,0 +1,422 @@
+# *freebayes*, a haplotype-based variant detector
+## user manual and guide
+
+### Erik Garrison <***@bc.edu>
+
+--------
+
+## Overview
+
+[*FreeBayes*](http://arxiv.org/abs/1207.3907) is a
+[Bayesian](http://en.wikipedia.org/wiki/Bayesian_inference) genetic variant
+detector designed to find small polymorphisms, specifically SNPs
+(single-nucleotide polymorphisms), indels (insertions and deletions), MNPs
+(multi-nucleotide polymorphisms), and complex events (composite insertion and
+substitution events) smaller than the length of a short-read sequencing
+alignment.
+
+*FreeBayes* is haplotype-based, in the sense that it calls variants based on
+the literal sequences of reads aligned to a particular target, not their
+precise alignment. This model is a straightforward generalization of previous
+ones (e.g. PolyBayes, samtools, GATK) which detect or report variants based on
+alignments. This method avoids one of the core problems with alignment-based
+variant detection--- that identical sequences may have multiple possible
+alignments:
+
+<img src="Loading Image..."
+width=500/>
+
+*FreeBayes* uses short-read alignments
+([BAM](http://samtools.sourceforge.net/SAMv1.pdf) files with
+[Phred+33](http://en.wikipedia.org/wiki/Phred_quality_score) encoded quality
+scores, now standard) for any number of individuals from a population and a
+[reference genome](http://en.wikipedia.org/wiki/Reference_genome) (in
+[FASTA](http://en.wikipedia.org/wiki/FASTA_format) format)
+to determine the most-likely combination of genotypes for the population at
+each position in the reference. It reports positions which it finds putatively
+polymorphic in variant call file ([VCF](http://www.1000genomes.org/node/101))
+format. It can also use an input set of variants (VCF) as a source of prior
+information, and a copy number variant map (BED) to define non-uniform ploidy
+variation across the samples under analysis.
+
+
+## Citing freebayes
+
+A preprint [Haplotype-based variant detection from short-read
+sequencing](http://arxiv.org/abs/1207.3907) provides an overview of the
+statistical models
+used in FreeBayes. We ask that you cite this paper if you use FreeBayes in
+work that leads to publication.
+
+If possible, please also refer to the version number provided by freebayes when
+it is run without arguments or with the `--help` option. For example, you
+should see something like this:
+
+ version: v0.9.10-3-g47a713e-dirty
+
+This provides both a point release number and a git commit id, which will
+ensure precise reproduction of results.
+
+
+## Obtaining
+
+To download FreeBayes, please use git to download the most recent development
+tree. Currently, the tree is hosted on github, and can be obtained via:
+
+ git clone --recursive git://github.com/ekg/freebayes.git
+
+Note the use of --recursive. This is required, as the project contains some
+nested git submodules for external repositories.
+
+### Resolving proxy issues with git
+
+Depending on your local network configuration, you may have problems obtaining
+freebayes via git. If you see something like this you may be behind a proxy
+that blocks access to standard git:// port (9418).
+
+ $ git clone --recursive git://github.com/ekg/freebayes.git
+ Cloning into 'freebayes'...
+ fatal: Unable to look up github.com (port 9418) (Name or service not known)
+
+Luckily, if you have access to https:// on port 443, then you can use this
+'magic' command as a workaround to enable download of the submodules:
+
+ git config --global url.https://github.com/.insteadOf git://github.com/
+
+
+## Compilation
+
+FreeBayes requires g++ and the standard C and C++ development libraries.
+Additionally, cmake is required for building the BamTools API.
+
+ make
+
+Will build the executable freebayes, as well as the utilities bamfiltertech and
+bamleftalign. These executables can be found in the `bin/` directory in the
+repository.
+
+Users may wish to install to e.g. /usr/local/bin (default), which is
+accomplished via
+
+ sudo make install
+
+
+## Usage
+
+In its simplest operation, freebayes requires only two inputs: a FASTA reference
+sequence, and a BAM-format alignment file sorted by reference position. For
+instance:
+
+ freebayes --fasta-reference h.sapiens.fasta NA20504.bam
+
+... produce (on standard output) a VCF file on standard out describing
+all SNPs, INDELs, MNPs, and Complex events between the reference and the
+alignments in NA20504.bam. In order to produce correct output, the reference
+supplied must be the reference to which NA20504.bam was aligned.
+
+Users may specify any number of BAM files on the command line. FreeBayes uses
+the [BamTools API](http://github.com/pezmaster31/bamtools) to open and parse
+these files in parallel, virtually merging them at runtime into one logical
+file with a merged header.
+
+For a description of available command-line options and their defaults, run:
+
+ freebayes --help
+
+
+## Calling variants
+
+You've sequenced some samples. You have a reference genome or assembled set of
+contigs, and you'd like to determine reference-relative variants in your
+samples. You can use freebayes to detect the variants, following these steps:
+
+* **Align** your reads to a suitable reference (e.g. with
+[bwa](http://bio-bwa.sourceforge.net/) or
+[MOSAIK](https://github.com/wanpinglee/MOSAIK))
+* Ensure your alignments have **read groups** attached so their sample may be
+identified by freebayes. Aligners allow you to do this, but you can also use
+[bamaddrg](http://github.com/ekg/bamaddrg) to do so post-alignment.
+* **Sort** the alignments (e.g. bamtools sort).
+* **Mark duplicates**, for instance with [samtools
+rmdup](http://samtools.sourceforge.net/) (if PCR was used in the preparation of
+your sequencing library)
+* ***Run freebayes*** on all your alignment data simultaneously, generating a
+VCF. The default settings should work for most use cases, but if your samples
+are not diploid, set the `--ploidy` and adjust the `--min-alternate-fraction`
+suitably.
+* **Filter** the output e.g. using reported QUAL and/or depth (DP) or
+observation count (AO).
+* **Interpret** your results.
+* (possibly, **Iterate** the variant detection process in response to insight
+gained from your interpretation)
+
+FreeBayes a standard VCF 4.1 outut stream. This format is designed for the
+probabilistic description of allelic variants within a population of samples,
+but it is equally suited to describing the probability of variation in a single
+sample.
+
+Of primary interest to most users is the QUAL field, which estimates the
+probability that there is a polymorphism at the loci described by the record.
+In freebayes, this value can be understood as 1 - P(locus is homozygous given
+the data). It is recommended that users use this value to filter their
+results, rather than accepting anything output by freebayes as ground truth.
+
+By default, records are output even if they have very low probability of
+variation, in expectation that the VCF will be filtered using tools such as
+[vcffilter](http://github.com/ekg/vcflib#vcffilter) in
+[vcflib](http://github.com/ekg/vcflib), which is also included in the
+repository under `vcflib/`. For instance,
+
+ freebayes -f ref.fa aln.bam | vcffilter -f "QUAL > 20" >results.vcf
+
+removes any sites with estimated probability of not being polymorphic less than
+phred 20 (aka 0.01), or probability of polymorphism &gt; 0.99.
+
+In simulation, the [receiver-operator
+characteristic](https://en.wikipedia.org/wiki/Receiver_operating_characteristic)
+ (ROC) tends to have a very sharp inflection between Q1 and Q30, depending on
+input data characteristics, and a filter setting in this range should provide
+decent performance. Users are encouraged to examine their output and both
+variants which are retained and those they filter out. Most problems tend to
+occur in low-depth areas, and so users may wish to remove these as well, which
+can also be done by filtering on the DP flag.
+
+
+## Calling variants in a population
+
+FreeBayes is designed to be run on many individuals from the same population
+(e.g. many human individuals) simultaneously. The algorithm exploits a neutral
+model of allele diffusion to impute most-confident genotypings
+across the entire population. In practice, the discriminant power of the
+method will improve if you run multiple samples simultaneously. In other
+words, if your
+study has multiple individuals, you should run freebayes against them at the
+same time. This also ensures consistent reporting of information about
+evidence for all samples at any locus where any are apparently polymorphic.
+
+To call variants in a population of samples, each alignment must have a read
+group identifier attached to it (RG tag), and the header of the BAM file in
+which it resides must map the RG tags to sample names (SM). Furthermore, read
+group IDs must be unique across all the files used in the analysis. One read
+group cannot map to multiple samples. The reason this is required is that
+freebayes operates on a virtually merged BAM stream provided by the BamTools
+API. If merging the files in your analysis using bamtools merge would generate
+a file in which multiple samples map to the same RG, the files are not suitable
+for use in population calling, and they must be modified.
+
+Users may add RG tags to BAM files which were generated without this
+information by using (as mentioned in "Calling variants" above)
+[bamaddrg](http://github.com/ekg/bamaddrg).
+If you have many files corresponding to
+many individuals, add a unique read group and sample name to each, and then
+open them all simultaneously with freebayes. The VCF output will have one
+column per sample in the input.
+
+
+## Observation filters and qualities
+
+### Input filters
+FreeBayes filters its input so as to ignore low-confidence alignments and
+alleles which are only supported by low-quality sequencing observations (see
+`--min-mapping-quality` and `--min-base-quality`). It also will only evaluate a
+position if at least one read has mapping quality of
+`--min-supporting-mapping-quality` and one allele has quality of at least
+`--min-supporting-base-quality`.
+
+Reads with more than a fixed number of high-quality mismatches can be excluded
+by specifying `--read-mismatch-limit`. This is meant as a workaround when
+mapping quality estimates are not appropriately calibrated.
+
+Reads marked as duplicates in the BAM file are ignored, but this can be
+disabled for testing purposes by providing `--use-duplicate-reads`. FreeBayes
+does not mark duplicates on its own, you must use another process to do this.
+
+### Observation thresholds
+As a guard against spurious variation caused by sequencing artifacts, positions
+are skipped when no more than `--min-alternate-count` or
+`--min-alternate-fraction`
+non-clonal observations of an alternate are found in one sample. These default
+to 2 and 0.2 respectively. The default setting of `--min-alternate-fraction
+0.2` is suitable for diploid samples but should be changed for ploidy > 2.
+
+### Allele type exclusion
+FreeBayes provides a few methods to ignore certain classes of allele, e.g.
+`--no-indels` and `--no-mnps`. Users are *strongly cautioned against using
+these*, because removing this information is very likely to reduce detection
+power. To generate a report only including SNPs, use vcffilter post-call as
+such:
+
+ freebayes ... | vcffilter -f "TYPE = snp"
+
+### Observation qualities
+
+FreeBayes estimates observation quality using several simple heuristics based
+on manipulations of the phred-scaled base qualities:
+
+* For single-base observations, *mismatches* and *reference observations*: the
+un-adjusted base quality provided in the BAM alignment record.
+* For *insertions*: the mean quality of the bases inside of the putatively
+inserted sequence.
+* For *deletions*: the mean quality of the bases flanking the putatively
+deleted sequence.
+* For *haplotypes*: the mean quality of allele observations within the
+haplotype.
+
+### Effective base depth
+
+By default, filters are left completely open, as both mapping quality and base
+quality are incorporated into the reported site quality (QUAL in the VCF) and
+genotype quality (GQ, when supplying `--genotype-qualities`). This integration
+is driven by the "Effective Base Depth" metric first developed in
+[snpTools](http://www.hgsc.bcm.edu/software/snptools), which scales observation
+quality by mapping quality. In short, *P(Obs|Genotype) ~
+P(MappedCorrectly(Obs))P(SequencedCorrectly(Obs))*.
+
+
+## Stream processing
+
+FreeBayes can read BAM from standard input `--stdin` instead of directly from
+files. This allows the application of any number of streaming BAM filters and
+calibrators to its input.
+
+ bam_merger.sh | streaming_filter_or_process.sh | freebayes --stdin ...
+
+This pattern allows the adjustment of alignments without rewriting BAM files,
+which could be expensive depending on context and available storage. A prime
+example of this would be graph-based realignment of reads to known variants as
+implemented in [glia](http://github.com/ekg/glia).
+
+Using this pattern, you can filter out reads with certain criteria using
+bamtools filter without having to modify the input BAM file. You can also use
+the bamtools API to write your own custom filters in C++. An example filter is
+bamfiltertech
+[src/bamfiltertech.cpp](http://github.com/ekg/freebayes/blob/master/src/bamfilte
+rtech.cpp), which could be used to filter out
+technologies which have characteristic errors which may frustrate certain types
+of variant detection.
+
+## INDELs
+
+In principle, any gapped aligner which is sensitive to indels will
+produce satisfactory input for use by freebayes. Due to potential ambiguity,
+indels are
+not parsed when they overlap the beginning or end of alignment boundaries.
+
+When calling indels, it is important to homogenize the positional distribution
+of insertions and deletions in the input by using left realignment. This is
+now done automatically by freebayes, but the behavior can be turned off via
+`--dont-left-align-indels` flag. You probably don't want to do this.
+
+Left realignment will place all indels in homopolymer and microsatellite
+repeats at the same position, provided that doing so does not introduce
+mismatches between the read and reference other than the indel. This method
+computationally inexpensive and handles the most common classes of alignment
+inconsistency.
+
+## Haplotype calls
+
+As freebayes is haplotype-based, left-alignment is necessary only for the
+determination of candidate polymorphic loci. Once such loci are determined,
+haplotype observations are extracted from reads where:
+
+1. putative variants lie within `--haplotype-window` bases of each other
+(default 3bp),
+2. the reference sequence has repeats (e.g. microsatellites or STRs are called
+as one haplotype),
+3. the haplotype which is called has Shannon entropy less than
+`--min-repeat-entropy`, which is off by default but can be set to ~1 for
+optimal genotyping of indels in lower-complexity sequence.
+
+After a haplotype window is determined by greedily expanding the window across
+overlapping haplotype observations, all reads overlapping the window are used
+to establish data likelihoods, *P(Observations|Genotype)*, for all haplotypes
+which have sufficient support to pass the input filters.
+
+Partial observations are considered to support those haplotypes which they
+could match exactly. For expedience, only haplotypes which are contiguously
+observed by the reads are considered as putative alleles in this process. This
+differs from other haplotype-based methods, such as
+[Platypus](http://www.well.ox.ac.uk/platypus), which consider all possible
+haplotypes composed of observed component alleles (SNPs, indels) in a given
+region when generating likelihoods.
+
+The primary adantages of this approach are conceptual simplicity and
+performance, and it is primarily limited in the case of short reads, an issue
+that is mitigated by increasing read lengths. Also, a hybrid approach must be
+used to call haplotypes from high-error rate long reads.
+
+### Re-genotyping known variants and calling long haplotypes
+
+For longer reads with higher error rates, it is possible to generate long
+haplotypes in two passes over the data. For instance, if we had very long
+reads (e.g. >10kb) at moderate depth and high error rate (>5%) such as might be
+produced by PacBio, we could do something like:
+
+ freebayes -f ref.fa aln.bam | vcffilter -f "QUAL > 20" >vars.vcf
+
+... thus generating candidate variants of suitable quality using the default
+detection window. We can then use these as "basis alleles" for the observation
+of haplotypes, considering all other putative variants supported by the
+alignment to be sequencing errors:
+
+ freebayes -f ref.fa --haplotype-window 500 \
+ --haplotype-basis-alleles vars.vcf aln.bam >haps.vcf
+
+These steps should allow us to read long haplotypes directly from input data
+with high error rates.
+
+The high error rate means that beyond a small window each read will contain a
+completely different literal haplotype. To a point, this property improves our
+signal to noise ratio and can effectively filter out sequencing errors at the
+point of the input filters, but it also decreases the effective observation
+depth will prevent the generation of any calls if a long `--haplotype-window`
+is combined with high a sequencing error rate.
+
+
+## Best practices and design philosophy
+
+FreeBayes follows the patterns suggested by the [Unix
+philosophy](https://en.wikipedia.org/wiki/Unix_philosophy), which promotes the
+development of simple, modular systems that perform a single function, and can
+be combined into more complex systems using stream processing of common
+interchange formats.
+
+FreeBayes incorporates a number of features in order to reduce the complexity
+of variant detection for researchers and developers:
+
+* **Indel realignment is accomplished internally** using a read-independent
+method, and issues resulting from discordant alignments are dramatically
+reducedy through the direct detection of haplotypes.
+* The need for **base quality recalibration is avoided** through the direct
+detection of haplotypes. Sequencing platform errors tend to cluster (e.g. at
+the ends of reads), and generate unique, non-repeating haplotypes at a given
+locus.
+* **Variant quality recalibration is avoided** by incorporating a number of
+metrics, such as read placement bias and allele balance, directly into the
+Bayesian model. (Our upcoming publication will discuss this in more detail.)
+
+A minimal pre-processing pipeline similar to that described in "Calling
+variants" should be sufficient for most uses. For more information, please
+refer to a recent post by Brad Chapman [on minimal BAM preprocessing
+methods](http://bcbio.wordpress.com/2013/10/21/updated-comparison-of-variant-det
+ection-methods-ensemble-freebayes-and-minimal-bam-preparation-pipelines/).
+
+For a push-button solution to variant detection, from reads to variant calls,
+look no further than the [gkno genome analysis platform](http://gkno.me/).
+
+
+## Support
+
+Please report any issues or questions to the [freebayes mailing
+list](https://groups.google.com/forum/#!forum/freebayes), [freebayes issue
+tracker](https://github.com/ekg/freebayes/issues), or by email to
+<***@bc.edu>.
+
+Note that if you encounter issues with the development HEAD and you would like
+a quick workaround for an issue that is likely to have been reintroduced
+recently, you can use `git checkout` to step back a few revisions.
+
+ git checkout [git-commit-id]
+
+It will also help with debugging to know if a problem has arisen in recent
+commits!
diff --git a/bamtools/CMakeLists.txt b/bamtools/CMakeLists.txt
new file mode 100644
index 0000000..9b97fa0
--- /dev/null
+++ b/bamtools/CMakeLists.txt
@@ -0,0 +1,65 @@
+# ==========================
+# BamTools CMakeLists.txt
+# (c) 2010 Derek Barnett
+#
+# top-level
+# ==========================
+
+# set project name
+project( BamTools )
+
+# Cmake requirements
+cmake_minimum_required( VERSION 2.6.4 )
+
+# Force the build directory to be different from source directory
+macro( ENSURE_OUT_OF_SOURCE_BUILD MSG )
+ string( COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" insource )
+ get_filename_component( PARENTDIR ${CMAKE_SOURCE_DIR} PATH )
+ string( COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${PARENTDIR}" insourcesubdir )
+ IF( insource OR insourcesubdir )
+ message( FATAL_ERROR "${MSG}" )
+ ENDIF( insource OR insourcesubdir )
+endmacro( ENSURE_OUT_OF_SOURCE_BUILD )
+
+ensure_out_of_source_build( "
+ ${PROJECT_NAME} requires an out of source build.
+ $ mkdir build
+ $ cd build
+ $ cmake ..
+ $ make
+(or the Windows equivalent)\n" )
+
+# set BamTools version information
+set( BamTools_VERSION_MAJOR 2 )
+set( BamTools_VERSION_MINOR 3 )
+set( BamTools_VERSION_BUILD 0 )
+
+# set our library and executable destination dirs
+set( EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin" )
+set( LIBRARY_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/lib" )
+
+# define compiler flags for all code
+set( CMAKE_BUILD_TYPE Release )
+add_definitions( -Wall -D_FILE_OFFSET_BITS=64 )
+
+# -----------------------------------------------
+# handle platform-/environment-specific defines
+
+# If planning to run in Node.js environment, run:
+# cmake -DEnableNodeJS=true
+if( EnableNodeJS )
+ add_definitions( -DSYSTEM_NODEJS=1 )
+endif()
+
+# If running on SunOS
+if( "${CMAKE_SYSTEM_NAME}" MATCHES "SunOS" )
+ add_definitions( -DSUN_OS )
+endif()
+
+# -------------------------------------------
+
+# add our includes root path
+include_directories( src )
+
+# list subdirectories to build in
+add_subdirectory( src )
diff --git a/bamtools/LICENSE b/bamtools/LICENSE
new file mode 100644
index 0000000..eaee1fd
--- /dev/null
+++ b/bamtools/LICENSE
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2009-2010 Derek Barnett, Erik Garrison, Gabor Marth, Michael Stromberg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/bamtools/README b/bamtools/README
new file mode 100644
index 0000000..498f4be
--- /dev/null
+++ b/bamtools/README
@@ -0,0 +1,60 @@
+--------------------------------------------------------------------------------
+README : BAMTOOLS
+--------------------------------------------------------------------------------
+
+BamTools provides both a programmer's API and an end-user's toolkit for handling
+BAM files.
+
+I. Learn More
+
+II. License
+
+III. Acknowledgements
+
+IV. Contact
+
+--------------------------------------------------------------------------------
+I. Learn More:
+--------------------------------------------------------------------------------
+
+Installation steps, tutorial, API documentation, etc. are all now available
+through the BamTools project wiki:
+
+https://github.com/pezmaster31/bamtools/wiki
+
+Join the mailing list(s) to stay informed of updates or get involved with
+contributing:
+
+https://github.com/pezmaster31/bamtools/wiki/Mailing-lists
+
+--------------------------------------------------------------------------------
+II. License :
+--------------------------------------------------------------------------------
+
+Both the BamTools API and toolkit are released under the MIT License.
+Copyright (c) 2009-2010 Derek Barnett, Erik Garrison, Gabor Marth,
+ Michael Stromberg
+
+See included file LICENSE for details.
+
+--------------------------------------------------------------------------------
+III. Acknowledgements :
+--------------------------------------------------------------------------------
+
+ * Aaron Quinlan for several key feature ideas and bug fix contributions
+ * Baptiste Lepilleur for the public-domain JSON parser (JsonCPP)
+ * Heng Li, author of SAMtools - the original C-language BAM API/toolkit.
+
+--------------------------------------------------------------------------------
+IV. Contact :
+--------------------------------------------------------------------------------
+
+Feel free to contact me with any questions, comments, suggestions, bug reports,
+ etc.
+
+Derek Barnett
+Marth Lab
+Biology Dept., Boston College
+
+Email: ***@gmail.com
+Project Website: http://github.com/pezmaster31/bamtools
diff --git a/bamtools/docs/Doxyfile b/bamtools/docs/Doxyfile
new file mode 100644
index 0000000..410ea27
--- /dev/null
+++ b/bamtools/docs/Doxyfile
@@ -0,0 +1,1605 @@
+# Doxyfile 1.6.3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = BamTools
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 2.3.0
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 1
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES = samSpecURL=http://samtools.sourceforge.net/SAM1.pdf
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set
+# FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page. This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = /home/derek/development/bamtools/src/api
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.d \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.idl \
+ *.odl \
+ *.cs \
+ *.php \
+ *.php3 \
+ *.inc \
+ *.m \
+ *.mm \
+ *.dox \
+ *.py \
+ *.f90 \
+ *.f \
+ *.vhd \
+ *.vhdl
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE = /home/derek/development/bamtools/src/api/internal
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS = BamTools::Internal \
+ BamTools::BamAlignment::BamAlignmentSupportData \
+ BamTools::RaiiBuffer \
+ UsesCharData \
+ sort_helper \
+ AlignmentSortBase
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = YES
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = NO
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = NO
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/bamtools/src/CMakeLists.txt b/bamtools/src/CMakeLists.txt
new file mode 100644
index 0000000..e359695
--- /dev/null
+++ b/bamtools/src/CMakeLists.txt
@@ -0,0 +1,16 @@
+# ==========================
+# BamTools CMakeLists.txt
+# (c) 2010 Derek Barnett
+#
+# src/
+# ==========================
+
+add_subdirectory( api )
+add_subdirectory( third_party )
+add_subdirectory( toolkit )
+add_subdirectory( utils )
+
+# export shared headers
+include( ExportHeader.cmake )
+set( SharedIncludeDir "shared" )
+ExportHeader( SharedHeaders shared/bamtools_global.h ${SharedIncludeDir} )
diff --git a/bamtools/src/ExportHeader.cmake b/bamtools/src/ExportHeader.cmake
new file mode 100644
index 0000000..ec62573
--- /dev/null
+++ b/bamtools/src/ExportHeader.cmake
@@ -0,0 +1,27 @@
+#
+# ExportHeader
+#
+
+function( ExportHeader MODULE FILE DEST )
+
+ # if haven't defined our custom 'build target'
+ # not exactly a build target, but lets this command get
+ # checked any time build step happens
+ if( NOT TARGET ${MODULE} )
+ add_custom_target( ${MODULE} ALL COMMENT "Exporting ${MODULE}" )
+ endif( NOT TARGET ${MODULE} )
+
+ # get the filename (without path)
+ get_filename_component( FILENAME "${FILE}" NAME )
+
+ # copy header to destination
+ add_custom_command( TARGET ${MODULE} COMMAND
+ ${CMAKE_COMMAND} -E copy_if_different
+ "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}"
+ "${CMAKE_SOURCE_DIR}/include/${DEST}/${FILENAME}" )
+
+ # make sure files are properly 'installed'
+ install( FILES "${FILE}" DESTINATION "include/bamtools/${DEST}" )
+
+endfunction( ExportHeader )
+
diff --git a/bamtools/src/api/BamAlgorithms.h b/bamtools/src/api/BamAlgorithms.h
new file mode 100644
index 0000000..6109412
--- /dev/null
+++ b/bamtools/src/api/BamAlgorithms.h
@@ -0,0 +1,21 @@
+// ***************************************************************************
+// BamAlgorithms.h (c) 2009 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// All rights reserved.
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides generic algorithms that are intended to work with BamTools data
+// structures. Where possible, these are intended to be STL-compatible.
+// ***************************************************************************
+
+#ifndef BAMALGORITHMS_H
+#define BAMALGORITHMS_H
+
+#include "api/algorithms/Sort.h"
+
+/*! \namespace BamTools::Algorithms
+ \brief Provides convenient classes & methods for working with BAM data
+*/
+
+#endif // BAM_ALGORITHMS_H
diff --git a/bamtools/src/api/BamAlignment.cpp b/bamtools/src/api/BamAlignment.cpp
new file mode 100644
index 0000000..620ba2e
--- /dev/null
+++ b/bamtools/src/api/BamAlignment.cpp
@@ -0,0 +1,1083 @@
+// ***************************************************************************
+// BamAlignment.cpp (c) 2009 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 4 December 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides the BamAlignment data structure
+// ***************************************************************************
+
+#include "api/BamAlignment.h"
+#include "api/BamConstants.h"
+using namespace BamTools;
+using namespace std;
+
+/*! \class BamTools::BamAlignment
+ \brief The main BAM alignment data structure.
+
+ Provides methods to query/modify BAM alignment data fields.
+*/
+/*! \var BamAlignment::Name
+ \brief read name
+*/
+/*! \var BamAlignment::Length
+ \brief length of query sequence
+*/
+/*! \var BamAlignment::QueryBases
+ \brief 'original' sequence (as reported from sequencing machine)
+
+ \note Setting this field to "*" indicates that the sequence is not to be stored on output.
+ In this case, the contents of the Qualities field should be invalidated as well (cleared or marked as "*").
+*/
+/*! \var BamAlignment::AlignedBases
+ \brief 'aligned' sequence (includes any indels, padding, clipping)
+
+ This field will be completely empty after reading from BamReader/BamMultiReader when
+ QueryBases is empty.
+*/
+/*! \var BamAlignment::Qualities
+ \brief FASTQ qualities (ASCII characters, not numeric values)
+
+ \note Setting this field to "*" indicates to BamWriter that the quality scores are not to be stored,
+ but instead will be output as a sequence of '0xFF'. Otherwise, QueryBases must not be a "*" and
+ the length of this field should equal the length of QueryBases.
+*/
+/*! \var BamAlignment::TagData
+ \brief tag data (use the provided methods to query/modify)
+*/
+/*! \var BamAlignment::RefID
+ \brief ID number for reference sequence
+*/
+/*! \var BamAlignment::Position
+ \brief position (0-based) where alignment starts
+*/
+/*! \var BamAlignment::Bin
+ \brief BAM (standard) index bin number for this alignment
+*/
+/*! \var BamAlignment::MapQuality
+ \brief mapping quality score
+*/
+/*! \var BamAlignment::AlignmentFlag
+ \brief alignment bit-flag (use the provided methods to query/modify)
+*/
+/*! \var BamAlignment::CigarData
+ \brief CIGAR operations for this alignment
+*/
+/*! \var BamAlignment::MateRefID
+ \brief ID number for reference sequence where alignment's mate was aligned
+*/
+/*! \var BamAlignment::MatePosition
+ \brief position (0-based) where alignment's mate starts
+*/
+/*! \var BamAlignment::InsertSize
+ \brief mate-pair insert size
+*/
+/*! \var BamAlignment::Filename
+ \brief name of BAM file which this alignment comes from
+*/
+
+/*! \fn BamAlignment::BamAlignment(void)
+ \brief constructor
+*/
+BamAlignment::BamAlignment(void)
+ : Length(0)
+ , RefID(-1)
+ , Position(-1)
+ , Bin(0)
+ , MapQuality(0)
+ , AlignmentFlag(0)
+ , MateRefID(-1)
+ , MatePosition(-1)
+ , InsertSize(0)
+{ }
+
+/*! \fn BamAlignment::BamAlignment(const BamAlignment& other)
+ \brief copy constructor
+*/
+BamAlignment::BamAlignment(const BamAlignment& other)
+ : Name(other.Name)
+ , Length(other.Length)
+ , QueryBases(other.QueryBases)
+ , AlignedBases(other.AlignedBases)
+ , Qualities(other.Qualities)
+ , TagData(other.TagData)
+ , RefID(other.RefID)
+ , Position(other.Position)
+ , Bin(other.Bin)
+ , MapQuality(other.MapQuality)
+ , AlignmentFlag(other.AlignmentFlag)
+ , CigarData(other.CigarData)
+ , MateRefID(other.MateRefID)
+ , MatePosition(other.MatePosition)
+ , InsertSize(other.InsertSize)
+ , Filename(other.Filename)
+ , SupportData(other.SupportData)
+{ }
+
+/*! \fn BamAlignment::~BamAlignment(void)
+ \brief destructor
+*/
+BamAlignment::~BamAlignment(void) { }
+
+/*! \fn bool BamAlignment::BuildCharData(void)
+ \brief Populates alignment string fields (read name, bases, qualities, tag data).
+
+ An alignment retrieved using BamReader::GetNextAlignmentCore() lacks this data.
+ Using that method makes parsing much quicker when only positional data is required.
+
+ However, if you later want to access the character data fields from such an alignment,
+ use this method to populate those fields. Provides ability to do 'lazy evaluation' of
+ alignment parsing.
+
+ \return \c true if character data populated successfully (or was already available to begin with)
+*/
+bool BamAlignment::BuildCharData(void) {
+
+ // skip if char data already parsed
+ if ( !SupportData.HasCoreOnly )
+ return true;
+
+ // check system endianness
+ bool IsBigEndian = BamTools::SystemIsBigEndian();
+
+ // calculate character lengths/offsets
+ const unsigned int dataLength = SupportData.BlockLength - Constants::BAM_CORE_SIZE;
+ const unsigned int seqDataOffset = SupportData.QueryNameLength + (SupportData.NumCigarOperations*4);
+ const unsigned int qualDataOffset = seqDataOffset + (SupportData.QuerySequenceLength+1)/2;
+ const unsigned int tagDataOffset = qualDataOffset + SupportData.QuerySequenceLength;
+ const unsigned int tagDataLength = dataLength - tagDataOffset;
+
+ // check offsets to see what char data exists
+ const bool hasSeqData = ( seqDataOffset < qualDataOffset );
+ const bool hasQualData = ( qualDataOffset < tagDataOffset );
+ const bool hasTagData = ( tagDataOffset < dataLength );
+
+ // store alignment name (relies on null char in name as terminator)
+ Name.assign(SupportData.AllCharData.data());
+
+ // save query sequence
+ QueryBases.clear();
+ if ( hasSeqData ) {
+ const char* seqData = SupportData.AllCharData.data() + seqDataOffset;
+ QueryBases.reserve(SupportData.QuerySequenceLength);
+ for ( size_t i = 0; i < SupportData.QuerySequenceLength; ++i ) {
+ const char singleBase = Constants::BAM_DNA_LOOKUP[ ( (seqData[(i/2)] >> (4*(1-(i%2)))) & 0xf ) ];
+ QueryBases.append(1, singleBase);
+ }
+ }
+
+ // save qualities
+
+ Qualities.clear();
+ if ( hasQualData ) {
+ const char* qualData = SupportData.AllCharData.data() + qualDataOffset;
+
+ // if marked as unstored (sequence of 0xFF) - don't do conversion, just fill with 0xFFs
+ if ( qualData[0] == (char)0xFF )
+ Qualities.resize(SupportData.QuerySequenceLength, (char)0xFF);
+
+ // otherwise convert from numeric QV to 'FASTQ-style' ASCII character
+ else {
+ Qualities.reserve(SupportData.QuerySequenceLength);
+ for ( size_t i = 0; i < SupportData.QuerySequenceLength; ++i )
+ Qualities.append(1, qualData[i]+33);
+ }
+ }
+
+ // clear previous AlignedBases
+ AlignedBases.clear();
+
+ // if QueryBases has data, build AlignedBases using CIGAR data
+ // otherwise, AlignedBases will remain empty (this case IS allowed)
+ if ( !QueryBases.empty() && QueryBases != "*" ) {
+
+ // resize AlignedBases
+ AlignedBases.reserve(SupportData.QuerySequenceLength);
+
+ // iterate over CigarOps
+ int k = 0;
+ vector<CigarOp>::const_iterator cigarIter = CigarData.begin();
+ vector<CigarOp>::const_iterator cigarEnd = CigarData.end();
+ for ( ; cigarIter != cigarEnd; ++cigarIter ) {
+ const CigarOp& op = (*cigarIter);
+
+ switch ( op.Type ) {
+
+ // for 'M', 'I', '=', 'X' - write bases
+ case (Constants::BAM_CIGAR_MATCH_CHAR) :
+ case (Constants::BAM_CIGAR_INS_CHAR) :
+ case (Constants::BAM_CIGAR_SEQMATCH_CHAR) :
+ case (Constants::BAM_CIGAR_MISMATCH_CHAR) :
+ AlignedBases.append(QueryBases.substr(k, op.Length));
+ // fall through
+
+ // for 'S' - soft clip, do not write bases
+ // but increment placeholder 'k'
+ case (Constants::BAM_CIGAR_SOFTCLIP_CHAR) :
+ k += op.Length;
+ break;
+
+ // for 'D' - write gap character
+ case (Constants::BAM_CIGAR_DEL_CHAR) :
+ AlignedBases.append(op.Length, Constants::BAM_DNA_DEL);
+ break;
+
+ // for 'P' - write padding character
+ case (Constants::BAM_CIGAR_PAD_CHAR) :
+ AlignedBases.append( op.Length, Constants::BAM_DNA_PAD );
+ break;
+
+ // for 'N' - write N's, skip bases in original query sequence
+ case (Constants::BAM_CIGAR_REFSKIP_CHAR) :
+ AlignedBases.append( op.Length, Constants::BAM_DNA_N );
+ break;
+
+ // for 'H' - hard clip, do nothing to AlignedBases, move to next op
+ case (Constants::BAM_CIGAR_HARDCLIP_CHAR) :
+ break;
+
+ // invalid CIGAR op-code
+ default:
+ const string message = string("invalid CIGAR operation type: ") + op.Type;
+ SetErrorString("BamAlignment::BuildCharData", message);
+ return false;
+ }
+ }
+ }
+
+ // save tag data
+ TagData.clear();
+ if ( hasTagData ) {
+
+ char* tagData = (((char*)SupportData.AllCharData.data()) + tagDataOffset);
+
+ if ( IsBigEndian ) {
+ size_t i = 0;
+ while ( i < tagDataLength ) {
+
+ i += Constants::BAM_TAG_TAGSIZE; // skip tag chars (e.g. "RG", "NM", etc.)
+ const char type = tagData[i]; // get tag type at position i
+ ++i; // move i past tag type
+
+ switch (type) {
+
+ case(Constants::BAM_TAG_TYPE_ASCII) :
+ case(Constants::BAM_TAG_TYPE_INT8) :
+ case(Constants::BAM_TAG_TYPE_UINT8) :
+ // no endian swapping necessary for single-byte data
+ ++i;
+ break;
+
+ case(Constants::BAM_TAG_TYPE_INT16) :
+ case(Constants::BAM_TAG_TYPE_UINT16) :
+ BamTools::SwapEndian_16p(&tagData[i]);
+ i += sizeof(uint16_t);
+ break;
+
+ case(Constants::BAM_TAG_TYPE_FLOAT) :
+ case(Constants::BAM_TAG_TYPE_INT32) :
+ case(Constants::BAM_TAG_TYPE_UINT32) :
+ BamTools::SwapEndian_32p(&tagData[i]);
+ i += sizeof(uint32_t);
+ break;
+
+ case(Constants::BAM_TAG_TYPE_HEX) :
+ case(Constants::BAM_TAG_TYPE_STRING) :
+ // no endian swapping necessary for hex-string/string data
+ while ( tagData[i] )
+ ++i;
+ // increment one more for null terminator
+ ++i;
+ break;
+
+ case(Constants::BAM_TAG_TYPE_ARRAY) :
+
+ {
+ // read array type
+ const char arrayType = tagData[i];
+ ++i;
+
+ // swap endian-ness of number of elements in place, then retrieve for loop
+ BamTools::SwapEndian_32p(&tagData[i]);
+ uint32_t numElements;
+ memcpy(&numElements, &tagData[i], sizeof(uint32_t));
+ i += sizeof(uint32_t);
+
+ // swap endian-ness of array elements
+ for ( size_t j = 0; j < numElements; ++j ) {
+ switch (arrayType) {
+ case (Constants::BAM_TAG_TYPE_INT8) :
+ case (Constants::BAM_TAG_TYPE_UINT8) :
+ // no endian-swapping necessary
+ ++i;
+ break;
+ case (Constants::BAM_TAG_TYPE_INT16) :
+ case (Constants::BAM_TAG_TYPE_UINT16) :
+ BamTools::SwapEndian_16p(&tagData[i]);
+ i += sizeof(uint16_t);
+ break;
+ case (Constants::BAM_TAG_TYPE_FLOAT) :
+ case (Constants::BAM_TAG_TYPE_INT32) :
+ case (Constants::BAM_TAG_TYPE_UINT32) :
+ BamTools::SwapEndian_32p(&tagData[i]);
+ i += sizeof(uint32_t);
+ break;
+ default:
+ const string message = string("invalid binary array type: ") + arrayType;
+ SetErrorString("BamAlignment::BuildCharData", message);
+ return false;
+ }
+ }
+
+ break;
+ }
+
+ // invalid tag type-code
+ default :
+ const string message = string("invalid tag type: ") + type;
+ SetErrorString("BamAlignment::BuildCharData", message);
+ return false;
+ }
+ }
+ }
+
+ // store tagData in alignment
+ TagData.resize(tagDataLength);
+ memcpy((char*)(TagData.data()), tagData, tagDataLength);
+ }
+
+ // clear core-only flag & return success
+ SupportData.HasCoreOnly = false;
+ return true;
+}
+
+/*! \fn bool BamAlignment::FindTag(const std::string& tag, char*& pTagData, const unsigned int& tagDataLength, unsigned int& numBytesParsed) const
+ \internal
+
+ Searches for requested tag in BAM tag data.
+
+ \param[in] tag requested 2-character tag name
+ \param[in,out] pTagData pointer to current position in BamAlignment::TagData
+ \param[in] tagDataLength length of BamAlignment::TagData
+ \param[in,out] numBytesParsed number of bytes parsed so far
+
+ \return \c true if found
+
+ \post If \a tag is found, \a pTagData will point to the byte where the tag data begins.
+ \a numBytesParsed will correspond to the position in the full TagData string.
+
+*/
+bool BamAlignment::FindTag(const std::string& tag,
+ char*& pTagData,
+ const unsigned int& tagDataLength,
+ unsigned int& numBytesParsed) const
+{
+
+ while ( numBytesParsed < tagDataLength ) {
+
+ const char* pTagType = pTagData;
+ const char* pTagStorageType = pTagData + 2;
+ pTagData += 3;
+ numBytesParsed += 3;
+
+ // check the current tag, return true on match
+ if ( strncmp(pTagType, tag.c_str(), 2) == 0 )
+ return true;
+
+ // get the storage class and find the next tag
+ if ( *pTagStorageType == '\0' ) return false;
+ if ( !SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) ) return false;
+ if ( *pTagData == '\0' ) return false;
+ }
+
+ // checked all tags, none match
+ return false;
+}
+
+/*! \fn bool BamAlignment::GetArrayTagType(const std::string& tag, char& type) const
+ \brief Retrieves the BAM tag type-code for the array elements associated with requested tag name.
+
+ \param[in] tag 2-character tag name
+ \param[out] type retrieved (1-character) type-code
+
+ \return \c true if found. False if not found, or if tag is not an array type.
+ \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
+*/
+bool BamAlignment::GetArrayTagType(const std::string& tag, char& type) const {
+
+ // skip if alignment is core-only
+ if ( SupportData.HasCoreOnly ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // skip if no tags present
+ if ( TagData.empty() ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // localize the tag data
+ char* pTagData = (char*)TagData.data();
+ const unsigned int tagDataLength = TagData.size();
+ unsigned int numBytesParsed = 0;
+
+ // if tag not found, return failure
+ if ( !FindTag(tag, pTagData, tagDataLength, numBytesParsed) ){
+ // TODO: set error string?
+ return false;
+ }
+
+ // check that tag type code is array
+ type = *(pTagData - 1);
+ if ( type != Constants::BAM_TAG_TYPE_ARRAY ) {
+ // TODO: set error string
+ return false;
+ }
+
+ // fetch element type
+ const char elementType = *pTagData;
+ switch ( elementType ) {
+
+ // allowable types
+ case (Constants::BAM_TAG_TYPE_INT8) :
+ case (Constants::BAM_TAG_TYPE_UINT8) :
+ case (Constants::BAM_TAG_TYPE_INT16) :
+ case (Constants::BAM_TAG_TYPE_UINT16) :
+ case (Constants::BAM_TAG_TYPE_INT32) :
+ case (Constants::BAM_TAG_TYPE_UINT32) :
+ case (Constants::BAM_TAG_TYPE_FLOAT) :
+ type = elementType;
+ break;
+
+ default:
+ //TODO: set error string
+ return false;
+ }
+
+ // if we get here, return success
+ return true;
+}
+
+
+/*! \fn int BamAlignment::GetEndPosition(bool usePadded = false, bool closedInterval = false) const
+ \brief Calculates alignment end position, based on its starting position and CIGAR data.
+
+ \warning The position returned now represents a zero-based, HALF-OPEN interval.
+ In previous versions of BamTools (0.x & 1.x) all intervals were treated
+ as zero-based, CLOSED.
+
+ \param[in] usePadded Allow inserted bases to affect the reported position. Default is
+ false, so that reported position stays synced with reference
+ coordinates.
+ \param[in] closedInterval Setting this to true will return a 0-based end coordinate. Default is
+ false, so that his value represents a standard, half-open interval.
+
+ \return alignment end position
+*/
+int BamAlignment::GetEndPosition(bool usePadded, bool closedInterval) const {
+
+ // initialize alignment end to starting position
+ int alignEnd = Position;
+
+ // iterate over cigar operations
+ vector<CigarOp>::const_iterator cigarIter = CigarData.begin();
+ vector<CigarOp>::const_iterator cigarEnd = CigarData.end();
+ for ( ; cigarIter != cigarEnd; ++cigarIter) {
+ const CigarOp& op = (*cigarIter);
+
+ switch ( op.Type ) {
+
+ // increase end position on CIGAR chars [DMXN=]
+ case Constants::BAM_CIGAR_DEL_CHAR :
+ case Constants::BAM_CIGAR_MATCH_CHAR :
+ case Constants::BAM_CIGAR_MISMATCH_CHAR :
+ case Constants::BAM_CIGAR_REFSKIP_CHAR :
+ case Constants::BAM_CIGAR_SEQMATCH_CHAR :
+ alignEnd += op.Length;
+ break;
+
+ // increase end position on insertion, only if @usePadded is true
+ case Constants::BAM_CIGAR_INS_CHAR :
+ if ( usePadded )
+ alignEnd += op.Length;
+ break;
+
+ // all other CIGAR chars do not affect end position
+ default :
+ break;
+ }
+ }
+
+ // adjust for closedInterval, if requested
+ if ( closedInterval )
+ alignEnd -= 1;
+
+ // return result
+ return alignEnd;
+}
+
+/*! \fn std::string BamAlignment::GetErrorString(void) const
+ \brief Returns a human-readable description of the last error that occurred
+
+ This method allows elimination of STDERR pollution. Developers of client code
+ may choose how the messages are displayed to the user, if at all.
+
+ \return error description
+*/
+std::string BamAlignment::GetErrorString(void) const {
+ return ErrorString;
+}
+
+/*! \fn bool BamAlignment::GetSoftClips(std::vector<int>& clipSizes, std::vector<int>& readPositions, std::vector<int>& genomePositions, bool usePadded = false) const
+ \brief Identifies if an alignment has a soft clip. If so, identifies the
+ sizes of the soft clips, as well as their positions in the read and reference.
+
+ \param[out] clipSizes vector of the sizes of each soft clip in the alignment
+ \param[out] readPositions vector of the 0-based read locations of each soft clip in the alignment.
+ These positions are basically indexes within the read, not genomic positions.
+ \param[out] genomePositions vector of the 0-based genome locations of each soft clip in the alignment
+ \param[in] usePadded inserted bases affect reported position. Default is false, so that
+ reported position stays 'sync-ed' with reference coordinates.
+
+ \return \c true if any soft clips were found in the alignment
+*/
+bool BamAlignment::GetSoftClips(vector<int>& clipSizes,
+ vector<int>& readPositions,
+ vector<int>& genomePositions,
+ bool usePadded) const
+{
+ // initialize positions & flags
+ int refPosition = Position;
+ int readPosition = 0;
+ bool softClipFound = false;
+ bool firstCigarOp = true;
+
+ // iterate over cigar operations
+ vector<CigarOp>::const_iterator cigarIter = CigarData.begin();
+ vector<CigarOp>::const_iterator cigarEnd = CigarData.end();
+ for ( ; cigarIter != cigarEnd; ++cigarIter) {
+ const CigarOp& op = (*cigarIter);
+
+ switch ( op.Type ) {
+
+ // increase both read & genome positions on CIGAR chars [DMXN=]
+ case Constants::BAM_CIGAR_DEL_CHAR :
+ case Constants::BAM_CIGAR_MATCH_CHAR :
+ case Constants::BAM_CIGAR_MISMATCH_CHAR :
+ case Constants::BAM_CIGAR_REFSKIP_CHAR :
+ case Constants::BAM_CIGAR_SEQMATCH_CHAR :
+ refPosition += op.Length;
+ readPosition += op.Length;
+ break;
+
+ // increase read position on insertion, genome position only if @usePadded is true
+ case Constants::BAM_CIGAR_INS_CHAR :
+ readPosition += op.Length;
+ if ( usePadded )
+ refPosition += op.Length;
+ break;
+
+ case Constants::BAM_CIGAR_SOFTCLIP_CHAR :
+
+ softClipFound = true;
+
+ //////////////////////////////////////////////////////////////////////////////
+ // if we are dealing with the *first* CIGAR operation
+ // for this alignment, we increment the read position so that
+ // the read and genome position of the clip are referring to the same base.
+ // For example, in the alignment below, the ref position would be 4, yet
+ // the read position would be 0. Thus, to "sync" the two,
+ // we need to increment the read position by the length of the
+ // soft clip.
+ // Read: ATCGTTTCGTCCCTGC
+ // Ref: GGGATTTCGTCCCTGC
+ // Cigar: SSSSMMMMMMMMMMMM
+ //
+ // NOTE: This only needs to be done if the soft clip is the _first_ CIGAR op.
+ //////////////////////////////////////////////////////////////////////////////
+ if ( firstCigarOp )
+ readPosition += op.Length;
+
+ // track the soft clip's size, read position, and genome position
+ clipSizes.push_back(op.Length);
+ readPositions.push_back(readPosition);
+ genomePositions.push_back(refPosition);
+
+ // any other CIGAR operations have no effect
+ default :
+ break;
+ }
+
+ // clear our "first pass" flag
+ firstCigarOp = false;
+ }
+
+ // return whether any soft clips found
+ return softClipFound;
+}
+
+/*! \fn std::vector<std::string> BamAlignment::GetTagNames(void) const
+ \brief Retrieves the BAM tag names.
+
+ When paired with GetTagType() and GetTag(), this method allows you
+ to iterate over an alignment's tag data without knowing the names (or types)
+ beforehand.
+
+ \return \c vector containing all tag names found (empty if none available)
+ \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
+*/
+std::vector<std::string> BamAlignment::GetTagNames(void) const {
+
+ std::vector<std::string> result;
+ if ( SupportData.HasCoreOnly || TagData.empty() )
+ return result;
+
+ char* pTagData = (char*)TagData.data();
+ const unsigned int tagDataLength = TagData.size();
+ unsigned int numBytesParsed = 0;
+ while ( numBytesParsed < tagDataLength ) {
+
+ // get current tag name & type
+ const char* pTagName = pTagData;
+ const char* pTagType = pTagData + 2;
+ pTagData += 3;
+ numBytesParsed +=3;
+
+ // store tag name
+ result.push_back( std::string(pTagName, 2) );
+
+ // find the next tag
+ if ( *pTagType == '\0' ) break;
+ if ( !SkipToNextTag(*pTagType, pTagData, numBytesParsed) ) break;
+ if ( *pTagData == '\0' ) break;
+ }
+
+ return result;
+}
+
+/*! \fn bool BamAlignment::GetTagType(const std::string& tag, char& type) const
+ \brief Retrieves the BAM tag type-code associated with requested tag name.
+
+ \param[in] tag 2-character tag name
+ \param[out] type retrieved (1-character) type-code
+
+ \return \c true if found
+ \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
+*/
+bool BamAlignment::GetTagType(const std::string& tag, char& type) const {
+
+ // skip if alignment is core-only
+ if ( SupportData.HasCoreOnly ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // skip if no tags present
+ if ( TagData.empty() ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // localize the tag data
+ char* pTagData = (char*)TagData.data();
+ const unsigned int tagDataLength = TagData.size();
+ unsigned int numBytesParsed = 0;
+
+ // if tag not found, return failure
+ if ( !FindTag(tag, pTagData, tagDataLength, numBytesParsed) ){
+ // TODO: set error string?
+ return false;
+ }
+
+ // otherwise, retrieve & validate tag type code
+ type = *(pTagData - 1);
+ switch ( type ) {
+ case (Constants::BAM_TAG_TYPE_ASCII) :
+ case (Constants::BAM_TAG_TYPE_INT8) :
+ case (Constants::BAM_TAG_TYPE_UINT8) :
+ case (Constants::BAM_TAG_TYPE_INT16) :
+ case (Constants::BAM_TAG_TYPE_UINT16) :
+ case (Constants::BAM_TAG_TYPE_INT32) :
+ case (Constants::BAM_TAG_TYPE_UINT32) :
+ case (Constants::BAM_TAG_TYPE_FLOAT) :
+ case (Constants::BAM_TAG_TYPE_STRING) :
+ case (Constants::BAM_TAG_TYPE_HEX) :
+ case (Constants::BAM_TAG_TYPE_ARRAY) :
+ return true;
+
+ // unknown tag type
+ default:
+ const string message = string("invalid tag type: ") + type;
+ SetErrorString("BamAlignment::GetTagType", message);
+ return false;
+ }
+}
+
+/*! \fn bool BamAlignment::HasTag(const std::string& tag) const
+ \brief Returns true if alignment has a record for requested tag.
+
+ \param[in] tag 2-character tag name
+ \return \c true if alignment has a record for tag
+*/
+bool BamAlignment::HasTag(const std::string& tag) const {
+
+ // return false if no tag data present
+ if ( SupportData.HasCoreOnly || TagData.empty() )
+ return false;
+
+ // localize the tag data for lookup
+ char* pTagData = (char*)TagData.data();
+ const unsigned int tagDataLength = TagData.size();
+ unsigned int numBytesParsed = 0;
+
+ // if result of tag lookup
+ return FindTag(tag, pTagData, tagDataLength, numBytesParsed);
+}
+
+/*! \fn bool BamAlignment::IsDuplicate(void) const
+ \return \c true if this read is a PCR duplicate
+*/
+bool BamAlignment::IsDuplicate(void) const {
+ return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_DUPLICATE) != 0 );
+}
+
+/*! \fn bool BamAlignment::IsFailedQC(void) const
+ \return \c true if this read failed quality control
+*/
+bool BamAlignment::IsFailedQC(void) const {
+ return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_QC_FAILED) != 0 );
+}
+
+/*! \fn bool BamAlignment::IsFirstMate(void) const
+ \return \c true if alignment is first mate on paired-end read
+*/
+bool BamAlignment::IsFirstMate(void) const {
+ return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_READ_1) != 0 );
+}
+
+/*! \fn bool BamAlignment::IsMapped(void) const
+ \return \c true if alignment is mapped
+*/
+bool BamAlignment::IsMapped(void) const {
+ return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_UNMAPPED) == 0 );
+}
+
+/*! \fn bool BamAlignment::IsMateMapped(void) const
+ \return \c true if alignment's mate is mapped
+*/
+bool BamAlignment::IsMateMapped(void) const {
+ return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_MATE_UNMAPPED) == 0 );
+}
+
+/*! \fn bool BamAlignment::IsMateReverseStrand(void) const
+ \return \c true if alignment's mate mapped to reverse strand
+*/
+bool BamAlignment::IsMateReverseStrand(void) const {
+ return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_MATE_REVERSE_STRAND) != 0 );
+}
+
+/*! \fn bool BamAlignment::IsPaired(void) const
+ \return \c true if alignment part of paired-end read
+*/
+bool BamAlignment::IsPaired(void) const {
+ return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_PAIRED) != 0 );
+}
+
+/*! \fn bool BamAlignment::IsPrimaryAlignment(void) const
+ \return \c true if reported position is primary alignment
+*/
+bool BamAlignment::IsPrimaryAlignment(void) const {
+ return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_SECONDARY) == 0 );
+}
+
+/*! \fn bool BamAlignment::IsProperPair(void) const
+ \return \c true if alignment is part of read that satisfied paired-end resolution
+*/
+bool BamAlignment::IsProperPair(void) const {
+ return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_PROPER_PAIR) != 0 );
+}
+
+/*! \fn bool BamAlignment::IsReverseStrand(void) const
+ \return \c true if alignment mapped to reverse strand
+*/
+bool BamAlignment::IsReverseStrand(void) const {
+ return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_REVERSE_STRAND) != 0 );
+}
+
+/*! \fn bool BamAlignment::IsSecondMate(void) const
+ \return \c true if alignment is second mate on read
+*/
+bool BamAlignment::IsSecondMate(void) const {
+ return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_READ_2) != 0 );
+}
+
+/*! \fn bool BamAlignment::IsValidSize(const std::string& tag, const std::string& type) const
+ \internal
+
+ Checks that tag name & type strings are expected sizes.
+
+ \param tag[in] BAM tag name
+ \param type[in] BAM tag type-code
+ \return \c true if both input strings are valid sizes
+*/
+bool BamAlignment::IsValidSize(const std::string& tag, const std::string& type) const {
+ return (tag.size() == Constants::BAM_TAG_TAGSIZE) &&
+ (type.size() == Constants::BAM_TAG_TYPESIZE);
+}
+
+/*! \fn void BamAlignment::RemoveTag(const std::string& tag)
+ \brief Removes field from BAM tags.
+
+ \param[in] tag 2-character name of field to remove
+*/
+void BamAlignment::RemoveTag(const std::string& tag) {
+
+ // if char data not populated, do that first
+ if ( SupportData.HasCoreOnly )
+ BuildCharData();
+
+ // skip if no tags available
+ if ( TagData.empty() )
+ return;
+
+ // localize the tag data
+ char* pOriginalTagData = (char*)TagData.data();
+ char* pTagData = pOriginalTagData;
+ const unsigned int originalTagDataLength = TagData.size();
+ unsigned int newTagDataLength = 0;
+ unsigned int numBytesParsed = 0;
+
+ // skip if tag not found
+ if ( !FindTag(tag, pTagData, originalTagDataLength, numBytesParsed) )
+ return;
+
+ // otherwise, remove it
+ RaiiBuffer newTagData(originalTagDataLength);
+
+ // copy original tag data up til desired tag
+ pTagData -= 3;
+ numBytesParsed -= 3;
+ const unsigned int beginningTagDataLength = numBytesParsed;
+ newTagDataLength += beginningTagDataLength;
+ memcpy(newTagData.Buffer, pOriginalTagData, numBytesParsed);
+
+ // attemp to skip to next tag
+ const char* pTagStorageType = pTagData + 2;
+ pTagData += 3;
+ numBytesParsed += 3;
+ if ( SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) ) {
+
+ // squeeze remaining tag data
+ const unsigned int skippedDataLength = (numBytesParsed - beginningTagDataLength);
+ const unsigned int endTagDataLength = originalTagDataLength - beginningTagDataLength - skippedDataLength;
+ memcpy(newTagData.Buffer + beginningTagDataLength, pTagData, endTagDataLength );
+
+ // save modified tag data in alignment
+ TagData.assign(newTagData.Buffer, beginningTagDataLength + endTagDataLength);
+ }
+}
+
+/*! \fn void BamAlignment::SetErrorString(const std::string& where, const std::string& what) const
+ \internal
+
+ Sets a formatted error string for this alignment.
+
+ \param[in] where class/method where error occurred
+ \param[in] what description of error
+*/
+void BamAlignment::SetErrorString(const std::string& where, const std::string& what) const {
+ static const string SEPARATOR = ": ";
+ ErrorString = where + SEPARATOR + what;
+}
+
+/*! \fn void BamAlignment::SetIsDuplicate(bool ok)
+ \brief Sets value of "PCR duplicate" flag to \a ok.
+*/
+void BamAlignment::SetIsDuplicate(bool ok) {
+ if (ok) AlignmentFlag |= Constants::BAM_ALIGNMENT_DUPLICATE;
+ else AlignmentFlag &= ~Constants::BAM_ALIGNMENT_DUPLICATE;
+}
+
+/*! \fn void BamAlignment::SetIsFailedQC(bool ok)
+ \brief Sets "failed quality control" flag to \a ok.
+*/
+void BamAlignment::SetIsFailedQC(bool ok) {
+ if (ok) AlignmentFlag |= Constants::BAM_ALIGNMENT_QC_FAILED;
+ else AlignmentFlag &= ~Constants::BAM_ALIGNMENT_QC_FAILED;
+}
+
+/*! \fn void BamAlignment::SetIsFirstMate(bool ok)
+ \brief Sets "alignment is first mate" flag to \a ok.
+*/
+void BamAlignment::SetIsFirstMate(bool ok) {
+ if (ok) AlignmentFlag |= Constants::BAM_ALIGNMENT_READ_1;
+ else AlignmentFlag &= ~Constants::BAM_ALIGNMENT_READ_1;
+}
+
+/*! \fn void BamAlignment::SetIsMapped(bool ok)
+ \brief Sets "alignment is mapped" flag to \a ok.
+*/
+void BamAlignment::SetIsMapped(bool ok) {
+ if (ok) AlignmentFlag &= ~Constants::BAM_ALIGNMENT_UNMAPPED;
+ else AlignmentFlag |= Constants::BAM_ALIGNMENT_UNMAPPED;
+}
+
+/*! \fn void BamAlignment::SetIsMateMapped(bool ok)
+ \brief Sets "alignment's mate is mapped" flag to \a ok.
+*/
+void BamAlignment::SetIsMateMapped(bool ok) {
+ if (ok) AlignmentFlag &= ~Constants::BAM_ALIGNMENT_MATE_UNMAPPED;
+ else AlignmentFlag |= Constants::BAM_ALIGNMENT_MATE_UNMAPPED;
+}
+
+/*! \fn void BamAlignment::SetIsMateReverseStrand(bool ok)
+ \brief Sets "alignment's mate mapped to reverse strand" flag to \a ok.
+*/
+void BamAlignment::SetIsMateReverseStrand(bool ok) {
+ if (ok) AlignmentFlag |= Constants::BAM_ALIGNMENT_MATE_REVERSE_STRAND;
+ else AlignmentFlag &= ~Constants::BAM_ALIGNMENT_MATE_REVERSE_STRAND;
+}
+
+/*! \fn void BamAlignment::SetIsPaired(bool ok)
+ \brief Sets "alignment part of paired-end read" flag to \a ok.
+*/
+void BamAlignment::SetIsPaired(bool ok) {
+ if (ok) AlignmentFlag |= Constants::BAM_ALIGNMENT_PAIRED;
+ else AlignmentFlag &= ~Constants::BAM_ALIGNMENT_PAIRED;
+}
+
+/*! \fn void BamAlignment::SetIsPrimaryAlignment(bool ok)
+ \brief Sets "position is primary alignment" flag to \a ok.
+*/
+void BamAlignment::SetIsPrimaryAlignment(bool ok) {
+ if (ok) AlignmentFlag &= ~Constants::BAM_ALIGNMENT_SECONDARY;
+ else AlignmentFlag |= Constants::BAM_ALIGNMENT_SECONDARY;
+}
+
+/*! \fn void BamAlignment::SetIsProperPair(bool ok)
+ \brief Sets "alignment is part of read that satisfied paired-end resolution" flag to \a ok.
+*/
+void BamAlignment::SetIsProperPair(bool ok) {
+ if (ok) AlignmentFlag |= Constants::BAM_ALIGNMENT_PROPER_PAIR;
+ else AlignmentFlag &= ~Constants::BAM_ALIGNMENT_PROPER_PAIR;
+}
+
+/*! \fn void BamAlignment::SetIsReverseStrand(bool ok)
+ \brief Sets "alignment mapped to reverse strand" flag to \a ok.
+*/
+void BamAlignment::SetIsReverseStrand(bool ok) {
+ if (ok) AlignmentFlag |= Constants::BAM_ALIGNMENT_REVERSE_STRAND;
+ else AlignmentFlag &= ~Constants::BAM_ALIGNMENT_REVERSE_STRAND;
+}
+
+/*! \fn void BamAlignment::SetIsSecondMate(bool ok)
+ \brief Sets "alignment is second mate on read" flag to \a ok.
+*/
+void BamAlignment::SetIsSecondMate(bool ok) {
+ if (ok) AlignmentFlag |= Constants::BAM_ALIGNMENT_READ_2;
+ else AlignmentFlag &= ~Constants::BAM_ALIGNMENT_READ_2;
+}
+
+/*! \fn bool BamAlignment::SkipToNextTag(const char storageType, char*& pTagData, unsigned int& numBytesParsed) const
+ \internal
+
+ Moves to next available tag in tag data string
+
+ \param[in] storageType BAM tag type-code that determines how far to move cursor
+ \param[in,out] pTagData pointer to current position (cursor) in tag string
+ \param[in,out] numBytesParsed report of how many bytes were parsed (cumulatively)
+
+ \return \c if storageType was a recognized BAM tag type
+
+ \post \a pTagData will point to the byte where the next tag data begins.
+ \a numBytesParsed will correspond to the cursor's position in the full TagData string.
+*/
+bool BamAlignment::SkipToNextTag(const char storageType,
+ char*& pTagData,
+ unsigned int& numBytesParsed) const
+{
+ switch (storageType) {
+
+ case (Constants::BAM_TAG_TYPE_ASCII) :
+ case (Constants::BAM_TAG_TYPE_INT8) :
+ case (Constants::BAM_TAG_TYPE_UINT8) :
+ ++numBytesParsed;
+ ++pTagData;
+ break;
+
+ case (Constants::BAM_TAG_TYPE_INT16) :
+ case (Constants::BAM_TAG_TYPE_UINT16) :
+ numBytesParsed += sizeof(uint16_t);
+ pTagData += sizeof(uint16_t);
+ break;
+
+ case (Constants::BAM_TAG_TYPE_FLOAT) :
+ case (Constants::BAM_TAG_TYPE_INT32) :
+ case (Constants::BAM_TAG_TYPE_UINT32) :
+ numBytesParsed += sizeof(uint32_t);
+ pTagData += sizeof(uint32_t);
+ break;
+
+ case (Constants::BAM_TAG_TYPE_STRING) :
+ case (Constants::BAM_TAG_TYPE_HEX) :
+ while( *pTagData ) {
+ ++numBytesParsed;
+ ++pTagData;
+ }
+ // increment for null-terminator
+ ++numBytesParsed;
+ ++pTagData;
+ break;
+
+ case (Constants::BAM_TAG_TYPE_ARRAY) :
+
+ {
+ // read array type
+ const char arrayType = *pTagData;
+ ++numBytesParsed;
+ ++pTagData;
+
+ // read number of elements
+ int32_t numElements;
+ memcpy(&numElements, pTagData, sizeof(uint32_t)); // already endian-swapped, if needed
+ numBytesParsed += sizeof(uint32_t);
+ pTagData += sizeof(uint32_t);
+
+ // calculate number of bytes to skip
+ int bytesToSkip = 0;
+ switch (arrayType) {
+ case (Constants::BAM_TAG_TYPE_INT8) :
+ case (Constants::BAM_TAG_TYPE_UINT8) :
+ bytesToSkip = numElements;
+ break;
+ case (Constants::BAM_TAG_TYPE_INT16) :
+ case (Constants::BAM_TAG_TYPE_UINT16) :
+ bytesToSkip = numElements*sizeof(uint16_t);
+ break;
+ case (Constants::BAM_TAG_TYPE_FLOAT) :
+ case (Constants::BAM_TAG_TYPE_INT32) :
+ case (Constants::BAM_TAG_TYPE_UINT32) :
+ bytesToSkip = numElements*sizeof(uint32_t);
+ break;
+ default:
+ const string message = string("invalid binary array type: ") + arrayType;
+ SetErrorString("BamAlignment::SkipToNextTag", message);
+ return false;
+ }
+
+ // skip binary array contents
+ numBytesParsed += bytesToSkip;
+ pTagData += bytesToSkip;
+ break;
+ }
+
+ default:
+ const string message = string("invalid tag type: ") + storageType;
+ SetErrorString("BamAlignment::SkipToNextTag", message);
+ return false;
+ }
+
+ // if we get here, tag skipped OK - return success
+ return true;
+}
diff --git a/bamtools/src/api/BamAlignment.h b/bamtools/src/api/BamAlignment.h
new file mode 100644
index 0000000..0f4fe73
--- /dev/null
+++ b/bamtools/src/api/BamAlignment.h
@@ -0,0 +1,638 @@
+// ***************************************************************************
+// BamAlignment.h (c) 2009 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 25 July 2013 (DB)
+// ---------------------------------------------------------------------------
+// Provides the BamAlignment data structure
+// ***************************************************************************
+
+#ifndef BAMALIGNMENT_H
+#define BAMALIGNMENT_H
+
+#include "api/api_global.h"
+#include "api/BamAux.h"
+#include "api/BamConstants.h"
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <vector>
+
+namespace BamTools {
+
+//! \cond
+// forward declaration of BamAlignment's "friends"
+namespace Internal {
+ class BamReaderPrivate;
+ class BamWriterPrivate;
+} // namespace Internal
+//! \endcond
+
+// BamAlignment data structure
+struct API_EXPORT BamAlignment {
+
+ // constructors & destructor
+ public:
+ BamAlignment(void);
+ BamAlignment(const BamAlignment& other);
+ ~BamAlignment(void);
+
+ // queries against alignment flags
+ public:
+ bool IsDuplicate(void) const; // returns true if this read is a PCR duplicate
+ bool IsFailedQC(void) const; // returns true if this read failed quality control
+ bool IsFirstMate(void) const; // returns true if alignment is first mate on read
+ bool IsMapped(void) const; // returns true if alignment is mapped
+ bool IsMateMapped(void) const; // returns true if alignment's mate is mapped
+ bool IsMateReverseStrand(void) const; // returns true if alignment's mate mapped to reverse strand
+ bool IsPaired(void) const; // returns true if alignment part of paired-end read
+ bool IsPrimaryAlignment(void) const; // returns true if reported position is primary alignment
+ bool IsProperPair(void) const; // returns true if alignment is part of read that satisfied paired-end resolution
+ bool IsReverseStrand(void) const; // returns true if alignment mapped to reverse strand
+ bool IsSecondMate(void) const; // returns true if alignment is second mate on read
+
+ // manipulate alignment flags
+ public:
+ void SetIsDuplicate(bool ok); // sets value of "PCR duplicate" flag
+ void SetIsFailedQC(bool ok); // sets value of "failed quality control" flag
+ void SetIsFirstMate(bool ok); // sets value of "alignment is first mate" flag
+ void SetIsMapped(bool ok); // sets value of "alignment is mapped" flag
+ void SetIsMateMapped(bool ok); // sets value of "alignment's mate is mapped" flag
+ void SetIsMateReverseStrand(bool ok); // sets value of "alignment's mate mapped to reverse strand" flag
+ void SetIsPaired(bool ok); // sets value of "alignment part of paired-end read" flag
+ void SetIsPrimaryAlignment(bool ok); // sets value of "position is primary alignment" flag
+ void SetIsProperPair(bool ok); // sets value of "alignment is part of read that satisfied paired-end resolution" flag
+ void SetIsReverseStrand(bool ok); // sets value of "alignment mapped to reverse strand" flag
+ void SetIsSecondMate(bool ok); // sets value of "alignment is second mate on read" flag
+
+ // tag data access methods
+ public:
+
+ // add a new tag
+ template<typename T> bool AddTag(const std::string& tag, const std::string& type, const T& value);
+ template<typename T> bool AddTag(const std::string& tag, const std::vector<T>& values);
+
+ // edit (or append) tag
+ template<typename T> bool EditTag(const std::string& tag, const std::string& type, const T& value);
+ template<typename T> bool EditTag(const std::string& tag, const std::vector<T>& values);
+
+ // retrieves tag data
+ template<typename T> bool GetTag(const std::string& tag, T& destination) const;
+ template<typename T> bool GetTag(const std::string& tag, std::vector<T>& destination) const;
+
+ // retrieves all current tag names
+ std::vector<std::string> GetTagNames(void) const;
+
+ // retrieves the SAM/BAM type-code for requested tag name
+ bool GetTagType(const std::string& tag, char& type) const;
+
+ // retrieves the SAM/BAM type-code for the data elements in an array tag
+ bool GetArrayTagType(const std::string& tag, char& type) const;
+
+ // returns true if alignment has a record for this tag name
+ bool HasTag(const std::string& tag) const;
+
+ // removes a tag
+ void RemoveTag(const std::string& tag);
+
+ // additional methods
+ public:
+ // populates alignment string fields
+ bool BuildCharData(void);
+
+ // calculates alignment end position
+ int GetEndPosition(bool usePadded = false, bool closedInterval = false) const;
+
+ // returns a description of the last error that occurred
+ std::string GetErrorString(void) const;
+
+ // retrieves the size, read locations and reference locations of soft-clip operations
+ bool GetSoftClips(std::vector<int>& clipSizes,
+ std::vector<int>& readPositions,
+ std::vector<int>& genomePositions,
+ bool usePadded = false) const;
+
+ // public data fields
+ public:
+ std::string Name; // read name
+ int32_t Length; // length of query sequence
+ std::string QueryBases; // 'original' sequence (contained in BAM file)
+ std::string AlignedBases; // 'aligned' sequence (QueryBases plus deletion, padding, clipping chars)
+ std::string Qualities; // FASTQ qualities (ASCII characters, not numeric values)
+ std::string TagData; // tag data (use provided methods to query/modify)
+ int32_t RefID; // ID number for reference sequence
+ int32_t Position; // position (0-based) where alignment starts
+ uint16_t Bin; // BAM (standard) index bin number for this alignment
+ uint16_t MapQuality; // mapping quality score
+ uint32_t AlignmentFlag; // alignment bit-flag (use provided methods to query/modify)
+ std::vector<CigarOp> CigarData; // CIGAR operations for this alignment
+ int32_t MateRefID; // ID number for reference sequence where alignment's mate was aligned
+ int32_t MatePosition; // position (0-based) where alignment's mate starts
+ int32_t InsertSize; // mate-pair insert size
+ std::string Filename; // name of BAM file which this alignment comes from
+
+ //! \internal
+ // internal utility methods
+ private:
+ bool FindTag(const std::string& tag,
+ char*& pTagData,
+ const unsigned int& tagDataLength,
+ unsigned int& numBytesParsed) const;
+ bool IsValidSize(const std::string& tag, const std::string& type) const;
+ void SetErrorString(const std::string& where, const std::string& what) const;
+ bool SkipToNextTag(const char storageType,
+ char*& pTagData,
+ unsigned int& numBytesParsed) const;
+
+ // internal data
+ private:
+
+ struct BamAlignmentSupportData {
+
+ // data members
+ std::string AllCharData;
+ uint32_t BlockLength;
+ uint32_t NumCigarOperations;
+ uint32_t QueryNameLength;
+ uint32_t QuerySequenceLength;
+ bool HasCoreOnly;
+
+ // constructor
+ BamAlignmentSupportData(void)
+ : BlockLength(0)
+ , NumCigarOperations(0)
+ , QueryNameLength(0)
+ , QuerySequenceLength(0)
+ , HasCoreOnly(false)
+ { }
+ };
+ BamAlignmentSupportData SupportData;
+ friend class Internal::BamReaderPrivate;
+ friend class Internal::BamWriterPrivate;
+
+ mutable std::string ErrorString; // mutable to allow updates even in logically const methods
+ //! \endinternal
+};
+
+// ---------------------------------------------------------
+// BamAlignment tag access methods
+
+/*! \fn bool AddTag(const std::string& tag, const std::string& type, const T& value)
+ \brief Adds a field to the BAM tags.
+
+ Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
+
+ \param[in] tag 2-character tag name
+ \param[in] type 1-character tag type
+ \param[in] value data to store
+ \return \c true if the \b new tag was added successfully
+ \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
+*/
+template<typename T>
+inline bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const T& value) {
+
+ // if char data not populated, do that first
+ if ( SupportData.HasCoreOnly )
+ BuildCharData();
+
+ // check tag/type size
+ if ( !IsValidSize(tag, type) ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // check that storage type code is OK for T
+ if ( !TagTypeHelper<T>::CanConvertTo(type.at(0)) ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // localize the tag data
+ char* pTagData = (char*)TagData.data();
+ const unsigned int tagDataLength = TagData.size();
+ unsigned int numBytesParsed = 0;
+
+ // if tag already exists, return false
+ // use EditTag explicitly instead
+ if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // otherwise, convert value to string
+ union { T value; char valueBuffer[sizeof(T)]; } un;
+ un.value = value;
+
+ // copy original tag data to temp buffer
+ const std::string newTag = tag + type;
+ const size_t newTagDataLength = tagDataLength + newTag.size() + sizeof(T); // leave room for new T
+ RaiiBuffer originalTagData(newTagDataLength);
+ memcpy(originalTagData.Buffer, TagData.c_str(), tagDataLength + 1); // '+1' for TagData null-term
+
+ // append newTag
+ strcat(originalTagData.Buffer + tagDataLength, newTag.data());
+ memcpy(originalTagData.Buffer + tagDataLength + newTag.size(), un.valueBuffer, sizeof(T));
+
+ // store temp buffer back in TagData
+ const char* newTagData = (const char*)originalTagData.Buffer;
+ TagData.assign(newTagData, newTagDataLength);
+ return true;
+}
+
+template<>
+inline bool BamAlignment::AddTag<std::string>(const std::string& tag,
+ const std::string& type,
+ const std::string& value)
+{
+ // if char data not populated, do that first
+ if ( SupportData.HasCoreOnly )
+ BuildCharData();
+
+ // check tag/type size
+ if ( !IsValidSize(tag, type) ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // check that storage type code is OK for string
+ if ( !TagTypeHelper<std::string>::CanConvertTo(type.at(0)) ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // localize the tag data
+ char* pTagData = (char*)TagData.data();
+ const unsigned int tagDataLength = TagData.size();
+ unsigned int numBytesParsed = 0;
+
+ // if tag already exists, return false
+ // use EditTag explicitly instead
+ if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // otherwise, copy tag data to temp buffer
+ const std::string newTag = tag + type + value;
+ const size_t newTagDataLength = tagDataLength + newTag.size() + 1; // leave room for null-term
+ RaiiBuffer originalTagData(newTagDataLength);
+ memcpy(originalTagData.Buffer, TagData.c_str(), tagDataLength + 1); // '+1' for TagData null-term
+
+ // append newTag (removes original null-term, then appends newTag + null-term)
+ strcat(originalTagData.Buffer + tagDataLength, newTag.data());
+
+ // store temp buffer back in TagData
+ const char* newTagData = (const char*)originalTagData.Buffer;
+ TagData.assign(newTagData, newTagDataLength);
+ return true;
+}
+
+/*! \fn template<typename T> bool AddTag(const std::string& tag, const std::vector<T>& values)
+ \brief Adds a numeric array field to the BAM tags.
+
+ Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
+
+ \param[in] tag 2-character tag name
+ \param[in] values vector of data values to store
+ \return \c true if the \b new tag was added successfully
+ \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
+*/
+template<typename T>
+inline bool BamAlignment::AddTag(const std::string& tag, const std::vector<T>& values) {
+
+ // if char data not populated, do that first
+ if ( SupportData.HasCoreOnly )
+ BuildCharData();
+
+ // check for valid tag name length
+ if ( tag.size() != Constants::BAM_TAG_TAGSIZE )
+ return false;
+
+ // localize the tag data
+ char* pTagData = (char*)TagData.data();
+ const unsigned int tagDataLength = TagData.size();
+ unsigned int numBytesParsed = 0;
+
+ // if tag already exists, return false
+ // use EditTag explicitly instead
+ if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // build new tag's base information
+ char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
+ memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
+ newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
+ newTagBase[3] = TagTypeHelper<T>::TypeCode();
+
+ // add number of array elements to newTagBase
+ const int32_t numElements = values.size();
+ memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
+
+ // copy current TagData string to temp buffer, leaving room for new tag's contents
+ const size_t newTagDataLength = tagDataLength +
+ Constants::BAM_TAG_ARRAYBASE_SIZE +
+ numElements*sizeof(T);
+ RaiiBuffer originalTagData(newTagDataLength);
+ memcpy(originalTagData.Buffer, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
+
+ // write newTagBase (removes old null term)
+ strcat(originalTagData.Buffer + tagDataLength, (const char*)newTagBase);
+
+ // add vector elements to tag
+ int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
+ for ( int i = 0 ; i < numElements; ++i ) {
+ const T& value = values.at(i);
+ memcpy(originalTagData.Buffer + elementsBeginOffset + i*sizeof(T), &value, sizeof(T));
+ }
+
+ // store temp buffer back in TagData
+ const char* newTagData = (const char*)originalTagData.Buffer;
+ TagData.assign(newTagData, newTagDataLength);
+ return true;
+}
+
+/*! \fn template<typename T> bool EditTag(const std::string& tag, const std::string& type, const T& value)
+ \brief Edits a BAM tag field.
+
+ If \a tag does not exist, a new entry is created.
+
+ \param tag[in] 2-character tag name
+ \param type[in] 1-character tag type (must be "Z" or "H")
+ \param value[in] new data value
+
+ \return \c true if the tag was modified/created successfully
+
+ \sa BamAlignment::RemoveTag()
+ \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
+*/
+template<typename T>
+inline bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const T& value) {
+
+ // if char data not populated, do that first
+ if ( SupportData.HasCoreOnly )
+ BuildCharData();
+
+ // remove existing tag if present, then append tag with new value
+ if ( HasTag(tag) )
+ RemoveTag(tag);
+ return AddTag(tag, type, value);
+}
+
+/*! \fn template<typename T> bool EditTag(const std::string& tag, const std::vector<T>& values)
+ \brief Edits a BAM tag field containing a numeric array.
+
+ If \a tag does not exist, a new entry is created.
+
+ \param tag[in] 2-character tag name
+ \param value[in] vector of data values
+
+ \return \c true if the tag was modified/created successfully
+ \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
+*/
+template<typename T>
+inline bool BamAlignment::EditTag(const std::string& tag, const std::vector<T>& values) {
+
+ // if char data not populated, do that first
+ if ( SupportData.HasCoreOnly )
+ BuildCharData();
+
+ // remove existing tag if present, then append tag with new values
+ if ( HasTag(tag) )
+ RemoveTag(tag);
+ return AddTag(tag, values);
+}
+
+
+/*! \fn template<typename T> bool GetTag(const std::string& tag, T& destination) const
+ \brief Retrieves the value associated with a BAM tag.
+
+ \param tag[in] 2-character tag name
+ \param destination[out] retrieved value
+ \return \c true if found
+*/
+template<typename T>
+inline bool BamAlignment::GetTag(const std::string& tag, T& destination) const {
+
+ // skip if alignment is core-only
+ if ( SupportData.HasCoreOnly ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // skip if no tags present
+ if ( TagData.empty() ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // localize the tag data
+ char* pTagData = (char*)TagData.data();
+ const unsigned int tagDataLength = TagData.size();
+ unsigned int numBytesParsed = 0;
+
+ // return failure if tag not found
+ if ( !FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // fetch data type
+ const char type = *(pTagData - 1);
+ if ( !TagTypeHelper<T>::CanConvertFrom(type) ) {
+ // TODO: set error string ?
+ return false;
+ }
+
+ // determine data length
+ int destinationLength = 0;
+ switch ( type ) {
+
+ // 1 byte data
+ case (Constants::BAM_TAG_TYPE_ASCII) :
+ case (Constants::BAM_TAG_TYPE_INT8) :
+ case (Constants::BAM_TAG_TYPE_UINT8) :
+ destinationLength = 1;
+ break;
+
+ // 2 byte data
+ case (Constants::BAM_TAG_TYPE_INT16) :
+ case (Constants::BAM_TAG_TYPE_UINT16) :
+ destinationLength = 2;
+ break;
+
+ // 4 byte data
+ case (Constants::BAM_TAG_TYPE_INT32) :
+ case (Constants::BAM_TAG_TYPE_UINT32) :
+ case (Constants::BAM_TAG_TYPE_FLOAT) :
+ destinationLength = 4;
+ break;
+
+ // var-length types not supported for numeric destination
+ case (Constants::BAM_TAG_TYPE_STRING) :
+ case (Constants::BAM_TAG_TYPE_HEX) :
+ case (Constants::BAM_TAG_TYPE_ARRAY) :
+ SetErrorString("BamAlignment::GetTag",
+ "cannot store variable length tag data into a numeric destination");
+ return false;
+
+ // unrecognized tag type
+ default:
+ const std::string message = std::string("invalid tag type: ") + type;
+ SetErrorString("BamAlignment::GetTag", message);
+ return false;
+ }
+
+ // store data in destination
+ destination = 0;
+ memcpy(&destination, pTagData, destinationLength);
+
+ // return success
+ return true;
+}
+
+template<>
+inline bool BamAlignment::GetTag<std::string>(const std::string& tag,
+ std::string& destination) const
+{
+ // skip if alignment is core-only
+ if ( SupportData.HasCoreOnly ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // skip if no tags present
+ if ( TagData.empty() ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // localize the tag data
+ char* pTagData = (char*)TagData.data();
+ const unsigned int tagDataLength = TagData.size();
+ unsigned int numBytesParsed = 0;
+
+ // return failure if tag not found
+ if ( !FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // otherwise copy data into destination
+ const unsigned int dataLength = strlen(pTagData);
+ destination.clear();
+ destination.resize(dataLength);
+ memcpy( (char*)destination.data(), pTagData, dataLength );
+
+ // return success
+ return true;
+}
+
+/*! \fn template<typename T> bool GetTag(const std::string& tag, std::vector<T>& destination) const
+ \brief Retrieves the numeric array associated with a BAM tag.
+
+ \param tag[in] 2-character tag name
+ \param destination[out] retrieved values
+ \return \c true if found
+*/
+template<typename T>
+inline bool BamAlignment::GetTag(const std::string& tag, std::vector<T>& destination) const {
+
+ // skip if alignment is core-only
+ if ( SupportData.HasCoreOnly ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // skip if no tags present
+ if ( TagData.empty() ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // localize the tag data
+ char* pTagData = (char*)TagData.data();
+ const unsigned int tagDataLength = TagData.size();
+ unsigned int numBytesParsed = 0;
+
+ // return false if tag not found
+ if ( !FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
+ // TODO: set error string?
+ return false;
+ }
+
+ // check that tag is array type
+ const char tagType = *(pTagData - 1);
+ if ( tagType != Constants::BAM_TAG_TYPE_ARRAY ) {
+ SetErrorString("BamAlignment::GetTag", "cannot store a non-array tag in array destination");
+ return false;
+ }
+
+ // fetch element type
+ const char elementType = *pTagData;
+ if ( !TagTypeHelper<T>::CanConvertFrom(elementType) ) {
+ // TODO: set error string ?
+ return false;
+ }
+ ++pTagData;
+
+ // calculate length of each element in tag's array
+ int elementLength = 0;
+ switch ( elementType ) {
+ case (Constants::BAM_TAG_TYPE_ASCII) :
+ case (Constants::BAM_TAG_TYPE_INT8) :
+ case (Constants::BAM_TAG_TYPE_UINT8) :
+ elementLength = sizeof(uint8_t);
+ break;
+
+ case (Constants::BAM_TAG_TYPE_INT16) :
+ case (Constants::BAM_TAG_TYPE_UINT16) :
+ elementLength = sizeof(uint16_t);
+ break;
+
+ case (Constants::BAM_TAG_TYPE_INT32) :
+ case (Constants::BAM_TAG_TYPE_UINT32) :
+ case (Constants::BAM_TAG_TYPE_FLOAT) :
+ elementLength = sizeof(uint32_t);
+ break;
+
+ // var-length types not supported for numeric destination
+ case (Constants::BAM_TAG_TYPE_STRING) :
+ case (Constants::BAM_TAG_TYPE_HEX) :
+ case (Constants::BAM_TAG_TYPE_ARRAY) :
+ SetErrorString("BamAlignment::GetTag",
+ "invalid array data, variable-length elements are not allowed");
+ return false;
+
+ // unknown tag type
+ default:
+ const std::string message = std::string("invalid array element type: ") + elementType;
+ SetErrorString("BamAlignment::GetTag", message);
+ return false;
+ }
+
+ // get number of elements
+ int32_t numElements;
+ memcpy(&numElements, pTagData, sizeof(int32_t));
+ pTagData += 4;
+ destination.clear();
+ destination.reserve(numElements);
+
+ // read in elements
+ T value;
+ for ( int i = 0 ; i < numElements; ++i ) {
+ memcpy(&value, pTagData, sizeof(T));
+ pTagData += sizeof(T);
+ destination.push_back(value);
+ }
+
+ // return success
+ return true;
+}
+
+typedef std::vector<BamAlignment> BamAlignmentVector;
+
+} // namespace BamTools
+
+#endif // BAMALIGNMENT_H
diff --git a/bamtools/src/api/BamAux.h b/bamtools/src/api/BamAux.h
new file mode 100644
index 0000000..0dd3e99
--- /dev/null
+++ b/bamtools/src/api/BamAux.h
@@ -0,0 +1,468 @@
+// ***************************************************************************
+// BamAux.h (c) 2009 Derek Barnett, Michael Str�mberg
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 25 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides data structures & utility methods that are used throughout the API.
+// ***************************************************************************
+
+#ifndef BAMAUX_H
+#define BAMAUX_H
+
+#include "api/api_global.h"
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+/*! \file BamAux.h
+
+ Provides data structures & utility methods that are used throughout the API.
+*/
+
+/*! \namespace BamTools
+ \brief Contains all BamTools classes & methods.
+
+ The BamTools API contained in this namespace contains classes and methods
+ for reading, writing, and manipulating BAM alignment files.
+*/
+namespace BamTools {
+
+// ----------------------------------------------------------------
+// CigarOp
+
+/*! \struct BamTools::CigarOp
+ \brief Represents a CIGAR alignment operation.
+
+ \sa \samSpecURL for more details on using CIGAR operations.
+*/
+struct API_EXPORT CigarOp {
+
+ char Type; //!< CIGAR operation type (MIDNSHPX=)
+ uint32_t Length; //!< CIGAR operation length (number of bases)
+
+ //! constructor
+ CigarOp(const char type = '\0',
+ const uint32_t& length = 0)
+ : Type(type)
+ , Length(length)
+ { }
+};
+
+// ----------------------------------------------------------------
+// RefData
+
+/*! \struct BamTools::RefData
+ \brief Represents a reference sequence entry
+*/
+struct API_EXPORT RefData {
+
+ std::string RefName; //!< name of reference sequence
+ int32_t RefLength; //!< length of reference sequence
+
+ //! constructor
+ RefData(const std::string& name = "",
+ const int32_t& length = 0)
+ : RefName(name)
+ , RefLength(length)
+ { }
+};
+
+//! convenience typedef for vector of RefData entries
+typedef std::vector<RefData> RefVector;
+
+// ----------------------------------------------------------------
+// BamRegion
+
+/*! \struct BamTools::BamRegion
+ \brief Represents a sequential genomic region
+
+ Allowed to span multiple (sequential) references.
+
+ \warning BamRegion now represents a zero-based, HALF-OPEN interval.
+ In previous versions of BamTools (0.x & 1.x) all intervals were treated
+ as zero-based, CLOSED.
+*/
+struct API_EXPORT BamRegion {
+
+ int LeftRefID; //!< reference ID for region's left boundary
+ int LeftPosition; //!< position for region's left boundary
+ int RightRefID; //!< reference ID for region's right boundary
+ int RightPosition; //!< position for region's right boundary
+
+ //! constructor
+ BamRegion(const int& leftID = -1,
+ const int& leftPos = -1,
+ const int& rightID = -1,
+ const int& rightPos = -1)
+ : LeftRefID(leftID)
+ , LeftPosition(leftPos)
+ , RightRefID(rightID)
+ , RightPosition(rightPos)
+ { }
+
+ //! copy constructor
+ BamRegion(const BamRegion& other)
+ : LeftRefID(other.LeftRefID)
+ , LeftPosition(other.LeftPosition)
+ , RightRefID(other.RightRefID)
+ , RightPosition(other.RightPosition)
+ { }
+
+ //! Clears region boundaries
+ void clear(void) {
+ LeftRefID = -1; LeftPosition = -1;
+ RightRefID = -1; RightPosition = -1;
+ }
+
+ //! Returns true if region has a left boundary
+ bool isLeftBoundSpecified(void) const {
+ return ( LeftRefID >= 0 && LeftPosition >= 0 );
+ }
+
+ //! Returns true if region boundaries are not defined
+ bool isNull(void) const {
+ return ( !isLeftBoundSpecified() && !isRightBoundSpecified() );
+ }
+
+ //! Returns true if region has a right boundary
+ bool isRightBoundSpecified(void) const {
+ return ( RightRefID >= 0 && RightPosition >= 1 );
+ }
+};
+
+// ----------------------------------------------------------------
+// General utility methods
+
+/*! \fn bool FileExists(const std::string& filename)
+ \brief returns true if the file exists
+*/
+API_EXPORT inline bool FileExists(const std::string& filename) {
+ std::ifstream f(filename.c_str(), std::ifstream::in);
+ return !f.fail();
+}
+
+/*! \fn void SwapEndian_16(int16_t& x)
+ \brief swaps endianness of signed 16-bit integer, in place
+*/
+API_EXPORT inline void SwapEndian_16(int16_t& x) {
+ x = ((x >> 8) | (x << 8));
+}
+
+/*! \fn void SwapEndian_16(uint16_t& x)
+ \brief swaps endianness of unsigned 16-bit integer, in place
+*/
+API_EXPORT inline void SwapEndian_16(uint16_t& x) {
+ x = ((x >> 8) | (x << 8));
+}
+
+/*! \fn void SwapEndian_32(int32_t& x)
+ \brief swaps endianness of signed 32-bit integer, in place
+*/
+API_EXPORT inline void SwapEndian_32(int32_t& x) {
+ x = ( (x >> 24) |
+ ((x << 8) & 0x00FF0000) |
+ ((x >> 8) & 0x0000FF00) |
+ (x << 24)
+ );
+}
+
+/*! \fn void SwapEndian_32(uint32_t& x)
+ \brief swaps endianness of unsigned 32-bit integer, in place
+*/
+API_EXPORT inline void SwapEndian_32(uint32_t& x) {
+ x = ( (x >> 24) |
+ ((x << 8) & 0x00FF0000) |
+ ((x >> 8) & 0x0000FF00) |
+ (x << 24)
+ );
+}
+
+/*! \fn void SwapEndian_64(int64_t& x)
+ \brief swaps endianness of signed 64-bit integer, in place
+*/
+API_EXPORT inline void SwapEndian_64(int64_t& x) {
+ x = ( (x >> 56) |
+ ((x << 40) & 0x00FF000000000000ll) |
+ ((x << 24) & 0x0000FF0000000000ll) |
+ ((x << 8) & 0x000000FF00000000ll) |
+ ((x >> 8) & 0x00000000FF000000ll) |
+ ((x >> 24) & 0x0000000000FF0000ll) |
+ ((x >> 40) & 0x000000000000FF00ll) |
+ (x << 56)
+ );
+}
+
+/*! \fn void SwapEndian_64(uint64_t& x)
+ \brief swaps endianness of unsigned 64-bit integer, in place
+*/
+API_EXPORT inline void SwapEndian_64(uint64_t& x) {
+ x = ( (x >> 56) |
+ ((x << 40) & 0x00FF000000000000ll) |
+ ((x << 24) & 0x0000FF0000000000ll) |
+ ((x << 8) & 0x000000FF00000000ll) |
+ ((x >> 8) & 0x00000000FF000000ll) |
+ ((x >> 24) & 0x0000000000FF0000ll) |
+ ((x >> 40) & 0x000000000000FF00ll) |
+ (x << 56)
+ );
+}
+
+/*! \fn void SwapEndian_16p(char* data)
+ \brief swaps endianness of the next 2 bytes in a buffer, in place
+*/
+API_EXPORT inline void SwapEndian_16p(char* data) {
+ uint16_t& value = (uint16_t&)*data;
+ SwapEndian_16(value);
+}
+
+/*! \fn void SwapEndian_32p(char* data)
+ \brief swaps endianness of the next 4 bytes in a buffer, in place
+*/
+API_EXPORT inline void SwapEndian_32p(char* data) {
+ uint32_t& value = (uint32_t&)*data;
+ SwapEndian_32(value);
+}
+
+/*! \fn void SwapEndian_64p(char* data)
+ \brief swaps endianness of the next 8 bytes in a buffer, in place
+*/
+API_EXPORT inline void SwapEndian_64p(char* data) {
+ uint64_t& value = (uint64_t&)*data;
+ SwapEndian_64(value);
+}
+
+/*! \fn bool SystemIsBigEndian(void)
+ \brief checks host architecture's byte order
+ \return \c true if system uses big-endian ordering
+*/
+API_EXPORT inline bool SystemIsBigEndian(void) {
+ const uint16_t one = 0x0001;
+ return ((*(char*) &one) == 0 );
+}
+
+/*! \fn void PackUnsignedInt(char* buffer, unsigned int value)
+ \brief stores unsigned integer value in a byte buffer
+
+ \param[out] buffer destination buffer
+ \param[in] value value to 'pack' in buffer
+*/
+API_EXPORT inline void PackUnsignedInt(char* buffer, unsigned int value) {
+ buffer[0] = (char)value;
+ buffer[1] = (char)(value >> 8);
+ buffer[2] = (char)(value >> 16);
+ buffer[3] = (char)(value >> 24);
+}
+
+/*! \fn void PackUnsignedShort(char* buffer, unsigned short value)
+ \brief stores unsigned short integer value in a byte buffer
+
+ \param[out] buffer destination buffer
+ \param[in] value value to 'pack' in buffer
+*/
+API_EXPORT inline void PackUnsignedShort(char* buffer, unsigned short value) {
+ buffer[0] = (char)value;
+ buffer[1] = (char)(value >> 8);
+}
+
+/*! \fn double UnpackDouble(const char* buffer)
+ \brief reads a double value from byte buffer
+
+ \param[in] buffer source byte buffer
+ \return the (double) value read from the buffer
+*/
+API_EXPORT inline double UnpackDouble(const char* buffer) {
+ union { double value; unsigned char valueBuffer[sizeof(double)]; } un;
+ un.value = 0;
+ un.valueBuffer[0] = buffer[0];
+ un.valueBuffer[1] = buffer[1];
+ un.valueBuffer[2] = buffer[2];
+ un.valueBuffer[3] = buffer[3];
+ un.valueBuffer[4] = buffer[4];
+ un.valueBuffer[5] = buffer[5];
+ un.valueBuffer[6] = buffer[6];
+ un.valueBuffer[7] = buffer[7];
+ return un.value;
+}
+
+/*! \fn double UnpackDouble(char* buffer)
+ \brief reads a double value from byte buffer
+
+ This is an overloaded function.
+
+ \param[in] buffer source byte buffer
+ \return the (double) value read from the buffer
+*/
+API_EXPORT inline double UnpackDouble(char* buffer) {
+ return UnpackDouble( (const char*)buffer );
+}
+
+/*! \fn double UnpackFloat(const char* buffer)
+ \brief reads a float value from byte buffer
+
+ \param[in] buffer source byte buffer
+ \return the (float) value read from the buffer
+*/
+API_EXPORT inline float UnpackFloat(const char* buffer) {
+ union { float value; unsigned char valueBuffer[sizeof(float)]; } un;
+ un.value = 0;
+ un.valueBuffer[0] = buffer[0];
+ un.valueBuffer[1] = buffer[1];
+ un.valueBuffer[2] = buffer[2];
+ un.valueBuffer[3] = buffer[3];
+ return un.value;
+}
+
+/*! \fn double UnpackFloat(char* buffer)
+ \brief reads a float value from byte buffer
+
+ This is an overloaded function.
+
+ \param[in] buffer source byte buffer
+ \return the (float) value read from the buffer
+*/
+API_EXPORT inline float UnpackFloat(char* buffer) {
+ return UnpackFloat( (const char*)buffer );
+}
+
+/*! \fn signed int UnpackSignedInt(const char* buffer)
+ \brief reads a signed integer value from byte buffer
+
+ \param[in] buffer source byte buffer
+ \return the (signed int) value read from the buffer
+*/
+API_EXPORT inline signed int UnpackSignedInt(const char* buffer) {
+ union { signed int value; unsigned char valueBuffer[sizeof(signed int)]; } un;
+ un.value = 0;
+ un.valueBuffer[0] = buffer[0];
+ un.valueBuffer[1] = buffer[1];
+ un.valueBuffer[2] = buffer[2];
+ un.valueBuffer[3] = buffer[3];
+ return un.value;
+}
+
+/*! \fn signed int UnpackSignedInt(char* buffer)
+ \brief reads a signed integer value from byte buffer
+
+ This is an overloaded function.
+
+ \param[in] buffer source byte buffer
+ \return the (signed int) value read from the buffer
+*/
+API_EXPORT inline signed int UnpackSignedInt(char* buffer) {
+ return UnpackSignedInt( (const char*) buffer );
+}
+
+/*! \fn signed short UnpackSignedShort(const char* buffer)
+ \brief reads a signed short integer value from byte buffer
+
+ \param[in] buffer source byte buffer
+ \return the (signed short) value read from the buffer
+*/
+API_EXPORT inline signed short UnpackSignedShort(const char* buffer) {
+ union { signed short value; unsigned char valueBuffer[sizeof(signed short)]; } un;
+ un.value = 0;
+ un.valueBuffer[0] = buffer[0];
+ un.valueBuffer[1] = buffer[1];
+ return un.value;
+}
+
+/*! \fn signed short UnpackSignedShort(char* buffer)
+ \brief reads a signed short integer value from byte buffer
+
+ This is an overloaded function.
+
+ \param[in] buffer source byte buffer
+ \return the (signed short) value read from the buffer
+*/
+API_EXPORT inline signed short UnpackSignedShort(char* buffer) {
+ return UnpackSignedShort( (const char*)buffer );
+}
+
+/*! \fn unsigned int UnpackUnsignedInt(const char* buffer)
+ \brief reads an unsigned integer value from byte buffer
+
+ \param[in] buffer source byte buffer
+ \return the (unsigned int) value read from the buffer
+*/
+API_EXPORT inline unsigned int UnpackUnsignedInt(const char* buffer) {
+ union { unsigned int value; unsigned char valueBuffer[sizeof(unsigned int)]; } un;
+ un.value = 0;
+ un.valueBuffer[0] = buffer[0];
+ un.valueBuffer[1] = buffer[1];
+ un.valueBuffer[2] = buffer[2];
+ un.valueBuffer[3] = buffer[3];
+ return un.value;
+}
+
+/*! \fn unsigned int UnpackUnsignedInt(char* buffer)
+ \brief reads an unsigned integer value from byte buffer
+
+ This is an overloaded function.
+
+ \param[in] buffer source byte buffer
+ \return the (unsigned int) value read from the buffer
+*/
+API_EXPORT inline unsigned int UnpackUnsignedInt(char* buffer) {
+ return UnpackUnsignedInt( (const char*)buffer );
+}
+
+/*! \fn unsigned short UnpackUnsignedShort(const char* buffer)
+ \brief reads an unsigned short integer value from byte buffer
+
+ \param[in] buffer source byte buffer
+ \return the (unsigned short) value read from the buffer
+*/
+API_EXPORT inline unsigned short UnpackUnsignedShort(const char* buffer) {
+ union { unsigned short value; unsigned char valueBuffer[sizeof(unsigned short)]; } un;
+ un.value = 0;
+ un.valueBuffer[0] = buffer[0];
+ un.valueBuffer[1] = buffer[1];
+ return un.value;
+}
+
+/*! \fn unsigned short UnpackUnsignedShort(char* buffer)
+ \brief reads an unsigned short integer value from byte buffer
+
+ This is an overloaded function.
+
+ \param[in] buffer source byte buffer
+ \return the (unsigned short) value read from the buffer
+*/
+API_EXPORT inline unsigned short UnpackUnsignedShort(char* buffer) {
+ return UnpackUnsignedShort( (const char*)buffer );
+}
+
+// ----------------------------------------------------------------
+// 'internal' helper structs
+
+/*! \struct RaiiBuffer
+ \internal
+*/
+struct RaiiBuffer {
+
+ // data members
+ char* Buffer;
+ const size_t NumBytes;
+
+ // ctor & dtor
+ RaiiBuffer(const size_t n)
+ : Buffer( new char[n]() )
+ , NumBytes(n)
+ { }
+
+ ~RaiiBuffer(void) {
+ delete[] Buffer;
+ }
+
+ // add'l methods
+ void Clear(void) {
+ memset(Buffer, 0, NumBytes);
+ }
+};
+
+} // namespace BamTools
+
+#endif // BAMAUX_H
diff --git a/bamtools/src/api/BamConstants.h b/bamtools/src/api/BamConstants.h
new file mode 100644
index 0000000..47f73a9
--- /dev/null
+++ b/bamtools/src/api/BamConstants.h
@@ -0,0 +1,282 @@
+// ***************************************************************************
+// BamConstants.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 16 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides basic constants for handling BAM files.
+// ***************************************************************************
+
+#ifndef BAM_CONSTANTS_H
+#define BAM_CONSTANTS_H
+
+#include "api/api_global.h"
+#include <cassert>
+#include <string>
+
+/*! \namespace BamTools::Constants
+ \brief Provides basic constants for handling BAM files.
+*/
+
+namespace BamTools {
+namespace Constants {
+
+const uint8_t BAM_SIZEOF_INT = 4;
+
+// header magic number
+const char* const BAM_HEADER_MAGIC = "BAM\1";
+const uint8_t BAM_HEADER_MAGIC_LENGTH = 4;
+
+// BAM alignment core size
+const uint8_t BAM_CORE_SIZE = 32;
+const uint8_t BAM_CORE_BUFFER_SIZE = 8;
+
+// BAM alignment flags
+const int BAM_ALIGNMENT_PAIRED = 0x0001;
+const int BAM_ALIGNMENT_PROPER_PAIR = 0x0002;
+const int BAM_ALIGNMENT_UNMAPPED = 0x0004;
+const int BAM_ALIGNMENT_MATE_UNMAPPED = 0x0008;
+const int BAM_ALIGNMENT_REVERSE_STRAND = 0x0010;
+const int BAM_ALIGNMENT_MATE_REVERSE_STRAND = 0x0020;
+const int BAM_ALIGNMENT_READ_1 = 0x0040;
+const int BAM_ALIGNMENT_READ_2 = 0x0080;
+const int BAM_ALIGNMENT_SECONDARY = 0x0100;
+const int BAM_ALIGNMENT_QC_FAILED = 0x0200;
+const int BAM_ALIGNMENT_DUPLICATE = 0x0400;
+
+// CIGAR constants
+const char* const BAM_CIGAR_LOOKUP = "MIDNSHP=X";
+const uint8_t BAM_CIGAR_MATCH = 0;
+const uint8_t BAM_CIGAR_INS = 1;
+const uint8_t BAM_CIGAR_DEL = 2;
+const uint8_t BAM_CIGAR_REFSKIP = 3;
+const uint8_t BAM_CIGAR_SOFTCLIP = 4;
+const uint8_t BAM_CIGAR_HARDCLIP = 5;
+const uint8_t BAM_CIGAR_PAD = 6;
+const uint8_t BAM_CIGAR_SEQMATCH = 7;
+const uint8_t BAM_CIGAR_MISMATCH = 8;
+
+const char BAM_CIGAR_MATCH_CHAR = 'M';
+const char BAM_CIGAR_INS_CHAR = 'I';
+const char BAM_CIGAR_DEL_CHAR = 'D';
+const char BAM_CIGAR_REFSKIP_CHAR = 'N';
+const char BAM_CIGAR_SOFTCLIP_CHAR = 'S';
+const char BAM_CIGAR_HARDCLIP_CHAR = 'H';
+const char BAM_CIGAR_PAD_CHAR = 'P';
+const char BAM_CIGAR_SEQMATCH_CHAR = '=';
+const char BAM_CIGAR_MISMATCH_CHAR = 'X';
+
+const int BAM_CIGAR_SHIFT = 4;
+const int BAM_CIGAR_MASK = ((1 << BAM_CIGAR_SHIFT) - 1);
+
+// BAM tag types & sizes
+const char BAM_TAG_TYPE_ASCII = 'A';
+const char BAM_TAG_TYPE_INT8 = 'c';
+const char BAM_TAG_TYPE_UINT8 = 'C';
+const char BAM_TAG_TYPE_INT16 = 's';
+const char BAM_TAG_TYPE_UINT16 = 'S';
+const char BAM_TAG_TYPE_INT32 = 'i';
+const char BAM_TAG_TYPE_UINT32 = 'I';
+const char BAM_TAG_TYPE_FLOAT = 'f';
+const char BAM_TAG_TYPE_STRING = 'Z';
+const char BAM_TAG_TYPE_HEX = 'H';
+const char BAM_TAG_TYPE_ARRAY = 'B';
+
+const uint8_t BAM_TAG_TAGSIZE = 2;
+const uint8_t BAM_TAG_TYPESIZE = 1;
+const uint8_t BAM_TAG_ARRAYBASE_SIZE = 8;
+
+// DNA bases
+const char* const BAM_DNA_LOOKUP = "=ACMGRSVTWYHKDBN";
+const uint8_t BAM_BASECODE_EQUAL = 0;
+const uint8_t BAM_BASECODE_A = 1;
+const uint8_t BAM_BASECODE_C = 2;
+const uint8_t BAM_BASECODE_M = 3;
+const uint8_t BAM_BASECODE_G = 4;
+const uint8_t BAM_BASECODE_R = 5;
+const uint8_t BAM_BASECODE_S = 6;
+const uint8_t BAM_BASECODE_V = 7;
+const uint8_t BAM_BASECODE_T = 8;
+const uint8_t BAM_BASECODE_W = 9;
+const uint8_t BAM_BASECODE_Y = 10;
+const uint8_t BAM_BASECODE_H = 11;
+const uint8_t BAM_BASECODE_K = 12;
+const uint8_t BAM_BASECODE_D = 13;
+const uint8_t BAM_BASECODE_B = 14;
+const uint8_t BAM_BASECODE_N = 15;
+
+const char BAM_DNA_EQUAL = '=';
+const char BAM_DNA_A = 'A';
+const char BAM_DNA_C = 'C';
+const char BAM_DNA_M = 'M';
+const char BAM_DNA_G = 'G';
+const char BAM_DNA_R = 'R';
+const char BAM_DNA_S = 'S';
+const char BAM_DNA_V = 'V';
+const char BAM_DNA_T = 'T';
+const char BAM_DNA_W = 'W';
+const char BAM_DNA_Y = 'Y';
+const char BAM_DNA_H = 'H';
+const char BAM_DNA_K = 'K';
+const char BAM_DNA_D = 'D';
+const char BAM_DNA_B = 'B';
+const char BAM_DNA_N = 'N';
+const char BAM_DNA_DEL = '-';
+const char BAM_DNA_PAD = '*';
+
+// zlib & BGZF constants
+const char GZIP_ID1 = 31;
+const char GZIP_ID2 = 139;
+const char CM_DEFLATE = 8;
+const char FLG_FEXTRA = 4;
+const char OS_UNKNOWN = 255;
+const char BGZF_XLEN = 6;
+const char BGZF_ID1 = 66;
+const char BGZF_ID2 = 67;
+const char BGZF_LEN = 2;
+
+const int8_t GZIP_WINDOW_BITS = -15;
+const int8_t Z_DEFAULT_MEM_LEVEL = 8;
+const uint8_t BGZF_BLOCK_HEADER_LENGTH = 18;
+const uint8_t BGZF_BLOCK_FOOTER_LENGTH = 8;
+const uint32_t BGZF_MAX_BLOCK_SIZE = 65536;
+const uint32_t BGZF_DEFAULT_BLOCK_SIZE = 65536;
+
+} // namespace Constants
+
+//! \cond
+// -------------------------
+// tag-type helper structs
+// -------------------------
+
+// fail on any types not specified below
+template<typename T>
+struct TagTypeHelper {
+ static bool CanConvertFrom(const char) { assert(false); return false; }
+ static bool CanConvertTo(const char) { assert(false); return false; }
+ static char TypeCode(void) { assert(false); return 0; }
+};
+
+template<>
+struct TagTypeHelper<uint8_t> {
+ static bool CanConvertFrom(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_ASCII ||
+ c == Constants::BAM_TAG_TYPE_UINT8 );
+ }
+ static bool CanConvertTo(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_ASCII ||
+ c == Constants::BAM_TAG_TYPE_UINT8 ||
+ c == Constants::BAM_TAG_TYPE_UINT16 ||
+ c == Constants::BAM_TAG_TYPE_UINT32 );
+ }
+
+ static char TypeCode(void) { return Constants::BAM_TAG_TYPE_UINT8; }
+};
+
+template<>
+struct TagTypeHelper<int8_t> {
+ static bool CanConvertFrom(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_ASCII ||
+ c == Constants::BAM_TAG_TYPE_INT8 );
+ }
+ static bool CanConvertTo(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_ASCII ||
+ c == Constants::BAM_TAG_TYPE_INT8 ||
+ c == Constants::BAM_TAG_TYPE_INT16 ||
+ c == Constants::BAM_TAG_TYPE_INT32 );
+ }
+ static char TypeCode(void) { return Constants::BAM_TAG_TYPE_INT8; }
+};
+
+template<>
+struct TagTypeHelper<uint16_t> {
+ static bool CanConvertFrom(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_ASCII ||
+ c == Constants::BAM_TAG_TYPE_UINT8 ||
+ c == Constants::BAM_TAG_TYPE_UINT16 );
+ }
+ static bool CanConvertTo(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_UINT16 ||
+ c == Constants::BAM_TAG_TYPE_UINT32);
+ }
+ static char TypeCode(void) { return Constants::BAM_TAG_TYPE_UINT16; }
+};
+
+template<>
+struct TagTypeHelper<int16_t> {
+ static bool CanConvertFrom(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_ASCII ||
+ c == Constants::BAM_TAG_TYPE_INT8 ||
+ c == Constants::BAM_TAG_TYPE_INT16 );
+ }
+ static bool CanConvertTo(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_INT16 ||
+ c == Constants::BAM_TAG_TYPE_INT32);
+ }
+ static char TypeCode(void) { return Constants::BAM_TAG_TYPE_INT16; }
+};
+
+template<>
+struct TagTypeHelper<uint32_t> {
+ static bool CanConvertFrom(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_ASCII ||
+ c == Constants::BAM_TAG_TYPE_UINT8 ||
+ c == Constants::BAM_TAG_TYPE_UINT16 ||
+ c == Constants::BAM_TAG_TYPE_UINT32 );
+ }
+ static bool CanConvertTo(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_UINT32 );
+ }
+ static char TypeCode(void) { return Constants::BAM_TAG_TYPE_UINT32; }
+};
+
+template<>
+struct TagTypeHelper<int32_t> {
+ static bool CanConvertFrom(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_ASCII ||
+ c == Constants::BAM_TAG_TYPE_INT8 ||
+ c == Constants::BAM_TAG_TYPE_INT16 ||
+ c == Constants::BAM_TAG_TYPE_INT32 );
+ }
+ static bool CanConvertTo(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_INT32 );
+ }
+ static char TypeCode(void) { return Constants::BAM_TAG_TYPE_INT32; }
+};
+
+template<>
+struct TagTypeHelper<float> {
+ static bool CanConvertFrom(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_ASCII ||
+ c == Constants::BAM_TAG_TYPE_UINT8 ||
+ c == Constants::BAM_TAG_TYPE_INT8 ||
+ c == Constants::BAM_TAG_TYPE_UINT16 ||
+ c == Constants::BAM_TAG_TYPE_INT16 ||
+ c == Constants::BAM_TAG_TYPE_UINT32 ||
+ c == Constants::BAM_TAG_TYPE_INT32 ||
+ c == Constants::BAM_TAG_TYPE_FLOAT);
+ }
+ static bool CanConvertTo(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_FLOAT );
+ }
+ static char TypeCode(void) { return Constants::BAM_TAG_TYPE_FLOAT; }
+};
+
+template<>
+struct TagTypeHelper<std::string> {
+ static bool CanConvertFrom(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_HEX ||
+ c == Constants::BAM_TAG_TYPE_STRING );
+ }
+ static bool CanConvertTo(const char c) {
+ return ( c == Constants::BAM_TAG_TYPE_HEX ||
+ c == Constants::BAM_TAG_TYPE_STRING );
+ }
+ static char TypeCode(void) { return Constants::BAM_TAG_TYPE_STRING; }
+};
+
+//! \endcond
+
+} // namespace BamTools
+
+#endif // BAM_CONSTANTS_H
diff --git a/bamtools/src/api/BamIndex.h b/bamtools/src/api/BamIndex.h
new file mode 100644
index 0000000..fd41f69
--- /dev/null
+++ b/bamtools/src/api/BamIndex.h
@@ -0,0 +1,90 @@
+// ***************************************************************************
+// BamIndex.h (c) 2009 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides basic BAM index interface
+// ***************************************************************************
+
+#ifndef BAM_INDEX_H
+#define BAM_INDEX_H
+
+#include "api/api_global.h"
+#include "api/BamAux.h"
+#include <string>
+
+namespace BamTools {
+
+namespace Internal {
+ class BamReaderPrivate;
+} // namespace Internal
+
+/*! \class BamTools::BamIndex
+ \brief Provides methods for generating & loading BAM index files.
+
+ This class straddles the line between public API and internal
+ implementation detail. Most client code should never have to use this
+ class directly.
+
+ It is exposed to the public API to allow advanced users to implement
+ their own custom indexing schemes.
+*/
+
+class API_EXPORT BamIndex {
+
+ // enums
+ public:
+
+ // list of supported BamIndex types
+ enum IndexType { BAMTOOLS = 0
+ , STANDARD
+ };
+
+ // ctor & dtor
+ public:
+ BamIndex(Internal::BamReaderPrivate* reader) : m_reader(reader) { }
+ virtual ~BamIndex(void) { }
+
+ // index interface
+ public:
+ // builds index from associated BAM file & writes out to index file
+ virtual bool Create(void) =0;
+
+ // returns a human-readable description of the last error encountered
+ std::string GetErrorString(void) { return m_errorString; }
+
+ // returns whether reference has alignments or no
+ virtual bool HasAlignments(const int& referenceID) const =0;
+
+ // attempts to use index data to jump to @region, returns success/fail
+ // a "successful" jump indicates no error, but not whether this region has data
+ // * thus, the method sets a flag to indicate whether there are alignments
+ // available after the jump position
+ virtual bool Jump(const BamTools::BamRegion& region, bool* hasAlignmentsInRegion) =0;
+
+ // loads existing data from file into memory
+ virtual bool Load(const std::string& filename) =0;
+
+ // returns the 'type' enum for derived index format
+ virtual BamIndex::IndexType Type(void) const =0;
+
+ //! \cond
+
+ // internal methods
+ protected:
+ void SetErrorString(const std::string& where, const std::string& what) const {
+ m_errorString = where + ": " + what;
+ }
+
+ // data members
+ protected:
+ Internal::BamReaderPrivate* m_reader; // copy, not owned
+ mutable std::string m_errorString;
+
+ //! \endcond
+};
+
+} // namespace BamTools
+
+#endif // BAM_INDEX_H
diff --git a/bamtools/src/api/BamMultiReader.cpp b/bamtools/src/api/BamMultiReader.cpp
new file mode 100644
index 0000000..5c2a065
--- /dev/null
+++ b/bamtools/src/api/BamMultiReader.cpp
@@ -0,0 +1,421 @@
+// ***************************************************************************
+// BamMultiReader.cpp (c) 2010 Erik Garrison, Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 14 January 2013 (DB)
+// ---------------------------------------------------------------------------
+// Convenience class for reading multiple BAM files.
+//
+// This functionality allows applications to work on very large sets of files
+// without requiring intermediate merge, sort, and index steps for each file
+// subset. It also improves the performance of our merge system as it
+// precludes the need to sort merged files.
+// ***************************************************************************
+
+#include "api/BamMultiReader.h"
+#include "api/internal/bam/BamMultiReader_p.h"
+using namespace BamTools;
+
+#include <string>
+#include <vector>
+using namespace std;
+
+/*! \class BamTools::BamMultiReader
+ \brief Convenience class for reading multiple BAM files.
+*/
+/*! \enum BamMultiReader::MergeOrder
+ \brief Used to describe the merge strategy of the BamMultiReader.
+
+ The merge strategy determines which alignment is 'next' from across
+ all opened BAM files.
+*/
+/*! \var BamMultiReader::MergeOrder BamMultiReader::RoundRobinMerge
+ \brief Merge strategy when BAM files are unsorted, or their sorted status is either unknown or ignored
+*/
+/*! \var BamMultiReader::MergeOrder BamMultiReader::MergeByCoordinate
+ \brief Merge strategy when BAM files are sorted by position ('coordinate')
+*/
+/*! \var BamMultiReader::MergeOrder BamMultiReader::MergeByName
+ \brief Merge strategy when BAM files are sorted by read name ('queryname')
+*/
+
+/*! \fn BamMultiReader::BamMultiReader(void)
+ \brief constructor
+*/
+BamMultiReader::BamMultiReader(void)
+ : d(new Internal::BamMultiReaderPrivate)
+{ }
+
+/*! \fn BamMultiReader::~BamMultiReader(void)
+ \brief destructor
+*/
+BamMultiReader::~BamMultiReader(void) {
+ delete d;
+ d = 0;
+}
+
+/*! \fn void BamMultiReader::Close(void)
+ \brief Closes all open BAM files.
+
+ Also clears out all header and reference data.
+
+ \sa CloseFile(), IsOpen(), Open(), BamReader::Close()
+*/
+bool BamMultiReader::Close(void) {
+ return d->Close();
+}
+
+/*! \fn void BamMultiReader::CloseFile(const std::string& filename)
+ \brief Closes requested BAM file.
+
+ Leaves any other file(s) open, along with header and reference data.
+
+ \param[in] filename name of specific BAM file to close
+
+ \sa Close(), IsOpen(), Open(), BamReader::Close()
+*/
+bool BamMultiReader::CloseFile(const std::string& filename) {
+ return d->CloseFile(filename);
+}
+
+/*! \fn bool BamMultiReader::CreateIndexes(const BamIndex::IndexType& type)
+ \brief Creates index files for the current BAM files.
+
+ \param[in] type file format to create, see BamIndex::IndexType for available formats
+ \return \c true if index files created OK
+ \sa LocateIndexes(), OpenIndexes(), BamReader::CreateIndex()
+*/
+bool BamMultiReader::CreateIndexes(const BamIndex::IndexType& type) {
+ return d->CreateIndexes(type);
+}
+
+/*! \fn const std::vector<std::string> BamMultiReader::Filenames(void) const
+ \brief Returns list of filenames for all open BAM files.
+
+ Retrieved filenames will contain whatever was passed via Open().
+ If you need full directory paths here, be sure to include them
+ when you open the BAM files.
+
+ \returns names of open BAM files. If no files are open, returns an empty vector.
+ \sa IsOpen(), BamReader::GetFilename()
+*/
+const std::vector<std::string> BamMultiReader::Filenames(void) const {
+ return d->Filenames();
+}
+
+/*! \fn std::string BamMultiReader::GetErrorString(void) const
+ \brief Returns a human-readable description of the last error that occurred
+
+ This method allows elimination of STDERR pollution. Developers of client code
+ may choose how the messages are displayed to the user, if at all.
+
+ \return error description
+*/
+std::string BamMultiReader::GetErrorString(void) const {
+ return d->GetErrorString();
+}
+
+/*! \fn SamHeader BamMultiReader::GetHeader(void) const
+ \brief Returns unified SAM-format header for all files
+
+ \note Modifying the retrieved text does NOT affect the current
+ BAM files. These files have been opened in a read-only mode. However,
+ your modified header text can be used in conjunction with BamWriter
+ to generate a new BAM file with the appropriate header information.
+
+ \returns header data wrapped in SamHeader object
+ \sa GetHeaderText(), BamReader::GetHeader()
+*/
+SamHeader BamMultiReader::GetHeader(void) const {
+ return d->GetHeader();
+}
+
+/*! \fn std::string BamMultiReader::GetHeaderText(void) const
+ \brief Returns unified SAM-format header text for all files
+
+ \note Modifying the retrieved text does NOT affect the current
+ BAM files. These files have been opened in a read-only mode. However,
+ your modified header text can be used in conjunction with BamWriter
+ to generate a new BAM file with the appropriate header information.
+
+ \returns SAM-formatted header text
+ \sa GetHeader(), BamReader::GetHeaderText()
+*/
+std::string BamMultiReader::GetHeaderText(void) const {
+ return d->GetHeaderText();
+}
+
+/*! \fn BamMultiReader::MergeOrder BamMultiReader::GetMergeOrder(void) const
+ \brief Returns curent merge order strategy.
+
+ \returns current merge order enum value
+ \sa BamMultiReader::MergeOrder, SetExplicitMergeOrder()
+*/
+BamMultiReader::MergeOrder BamMultiReader::GetMergeOrder(void) const {
+ return d->GetMergeOrder();
+}
+
+/*! \fn bool BamMultiReader::GetNextAlignment(BamAlignment& alignment)
+ \brief Retrieves next available alignment.
+
+ Equivalent to BamReader::GetNextAlignment() with respect to what is a valid
+ overlapping alignment and what data gets populated.
+
+ This method takes care of determining which alignment actually is 'next'
+ across multiple files, depending on their sort order.
+
+ \param[out] alignment destination for alignment record data
+ \returns \c true if a valid alignment was found
+ \sa GetNextAlignmentCore(), SetExplicitMergeOrder(), SetRegion(), BamReader::GetNextAlignment()
+*/
+bool BamMultiReader::GetNextAlignment(BamAlignment& nextAlignment) {
+ return d->GetNextAlignment(nextAlignment);
+}
+
+/*! \fn bool BamMultiReader::GetNextAlignmentCore(BamAlignment& alignment)
+ \brief Retrieves next available alignment.
+
+ Equivalent to BamReader::GetNextAlignmentCore() with respect to what is a valid
+ overlapping alignment and what data gets populated.
+
+ This method takes care of determining which alignment actually is 'next'
+ across multiple files, depending on their sort order.
+
+ \param[out] alignment destination for alignment record data
+ \returns \c true if a valid alignment was found
+ \sa GetNextAlignment(), SetExplicitMergeOrder(), SetRegion(), BamReader::GetNextAlignmentCore()
+*/
+bool BamMultiReader::GetNextAlignmentCore(BamAlignment& nextAlignment) {
+ return d->GetNextAlignmentCore(nextAlignment);
+}
+
+/*! \fn int BamMultiReader::GetReferenceCount(void) const
+ \brief Returns number of reference sequences.
+ \sa BamReader::GetReferenceCount()
+*/
+int BamMultiReader::GetReferenceCount(void) const {
+ return d->GetReferenceCount();
+}
+
+/*! \fn const RefVector& BamMultiReader::GetReferenceData(void) const
+ \brief Returns all reference sequence entries.
+ \sa RefData, BamReader::GetReferenceData()
+*/
+const BamTools::RefVector BamMultiReader::GetReferenceData(void) const {
+ return d->GetReferenceData();
+}
+
+/*! \fn int BamMultiReader::GetReferenceID(const std::string& refName) const
+ \brief Returns the ID of the reference with this name.
+
+ If \a refName is not found, returns -1.
+
+ \param[in] refName name of reference to look up
+ \sa BamReader::GetReferenceID()
+*/
+int BamMultiReader::GetReferenceID(const std::string& refName) const {
+ return d->GetReferenceID(refName);
+}
+
+/*! \fn bool BamMultiReader::HasIndexes(void) const
+ \brief Returns \c true if all BAM files have index data available.
+ \sa BamReader::HasIndex()
+*/
+bool BamMultiReader::HasIndexes(void) const {
+ return d->HasIndexes();
+}
+
+/*! \fn bool BamMultiReader::HasOpenReaders(void) const
+ \brief Returns \c true if there are any open BAM files.
+*/
+bool BamMultiReader::HasOpenReaders(void) const {
+ return d->HasOpenReaders();
+}
+
+/*! \fn bool BamMultiReader::Jump(int refID, int position)
+ \brief Performs a random-access jump within current BAM files.
+
+ This is a convenience method, equivalent to calling SetRegion()
+ with only a left boundary specified.
+
+ \param[in] refID ID of reference to jump to
+ \param[in] position (0-based) left boundary
+
+ \returns \c true if jump was successful
+ \sa HasIndex(), BamReader::Jump()
+*/
+
+bool BamMultiReader::Jump(int refID, int position) {
+ return d->Jump(refID, position);
+}
+
+/*! \fn bool BamMultiReader::LocateIndexes(const BamIndex::IndexType& preferredType)
+ \brief Looks for index files that match current BAM files.
+
+ Use this function when you need index files, and perhaps have a
+ preferred index format, but do not depend heavily on which indexes
+ actually get loaded at runtime.
+
+ For each BAM file, this function will defer to your \a preferredType
+ whenever possible. However, if an index file of \a preferredType can
+ not be found, then it will look for any other index file that matches
+ that BAM file.
+
+ An example case would look this:
+ \code
+ BamMultiReader reader;
+
+ // do setup...
+
+ // ensure that all files have an index
+ if ( !reader.LocateIndexes() ) // opens any existing index files that match our BAM files
+ reader.CreateIndexes(); // creates index files for any BAM files that still lack one
+
+ // do interesting stuff using random-access...
+
+ \endcode
+
+ If you want precise control over which index files are loaded, use OpenIndexes()
+ with the desired index filenames. If that function returns false, you can use
+ CreateIndexes() to then build index files of the exact requested format.
+
+ \param[in] preferredType desired index file format, see BamIndex::IndexType for available formats
+ \returns \c true if index files could be found for \b ALL open BAM files
+ \sa BamReader::LocateIndex()
+*/
+bool BamMultiReader::LocateIndexes(const BamIndex::IndexType& preferredType) {
+ return d->LocateIndexes(preferredType);
+}
+
+/*! \fn bool BamMultiReader::Open(const std::vector<std::string>& filenames)
+ \brief Opens BAM files.
+
+ \note Opening BAM files will invalidate any current region set on the multireader.
+ All file pointers will be returned to the beginning of the alignment data. Follow
+ this with Jump() or SetRegion() to establish a region of interest.
+
+ \param[in] filenames list of BAM filenames to open
+ \returns \c true if BAM files were opened successfully
+ \sa Close(), HasOpenReaders(), OpenFile(), OpenIndexes(), BamReader::Open()
+*/
+bool BamMultiReader::Open(const std::vector<std::string>& filenames) {
+ return d->Open(filenames);
+}
+
+/*! \fn bool BamMultiReader::OpenFile(const std::string& filename)
+ \brief Opens a single BAM file.
+
+ Adds another BAM file to multireader "on-the-fly".
+
+ \note Opening a BAM file will invalidate any current region set on the multireader.
+ All file pointers will be returned to the beginning of the alignment data. Follow
+ this with Jump() or SetRegion() to establish a region of interest.
+
+ \param[in] filename BAM filename to open
+ \returns \c true if BAM file was opened successfully
+ \sa Close(), HasOpenReaders(), Open(), OpenIndexes(), BamReader::Open()
+*/
+bool BamMultiReader::OpenFile(const std::string& filename) {
+ return d->OpenFile(filename);
+}
+
+/*! \fn bool BamMultiReader::OpenIndexes(const std::vector<std::string>& indexFilenames)
+ \brief Opens index files for current BAM files.
+
+ \note Currently assumes that index filenames match the order (and number) of
+ BAM files passed to Open().
+
+ \param[in] indexFilenames list of BAM index file names
+ \returns \c true if BAM index file was opened & data loaded successfully
+ \sa LocateIndex(), Open(), SetIndex(), BamReader::OpenIndex()
+*/
+bool BamMultiReader::OpenIndexes(const std::vector<std::string>& indexFilenames) {
+ return d->OpenIndexes(indexFilenames);
+}
+
+/*! \fn bool BamMultiReader::Rewind(void)
+ \brief Returns the internal file pointers to the beginning of alignment records.
+
+ Useful for performing multiple sequential passes through BAM files.
+ Calling this function clears any prior region that may have been set.
+
+ \returns \c true if rewind operation was successful
+ \sa Jump(), SetRegion(), BamReader::Rewind()
+*/
+bool BamMultiReader::Rewind(void) {
+ return d->Rewind();
+}
+
+/*! \fn void BamMultiReader::SetExplicitMergeOrder(BamMultiReader::MergeOrder order)
+ \brief Sets an explicit merge order, regardless of the BAM files' SO header tag.
+
+ The default behavior of the BamMultiReader is to check the SO tag in the BAM files'
+ SAM header text to determine the merge strategy". The merge strategy is used to
+ determine from which BAM file the next alignment should come when either
+ GetNextAlignment() or GetNextAlignmentCore() are called. If files share a
+ 'coordinate' or 'queryname' value for this tag, then the merge strategy is
+ selected accordingly. If any of them do not match, or if any fileis marked as
+ 'unsorted', then the merge strategy is simply a round-robin.
+
+ This method allows client code to explicitly override the lookup behavior. This
+ method can be useful when you know, for example, that your BAM files are sorted
+ by coordinate but upstream processes did not set the header tag properly.
+
+ \note This method should \bold not be called while reading alignments via
+ GetNextAlignment() or GetNextAlignmentCore(). For proper results, you should
+ call this method before (or immediately after) opening files, rewinding,
+ jumping, etc. but \bold not once alignment fetching has started. There is
+ nothing in the API to prevent you from doing so, but the results may be
+ unexpected.
+
+ \returns \c true if merge order could be successfully applied
+ \sa BamMultiReader::MergeOrder, GetMergeOrder(), GetNextAlignment(), GetNextAlignmentCore()
+*/
+bool BamMultiReader::SetExplicitMergeOrder(BamMultiReader::MergeOrder order) {
+ return d->SetExplicitMergeOrder(order);
+}
+
+/*! \fn bool BamMultiReader::SetRegion(const BamRegion& region)
+ \brief Sets a target region of interest
+
+ Equivalent to calling BamReader::SetRegion() on all open BAM files.
+
+ \warning BamRegion now represents a zero-based, HALF-OPEN interval.
+ In previous versions of BamTools (0.x & 1.x) all intervals were treated
+ as zero-based, CLOSED.
+
+ \param[in] region desired region-of-interest to activate
+ \returns \c true if ALL readers set the region successfully
+ \sa HasIndexes(), Jump(), BamReader::SetRegion()
+*/
+bool BamMultiReader::SetRegion(const BamRegion& region) {
+ return d->SetRegion(region);
+}
+
+/*! \fn bool BamMultiReader::SetRegion(const int& leftRefID,
+ const int& leftPosition,
+ const int& rightRefID,
+ const int& rightPosition)
+ \brief Sets a target region of interest
+
+ This is an overloaded function. Equivalent to calling BamReader::SetRegion() on all open BAM files.
+
+ \warning This function now expects a zero-based, HALF-OPEN interval.
+ In previous versions of BamTools (0.x & 1.x) all intervals were treated
+ as zero-based, CLOSED.
+
+ \param[in] leftRefID referenceID of region's left boundary
+ \param[in] leftPosition position of region's left boundary
+ \param[in] rightRefID reference ID of region's right boundary
+ \param[in] rightPosition position of region's right boundary
+
+ \returns \c true if ALL readers set the region successfully
+ \sa HasIndexes(), Jump(), BamReader::SetRegion()
+*/
+bool BamMultiReader::SetRegion(const int& leftRefID,
+ const int& leftPosition,
+ const int& rightRefID,
+ const int& rightPosition)
+{
+ return d->SetRegion( BamRegion(leftRefID, leftPosition, rightRefID, rightPosition) );
+}
diff --git a/bamtools/src/api/BamMultiReader.h b/bamtools/src/api/BamMultiReader.h
new file mode 100644
index 0000000..4f8c133
--- /dev/null
+++ b/bamtools/src/api/BamMultiReader.h
@@ -0,0 +1,127 @@
+// ***************************************************************************
+// BamMultiReader.h (c) 2010 Erik Garrison, Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 14 January 2013 (DB)
+// ---------------------------------------------------------------------------
+// Convenience class for reading multiple BAM files.
+// ***************************************************************************
+
+#ifndef BAMMULTIREADER_H
+#define BAMMULTIREADER_H
+
+#include "api/api_global.h"
+#include "api/BamReader.h"
+#include <map>
+#include <sstream>
+#include <string>
+#include <utility>
+
+namespace BamTools {
+
+namespace Internal {
+ class BamMultiReaderPrivate;
+} // namespace Internal
+
+class API_EXPORT BamMultiReader {
+
+ // enums
+ public:
+ // possible merge order strategies
+ enum MergeOrder { RoundRobinMerge = 0
+ , MergeByCoordinate
+ , MergeByName
+ };
+
+ // constructor / destructor
+ public:
+ BamMultiReader(void);
+ ~BamMultiReader(void);
+
+ // public interface
+ public:
+
+ // ----------------------
+ // BAM file operations
+ // ----------------------
+
+ // closes all open BAM files
+ bool Close(void);
+ // close only the requested BAM file
+ bool CloseFile(const std::string& filename);
+ // returns list of filenames for all open BAM files
+ const std::vector<std::string> Filenames(void) const;
+ // returns curent merge order strategy
+ BamMultiReader::MergeOrder GetMergeOrder(void) const;
+ // returns true if multireader has any open BAM files
+ bool HasOpenReaders(void) const;
+ // performs random-access jump within current BAM files
+ bool Jump(int refID, int position = 0);
+ // opens BAM files
+ bool Open(const std::vector<std::string>& filenames);
+ // opens a single BAM file, adding to any other current BAM files
+ bool OpenFile(const std::string& filename);
+ // returns file pointers to beginning of alignments
+ bool Rewind(void);
+ // sets an explicit merge order, regardless of the BAM files' SO header tag
+ bool SetExplicitMergeOrder(BamMultiReader::MergeOrder order);
+ // sets the target region of interest
+ bool SetRegion(const BamRegion& region);
+ // sets the target region of interest
+ bool SetRegion(const int& leftRefID,
+ const int& leftPosition,
+ const int& rightRefID,
+ const int& rightPosition);
+
+ // ----------------------
+ // access alignment data
+ // ----------------------
+
+ // retrieves next available alignment
+ bool GetNextAlignment(BamAlignment& alignment);
+ // retrieves next available alignment (without populating the alignment's string data fields)
+ bool GetNextAlignmentCore(BamAlignment& alignment);
+
+ // ----------------------
+ // access auxiliary data
+ // ----------------------
+
+ // returns unified SAM header for all files
+ SamHeader GetHeader(void) const;
+ // returns unified SAM header text for all files
+ std::string GetHeaderText(void) const;
+ // returns number of reference sequences
+ int GetReferenceCount(void) const;
+ // returns all reference sequence entries.
+ const BamTools::RefVector GetReferenceData(void) const;
+ // returns the ID of the reference with this name.
+ int GetReferenceID(const std::string& refName) const;
+
+ // ----------------------
+ // BAM index operations
+ // ----------------------
+
+ // creates index files for current BAM files
+ bool CreateIndexes(const BamIndex::IndexType& type = BamIndex::STANDARD);
+ // returns true if all BAM files have index data available
+ bool HasIndexes(void) const;
+ // looks for index files that match current BAM files
+ bool LocateIndexes(const BamIndex::IndexType& preferredType = BamIndex::STANDARD);
+ // opens index files for current BAM files.
+ bool OpenIndexes(const std::vector<std::string>& indexFilenames);
+
+ // ----------------------
+ // error handling
+ // ----------------------
+
+ // returns a human-readable description of the last error that occurred
+ std::string GetErrorString(void) const;
+
+ // private implementation
+ private:
+ Internal::BamMultiReaderPrivate* d;
+};
+
+} // namespace BamTools
+
+#endif // BAMMULTIREADER_H
diff --git a/bamtools/src/api/BamReader.cpp b/bamtools/src/api/BamReader.cpp
new file mode 100644
index 0000000..0728d96
--- /dev/null
+++ b/bamtools/src/api/BamReader.cpp
@@ -0,0 +1,383 @@
+// ***************************************************************************
+// BamReader.cpp (c) 2009 Derek Barnett, Michael Str�mberg
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 18 November 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides read access to BAM files.
+// ***************************************************************************
+
+#include "api/BamReader.h"
+#include "api/internal/bam/BamReader_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <string>
+#include <vector>
+using namespace std;
+
+/*! \class BamTools::BamReader
+ \brief Provides read access to BAM files.
+*/
+
+/*! \fn BamReader::BamReader(void)
+ \brief constructor
+*/
+BamReader::BamReader(void)
+ : d(new BamReaderPrivate(this))
+{ }
+
+/*! \fn BamReader::~BamReader(void)
+ \brief destructor
+*/
+BamReader::~BamReader(void) {
+ delete d;
+ d = 0;
+}
+
+/*! \fn bool BamReader::Close(void)
+ \brief Closes the current BAM file.
+
+ Also clears out all header and reference data.
+
+ \return \c true if file closed OK
+ \sa IsOpen(), Open()
+*/
+bool BamReader::Close(void) {
+ return d->Close();
+}
+
+/*! \fn bool BamReader::CreateIndex(const BamIndex::IndexType& type)
+ \brief Creates an index file for current BAM file.
+
+ \param[in] type file format to create, see BamIndex::IndexType for available formats
+ \return \c true if index created OK
+ \sa LocateIndex(), OpenIndex()
+*/
+bool BamReader::CreateIndex(const BamIndex::IndexType& type) {
+ return d->CreateIndex(type);
+}
+
+/*! \fn const SamHeader& BamReader::GetConstSamHeader(void) const
+ \brief Returns const reference to SAM header data.
+
+ Allows for read-only queries of SAM header data.
+
+ If you do not need to modify the SAM header, use this method to avoid the
+ potentially expensive copy used by GetHeader().
+
+ \note
+ \returns const reference to header data object
+ \sa GetHeader(), GetHeaderText()
+*/
+const SamHeader& BamReader::GetConstSamHeader(void) const {
+ return d->GetConstSamHeader();
+}
+
+/*! \fn std::string BamReader::GetErrorString(void) const
+ \brief Returns a human-readable description of the last error that occurred
+
+ This method allows elimination of STDERR pollution. Developers of client code
+ may choose how the messages are displayed to the user, if at all.
+
+ \return error description
+*/
+string BamReader::GetErrorString(void) const {
+ return d->GetErrorString();
+}
+
+/*! \fn const std::string BamReader::GetFilename(void) const
+ \brief Returns name of current BAM file.
+
+ Retrieved filename will contain whatever was passed via Open().
+ If you need full directory paths here, be sure to include them
+ when you open the BAM file.
+
+ \returns name of open BAM file. If no file is open, returns an empty string.
+ \sa IsOpen()
+*/
+const std::string BamReader::GetFilename(void) const {
+ return d->Filename();
+}
+
+/*! \fn SamHeader BamReader::GetHeader(void) const
+ \brief Returns SAM header data.
+
+ Header data is wrapped in a SamHeader object that can be conveniently queried and/or modified.
+ If you only need read access, consider using GetConstSamHeader() instead.
+
+ \note Modifying the retrieved SamHeader object does NOT affect the
+ current BAM file. This file has been opened in a read-only mode.
+ However, your modified SamHeader object can be used in conjunction with
+ BamWriter to generate a new BAM file with the appropriate header information.
+
+ \returns header data object
+ \sa GetConstSamHeader(), GetHeaderText()
+*/
+SamHeader BamReader::GetHeader(void) const {
+ return d->GetSamHeader();
+}
+
+/*! \fn std::string BamReader::GetHeaderText(void) const
+ \brief Returns SAM header data, as SAM-formatted text.
+
+ \note Modifying the retrieved text does NOT affect the current
+ BAM file. This file has been opened in a read-only mode. However,
+ your modified header text can be used in conjunction with BamWriter
+ to generate a new BAM file with the appropriate header information.
+
+ \returns SAM-formatted header text
+ \sa GetHeader()
+*/
+std::string BamReader::GetHeaderText(void) const {
+ return d->GetHeaderText();
+}
+
+/*! \fn bool BamReader::GetNextAlignment(BamAlignment& alignment)
+ \brief Retrieves next available alignment.
+
+ Attempts to read the next alignment record from BAM file, and checks to see
+ if it overlaps the current region. If no region is currently set, then the
+ next alignment available is always considered valid.
+
+ If a region has been set, via Jump() or SetRegion(), an alignment is only
+ considered valid if it overlaps the region. If the actual 'next' alignment record
+ in the BAM file does not overlap this region, then this function will read sequentially
+ through the file until the next alignment that overlaps this region is found.
+ Once the region has been exhausted (i.e. the next alignment loaded is beyond the region),
+ the function aborts and returns \c false. In this case, there is no point to continue
+ reading, assuming properly sorted alignments.
+
+ This function fully populates all of the alignment's available data fields,
+ including the string data fields (read name, bases, qualities, tags, filename).
+ If only positional data (refID, position, CIGAR ops, alignment flags, etc.)
+ are required, consider using GetNextAlignmentCore() for a significant
+ performance boost.
+
+ \param[out] alignment destination for alignment record data
+ \returns \c true if a valid alignment was found
+*/
+bool BamReader::GetNextAlignment(BamAlignment& alignment) {
+ return d->GetNextAlignment(alignment);
+}
+
+/*! \fn bool BamReader::GetNextAlignmentCore(BamAlignment& alignment)
+ \brief Retrieves next available alignment, without populating the alignment's string data fields.
+
+ Equivalent to GetNextAlignment() with respect to what is a valid overlapping alignment.
+
+ However, this method does NOT populate the alignment's string data fields
+ (read name, bases, qualities, tags, filename). This provides a boost in speed
+ when these fields are not required for every alignment. These fields can be
+ populated 'lazily' (as needed) by calling BamAlignment::BuildCharData() later.
+
+ \param[out] alignment destination for alignment record data
+ \returns \c true if a valid alignment was found
+ \sa SetRegion()
+*/
+bool BamReader::GetNextAlignmentCore(BamAlignment& alignment) {
+ return d->GetNextAlignmentCore(alignment);
+}
+
+/*! \fn int BamReader::GetReferenceCount(void) const
+ \brief Returns number of reference sequences.
+*/
+int BamReader::GetReferenceCount(void) const {
+ return d->GetReferenceCount();
+}
+
+/*! \fn const RefVector& BamReader::GetReferenceData(void) const
+ \brief Returns all reference sequence entries.
+ \sa RefData
+*/
+const RefVector& BamReader::GetReferenceData(void) const {
+ return d->GetReferenceData();
+}
+
+/*! \fn int BamReader::GetReferenceID(const std::string& refName) const
+ \brief Returns the ID of the reference with this name.
+
+ If \a refName is not found, returns -1.
+
+ \param[in] refName name of reference to look up
+*/
+int BamReader::GetReferenceID(const std::string& refName) const {
+ return d->GetReferenceID(refName);
+}
+
+/*! \fn bool BamReader::HasIndex(void) const
+ \brief Returns \c true if index data is available.
+*/
+bool BamReader::HasIndex(void) const {
+ return d->HasIndex();
+}
+
+/*! \fn bool BamReader::IsOpen(void) const
+ \brief Returns \c true if a BAM file is open for reading.
+*/
+bool BamReader::IsOpen(void) const {
+ return d->IsOpen();
+}
+
+/*! \fn bool BamReader::Jump(int refID, int position)
+ \brief Performs a random-access jump within BAM file.
+
+ This is a convenience method, equivalent to calling SetRegion()
+ with only a left boundary specified.
+
+ \param[in] refID left-bound reference ID
+ \param[in] position left-bound position
+
+ \returns \c true if jump was successful
+ \sa HasIndex()
+*/
+bool BamReader::Jump(int refID, int position) {
+ return d->SetRegion( BamRegion(refID, position) );
+}
+
+/*! \fn bool BamReader::LocateIndex(const BamIndex::IndexType& preferredType)
+ \brief Looks in BAM file's directory for a matching index file.
+
+ Use this function when you need an index file, and perhaps have a
+ preferred index format, but do not depend heavily on which format
+ actually gets loaded at runtime.
+
+ This function will defer to your \a preferredType whenever possible.
+ However, if an index file of \a preferredType can not be found, then
+ it will look for any other index file that corresponds to this BAM file.
+
+ If you want precise control over which index file is loaded, use OpenIndex()
+ with the desired index filename. If that function returns false, you can use
+ CreateIndex() to then build an index of the exact requested format.
+
+ \param[in] preferredType desired index file format, see BamIndex::IndexType for available formats
+
+ \returns \c true if (any) index file could be found
+*/
+bool BamReader::LocateIndex(const BamIndex::IndexType& preferredType) {
+ return d->LocateIndex(preferredType);
+}
+
+/*! \fn bool BamReader::Open(const std::string& filename)
+ \brief Opens a BAM file.
+
+ If BamReader is already opened on another file, this function closes
+ that file, then attempts to open requested \a filename.
+
+ \param[in] filename name of BAM file to open
+
+ \returns \c true if BAM file was opened successfully
+ \sa Close(), IsOpen(), OpenIndex()
+*/
+bool BamReader::Open(const std::string& filename) {
+ return d->Open(filename);
+}
+
+/*! \fn bool BamReader::OpenIndex(const std::string& indexFilename)
+ \brief Opens a BAM index file.
+
+ \param[in] indexFilename name of BAM index file to open
+
+ \returns \c true if BAM index file was opened & data loaded successfully
+ \sa LocateIndex(), Open(), SetIndex()
+*/
+bool BamReader::OpenIndex(const std::string& indexFilename) {
+ return d->OpenIndex(indexFilename);
+}
+
+/*! \fn bool BamReader::Rewind(void)
+ \brief Returns the internal file pointer to the first alignment record.
+
+ Useful for performing multiple sequential passes through a BAM file.
+ Calling this function clears any prior region that may have been set.
+
+ \note This function sets the file pointer to first alignment record
+ in the BAM file, NOT the beginning of the file.
+
+ \returns \c true if rewind operation was successful
+ \sa Jump(), SetRegion()
+*/
+bool BamReader::Rewind(void) {
+ return d->Rewind();
+}
+
+/*! \fn void BamReader::SetIndex(BamIndex* index)
+ \brief Sets a custom BamIndex on this reader.
+
+ Only necessary for custom BamIndex subclasses. Most clients should
+ never have to use this function.
+
+ Example:
+ \code
+ BamReader reader;
+ reader.SetIndex(new MyCustomBamIndex);
+ \endcode
+
+ \note BamReader takes ownership of \a index - i.e. the BamReader will
+ take care of deleting it when the reader is destructed, when the current
+ BAM file is closed, or when a new index is requested.
+
+ \param[in] index custom BamIndex subclass created by client
+ \sa CreateIndex(), LocateIndex(), OpenIndex()
+*/
+void BamReader::SetIndex(BamIndex* index) {
+ d->SetIndex(index);
+}
+
+/*! \fn bool BamReader::SetRegion(const BamRegion& region)
+ \brief Sets a target region of interest
+
+ Requires that index data be available. Attempts a random-access
+ jump in the BAM file, near \a region left boundary position.
+
+ Subsequent calls to GetNextAlignment() or GetNextAlignmentCore()
+ will only return \c true when alignments can be found that overlap
+ this \a region.
+
+ A \a region with no right boundary is considered open-ended, meaning
+ that all alignments that lie downstream of the left boundary are
+ considered valid, continuing to the end of the BAM file.
+
+ \warning BamRegion now represents a zero-based, HALF-OPEN interval.
+ In previous versions of BamTools (0.x & 1.x) all intervals were treated
+ as zero-based, CLOSED.
+
+ \param[in] region desired region-of-interest to activate
+
+ \returns \c true if reader was able to jump successfully to the region's left boundary
+ \sa HasIndex(), Jump()
+*/
+bool BamReader::SetRegion(const BamRegion& region) {
+ return d->SetRegion(region);
+}
+
+/*! \fn bool BamReader::SetRegion(const int& leftRefID,
+ const int& leftPosition,
+ const int& rightRefID,
+ const int& rightPosition)
+ \brief Sets a target region of interest.
+
+ This is an overloaded function.
+
+ \warning This function expects a zero-based, HALF-OPEN interval.
+ In previous versions of BamTools (0.x & 1.x) all intervals were treated
+ as zero-based, CLOSED.
+
+ \param[in] leftRefID referenceID of region's left boundary
+ \param[in] leftPosition position of region's left boundary
+ \param[in] rightRefID reference ID of region's right boundary
+ \param[in] rightPosition position of region's right boundary
+
+ \returns \c true if reader was able to jump successfully to the region's left boundary
+ \sa HasIndex(), Jump()
+*/
+bool BamReader::SetRegion(const int& leftRefID,
+ const int& leftBound,
+ const int& rightRefID,
+ const int& rightBound)
+{
+ return d->SetRegion( BamRegion(leftRefID, leftBound, rightRefID, rightBound) );
+}
diff --git a/bamtools/src/api/BamReader.h b/bamtools/src/api/BamReader.h
new file mode 100644
index 0000000..15b4135
--- /dev/null
+++ b/bamtools/src/api/BamReader.h
@@ -0,0 +1,119 @@
+// ***************************************************************************
+// BamReader.h (c) 2009 Derek Barnett, Michael Str�mberg
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 18 November 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides read access to BAM files.
+// ***************************************************************************
+
+#ifndef BAMREADER_H
+#define BAMREADER_H
+
+#include "api/api_global.h"
+#include "api/BamAlignment.h"
+#include "api/BamIndex.h"
+#include "api/SamHeader.h"
+#include <string>
+
+namespace BamTools {
+
+namespace Internal {
+ class BamReaderPrivate;
+} // namespace Internal
+
+class API_EXPORT BamReader {
+
+ // constructor / destructor
+ public:
+ BamReader(void);
+ ~BamReader(void);
+
+ // public interface
+ public:
+
+ // ----------------------
+ // BAM file operations
+ // ----------------------
+
+ // closes the current BAM file
+ bool Close(void);
+ // returns filename of current BAM file
+ const std::string GetFilename(void) const;
+ // returns true if a BAM file is open for reading
+ bool IsOpen(void) const;
+ // performs random-access jump within BAM file
+ bool Jump(int refID, int position = 0);
+ // opens a BAM file
+ bool Open(const std::string& filename);
+ // returns internal file pointer to beginning of alignment data
+ bool Rewind(void);
+ // sets the target region of interest
+ bool SetRegion(const BamRegion& region);
+ // sets the target region of interest
+ bool SetRegion(const int& leftRefID,
+ const int& leftPosition,
+ const int& rightRefID,
+ const int& rightPosition);
+
+ // ----------------------
+ // access alignment data
+ // ----------------------
+
+ // retrieves next available alignment
+ bool GetNextAlignment(BamAlignment& alignment);
+ // retrieves next available alignmnet (without populating the alignment's string data fields)
+ bool GetNextAlignmentCore(BamAlignment& alignment);
+
+ // ----------------------
+ // access header data
+ // ----------------------
+
+ // returns a read-only reference to SAM header data
+ const SamHeader& GetConstSamHeader(void) const;
+ // returns an editable copy of SAM header data
+ SamHeader GetHeader(void) const;
+ // returns SAM header data, as SAM-formatted text
+ std::string GetHeaderText(void) const;
+
+ // ----------------------
+ // access reference data
+ // ----------------------
+
+ // returns the number of reference sequences
+ int GetReferenceCount(void) const;
+ // returns all reference sequence entries
+ const RefVector& GetReferenceData(void) const;
+ // returns the ID of the reference with this name
+ int GetReferenceID(const std::string& refName) const;
+
+ // ----------------------
+ // BAM index operations
+ // ----------------------
+
+ // creates an index file for current BAM file, using the requested index type
+ bool CreateIndex(const BamIndex::IndexType& type = BamIndex::STANDARD);
+ // returns true if index data is available
+ bool HasIndex(void) const;
+ // looks in BAM file's directory for a matching index file
+ bool LocateIndex(const BamIndex::IndexType& preferredType = BamIndex::STANDARD);
+ // opens a BAM index file
+ bool OpenIndex(const std::string& indexFilename);
+ // sets a custom BamIndex on this reader
+ void SetIndex(BamIndex* index);
+
+ // ----------------------
+ // error handling
+ // ----------------------
+
+ // returns a human-readable description of the last error that occurred
+ std::string GetErrorString(void) const;
+
+ // private implementation
+ private:
+ Internal::BamReaderPrivate* d;
+};
+
+} // namespace BamTools
+
+#endif // BAMREADER_H
diff --git a/bamtools/src/api/BamWriter.cpp b/bamtools/src/api/BamWriter.cpp
new file mode 100644
index 0000000..cbbfdae
--- /dev/null
+++ b/bamtools/src/api/BamWriter.cpp
@@ -0,0 +1,152 @@
+// ***************************************************************************
+// BamWriter.cpp (c) 2009 Michael Str�mberg, Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 25 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides the basic functionality for producing BAM files
+// ***************************************************************************
+
+#include "api/BamAlignment.h"
+#include "api/BamWriter.h"
+#include "api/SamHeader.h"
+#include "api/internal/bam/BamWriter_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+using namespace std;
+
+/*! \class BamTools::BamWriter
+ \brief Provides write access for generating BAM files.
+*/
+/*! \enum BamTools::BamWriter::CompressionMode
+ \brief This enum describes the compression behaviors for output BAM files.
+*/
+/*! \var BamWriter::CompressionMode BamWriter::Compressed
+ \brief Use normal BAM compression
+*/
+/*! \var BamWriter::CompressionMode BamWriter::Uncompressed
+ \brief Disable BAM compression
+
+ Useful in situations where the BAM data is streamed (e.g. piping).
+ It would be wasteful to compress, and then immediately decompress
+ the data.
+*/
+
+/*! \fn BamWriter::BamWriter(void)
+ \brief constructor
+*/
+BamWriter::BamWriter(void)
+ : d(new BamWriterPrivate)
+{ }
+
+/*! \fn BamWriter::~BamWriter(void)
+ \brief destructor
+*/
+BamWriter::~BamWriter(void) {
+ delete d;
+ d = 0;
+}
+
+/*! \fn BamWriter::Close(void)
+ \brief Closes the current BAM file.
+ \sa Open()
+*/
+void BamWriter::Close(void) {
+ d->Close();
+}
+
+/*! \fn std::string BamWriter::GetErrorString(void) const
+ \brief Returns a human-readable description of the last error that occurred
+
+ This method allows elimination of STDERR pollution. Developers of client code
+ may choose how the messages are displayed to the user, if at all.
+
+ \return error description
+*/
+std::string BamWriter::GetErrorString(void) const {
+ return d->GetErrorString();
+}
+
+/*! \fn bool BamWriter::IsOpen(void) const
+ \brief Returns \c true if BAM file is open for writing.
+ \sa Open()
+*/
+bool BamWriter::IsOpen(void) const {
+ return d->IsOpen();
+}
+
+/*! \fn bool BamWriter::Open(const std::string& filename,
+ const std::string& samHeaderText,
+ const RefVector& referenceSequences)
+ \brief Opens a BAM file for writing.
+
+ Will overwrite the BAM file if it already exists.
+
+ \param[in] filename name of output BAM file
+ \param[in] samHeaderText header data, as SAM-formatted string
+ \param[in] referenceSequences list of reference entries
+
+ \return \c true if opened successfully
+ \sa Close(), IsOpen(), BamReader::GetHeaderText(), BamReader::GetReferenceData()
+*/
+bool BamWriter::Open(const std::string& filename,
+ const std::string& samHeaderText,
+ const RefVector& referenceSequences)
+{
+ return d->Open(filename, samHeaderText, referenceSequences);
+}
+
+/*! \fn bool BamWriter::Open(const std::string& filename,
+ const SamHeader& samHeader,
+ const RefVector& referenceSequences)
+ \brief Opens a BAM file for writing.
+
+ This is an overloaded function.
+
+ Will overwrite the BAM file if it already exists.
+
+ \param[in] filename name of output BAM file
+ \param[in] samHeader header data, wrapped in SamHeader object
+ \param[in] referenceSequences list of reference entries
+
+ \return \c true if opened successfully
+ \sa Close(), IsOpen(), BamReader::GetHeader(), BamReader::GetReferenceData()
+*/
+bool BamWriter::Open(const std::string& filename,
+ const SamHeader& samHeader,
+ const RefVector& referenceSequences)
+{
+ return d->Open(filename, samHeader.ToString(), referenceSequences);
+}
+
+/*! \fn void BamWriter::SaveAlignment(const BamAlignment& alignment)
+ \brief Saves an alignment to the BAM file.
+
+ \param[in] alignment BamAlignment record to save
+ \sa BamReader::GetNextAlignment(), BamReader::GetNextAlignmentCore()
+*/
+bool BamWriter::SaveAlignment(const BamAlignment& alignment) {
+ return d->SaveAlignment(alignment);
+}
+
+/*! \fn void BamWriter::SetCompressionMode(const BamWriter::CompressionMode& compressionMode)
+ \brief Sets the output compression mode.
+
+ Default mode is BamWriter::Compressed.
+
+ \note Changing the compression mode is disabled on open files (i.e. the request will
+ be ignored). Be sure to call this function before opening the BAM file.
+
+ \code
+ BamWriter writer;
+ writer.SetCompressionMode(BamWriter::Uncompressed);
+ writer.Open( ... );
+ // ...
+ \endcode
+
+ \param[in] compressionMode desired output compression behavior
+ \sa IsOpen(), Open()
+*/
+void BamWriter::SetCompressionMode(const BamWriter::CompressionMode& compressionMode) {
+ d->SetWriteCompressed( compressionMode == BamWriter::Compressed );
+}
diff --git a/bamtools/src/api/BamWriter.h b/bamtools/src/api/BamWriter.h
new file mode 100644
index 0000000..68257ee
--- /dev/null
+++ b/bamtools/src/api/BamWriter.h
@@ -0,0 +1,69 @@
+// ***************************************************************************
+// BamWriter.h (c) 2009 Michael Str�mberg, Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides the basic functionality for producing BAM files
+// ***************************************************************************
+
+#ifndef BAMWRITER_H
+#define BAMWRITER_H
+
+#include "api/api_global.h"
+#include "api/BamAux.h"
+#include <string>
+
+namespace BamTools {
+
+class BamAlignment;
+class SamHeader;
+
+//! \cond
+namespace Internal {
+ class BamWriterPrivate;
+} // namespace Internal
+//! \endcond
+
+class API_EXPORT BamWriter {
+
+ // enums
+ public:
+ enum CompressionMode { Compressed = 0
+ , Uncompressed
+ };
+
+ // ctor & dtor
+ public:
+ BamWriter(void);
+ ~BamWriter(void);
+
+ // public interface
+ public:
+ // closes the current BAM file
+ void Close(void);
+ // returns a human-readable description of the last error that occurred
+ std::string GetErrorString(void) const;
+ // returns true if BAM file is open for writing
+ bool IsOpen(void) const;
+ // opens a BAM file for writing
+ bool Open(const std::string& filename,
+ const std::string& samHeaderText,
+ const RefVector& referenceSequences);
+ // opens a BAM file for writing
+ bool Open(const std::string& filename,
+ const SamHeader& samHeader,
+ const RefVector& referenceSequences);
+ // saves the alignment to the alignment archive
+ bool SaveAlignment(const BamAlignment& alignment);
+ // sets the output compression mode
+ void SetCompressionMode(const BamWriter::CompressionMode& compressionMode);
+
+ // private implementation
+ private:
+ Internal::BamWriterPrivate* d;
+};
+
+} // namespace BamTools
+
+#endif // BAMWRITER_H
diff --git a/bamtools/src/api/CMakeLists.txt b/bamtools/src/api/CMakeLists.txt
new file mode 100644
index 0000000..7e3d3ca
--- /dev/null
+++ b/bamtools/src/api/CMakeLists.txt
@@ -0,0 +1,83 @@
+# ==========================
+# BamTools CMakeLists.txt
+# (c) 2010 Derek Barnett
+#
+# src/api/
+# ==========================
+
+# list include paths
+include_directories( ${BamTools_SOURCE_DIR}/src )
+
+# add compiler definitions
+add_definitions( -DBAMTOOLS_API_LIBRARY ) # (for proper exporting of library symbols)
+add_definitions( -fPIC ) # (attempt to force PIC compiling on CentOS, not being set on shared libs by CMake)
+
+# fetch all internal source files
+add_subdirectory( internal )
+
+# make list of all API source files
+set( BamToolsAPISources
+ BamAlignment.cpp
+ BamMultiReader.cpp
+ BamReader.cpp
+ BamWriter.cpp
+ SamHeader.cpp
+ SamProgram.cpp
+ SamProgramChain.cpp
+ SamReadGroup.cpp
+ SamReadGroupDictionary.cpp
+ SamSequence.cpp
+ SamSequenceDictionary.cpp
+ ${InternalSources}
+)
+
+# create main BamTools API shared library
+add_library( BamTools SHARED ${BamToolsAPISources} )
+set_target_properties( BamTools PROPERTIES
+ SOVERSION "2.3.0"
+ OUTPUT_NAME "bamtools" )
+
+# create main BamTools API static library
+add_library( BamTools-static STATIC ${BamToolsAPISources} )
+set_target_properties( BamTools-static PROPERTIES
+ OUTPUT_NAME "bamtools"
+ PREFIX "lib" )
+
+# link libraries automatically with zlib (and Winsock2, if applicable)
+if( WIN32 )
+ set( APILibs z ws2_32 )
+else()
+ set( APILibs z )
+endif()
+
+target_link_libraries( BamTools ${APILibs} )
+target_link_libraries( BamTools-static ${APILibs} )
+
+# set library install destinations
+install( TARGETS BamTools LIBRARY DESTINATION "lib/bamtools" RUNTIME DESTINATION "bin")
+install( TARGETS BamTools-static ARCHIVE DESTINATION "lib/bamtools")
+
+# export API headers
+include(../ExportHeader.cmake)
+set(ApiIncludeDir "api")
+ExportHeader(APIHeaders api_global.h ${ApiIncludeDir})
+ExportHeader(APIHeaders BamAlgorithms.h ${ApiIncludeDir})
+ExportHeader(APIHeaders BamAlignment.h ${ApiIncludeDir})
+ExportHeader(APIHeaders BamAux.h ${ApiIncludeDir})
+ExportHeader(APIHeaders BamConstants.h ${ApiIncludeDir})
+ExportHeader(APIHeaders BamIndex.h ${ApiIncludeDir})
+ExportHeader(APIHeaders BamMultiReader.h ${ApiIncludeDir})
+ExportHeader(APIHeaders BamReader.h ${ApiIncludeDir})
+ExportHeader(APIHeaders BamWriter.h ${ApiIncludeDir})
+ExportHeader(APIHeaders IBamIODevice.h ${ApiIncludeDir})
+ExportHeader(APIHeaders SamConstants.h ${ApiIncludeDir})
+ExportHeader(APIHeaders SamHeader.h ${ApiIncludeDir})
+ExportHeader(APIHeaders SamProgram.h ${ApiIncludeDir})
+ExportHeader(APIHeaders SamProgramChain.h ${ApiIncludeDir})
+ExportHeader(APIHeaders SamReadGroup.h ${ApiIncludeDir})
+ExportHeader(APIHeaders SamReadGroupDictionary.h ${ApiIncludeDir})
+ExportHeader(APIHeaders SamSequence.h ${ApiIncludeDir})
+ExportHeader(APIHeaders SamSequenceDictionary.h ${ApiIncludeDir})
+
+set( AlgorithmsIncludeDir "api/algorithms" )
+ExportHeader( AlgorithmsHeaders algorithms/Sort.h ${AlgorithmsIncludeDir} )
diff --git a/bamtools/src/api/IBamIODevice.h b/bamtools/src/api/IBamIODevice.h
new file mode 100644
index 0000000..cf64129
--- /dev/null
+++ b/bamtools/src/api/IBamIODevice.h
@@ -0,0 +1,98 @@
+// ***************************************************************************
+// IBamIODevice.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Base class for all BAM I/O devices (e.g. local file, pipe, HTTP, FTP, etc.)
+//
+// Derived classes should provide protocol-specific implementations for
+// reading/writing plain bytes, as well as other I/O-related behaviors.
+//
+// Since IBamIODevices may be defined in client code, the internal
+// BamExceptions are NOT allowed to be thrown from devices, including the
+// built-in ones. This keeps a consistent interface at the BgzfStream for
+// handling any device type. Use the error string for relaying error messages.
+// ***************************************************************************
+
+#ifndef IBAMIODEVICE_H
+#define IBAMIODEVICE_H
+
+#include "api/api_global.h"
+#include <cstdio>
+#include <string>
+
+namespace BamTools {
+
+class API_EXPORT IBamIODevice {
+
+ // enums
+ public: enum OpenMode { NotOpen = 0x0000
+ , ReadOnly = 0x0001
+ , WriteOnly = 0x0002
+ , ReadWrite = ReadOnly | WriteOnly
+ };
+
+ // ctor & dtor
+ public:
+ virtual ~IBamIODevice(void) { }
+
+ // IBamIODevice interface
+ public:
+
+ // TODO: add seek(pos, *from*)
+
+ // pure virtuals
+ virtual void Close(void) =0;
+ virtual bool IsRandomAccess(void) const =0;
+ virtual bool Open(const OpenMode mode) =0;
+ virtual int64_t Read(char* data, const unsigned int numBytes) =0;
+ virtual bool Seek(const int64_t& position, const int origin = SEEK_SET) =0;
+ virtual int64_t Tell(void) const =0;
+ virtual int64_t Write(const char* data, const unsigned int numBytes) =0;
+
+ // default implementation provided
+ virtual std::string GetErrorString(void);
+ virtual bool IsOpen(void) const;
+ virtual OpenMode Mode(void) const;
+
+ // internal methods
+ protected:
+ IBamIODevice(void); // hidden ctor
+ void SetErrorString(const std::string& where, const std::string& what);
+
+ // data members
+ protected:
+ OpenMode m_mode;
+ std::string m_errorString;
+};
+
+inline
+IBamIODevice::IBamIODevice(void)
+ : m_mode(IBamIODevice::NotOpen)
+{ }
+
+inline
+std::string IBamIODevice::GetErrorString(void) {
+ return m_errorString;
+}
+
+inline
+bool IBamIODevice::IsOpen(void) const {
+ return ( m_mode != IBamIODevice::NotOpen );
+}
+
+inline
+IBamIODevice::OpenMode IBamIODevice::Mode(void) const {
+ return m_mode;
+}
+
+inline
+void IBamIODevice::SetErrorString(const std::string& where, const std::string& what) {
+ static const std::string SEPARATOR = ": ";
+ m_errorString = where + SEPARATOR + what;
+}
+
+} // namespace BamTools
+
+#endif // IBAMIODEVICE_H
diff --git a/bamtools/src/api/SamConstants.h b/bamtools/src/api/SamConstants.h
new file mode 100644
index 0000000..4bb7ee9
--- /dev/null
+++ b/bamtools/src/api/SamConstants.h
@@ -0,0 +1,97 @@
+// ***************************************************************************
+// SamConstants.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 27 March 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides constants for SAM header
+// ***************************************************************************
+
+#ifndef SAM_CONSTANTS_H
+#define SAM_CONSTANTS_H
+
+#include "api/api_global.h"
+#include <string>
+
+namespace BamTools {
+namespace Constants {
+
+// basic char constants used in SAM format
+const char SAM_COLON = ':';
+const char SAM_EQUAL = '=';
+const char SAM_PERIOD = '.';
+const char SAM_STAR = '*';
+const char SAM_TAB = '\t';
+const std::string SAM_DIGITS = "0123456789";
+
+const std::string SAM_CURRENT_VERSION = "1.4";
+
+// HD entries
+const std::string SAM_HD_BEGIN_TOKEN = "@HD";
+const std::string SAM_HD_VERSION_TAG = "VN";
+const std::string SAM_HD_SORTORDER_TAG = "SO";
+const std::string SAM_HD_GROUPORDER_TAG = "GO";
+
+// SQ entries
+const std::string SAM_SQ_BEGIN_TOKEN = "@SQ";
+const std::string SAM_SQ_ASSEMBLYID_TAG = "AS";
+const std::string SAM_SQ_CHECKSUM_TAG = "M5";
+const std::string SAM_SQ_LENGTH_TAG = "LN";
+const std::string SAM_SQ_NAME_TAG = "SN";
+const std::string SAM_SQ_SPECIES_TAG = "SP";
+const std::string SAM_SQ_URI_TAG = "UR";
+
+// RG entries
+const std::string SAM_RG_BEGIN_TOKEN = "@RG";
+const std::string SAM_RG_DESCRIPTION_TAG = "DS";
+const std::string SAM_RG_FLOWORDER_TAG = "FO";
+const std::string SAM_RG_ID_TAG = "ID";
+const std::string SAM_RG_KEYSEQUENCE_TAG = "KS";
+const std::string SAM_RG_LIBRARY_TAG = "LB";
+const std::string SAM_RG_PLATFORMUNIT_TAG = "PU";
+const std::string SAM_RG_PREDICTEDINSERTSIZE_TAG = "PI";
+const std::string SAM_RG_PRODUCTIONDATE_TAG = "DT";
+const std::string SAM_RG_PROGRAM_TAG = "PG";
+const std::string SAM_RG_SAMPLE_TAG = "SM";
+const std::string SAM_RG_SEQCENTER_TAG = "CN";
+const std::string SAM_RG_SEQTECHNOLOGY_TAG = "PL";
+
+// PG entries
+const std::string SAM_PG_BEGIN_TOKEN = "@PG";
+const std::string SAM_PG_COMMANDLINE_TAG = "CL";
+const std::string SAM_PG_ID_TAG = "ID";
+const std::string SAM_PG_NAME_TAG = "PN";
+const std::string SAM_PG_PREVIOUSPROGRAM_TAG = "PP";
+const std::string SAM_PG_VERSION_TAG = "VN";
+
+// CO entries
+const std::string SAM_CO_BEGIN_TOKEN = "@CO";
+
+// HD:SO values
+const std::string SAM_HD_SORTORDER_COORDINATE = "coordinate";
+const std::string SAM_HD_SORTORDER_QUERYNAME = "queryname";
+const std::string SAM_HD_SORTORDER_UNKNOWN = "unknown";
+const std::string SAM_HD_SORTORDER_UNSORTED = "unsorted";
+
+// HD:GO values
+const std::string SAM_HD_GROUPORDER_NONE = "none";
+const std::string SAM_HD_GROUPORDER_QUERY = "query";
+const std::string SAM_HD_GROUPORDER_REFERENCE = "reference";
+
+// SQ:LN values
+const unsigned int SAM_SQ_LENGTH_MIN = 1;
+const unsigned int SAM_SQ_LENGTH_MAX = 536870911; // 2^29 - 1
+
+// RG:PL values
+const std::string SAM_RG_SEQTECHNOLOGY_CAPILLARY = "CAPILLARY";
+const std::string SAM_RG_SEQTECHNOLOGY_HELICOS = "HELICOS";
+const std::string SAM_RG_SEQTECHNOLOGY_ILLUMINA = "ILLUMINA";
+const std::string SAM_RG_SEQTECHNOLOGY_IONTORRENT = "IONTORRENT";
+const std::string SAM_RG_SEQTECHNOLOGY_LS454 = "LS454";
+const std::string SAM_RG_SEQTECHNOLOGY_PACBIO = "PACBIO";
+const std::string SAM_RG_SEQTECHNOLOGY_SOLID = "SOLID";
+
+} // namespace Constants
+} // namespace BamTools
+
+#endif // SAM_CONSTANTS_H
diff --git a/bamtools/src/api/SamHeader.cpp b/bamtools/src/api/SamHeader.cpp
new file mode 100644
index 0000000..9221944
--- /dev/null
+++ b/bamtools/src/api/SamHeader.cpp
@@ -0,0 +1,236 @@
+// ***************************************************************************
+// SamHeader.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 25 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides direct read/write access to the SAM header data fields.
+// ***************************************************************************
+
+#include "api/SamConstants.h"
+#include "api/SamHeader.h"
+#include "api/internal/utils/BamException_p.h"
+#include "api/internal/sam/SamFormatParser_p.h"
+#include "api/internal/sam/SamFormatPrinter_p.h"
+#include "api/internal/sam/SamHeaderValidator_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+using namespace std;
+
+/*! \struct BamTools::SamHeader
+ \brief Represents the SAM-formatted text header that is part of the BAM file header.
+
+ Provides direct read/write access to the SAM header data fields.
+
+ \sa \samSpecURL
+*/
+/*! \var SamHeader::Version
+ \brief corresponds to \@HD VN:\<Version\>
+
+ Required for valid SAM header, if \@HD record is present.
+*/
+/*! \var SamHeader::SortOrder
+ \brief corresponds to \@HD SO:\<SortOrder\>
+*/
+/*! \var SamHeader::GroupOrder
+ \brief corresponds to \@HD GO:\<GroupOrder\>
+*/
+/*! \var SamHeader::Sequences
+ \brief corresponds to \@SQ entries
+ \sa SamSequence, SamSequenceDictionary
+*/
+/*! \var SamHeader::ReadGroups
+ \brief corresponds to \@RG entries
+ \sa SamReadGroup, SamReadGroupDictionary
+*/
+/*! \var SamHeader::Programs
+ \brief corresponds to \@PG entries
+ \sa SamProgram, SamProgramChain
+*/
+/*! \var SamHeader::Comments
+ \brief corresponds to \@CO entries
+*/
+
+/*! \fn SamHeader::SamHeader(const std::string& headerText = "")
+ \brief constructor
+*/
+SamHeader::SamHeader(const std::string& headerText)
+ : Version("")
+ , SortOrder(Constants::SAM_HD_SORTORDER_UNKNOWN)
+ , GroupOrder("")
+{
+ SetHeaderText(headerText);
+}
+
+/*! \fn SamHeader::SamHeader(const SamHeader& other)
+ \brief copy constructor
+*/
+SamHeader::SamHeader(const SamHeader& other)
+ : Version(other.Version)
+ , SortOrder(other.SortOrder)
+ , GroupOrder(other.GroupOrder)
+ , Sequences(other.Sequences)
+ , ReadGroups(other.ReadGroups)
+ , Programs(other.Programs)
+ , Comments(other.Comments)
+ , m_errorString(other.GetErrorString())
+{ }
+
+/*! \fn SamHeader::~SamHeader(void)
+ \brief destructor
+*/
+SamHeader::~SamHeader(void) { }
+
+/*! \fn void SamHeader::Clear(void)
+ \brief Clears all header contents.
+*/
+void SamHeader::Clear(void) {
+
+ // clear SAM header components
+ Version.clear();
+ SortOrder.clear();
+ GroupOrder.clear();
+ Sequences.Clear();
+ ReadGroups.Clear();
+ Programs.Clear();
+ Comments.clear();
+
+ // clear error string
+ m_errorString.clear();
+}
+
+/*! \fn std::string SamHeader::GetErrorString(void) const
+ \brief Returns a human-readable description of the last error that occurred
+
+ This method allows elimination of STDERR pollution. Developers of client code
+ may choose how the messages are displayed to the user, if at all.
+
+ \return error description
+*/
+std::string SamHeader::GetErrorString(void) const {
+ return m_errorString;
+}
+
+/*! \fn bool SamHeader::HasError(void) const
+ \brief Returns \c true if header encountered an error
+*/
+bool SamHeader::HasError(void) const {
+ return (!m_errorString.empty());
+}
+
+/*! \fn bool SamHeader::HasVersion(void) const
+ \brief Returns \c true if header contains \@HD ID:\<Version\>
+*/
+bool SamHeader::HasVersion(void) const {
+ return (!Version.empty());
+}
+
+/*! \fn bool SamHeader::HasSortOrder(void) const
+ \brief Returns \c true if header contains \@HD SO:\<SortOrder\>
+*/
+bool SamHeader::HasSortOrder(void) const {
+ return (!SortOrder.empty());
+}
+
+/*! \fn bool SamHeader::HasGroupOrder(void) const
+ \brief Returns \c true if header contains \@HD GO:\<GroupOrder\>
+*/
+bool SamHeader::HasGroupOrder(void) const {
+ return (!GroupOrder.empty());
+}
+
+/*! \fn bool SamHeader::HasSequences(void) const
+ \brief Returns \c true if header contains any \@SQ entries
+*/
+bool SamHeader::HasSequences(void) const {
+ return (!Sequences.IsEmpty());
+}
+
+/*! \fn bool SamHeader::HasReadGroups(void) const
+ \brief Returns \c true if header contains any \@RG entries
+*/
+bool SamHeader::HasReadGroups(void) const {
+ return (!ReadGroups.IsEmpty());
+}
+
+/*! \fn bool SamHeader::HasPrograms(void) const
+ \brief Returns \c true if header contains any \@PG entries
+*/
+bool SamHeader::HasPrograms(void) const {
+ return (!Programs.IsEmpty());
+}
+
+/*! \fn bool SamHeader::HasComments(void) const
+ \brief Returns \c true if header contains any \@CO entries
+*/
+bool SamHeader::HasComments(void) const {
+ return (!Comments.empty());
+}
+
+/*! \fn bool SamHeader::IsValid(bool verbose = false) const
+ \brief Checks header contents for required data and proper formatting.
+
+ \param[in] verbose If set to true, validation errors & warnings will be printed to stderr.
+ Otherwise, messages are available through SamHeader::GetErrorString().
+ \return \c true if SAM header is well-formed
+*/
+bool SamHeader::IsValid(bool verbose) const {
+
+ SamHeaderValidator validator(*this);
+
+ // if SAM header is valid, return success
+ if ( validator.Validate() )
+ return true;
+
+ // otherwiser
+ else {
+
+ // print messages to stderr
+ if ( verbose )
+ validator.PrintMessages(std::cerr);
+
+ // or catch in local error string
+ else {
+ stringstream errorStream("");
+ validator.PrintMessages(errorStream);
+ m_errorString = errorStream.str();
+ }
+ return false;
+ }
+}
+
+/*! \fn void SamHeader::SetHeaderText(const std::string& headerText)
+ \brief Replaces header contents with \a headerText.
+
+ \param[in] headerText SAM formatted-text that will be parsed into data fields
+*/
+void SamHeader::SetHeaderText(const std::string& headerText) {
+
+ // clear prior data
+ Clear();
+
+ try {
+ SamFormatParser parser(*this);
+ parser.Parse(headerText);
+ } catch ( BamException& e ) {
+
+ // clear anything parsed so far
+ // no telling what's valid and what's partially parsed
+ Clear();
+
+ // set error string
+ m_errorString = e.what();
+ }
+}
+
+/*! \fn std::string SamHeader::ToString(void) const
+ \brief Converts data fields to SAM-formatted text.
+
+ Applies any local modifications made since creating this object or calling SetHeaderText().
+
+ \return SAM-formatted header text
+*/
+string SamHeader::ToString(void) const {
+ SamFormatPrinter printer(*this);
+ return printer.ToString();
+}
diff --git a/bamtools/src/api/SamHeader.h b/bamtools/src/api/SamHeader.h
new file mode 100644
index 0000000..5004994
--- /dev/null
+++ b/bamtools/src/api/SamHeader.h
@@ -0,0 +1,74 @@
+// ***************************************************************************
+// SamHeader.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides direct read/write access to the SAM header data fields.
+// ***************************************************************************
+
+#ifndef SAM_HEADER_H
+#define SAM_HEADER_H
+
+#include "api/api_global.h"
+#include "api/SamProgramChain.h"
+#include "api/SamReadGroupDictionary.h"
+#include "api/SamSequenceDictionary.h"
+#include <string>
+#include <vector>
+
+namespace BamTools {
+
+struct API_EXPORT SamHeader {
+
+ // ctor & dtor
+ SamHeader(const std::string& headerText = "");
+ SamHeader(const SamHeader& other);
+ ~SamHeader(void);
+
+ // query/modify entire SamHeader
+ void Clear(void); // clears all header contents
+ std::string GetErrorString(void) const;
+ bool HasError(void) const;
+ bool IsValid(bool verbose = false) const; // returns true if SAM header is well-formed
+ void SetHeaderText(const std::string& headerText); // replaces data fields with contents of SAM-formatted text
+ std::string ToString(void) const; // returns the printable, SAM-formatted header text
+
+ // convenience query methods
+ bool HasVersion(void) const; // returns true if header contains format version entry
+ bool HasSortOrder(void) const; // returns true if header contains sort order entry
+ bool HasGroupOrder(void) const; // returns true if header contains group order entry
+ bool HasSequences(void) const; // returns true if header contains any sequence entries
+ bool HasReadGroups(void) const; // returns true if header contains any read group entries
+ bool HasPrograms(void) const; // returns true if header contains any program record entries
+ bool HasComments(void) const; // returns true if header contains comments
+
+ // --------------
+ // data members
+ // --------------
+
+ // header metadata (@HD line)
+ std::string Version; // VN:<Version> *Required, if @HD record is present*
+ std::string SortOrder; // SO:<SortOrder>
+ std::string GroupOrder; // GO:<GroupOrder>
+
+ // header sequences (@SQ entries)
+ SamSequenceDictionary Sequences;
+
+ // header read groups (@RG entries)
+ SamReadGroupDictionary ReadGroups;
+
+ // header program data (@PG entries)
+ SamProgramChain Programs;
+
+ // header comments (@CO entries)
+ std::vector<std::string> Comments;
+
+ // internal data
+ private:
+ mutable std::string m_errorString;
+};
+
+} // namespace BamTools
+
+#endif // SAM_HEADER_H
diff --git a/bamtools/src/api/SamProgram.cpp b/bamtools/src/api/SamProgram.cpp
new file mode 100644
index 0000000..1720678
--- /dev/null
+++ b/bamtools/src/api/SamProgram.cpp
@@ -0,0 +1,139 @@
+// ***************************************************************************
+// SamProgram.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides direct read/write access to the SAM header program records.
+// ***************************************************************************
+
+#include "api/SamProgram.h"
+using namespace BamTools;
+using namespace std;
+
+/*! \struct BamTools::SamProgram
+ \brief Represents a SAM program record.
+
+ Provides direct read/write access to the SAM header program records.
+
+ \sa \samSpecURL
+*/
+/*! \var SamProgram::CommandLine
+ \brief corresponds to \@PG CL:\<CommandLine\>
+*/
+/*! \var SamProgram::ID
+ \brief corresponds to \@PG ID:\<ID\>
+
+ Required for valid SAM header.
+*/
+/*! \var SamProgram::Name
+ \brief corresponds to \@PG PN:\<Name\>
+*/
+/*! \var SamProgram::PreviousProgramID
+ \brief corresponds to \@PG PP:\<PreviousProgramID\>
+*/
+/*! \var SamProgram::Version
+ \brief corresponds to \@PG VN:\<Version\>
+*/
+/*! \var SamProgram::NextProgramID
+ \internal
+ Holds ID of the "next" program record in a SamProgramChain
+*/
+
+/*! \fn SamProgram::SamProgram(void)
+ \brief default constructor
+*/
+SamProgram::SamProgram(void)
+ : CommandLine("")
+ , ID("")
+ , Name("")
+ , PreviousProgramID("")
+ , Version("")
+ , NextProgramID("")
+{ }
+
+/*! \fn SamProgram::SamProgram(const std::string& id)
+ \brief constructs program record with \a id
+
+ \param id desired program record ID
+*/
+SamProgram::SamProgram(const std::string& id)
+ : CommandLine("")
+ , ID(id)
+ , Name("")
+ , PreviousProgramID("")
+ , Version("")
+ , NextProgramID("")
+{ }
+
+/*! \fn SamProgram::SamProgram(const SamProgram& other)
+ \brief copy constructor
+*/
+SamProgram::SamProgram(const SamProgram& other)
+ : CommandLine(other.CommandLine)
+ , ID(other.ID)
+ , Name(other.Name)
+ , PreviousProgramID(other.PreviousProgramID)
+ , Version(other.Version)
+ , NextProgramID(other.NextProgramID)
+{ }
+
+/*! \fn SamProgram::~SamProgram(void)
+ \brief destructor
+*/
+SamProgram::~SamProgram(void) { }
+
+/*! \fn void SamProgram::Clear(void)
+ \brief Clears all data fields.
+*/
+void SamProgram::Clear(void) {
+ CommandLine.clear();
+ ID.clear();
+ Name.clear();
+ PreviousProgramID.clear();
+ Version.clear();
+ NextProgramID.clear();
+}
+
+/*! \fn bool SamProgram::HasCommandLine(void) const
+ \brief Returns \c true if program record contains \@PG: CL:\<CommandLine\>
+*/
+bool SamProgram::HasCommandLine(void) const {
+ return (!CommandLine.empty());
+}
+
+/*! \fn bool SamProgram::HasID(void) const
+ \brief Returns \c true if program record contains \@PG: ID:\<ID\>
+*/
+bool SamProgram::HasID(void) const {
+ return (!ID.empty());
+}
+
+/*! \fn bool SamProgram::HasName(void) const
+ \brief Returns \c true if program record contains \@PG: PN:\<Name\>
+*/
+bool SamProgram::HasName(void) const {
+ return (!Name.empty());
+}
+
+/*! \fn bool SamProgram::HasNextProgramID(void) const
+ \internal
+ \return true if program has a "next" record in a SamProgramChain
+*/
+bool SamProgram::HasNextProgramID(void) const {
+ return (!NextProgramID.empty());
+}
+
+/*! \fn bool SamProgram::HasPreviousProgramID(void) const
+ \brief Returns \c true if program record contains \@PG: PP:\<PreviousProgramID\>
+*/
+bool SamProgram::HasPreviousProgramID(void) const {
+ return (!PreviousProgramID.empty());
+}
+
+/*! \fn bool SamProgram::HasVersion(void) const
+ \brief Returns \c true if program record contains \@PG: VN:\<Version\>
+*/
+bool SamProgram::HasVersion(void) const {
+ return (!Version.empty());
+}
diff --git a/bamtools/src/api/SamProgram.h b/bamtools/src/api/SamProgram.h
new file mode 100644
index 0000000..54da872
--- /dev/null
+++ b/bamtools/src/api/SamProgram.h
@@ -0,0 +1,61 @@
+// ***************************************************************************
+// SamProgram.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides direct read/write access to the SAM header program records.
+// ***************************************************************************
+
+#ifndef SAM_PROGRAM_H
+#define SAM_PROGRAM_H
+
+#include "api/api_global.h"
+#include <string>
+
+namespace BamTools {
+
+class SamProgramChain;
+
+struct API_EXPORT SamProgram {
+
+ // ctor & dtor
+ SamProgram(void);
+ SamProgram(const std::string& id);
+ SamProgram(const SamProgram& other);
+ ~SamProgram(void);
+
+ // query/modify entire program record
+ void Clear(void); // clears all data fields
+
+ // convenience query methods
+ bool HasCommandLine(void) const; // returns true if program record has a command line entry
+ bool HasID(void) const; // returns true if program record has an ID
+ bool HasName(void) const; // returns true if program record has a name
+ bool HasPreviousProgramID(void) const; // returns true if program record has a 'previous program ID'
+ bool HasVersion(void) const; // returns true if program record has a version
+
+ // data members
+ std::string CommandLine; // CL:<CommandLine>
+ std::string ID; // ID:<ID> *Required for valid SAM header*
+ std::string Name; // PN:<Name>
+ std::string PreviousProgramID; // PP:<PreviousProgramID>
+ std::string Version; // VN:<Version>
+
+ // internal (non-standard) methods & fields
+ private:
+ bool HasNextProgramID(void) const;
+ std::string NextProgramID;
+ friend class BamTools::SamProgramChain;
+};
+
+/*! \fn bool operator==(const SamProgram& lhs, const SamProgram& rhs)
+ \brief tests equality by comparing program IDs
+*/
+API_EXPORT inline bool operator==(const SamProgram& lhs, const SamProgram& rhs) {
+ return lhs.ID == rhs.ID;
+}
+
+} // namespace BamTools
+
+#endif // SAM_PROGRAM_H
diff --git a/bamtools/src/api/SamProgramChain.cpp b/bamtools/src/api/SamProgramChain.cpp
new file mode 100644
index 0000000..88c2185
--- /dev/null
+++ b/bamtools/src/api/SamProgramChain.cpp
@@ -0,0 +1,354 @@
+// ***************************************************************************
+// SamProgramChain.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides methods for operating on a SamProgram record "chain"
+// ***************************************************************************
+
+#include "api/SamProgramChain.h"
+using namespace BamTools;
+
+#include <algorithm>
+#include <iostream>
+#include <cstdlib>
+using namespace std;
+
+/*! \class BamTools::SamProgramChain
+ \brief Sorted container "chain" of SamProgram records.
+
+ Provides methods for operating on a collection of SamProgram records.
+
+ \note Underlying container is *NOT* ordered by linkage, but by order of
+ appearance in SamHeader and subsequent Add() calls. Using the current
+ iterators will not allow you to step through the header's program history.
+ Instead use First()/Last() to access oldest/newest records, respectively.
+*/
+
+/*! \fn SamProgramChain::SamProgramChain(void)
+ \brief constructor
+*/
+SamProgramChain::SamProgramChain(void) { }
+
+/*! \fn SamProgramChain::SamProgramChain(const SamProgramChain& other)
+ \brief copy constructor
+*/
+SamProgramChain::SamProgramChain(const SamProgramChain& other)
+ : m_data(other.m_data)
+{ }
+
+/*! \fn SamProgramChain::~SamProgramChain(void)
+ \brief destructor
+*/
+SamProgramChain::~SamProgramChain(void) { }
+
+/*! \fn void SamProgramChain::Add(SamProgram& program)
+ \brief Appends a program to program chain.
+
+ Duplicate entries are silently discarded.
+
+ \note Underlying container is *NOT* ordered by linkage, but by order of
+ appearance in SamHeader and subsequent Add() calls. Using the current
+ iterators will not allow you to step through the header's program history.
+ Instead use First()/Last() to access oldest/newest records, respectively.
+
+ \param[in] program entry to be appended
+*/
+void SamProgramChain::Add(SamProgram& program) {
+
+ // ignore duplicated records
+ if ( Contains(program) )
+ return;
+
+ // if other programs already in chain, try to find the "next" record
+ // tries to match another record's PPID with @program's ID
+ if ( !IsEmpty() )
+ program.NextProgramID = NextIdFor(program.ID);
+
+ // store program record
+ m_data.push_back(program);
+}
+
+/*! \fn void SamProgramChain::Add(std::vector<SamProgram>& programs)
+ \brief Appends a batch of programs to the end of the chain.
+
+ This is an overloaded function.
+
+ \param[in] programs batch of program records to append
+ \sa Add()
+*/
+void SamProgramChain::Add(std::vector<SamProgram>& programs) {
+ vector<SamProgram>::iterator pgIter = programs.begin();
+ vector<SamProgram>::iterator pgEnd = programs.end();
+ for ( ; pgIter != pgEnd; ++pgIter )
+ Add(*pgIter);
+}
+
+/*! \fn SamProgramIterator SamProgramChain::Begin(void)
+ \return an STL iterator pointing to the first (oldest) program record
+ \sa ConstBegin(), End(), First()
+*/
+SamProgramIterator SamProgramChain::Begin(void) {
+ return m_data.begin();
+}
+
+/*! \fn SamProgramConstIterator SamProgramChain::Begin(void) const
+ \return an STL const_iterator pointing to the first (oldest) program record
+
+ This is an overloaded function.
+
+ \sa ConstBegin(), End(), First()
+*/
+SamProgramConstIterator SamProgramChain::Begin(void) const {
+ return m_data.begin();
+}
+
+/*! \fn void SamProgramChain::Clear(void)
+ \brief Clears all program records.
+*/
+void SamProgramChain::Clear(void) {
+ m_data.clear();
+}
+
+/*! \fn SamProgramConstIterator SamProgramChain::ConstBegin(void) const
+ \return an STL const_iterator pointing to the first (oldest) program record
+ \sa Begin(), ConstEnd(), First()
+*/
+SamProgramConstIterator SamProgramChain::ConstBegin(void) const {
+ return m_data.begin();
+}
+
+/*! \fn SamProgramConstIterator SamProgramChain::ConstEnd(void) const
+ \return an STL const_iterator pointing to the imaginary entry after the last (newest) program record
+ \sa ConstBegin(), End(), Last()
+*/
+SamProgramConstIterator SamProgramChain::ConstEnd(void) const {
+ return m_data.end();
+}
+
+/*! \fn bool SamProgramChain::Contains(const SamProgram& program) const
+ \brief Returns true if chains has this program record (matching on ID).
+
+ This is an overloaded function.
+
+ \param[in] program SamProgram to search for
+ \return \c true if chain contains program (matching on ID)
+*/
+bool SamProgramChain::Contains(const SamProgram& program) const {
+ return Contains(program.ID);
+}
+
+/*! \fn bool SamProgramChain::Contains(const std::string& programId) const
+ \brief Returns true if chains has a program record with this ID
+
+ \param[in] programId search for program matching this ID
+ \return \c true if chain contains a program record with this ID
+*/
+bool SamProgramChain::Contains(const std::string& programId) const {
+ return ( IndexOf(programId) != (int)m_data.size() );
+}
+
+/*! \fn SamProgramIterator SamProgramChain::End(void)
+ \return an STL iterator pointing to the imaginary entry after the last (newest) program record
+ \sa Begin(), ConstEnd(), Last()
+*/
+SamProgramIterator SamProgramChain::End(void) {
+ return m_data.end();
+}
+
+/*! \fn SamProgramConstIterator SamProgramChain::End(void) const
+ \return an STL const_iterator pointing to the imaginary entry after the last (newest) program record
+
+ This is an overloaded function.
+
+ \sa Begin(), ConstEnd(), Last()
+*/
+SamProgramConstIterator SamProgramChain::End(void) const {
+ return m_data.end();
+}
+
+/*! \fn SamProgram& SamProgramChain::First(void)
+ \brief Fetches first (oldest) record in the chain.
+
+ \warning This function will fail if the chain is empty. If this is possible,
+ check the result of IsEmpty() before calling this function.
+
+ \return a modifiable reference to the first (oldest) program entry
+ \sa Begin(), Last()
+*/
+SamProgram& SamProgramChain::First(void) {
+
+ // find first record in container that has no PreviousProgramID entry
+ SamProgramIterator iter = Begin();
+ SamProgramIterator end = End();
+ for ( ; iter != end; ++iter ) {
+ SamProgram& current = (*iter);
+ if ( !current.HasPreviousProgramID() )
+ return current;
+ }
+
+ // otherwise error
+ cerr << "SamProgramChain::First: could not find any record without a PP tag" << endl;
+ exit(1);
+}
+
+/*! \fn const SamProgram& SamProgramChain::First(void) const
+ \brief Fetches first (oldest) record in the chain.
+
+ This is an overloaded function.
+
+ \warning This function will fail if the chain is empty. If this is possible,
+ check the result of IsEmpty() before calling this function.
+
+ \return a read-only reference to the first (oldest) program entry
+ \sa Begin(), ConstBegin(), Last()
+*/
+const SamProgram& SamProgramChain::First(void) const {
+
+ // find first record in container that has no PreviousProgramID entry
+ SamProgramConstIterator iter = ConstBegin();
+ SamProgramConstIterator end = ConstEnd();
+ for ( ; iter != end; ++iter ) {
+ const SamProgram& current = (*iter);
+ if ( !current.HasPreviousProgramID() )
+ return current;
+ }
+
+ // otherwise error
+ cerr << "SamProgramChain::First: could not find any record without a PP tag" << endl;
+ exit(1);
+}
+
+/*! \fn int SamProgramChain::IndexOf(const std::string& programId) const
+ \internal
+ \return index of program record if found.
+ Otherwise, returns vector::size() (invalid index).
+*/
+int SamProgramChain::IndexOf(const std::string& programId) const {
+ SamProgramConstIterator begin = ConstBegin();
+ SamProgramConstIterator iter = begin;
+ SamProgramConstIterator end = ConstEnd();
+ for ( ; iter != end; ++iter ) {
+ const SamProgram& current = (*iter);
+ if ( current.ID == programId )
+ break;
+ }
+ return distance( begin, iter );
+}
+
+/*! \fn bool SamProgramChain::IsEmpty(void) const
+ \brief Returns \c true if chain contains no records
+ \sa Size()
+*/
+bool SamProgramChain::IsEmpty(void) const {
+ return m_data.empty();
+}
+
+/*! \fn SamProgram& SamProgramChain::Last(void)
+ \brief Fetches last (newest) record in the chain.
+
+ \warning This function will fail if the chain is empty. If this is possible,
+ check the result of IsEmpty() before calling this function.
+
+ \return a modifiable reference to the last (newest) program entry
+ \sa End(), First()
+*/
+SamProgram& SamProgramChain::Last(void) {
+ // find first record in container that has no NextProgramID entry
+ SamProgramIterator iter = Begin();
+ SamProgramIterator end = End();
+ for ( ; iter != end; ++iter ) {
+ SamProgram& current = (*iter);
+ if ( !current.HasNextProgramID() )
+ return current;
+ }
+
+ // otherwise error
+ cerr << "SamProgramChain::Last: could not determine last record" << endl;
+ exit(1);
+}
+
+/*! \fn const SamProgram& SamProgramChain::Last(void) const
+ \brief Fetches last (newest) record in the chain.
+
+ This is an overloaded function.
+
+ \warning This function will fail if the chain is empty. If this is possible,
+ check the result of IsEmpty() before calling this function.
+
+ \return a read-only reference to the last (newest) program entry
+ \sa End(), ConstEnd(), First()
+*/
+const SamProgram& SamProgramChain::Last(void) const {
+ // find first record in container that has no NextProgramID entry
+ SamProgramConstIterator iter = ConstBegin();
+ SamProgramConstIterator end = ConstEnd();
+ for ( ; iter != end; ++iter ) {
+ const SamProgram& current = (*iter);
+ if ( !current.HasNextProgramID() )
+ return current;
+ }
+
+ // otherwise error
+ cerr << "SamProgramChain::Last: could not determine last record" << endl;
+ exit(1);
+}
+
+/*! \fn const std::string SamProgramChain::NextIdFor(const std::string& programId) const
+ \internal
+
+ \return ID of program record, whose PreviousProgramID matches \a programId.
+ Otherwise, returns empty string if none found.
+*/
+const std::string SamProgramChain::NextIdFor(const std::string& programId) const {
+
+ // find first record in container whose PreviousProgramID matches @programId
+ SamProgramConstIterator iter = ConstBegin();
+ SamProgramConstIterator end = ConstEnd();
+ for ( ; iter != end; ++iter ) {
+ const SamProgram& current = (*iter);
+ if ( !current.HasPreviousProgramID() &&
+ current.PreviousProgramID == programId
+ )
+ {
+ return current.ID;
+ }
+ }
+
+ // none found
+ return string();
+}
+
+/*! \fn int SamProgramChain::Size(void) const
+ \brief Returns number of program records in the chain.
+ \sa IsEmpty()
+*/
+int SamProgramChain::Size(void) const {
+ return m_data.size();
+}
+
+/*! \fn SamProgram& SamProgramChain::operator[](const std::string& programId)
+ \brief Retrieves the modifiable SamProgram record that matches \a programId.
+
+ \warning If the chain contains no read group matching this ID, this function will
+ print an error and terminate. Check the return value of Contains() if this may be
+ possible.
+
+ \param[in] programId ID of program record to retrieve
+ \return a modifiable reference to the SamProgram associated with the ID
+*/
+SamProgram& SamProgramChain::operator[](const std::string& programId) {
+
+ // look up program record matching this ID
+ int index = IndexOf(programId);
+
+ // if record not found
+ if ( index == (int)m_data.size() ) {
+ cerr << "SamProgramChain::operator[] - unknown programId: " << programId << endl;
+ exit(1);
+ }
+
+ // otherwise return program record at index
+ return m_data.at(index);
+}
diff --git a/bamtools/src/api/SamProgramChain.h b/bamtools/src/api/SamProgramChain.h
new file mode 100644
index 0000000..a2bd532
--- /dev/null
+++ b/bamtools/src/api/SamProgramChain.h
@@ -0,0 +1,85 @@
+// ***************************************************************************
+// SamProgramChain.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides methods for operating on a SamProgram record "chain"
+// ***************************************************************************
+
+#ifndef SAM_PROGRAMCHAIN_H
+#define SAM_PROGRAMCHAIN_H
+
+#include "api/api_global.h"
+#include "api/SamProgram.h"
+#include <string>
+#include <vector>
+
+namespace BamTools {
+
+// chain is *NOT* sorted in any order
+// use First()/Last() to retrieve oldest/newest programs, respectively
+typedef std::vector<SamProgram> SamProgramContainer;
+typedef SamProgramContainer::iterator SamProgramIterator;
+typedef SamProgramContainer::const_iterator SamProgramConstIterator;
+
+class API_EXPORT SamProgramChain {
+
+ // ctor & dtor
+ public:
+ SamProgramChain(void);
+ SamProgramChain(const SamProgramChain& other);
+ ~SamProgramChain(void);
+
+ // query/modify program data
+ public:
+ // appends a program record to the chain
+ void Add(SamProgram& program);
+ void Add(std::vector<SamProgram>& programs);
+
+ // clears all read group entries
+ void Clear(void);
+
+ // returns true if chain contains this program record (matches on ID)
+ bool Contains(const SamProgram& program) const;
+ bool Contains(const std::string& programId) const;
+
+ // returns the first (oldest) program in the chain
+ SamProgram& First(void);
+ const SamProgram& First(void) const;
+
+ // returns true if chain is empty
+ bool IsEmpty(void) const;
+
+ // returns last (most recent) program in the chain
+ SamProgram& Last(void);
+ const SamProgram& Last(void) const;
+
+ // returns number of program records in the chain
+ int Size(void) const;
+
+ // retrieves a modifiable reference to the SamProgram object associated with this ID
+ SamProgram& operator[](const std::string& programId);
+
+ // retrieve STL-compatible iterators
+ public:
+ SamProgramIterator Begin(void); // returns iterator to begin()
+ SamProgramConstIterator Begin(void) const; // returns const_iterator to begin()
+ SamProgramConstIterator ConstBegin(void) const; // returns const_iterator to begin()
+ SamProgramIterator End(void); // returns iterator to end()
+ SamProgramConstIterator End(void) const; // returns const_iterator to end()
+ SamProgramConstIterator ConstEnd(void) const; // returns const_iterator to end()
+
+ // internal methods
+ private:
+ int IndexOf(const std::string& programId) const;
+ const std::string NextIdFor(const std::string& programId) const;
+
+ // data members
+ private:
+ SamProgramContainer m_data;
+};
+
+} // namespace BamTools
+
+#endif // SAM_PROGRAMCHAIN_H
diff --git a/bamtools/src/api/SamReadGroup.cpp b/bamtools/src/api/SamReadGroup.cpp
new file mode 100644
index 0000000..ce022ab
--- /dev/null
+++ b/bamtools/src/api/SamReadGroup.cpp
@@ -0,0 +1,221 @@
+// ***************************************************************************
+// SamReadGroup.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides direct read/write access to the SAM read group data fields.
+// ***************************************************************************
+
+#include "api/SamReadGroup.h"
+using namespace BamTools;
+using namespace std;
+
+/*! \struct BamTools::SamReadGroup
+ \brief Represents a SAM read group entry.
+
+ Provides direct read/write access to the SAM read group data fields.
+
+ \sa \samSpecURL
+*/
+/*! \var SamReadGroup::Description
+ \brief corresponds to \@RG DS:\<Description\>
+*/
+/*! \var SamReadGroup::FlowOrder
+ \brief corresponds to \@RG FO:\<FlowOrder\>
+*/
+/*! \var SamReadGroup::ID
+ \brief corresponds to \@RG ID:\<ID\>
+
+ Required for valid SAM header.
+*/
+/*! \var SamReadGroup::KeySequence
+ \brief corresponds to \@RG KS:\<KeySequence\>
+*/
+/*! \var SamReadGroup::Library
+ \brief corresponds to \@RG LB:\<Library\>
+*/
+/*! \var SamReadGroup::PlatformUnit
+ \brief corresponds to \@RG PU:\<PlatformUnit\>
+*/
+/*! \var SamReadGroup::PredictedInsertSize
+ \brief corresponds to \@RG PI:\<PredictedInsertSize\>
+*/
+/*! \var SamReadGroup::ProductionDate
+ \brief corresponds to \@RG DT:\<ProductionDate\>
+*/
+/*! \var SamReadGroup::Program
+ \brief corresponds to \@RG PG:\<Program\>
+*/
+/*! \var SamReadGroup::Sample
+ \brief corresponds to \@RG SM:\<Sample\>
+*/
+/*! \var SamReadGroup::SequencingCenter
+ \brief corresponds to \@RG CN:\<SequencingCenter\>
+*/
+/*! \var SamReadGroup::SequencingTechnology
+ \brief corresponds to \@RG PL:\<SequencingTechnology\>
+*/
+
+/*! \fn SamReadGroup::SamReadGroup(void)
+ \brief default constructor
+*/
+SamReadGroup::SamReadGroup(void)
+ : Description("")
+ , FlowOrder("")
+ , ID("")
+ , KeySequence("")
+ , Library("")
+ , PlatformUnit("")
+ , PredictedInsertSize("")
+ , ProductionDate("")
+ , Program("")
+ , Sample("")
+ , SequencingCenter("")
+ , SequencingTechnology("")
+{ }
+
+/*! \fn SamReadGroup::SamReadGroup(const std::string& id)
+ \brief constructs read group with \a id
+
+ \param id desired read group ID
+*/
+SamReadGroup::SamReadGroup(const std::string& id)
+ : Description("")
+ , FlowOrder("")
+ , ID(id)
+ , KeySequence("")
+ , Library("")
+ , PlatformUnit("")
+ , PredictedInsertSize("")
+ , ProductionDate("")
+ , Program("")
+ , Sample("")
+ , SequencingCenter("")
+ , SequencingTechnology("")
+{ }
+
+/*! \fn SamReadGroup::SamReadGroup(const SamReadGroup& other)
+ \brief copy constructor
+*/
+SamReadGroup::SamReadGroup(const SamReadGroup& other)
+ : Description(other.Description)
+ , FlowOrder(other.FlowOrder)
+ , ID(other.ID)
+ , KeySequence(other.KeySequence)
+ , Library(other.Library)
+ , PlatformUnit(other.PlatformUnit)
+ , PredictedInsertSize(other.PredictedInsertSize)
+ , ProductionDate(other.ProductionDate)
+ , Program(other.Program)
+ , Sample(other.Sample)
+ , SequencingCenter(other.SequencingCenter)
+ , SequencingTechnology(other.SequencingTechnology)
+{ }
+
+/*! \fn SamReadGroup::~SamReadGroup(void)
+ \brief destructor
+*/
+SamReadGroup::~SamReadGroup(void) { }
+
+/*! \fn void SamReadGroup::Clear(void)
+ \brief Clears all data fields.
+*/
+void SamReadGroup::Clear(void) {
+ Description.clear();
+ FlowOrder.clear();
+ ID.clear();
+ KeySequence.clear();
+ Library.clear();
+ PlatformUnit.clear();
+ PredictedInsertSize.clear();
+ ProductionDate.clear();
+ Program.clear();
+ Sample.clear();
+ SequencingCenter.clear();
+ SequencingTechnology.clear();
+}
+
+/*! \fn bool SamReadGroup::HasDescription(void) const
+ \brief Returns \c true if read group contains \@RG DS:\<Description\>
+*/
+bool SamReadGroup::HasDescription(void) const {
+ return (!Description.empty());
+}
+
+/*! \fn bool SamReadGroup::HasFlowOrder(void) const
+ \brief Returns \c true if read group contains \@RG FO:\<FlowOrder\>
+*/
+bool SamReadGroup::HasFlowOrder(void) const {
+ return (!FlowOrder.empty());
+}
+
+/*! \fn bool SamReadGroup::HasID(void) const
+ \brief Returns \c true if read group contains \@RG: ID:\<ID\>
+*/
+bool SamReadGroup::HasID(void) const {
+ return (!ID.empty());
+}
+
+/*! \fn bool SamReadGroup::HasKeySequence(void) const
+ \brief Returns \c true if read group contains \@RG KS:\<KeySequence\>
+*/
+bool SamReadGroup::HasKeySequence(void) const {
+ return (!KeySequence.empty());
+}
+
+/*! \fn bool SamReadGroup::HasLibrary(void) const
+ \brief Returns \c true if read group contains \@RG LB:\<Library\>
+*/
+bool SamReadGroup::HasLibrary(void) const {
+ return (!Library.empty());
+}
+
+/*! \fn bool SamReadGroup::HasPlatformUnit(void) const
+ \brief Returns \c true if read group contains \@RG PU:\<PlatformUnit\>
+*/
+bool SamReadGroup::HasPlatformUnit(void) const {
+ return (!PlatformUnit.empty());
+}
+
+/*! \fn bool SamReadGroup::HasPredictedInsertSize(void) const
+ \brief Returns \c true if read group contains \@RG PI:\<PredictedInsertSize\>
+*/
+bool SamReadGroup::HasPredictedInsertSize(void) const {
+ return (!PredictedInsertSize.empty());
+}
+
+/*! \fn bool SamReadGroup::HasProductionDate(void) const
+ \brief Returns \c true if read group contains \@RG DT:\<ProductionDate\>
+*/
+bool SamReadGroup::HasProductionDate(void) const {
+ return (!ProductionDate.empty());
+}
+
+/*! \fn bool SamReadGroup::HasProgram(void) const
+ \brief Returns \c true if read group contains \@RG PG:\<Program\>
+*/
+bool SamReadGroup::HasProgram(void) const {
+ return (!Program.empty());
+}
+
+/*! \fn bool SamReadGroup::HasSample(void) const
+ \brief Returns \c true if read group contains \@RG SM:\<Sample\>
+*/
+bool SamReadGroup::HasSample(void) const {
+ return (!Sample.empty());
+}
+
+/*! \fn bool SamReadGroup::HasSequencingCenter(void) const
+ \brief Returns \c true if read group contains \@RG CN:\<SequencingCenter\>
+*/
+bool SamReadGroup::HasSequencingCenter(void) const {
+ return (!SequencingCenter.empty());
+}
+
+/*! \fn bool SamReadGroup::HasSequencingTechnology(void) const
+ \brief Returns \c true if read group contains \@RG PL:\<SequencingTechnology\>
+*/
+bool SamReadGroup::HasSequencingTechnology(void) const {
+ return (!SequencingTechnology.empty());
+}
diff --git a/bamtools/src/api/SamReadGroup.h b/bamtools/src/api/SamReadGroup.h
new file mode 100644
index 0000000..093ce2d
--- /dev/null
+++ b/bamtools/src/api/SamReadGroup.h
@@ -0,0 +1,68 @@
+// ***************************************************************************
+// SamReadGroup.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides direct read/write access to the SAM read group data fields.
+// ***************************************************************************
+
+#ifndef SAM_READGROUP_H
+#define SAM_READGROUP_H
+
+#include "api/api_global.h"
+#include <string>
+
+namespace BamTools {
+
+struct API_EXPORT SamReadGroup {
+
+ // ctor & dtor
+ SamReadGroup(void);
+ SamReadGroup(const std::string& id);
+ SamReadGroup(const SamReadGroup& other);
+ ~SamReadGroup(void);
+
+ // query/modify entire read group
+ void Clear(void); // clears all data fields
+
+ // convenience query methods
+ bool HasDescription(void) const; // returns true if read group has a description
+ bool HasFlowOrder(void) const; // returns true if read group has a flow order entry
+ bool HasID(void) const; // returns true if read group has a group ID
+ bool HasKeySequence(void) const; // returns true if read group has a key sequence
+ bool HasLibrary(void) const; // returns true if read group has a library name
+ bool HasPlatformUnit(void) const; // returns true if read group has a platform unit ID
+ bool HasPredictedInsertSize(void) const; // returns true if read group has a predicted insert size
+ bool HasProductionDate(void) const; // returns true if read group has a production date
+ bool HasProgram(void) const; // returns true if read group has a program entry
+ bool HasSample(void) const; // returns true if read group has a sample name
+ bool HasSequencingCenter(void) const; // returns true if read group has a sequencing center ID
+ bool HasSequencingTechnology(void) const; // returns true if read group has a sequencing technology ID
+
+
+ // data fields
+ std::string Description; // DS:<Description>
+ std::string FlowOrder; // FO:<FlowOrder>
+ std::string ID; // ID:<ID> *Required for valid SAM header*
+ std::string KeySequence; // KS:<KeySequence>
+ std::string Library; // LB:<Library>
+ std::string PlatformUnit; // PU:<PlatformUnit>
+ std::string PredictedInsertSize; // PI:<PredictedInsertSize>
+ std::string ProductionDate; // DT:<ProductionDate>
+ std::string Program; // PG:<Program>
+ std::string Sample; // SM:<Sample>
+ std::string SequencingCenter; // CN:<SequencingCenter>
+ std::string SequencingTechnology; // PL:<SequencingTechnology>
+};
+
+/*! \fn bool operator==(const SamReadGroup& lhs, const SamReadGroup& rhs)
+ \brief tests equality by comparing read group IDs
+*/
+API_EXPORT inline bool operator==(const SamReadGroup& lhs, const SamReadGroup& rhs) {
+ return lhs.ID == rhs.ID;
+}
+
+} // namespace BamTools
+
+#endif // SAM_READGROUP_H
diff --git a/bamtools/src/api/SamReadGroupDictionary.cpp b/bamtools/src/api/SamReadGroupDictionary.cpp
new file mode 100644
index 0000000..007221a
--- /dev/null
+++ b/bamtools/src/api/SamReadGroupDictionary.cpp
@@ -0,0 +1,297 @@
+// ***************************************************************************
+// SamReadGroupDictionary.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 16 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides methods for operating on a collection of SamReadGroup entries.
+// ***************************************************************************
+
+#include "api/SamReadGroupDictionary.h"
+using namespace BamTools;
+
+#include <iostream>
+using namespace std;
+
+/*! \class BamTools::SamReadGroupDictionary
+ \brief Container of SamReadGroup entries.
+
+ Provides methods for operating on a collection of SamReadGroup entries.
+*/
+
+/*! \fn SamReadGroupDictionary::SamReadGroupDictionary(void)
+ \brief constructor
+*/
+SamReadGroupDictionary::SamReadGroupDictionary(void) { }
+
+/*! \fn SamReadGroupDictionary::SamReadGroupDictionary(const SamReadGroupDictionary& other)
+ \brief copy constructor
+*/
+SamReadGroupDictionary::SamReadGroupDictionary(const SamReadGroupDictionary& other)
+ : m_data(other.m_data)
+ , m_lookupData(other.m_lookupData)
+{ }
+
+/*! \fn SamReadGroupDictionary::~SamReadGroupDictionary(void)
+ \brief destructor
+*/
+SamReadGroupDictionary::~SamReadGroupDictionary(void) { }
+
+/*! \fn void SamReadGroupDictionary::Add(const SamReadGroup& readGroup)
+ \brief Appends a read group to the dictionary.
+
+ Duplicate entries are silently discarded.
+
+ \param[in] readGroup entry to be added
+*/
+void SamReadGroupDictionary::Add(const SamReadGroup& readGroup) {
+ if ( IsEmpty() || !Contains(readGroup) ) {
+ m_data.push_back(readGroup);
+ m_lookupData[readGroup.ID] = m_data.size() - 1;
+ }
+}
+
+/*! \fn void SamReadGroupDictionary::Add(const std::string& readGroupId)
+ \brief Appends a read group to the dictionary.
+
+ This is an overloaded function.
+
+ \param[in] readGroupId ID of read group to be added
+ \sa Add()
+*/
+void SamReadGroupDictionary::Add(const std::string& readGroupId) {
+ Add( SamReadGroup(readGroupId) );
+}
+
+/*! \fn void SamReadGroupDictionary::Add(const SamReadGroupDictionary& readGroups)
+ \brief Appends another read group dictionary to this one.
+
+ This is an overloaded function.
+
+ \param[in] readGroups entries to be added
+ \sa Add()
+*/
+void SamReadGroupDictionary::Add(const SamReadGroupDictionary& readGroups) {
+ SamReadGroupConstIterator rgIter = readGroups.ConstBegin();
+ SamReadGroupConstIterator rgEnd = readGroups.ConstEnd();
+ for ( ; rgIter != rgEnd; ++rgIter )
+ Add(*rgIter);
+}
+
+/*! \fn void SamReadGroupDictionary::Add(const std::vector<SamReadGroup>& readGroups)
+ \brief Appends multiple read groups to the dictionary.
+
+ This is an overloaded function.
+
+ \param[in] readGroups entries to be added
+ \sa Add()
+*/
+void SamReadGroupDictionary::Add(const std::vector<SamReadGroup>& readGroups) {
+ vector<SamReadGroup>::const_iterator rgIter = readGroups.begin();
+ vector<SamReadGroup>::const_iterator rgEnd = readGroups.end();
+ for ( ; rgIter!= rgEnd; ++rgIter )
+ Add(*rgIter);
+}
+
+/*! \fn void SamReadGroupDictionary::Add(const std::vector<std::string>& readGroupIds)
+ \brief Appends multiple read groups to the dictionary.
+
+ This is an overloaded function.
+
+ \param[in] readGroupIds IDs of read groups to be added
+ \sa Add()
+*/
+void SamReadGroupDictionary::Add(const std::vector<std::string>& readGroupIds) {
+ vector<string>::const_iterator rgIter = readGroupIds.begin();
+ vector<string>::const_iterator rgEnd = readGroupIds.end();
+ for ( ; rgIter!= rgEnd; ++rgIter )
+ Add(*rgIter);
+}
+
+/*! \fn SamReadGroupIterator SamReadGroupDictionary::Begin(void)
+ \return an STL iterator pointing to the first read group
+ \sa ConstBegin(), End()
+*/
+SamReadGroupIterator SamReadGroupDictionary::Begin(void) {
+ return m_data.begin();
+}
+
+/*! \fn SamReadGroupConstIterator SamReadGroupDictionary::Begin(void) const
+ \return an STL const_iterator pointing to the first read group
+
+ This is an overloaded function.
+
+ \sa ConstBegin(), End()
+*/
+SamReadGroupConstIterator SamReadGroupDictionary::Begin(void) const {
+ return m_data.begin();
+}
+
+/*! \fn void SamReadGroupDictionary::Clear(void)
+ \brief Clears all read group entries.
+*/
+void SamReadGroupDictionary::Clear(void) {
+ m_data.clear();
+ m_lookupData.clear();
+}
+
+/*! \fn SamReadGroupConstIterator SamReadGroupDictionary::ConstBegin(void) const
+ \return an STL const_iterator pointing to the first read group
+ \sa Begin(), ConstEnd()
+*/
+SamReadGroupConstIterator SamReadGroupDictionary::ConstBegin(void) const {
+ return m_data.begin();
+}
+
+/*! \fn SamReadGroupConstIterator SamReadGroupDictionary::ConstEnd(void) const
+ \return an STL const_iterator pointing to the imaginary entry after the last read group
+ \sa ConstBegin(), End()
+*/
+SamReadGroupConstIterator SamReadGroupDictionary::ConstEnd(void) const {
+ return m_data.end();
+}
+
+/*! \fn bool SamReadGroupDictionary::Contains(const std::string& readGroupId) const
+ \brief Returns true if dictionary contains read group.
+
+ \param[in] readGroupId search for read group matching this ID
+ \return \c true if dictionary contains a read group with this ID
+*/
+bool SamReadGroupDictionary::Contains(const std::string& readGroupId) const {
+ return ( m_lookupData.find(readGroupId) != m_lookupData.end() );
+}
+
+/*! \fn bool SamReadGroupDictionary::Contains(const SamReadGroup& readGroup) const
+ \brief Returns true if dictionary contains read group (matching on ID).
+
+ This is an overloaded function.
+
+ \param[in] readGroup search for this read group
+ \return \c true if dictionary contains read group (matching on ID).
+*/
+bool SamReadGroupDictionary::Contains(const SamReadGroup& readGroup) const {
+ return Contains(readGroup.ID);
+}
+
+/*! \fn SamReadGroupIterator SamReadGroupDictionary::End(void)
+ \return an STL iterator pointing to the imaginary entry after the last read group
+ \sa Begin(), ConstEnd()
+*/
+SamReadGroupIterator SamReadGroupDictionary::End(void) {
+ return m_data.end();
+}
+
+/*! \fn SamReadGroupConstIterator SamReadGroupDictionary::End(void) const
+ \return an STL const_iterator pointing to the imaginary entry after the last read group
+
+ This is an overloaded function.
+
+ \sa Begin(), ConstEnd()
+*/
+SamReadGroupConstIterator SamReadGroupDictionary::End(void) const {
+ return m_data.end();
+}
+
+/*! \fn bool SamReadGroupDictionary::IsEmpty(void) const
+ \brief Returns \c true if dictionary contains no read groups
+ \sa Size()
+*/
+bool SamReadGroupDictionary::IsEmpty(void) const {
+ return m_data.empty();
+}
+
+/*! \fn void SamReadGroupDictionary::Remove(const SamReadGroup& readGroup)
+ \brief Removes read group from dictionary, if found (matching on ID).
+
+ This is an overloaded function.
+
+ \param[in] readGroup read group to remove (matches on ID)
+*/
+void SamReadGroupDictionary::Remove(const SamReadGroup& readGroup) {
+ Remove(readGroup.ID);
+}
+
+/*! \fn void SamReadGroupDictionary::Remove(const std::string& readGroupId)
+ \brief Removes read group from dictionary, if found.
+
+ \param[in] readGroupId ID of read group to remove
+ \sa Remove()
+*/
+void SamReadGroupDictionary::Remove(const std::string& readGroupId) {
+
+ // skip if empty dictionary or if ID unknown
+ if ( IsEmpty() || !Contains(readGroupId) )
+ return;
+
+ // update 'lookup index' for every entry after @readGroupId
+ const size_t indexToRemove = m_lookupData[readGroupId];
+ const size_t numEntries = m_data.size();
+ for ( size_t i = indexToRemove+1; i < numEntries; ++i ) {
+ const SamReadGroup& rg = m_data.at(i);
+ --m_lookupData[rg.ID];
+ }
+
+ // erase entry from containers
+ m_data.erase( Begin() + indexToRemove );
+ m_lookupData.erase(readGroupId);
+}
+
+/*! \fn void SamReadGroupDictionary::Remove(const std::vector<SamReadGroup>& readGroups)
+ \brief Removes multiple read groups from dictionary (matching on ID).
+
+ This is an overloaded function.
+
+ \param[in] readGroups read groups to remove
+ \sa Remove()
+*/
+void SamReadGroupDictionary::Remove(const std::vector<SamReadGroup>& readGroups) {
+ vector<SamReadGroup>::const_iterator rgIter = readGroups.begin();
+ vector<SamReadGroup>::const_iterator rgEnd = readGroups.end();
+ for ( ; rgIter!= rgEnd; ++rgIter )
+ Remove(*rgIter);
+}
+
+/*! \fn void SamReadGroupDictionary::Remove(const std::vector<std::string>& readGroupIds)
+ \brief Removes multiple read groups from dictionary.
+
+ This is an overloaded function.
+
+ \param[in] readGroupIds IDs of the read groups to remove
+ \sa Remove()
+*/
+void SamReadGroupDictionary::Remove(const std::vector<std::string>& readGroupIds) {
+ vector<string>::const_iterator rgIter = readGroupIds.begin();
+ vector<string>::const_iterator rgEnd = readGroupIds.end();
+ for ( ; rgIter!= rgEnd; ++rgIter )
+ Remove(*rgIter);
+}
+
+/*! \fn int SamReadGroupDictionary::Size(void) const
+ \brief Returns number of read groups in dictionary.
+ \sa IsEmpty()
+*/
+int SamReadGroupDictionary::Size(void) const {
+ return m_data.size();
+}
+
+/*! \fn SamReadGroup& SamReadGroupDictionary::operator[](const std::string& readGroupId)
+ \brief Retrieves the modifiable SamReadGroup that matches \a readGroupId.
+
+ \note If the dictionary contains no read group matching this ID, this function inserts
+ a new one with this ID, and returns a reference to it. If you want to avoid this insertion
+ behavior, check the result of Contains() before using this operator.
+
+ \param[in] readGroupId ID of read group to retrieve
+ \return a modifiable reference to the SamReadGroup associated with the ID
+*/
+SamReadGroup& SamReadGroupDictionary::operator[](const std::string& readGroupId) {
+
+ if ( !Contains(readGroupId) ) {
+ SamReadGroup rg(readGroupId);
+ m_data.push_back(rg);
+ m_lookupData[readGroupId] = m_data.size() - 1;
+ }
+
+ const size_t index = m_lookupData[readGroupId];
+ return m_data.at(index);
+}
diff --git a/bamtools/src/api/SamReadGroupDictionary.h b/bamtools/src/api/SamReadGroupDictionary.h
new file mode 100644
index 0000000..a4aeda9
--- /dev/null
+++ b/bamtools/src/api/SamReadGroupDictionary.h
@@ -0,0 +1,85 @@
+// ***************************************************************************
+// SamReadGroupDictionary.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 16 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides methods for operating on a collection of SamReadGroup entries.
+// ***************************************************************************
+
+#ifndef SAM_READGROUP_DICTIONARY_H
+#define SAM_READGROUP_DICTIONARY_H
+
+#include "api/api_global.h"
+#include "api/SamReadGroup.h"
+#include <map>
+#include <string>
+#include <vector>
+
+namespace BamTools {
+
+typedef std::vector<SamReadGroup> SamReadGroupContainer;
+typedef SamReadGroupContainer::iterator SamReadGroupIterator;
+typedef SamReadGroupContainer::const_iterator SamReadGroupConstIterator;
+
+class API_EXPORT SamReadGroupDictionary {
+
+ // ctor & dtor
+ public:
+ SamReadGroupDictionary(void);
+ SamReadGroupDictionary(const SamReadGroupDictionary& other);
+ ~SamReadGroupDictionary(void);
+
+ // query/modify read group data
+ public:
+ // adds a read group
+ void Add(const SamReadGroup& readGroup);
+ void Add(const std::string& readGroupId);
+
+ // adds multiple read groups
+ void Add(const SamReadGroupDictionary& readGroups);
+ void Add(const std::vector<SamReadGroup>& readGroups);
+ void Add(const std::vector<std::string>& readGroupIds);
+
+ // clears all read group entries
+ void Clear(void);
+
+ // returns true if dictionary contains this read group
+ bool Contains(const SamReadGroup& readGroup) const;
+ bool Contains(const std::string& readGroupId) const;
+
+ // returns true if dictionary is empty
+ bool IsEmpty(void) const;
+
+ // removes read group, if found
+ void Remove(const SamReadGroup& readGroup);
+ void Remove(const std::string& readGroupId);
+
+ // removes multiple read groups
+ void Remove(const std::vector<SamReadGroup>& readGroups);
+ void Remove(const std::vector<std::string>& readGroupIds);
+
+ // returns number of read groups in dictionary
+ int Size(void) const;
+
+ // retrieves a modifiable reference to the SamReadGroup object associated with this ID
+ SamReadGroup& operator[](const std::string& readGroupId);
+
+ // retrieve STL-compatible iterators
+ public:
+ SamReadGroupIterator Begin(void); // returns iterator to begin()
+ SamReadGroupConstIterator Begin(void) const; // returns const_iterator to begin()
+ SamReadGroupConstIterator ConstBegin(void) const; // returns const_iterator to begin()
+ SamReadGroupIterator End(void); // returns iterator to end()
+ SamReadGroupConstIterator End(void) const; // returns const_iterator to end()
+ SamReadGroupConstIterator ConstEnd(void) const; // returns const_iterator to end()
+
+ // data members
+ private:
+ SamReadGroupContainer m_data;
+ std::map<std::string, size_t> m_lookupData;
+};
+
+} // namespace BamTools
+
+#endif // SAM_READGROUP_DICTIONARY_H
diff --git a/bamtools/src/api/SamSequence.cpp b/bamtools/src/api/SamSequence.cpp
new file mode 100644
index 0000000..dea176b
--- /dev/null
+++ b/bamtools/src/api/SamSequence.cpp
@@ -0,0 +1,161 @@
+// ***************************************************************************
+// SamSequence.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides direct read/write access to the SAM sequence data fields.
+// ***************************************************************************
+
+#include "api/SamSequence.h"
+#include <sstream>
+using namespace BamTools;
+using namespace std;
+
+/*! \struct BamTools::SamSequence
+ \brief Represents a SAM sequence entry.
+
+ Provides direct read/write access to the SAM sequence data fields.
+
+ \sa \samSpecURL
+*/
+/*! \var SamSequence::AssemblyID
+ \brief corresponds to \@SQ AS:\<AssemblyID\>
+*/
+/*! \var SamSequence::Checksum
+ \brief corresponds to \@SQ M5:\<Checksum\>
+*/
+/*! \var SamSequence::Length
+ \brief corresponds to \@SQ LN:\<Length\>
+
+ Required for valid SAM header.
+*/
+/*! \var SamSequence::Name
+ \brief corresponds to \@SQ SN:\<Name\>
+
+ Required for valid SAM header.
+*/
+/*! \var SamSequence::Species
+ \brief corresponds to \@SQ SP:\<Species\>
+*/
+/*! \var SamSequence::URI
+ \brief corresponds to \@SQ UR:\<URI\>
+*/
+
+/*! \fn SamSequence::SamSequence(void)
+ \brief default constructor
+*/
+SamSequence::SamSequence(void)
+ : AssemblyID("")
+ , Checksum("")
+ , Length("")
+ , Name("")
+ , Species("")
+ , URI("")
+{ }
+
+/*! \fn SamSequence::SamSequence(const std::string& name, const int& length)
+ \brief constructs sequence with \a name and \a length
+
+ \param name desired sequence name
+ \param length desired sequence length (numeric value)
+*/
+SamSequence::SamSequence(const std::string& name,
+ const int& length)
+ : AssemblyID("")
+ , Checksum("")
+ , Name(name)
+ , Species("")
+ , URI("")
+{
+ stringstream s("");
+ s << length;
+ Length = s.str();
+}
+
+/*! \fn SamSequence::SamSequence(const std::string& name, const std::string& length)
+ \brief constructs sequence with \a name and \a length
+
+ \param name desired sequence name
+ \param length desired sequence length (string value)
+*/
+SamSequence::SamSequence(const std::string& name,
+ const std::string& length)
+ : AssemblyID("")
+ , Checksum("")
+ , Length(length)
+ , Name(name)
+ , Species("")
+ , URI("")
+{ }
+
+/*! \fn SamSequence::SamSequence(const SamSequence& other)
+ \brief copy constructor
+*/
+SamSequence::SamSequence(const SamSequence& other)
+ : AssemblyID(other.AssemblyID)
+ , Checksum(other.Checksum)
+ , Length(other.Length)
+ , Name(other.Name)
+ , Species(other.Species)
+ , URI(other.URI)
+{ }
+
+/*! \fn SamSequence::~SamSequence(void)
+ \brief destructor
+*/
+SamSequence::~SamSequence(void) { }
+
+/*! \fn void SamSequence::Clear(void)
+ \brief Clears all data fields.
+*/
+void SamSequence::Clear(void) {
+ AssemblyID.clear();
+ Checksum.clear();
+ Length.clear();
+ Name.clear();
+ Species.clear();
+ URI.clear();
+}
+
+/*! \fn bool SamSequence::HasAssemblyID(void) const
+ \brief Returns \c true if sequence contains \@SQ AS:\<AssemblyID\>
+*/
+bool SamSequence::HasAssemblyID(void) const {
+ return (!AssemblyID.empty());
+}
+
+/*! \fn bool SamSequence::HasChecksum(void) const
+ \brief Returns \c true if sequence contains \@SQ M5:\<Checksum\>
+*/
+bool SamSequence::HasChecksum(void) const {
+ return (!Checksum.empty());
+}
+
+/*! \fn bool SamSequence::HasLength(void) const
+ \brief Returns \c true if sequence contains \@SQ LN:\<Length\>
+*/
+bool SamSequence::HasLength(void) const {
+ return (!Length.empty());
+}
+
+/*! \fn bool SamSequence::HasName(void) const
+ \brief Returns \c true if sequence contains \@SQ SN:\<Name\>
+*/
+bool SamSequence::HasName(void) const {
+ return (!Name.empty());
+}
+
+/*! \fn bool SamSequence::HasSpecies(void) const
+ \brief Returns \c true if sequence contains \@SQ SP:\<Species\>
+*/
+bool SamSequence::HasSpecies(void) const {
+ return (!Species.empty());
+}
+
+/*! \fn bool SamSequence::HasURI(void) const
+ \brief Returns \c true if sequence contains \@SQ UR:\<URI\>
+*/
+bool SamSequence::HasURI(void) const {
+ return (!URI.empty());
+}
diff --git a/bamtools/src/api/SamSequence.h b/bamtools/src/api/SamSequence.h
new file mode 100644
index 0000000..c1a8792
--- /dev/null
+++ b/bamtools/src/api/SamSequence.h
@@ -0,0 +1,60 @@
+// ***************************************************************************
+// SamSequence.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides direct read/write access to the SAM sequence data fields.
+// ***************************************************************************
+
+#ifndef SAM_SEQUENCE_H
+#define SAM_SEQUENCE_H
+
+#include "api/api_global.h"
+#include <string>
+
+namespace BamTools {
+
+struct API_EXPORT SamSequence {
+
+ // ctor & dtor
+ SamSequence(void);
+ SamSequence(const std::string& name, const int& length);
+ SamSequence(const std::string& name, const std::string& length);
+ SamSequence(const SamSequence& other);
+ ~SamSequence(void);
+
+ // query/modify entire sequence
+ void Clear(void); // clears all contents
+
+ // convenience query methods
+ bool HasAssemblyID(void) const; // returns true if sequence has an assembly ID
+ bool HasChecksum(void) const; // returns true if sequence has an MD5 checksum
+ bool HasLength(void) const; // returns true if sequence has a length
+ bool HasName(void) const; // returns true if sequence has a name
+ bool HasSpecies(void) const; // returns true if sequence has a species ID
+ bool HasURI(void) const; // returns true if sequence has a URI
+
+ // data members
+ std::string AssemblyID; // AS:<AssemblyID>
+ std::string Checksum; // M5:<Checksum>
+ std::string Length; // LN:<Length> *Required for valid SAM header*
+ std::string Name; // SN:<Name> *Required for valid SAM header*
+ std::string Species; // SP:<Species>
+ std::string URI; // UR:<URI>
+};
+
+/*! \fn bool operator==(const SamSequence& lhs, const SamSequence& rhs)
+ \brief tests equality by comparing sequence names, lengths, & checksums (if available)
+*/
+API_EXPORT inline bool operator==(const SamSequence& lhs, const SamSequence& rhs) {
+ if ( lhs.Name != rhs.Name ) return false;
+ if ( lhs.Length != rhs.Length ) return false;
+ if ( lhs.HasChecksum() && rhs.HasChecksum() )
+ return (lhs.Checksum == rhs.Checksum);
+ else return true;
+}
+
+} // namespace BamTools
+
+#endif // SAM_SEQUENCE_H
diff --git a/bamtools/src/api/SamSequenceDictionary.cpp b/bamtools/src/api/SamSequenceDictionary.cpp
new file mode 100644
index 0000000..5d2ab64
--- /dev/null
+++ b/bamtools/src/api/SamSequenceDictionary.cpp
@@ -0,0 +1,301 @@
+// ***************************************************************************
+// SamSequenceDictionary.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 16 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides methods for operating on a collection of SamSequence entries.
+// *************************************************************************
+
+#include "api/SamSequenceDictionary.h"
+using namespace BamTools;
+
+#include <iostream>
+using namespace std;
+
+/*! \class BamTools::SamSequenceDictionary
+ \brief Container of SamSequence entries.
+
+ Provides methods for operating on a collection of SamSequence entries.
+*/
+
+/*! \fn SamSequenceDictionary::SamSequenceDictionary(void)
+ \brief constructor
+*/
+SamSequenceDictionary::SamSequenceDictionary(void) { }
+
+/*! \fn SamSequenceDictionary::SamSequenceDictionary(const SamSequenceDictionary& other)
+ \brief copy constructor
+*/
+SamSequenceDictionary::SamSequenceDictionary(const SamSequenceDictionary& other)
+ : m_data(other.m_data)
+ , m_lookupData(other.m_lookupData)
+{ }
+
+/*! \fn SamSequenceDictionary::~SamSequenceDictionary(void)
+ \brief destructor
+*/
+SamSequenceDictionary::~SamSequenceDictionary(void) { }
+
+/*! \fn void SamSequenceDictionary::Add(const SamSequence& sequence)
+ \brief Appends a sequence to the dictionary.
+
+ Duplicate entries are silently discarded.
+
+ \param[in] sequence entry to be added
+*/
+void SamSequenceDictionary::Add(const SamSequence& sequence) {
+ if ( IsEmpty() || !Contains(sequence) ) {
+ m_data.push_back(sequence);
+ m_lookupData[sequence.Name] = m_data.size() - 1;
+ }
+}
+
+/*! \fn void SamSequenceDictionary::Add(const std::string& name, const int& length)
+ \brief Appends a sequence to the dictionary.
+
+ This is an overloaded function.
+
+ \param[in] name name of sequence entry to be added
+ \param[in] length length of sequence entry to be added
+ \sa Add()
+*/
+void SamSequenceDictionary::Add(const std::string& name, const int& length) {
+ Add( SamSequence(name, length) );
+}
+
+/*! \fn void SamSequenceDictionary::Add(const SamSequenceDictionary& sequences)
+ \brief Appends another sequence dictionary to this one
+
+ This is an overloaded function.
+
+ \param[in] sequences sequence dictionary to be appended
+ \sa Add()
+*/
+void SamSequenceDictionary::Add(const SamSequenceDictionary& sequences) {
+ SamSequenceConstIterator seqIter = sequences.ConstBegin();
+ SamSequenceConstIterator seqEnd = sequences.ConstEnd();
+ for ( ; seqIter != seqEnd; ++seqIter )
+ Add(*seqIter);
+}
+
+/*! \fn void SamSequenceDictionary::Add(const std::vector<SamSequence>& sequences)
+ \brief Appends multiple sequences to the dictionary.
+
+ This is an overloaded function.
+
+ \param[in] sequences entries to be added
+ \sa Add()
+*/
+void SamSequenceDictionary::Add(const std::vector<SamSequence>& sequences) {
+ vector<SamSequence>::const_iterator seqIter = sequences.begin();
+ vector<SamSequence>::const_iterator seqEnd = sequences.end();
+ for ( ; seqIter!= seqEnd; ++seqIter )
+ Add(*seqIter);
+}
+
+/*! \fn void SamSequenceDictionary::Add(const std::map<std::string, int>& sequenceMap)
+ \brief Appends multiple sequences to the dictionary.
+
+ This is an overloaded function.
+
+ \param[in] sequenceMap map of sequence entries (name => length) to be added
+ \sa Add()
+*/
+void SamSequenceDictionary::Add(const std::map<std::string, int>& sequenceMap) {
+ map<string, int>::const_iterator seqIter = sequenceMap.begin();
+ map<string, int>::const_iterator seqEnd = sequenceMap.end();
+ for ( ; seqIter != seqEnd; ++seqIter ) {
+ const string& name = (*seqIter).first;
+ const int& length = (*seqIter).second;
+ Add( SamSequence(name, length) );
+ }
+}
+
+/*! \fn SamSequenceIterator SamSequenceDictionary::Begin(void)
+ \return an STL iterator pointing to the first sequence
+ \sa ConstBegin(), End()
+*/
+SamSequenceIterator SamSequenceDictionary::Begin(void) {
+ return m_data.begin();
+}
+
+/*! \fn SamSequenceConstIterator SamSequenceDictionary::Begin(void) const
+ \return an STL const_iterator pointing to the first sequence
+
+ This is an overloaded function.
+
+ \sa ConstBegin(), End()
+*/
+SamSequenceConstIterator SamSequenceDictionary::Begin(void) const {
+ return m_data.begin();
+}
+
+/*! \fn void SamSequenceDictionary::Clear(void)
+ \brief Clears all sequence entries.
+*/
+void SamSequenceDictionary::Clear(void) {
+ m_data.clear();
+ m_lookupData.clear();
+}
+
+/*! \fn SamSequenceConstIterator SamSequenceDictionary::ConstBegin(void) const
+ \return an STL const_iterator pointing to the first sequence
+ \sa Begin(), ConstEnd()
+*/
+SamSequenceConstIterator SamSequenceDictionary::ConstBegin(void) const {
+ return m_data.begin();
+}
+
+/*! \fn SamSequenceConstIterator SamSequenceDictionary::ConstEnd(void) const
+ \return an STL const_iterator pointing to the imaginary entry after the last sequence
+ \sa End(), ConstBegin()
+*/
+SamSequenceConstIterator SamSequenceDictionary::ConstEnd(void) const {
+ return m_data.end();
+}
+
+/*! \fn bool SamSequenceDictionary::Contains(const std::string& sequenceName) const
+ \brief Returns true if dictionary contains sequence.
+
+ \param[in] sequenceName search for sequence matching this name
+ \return \c true if dictionary contains a sequence with this name
+*/
+bool SamSequenceDictionary::Contains(const std::string& sequenceName) const {
+ return ( m_lookupData.find(sequenceName) != m_lookupData.end() );
+}
+
+/*! \fn bool SamSequenceDictionary::Contains(const SamSequence& sequence) const
+ \brief Returns true if dictionary contains sequence (matches on name).
+
+ This is an overloaded function.
+
+ \param[in] sequence search for this sequence
+ \return \c true if dictionary contains sequence (matching on name)
+*/
+bool SamSequenceDictionary::Contains(const SamSequence& sequence) const {
+ return Contains(sequence.Name);
+}
+
+/*! \fn SamSequenceIterator SamSequenceDictionary::End(void)
+ \return an STL iterator pointing to the imaginary entry after the last sequence
+ \sa Begin(), ConstEnd()
+*/
+SamSequenceIterator SamSequenceDictionary::End(void) {
+ return m_data.end();
+}
+
+/*! \fn SamSequenceConstIterator SamSequenceDictionary::End(void) const
+ \return an STL const_iterator pointing to the imaginary entry after the last sequence
+
+ This is an overloaded function.
+
+ \sa Begin(), ConstEnd()
+*/
+SamSequenceConstIterator SamSequenceDictionary::End(void) const {
+ return m_data.end();
+}
+
+/*! \fn bool SamSequenceDictionary::IsEmpty(void) const
+ \brief Returns \c true if dictionary contains no sequences
+ \sa Size()
+*/
+bool SamSequenceDictionary::IsEmpty(void) const {
+ return m_data.empty();
+}
+
+/*! \fn void SamSequenceDictionary::Remove(const SamSequence& sequence)
+ \brief Removes sequence from dictionary, if found (matches on name).
+
+ This is an overloaded function.
+
+ \param[in] sequence SamSequence to remove (matching on name)
+*/
+void SamSequenceDictionary::Remove(const SamSequence& sequence) {
+ Remove(sequence.Name);
+}
+
+/*! \fn void SamSequenceDictionary::Remove(const std::string& sequenceName)
+ \brief Removes sequence from dictionary, if found.
+
+ \param[in] sequenceName name of sequence to remove
+ \sa Remove()
+*/
+void SamSequenceDictionary::Remove(const std::string& sequenceName) {
+
+ // skip if empty dictionary or if name unknown
+ if ( IsEmpty() || !Contains(sequenceName) )
+ return;
+
+ // update 'lookup index' for every entry after @sequenceName
+ const size_t indexToRemove = m_lookupData[sequenceName];
+ const size_t numEntries = m_data.size();
+ for ( size_t i = indexToRemove+1; i < numEntries; ++i ) {
+ const SamSequence& sq = m_data.at(i);
+ --m_lookupData[sq.Name];
+ }
+
+ // erase entry from containers
+ m_data.erase( Begin() + indexToRemove );
+ m_lookupData.erase(sequenceName);
+}
+
+/*! \fn void SamSequenceDictionary::Remove(const std::vector<SamSequence>& sequences)
+ \brief Removes multiple sequences from dictionary.
+
+ This is an overloaded function.
+
+ \param[in] sequences sequences to remove
+ \sa Remove()
+*/
+void SamSequenceDictionary::Remove(const std::vector<SamSequence>& sequences) {
+ vector<SamSequence>::const_iterator rgIter = sequences.begin();
+ vector<SamSequence>::const_iterator rgEnd = sequences.end();
+ for ( ; rgIter!= rgEnd; ++rgIter )
+ Remove(*rgIter);
+}
+
+/*! \fn void SamSequenceDictionary::Remove(const std::vector<std::string>& sequenceNames)
+ \brief Removes multiple sequences from dictionary.
+
+ This is an overloaded function.
+
+ \param[in] sequenceNames names of the sequences to remove
+ \sa Remove()
+*/
+void SamSequenceDictionary::Remove(const std::vector<std::string>& sequenceNames) {
+ vector<string>::const_iterator rgIter = sequenceNames.begin();
+ vector<string>::const_iterator rgEnd = sequenceNames.end();
+ for ( ; rgIter!= rgEnd; ++rgIter )
+ Remove(*rgIter);
+}
+
+/*! \fn int SamSequenceDictionary::Size(void) const
+ \brief Returns number of sequences in dictionary.
+ \sa IsEmpty()
+*/
+int SamSequenceDictionary::Size(void) const {
+ return m_data.size();
+}
+
+/*! \fn SamSequence& SamSequenceDictionary::operator[](const std::string& sequenceName)
+ \brief Retrieves the modifiable SamSequence that matches \a sequenceName.
+
+ \note If the dictionary contains no sequence matching this name, this function inserts
+ a new one with this name (length:0), and returns a reference to it. If you want to avoid
+ this insertion behavior, check the result of Contains() before using this operator.
+
+ \param[in] sequenceName name of sequence to retrieve
+ \return a modifiable reference to the SamSequence associated with the name
+*/
+SamSequence& SamSequenceDictionary::operator[](const std::string& sequenceName) {
+
+ if ( !Contains(sequenceName) ) {
+ SamSequence seq(sequenceName, 0);
+ m_data.push_back(seq);
+ m_lookupData[sequenceName] = m_data.size() - 1;
+ }
+
+ const size_t index = m_lookupData[sequenceName];
+ return m_data.at(index);
+}
diff --git a/bamtools/src/api/SamSequenceDictionary.h b/bamtools/src/api/SamSequenceDictionary.h
new file mode 100644
index 0000000..d267dbd
--- /dev/null
+++ b/bamtools/src/api/SamSequenceDictionary.h
@@ -0,0 +1,86 @@
+// ***************************************************************************
+// SamSequenceDictionary.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 16 October 2011
+// ---------------------------------------------------------------------------
+// Provides methods for operating on a collection of SamSequence entries.
+// ***************************************************************************
+
+#ifndef SAM_SEQUENCE_DICTIONARY_H
+#define SAM_SEQUENCE_DICTIONARY_H
+
+#include "api/api_global.h"
+#include "api/SamSequence.h"
+#include <map>
+#include <string>
+#include <vector>
+
+namespace BamTools {
+
+typedef std::vector<SamSequence> SamSequenceContainer;
+typedef SamSequenceContainer::iterator SamSequenceIterator;
+typedef SamSequenceContainer::const_iterator SamSequenceConstIterator;
+
+class API_EXPORT SamSequenceDictionary {
+
+ // ctor & dtor
+ public:
+ SamSequenceDictionary(void);
+ SamSequenceDictionary(const SamSequenceDictionary& other);
+ ~SamSequenceDictionary(void);
+
+ // query/modify sequence data
+ public:
+ // adds a sequence
+ void Add(const SamSequence& sequence);
+ void Add(const std::string& name, const int& length);
+
+ // adds multiple sequences
+ void Add(const SamSequenceDictionary& sequences);
+ void Add(const std::vector<SamSequence>& sequences);
+ void Add(const std::map<std::string, int>& sequenceMap);
+
+ // clears all sequence entries
+ void Clear(void);
+
+ // returns true if dictionary contains this sequence
+ bool Contains(const SamSequence& sequence) const;
+ bool Contains(const std::string& sequenceName) const;
+
+ // returns true if dictionary is empty
+ bool IsEmpty(void) const;
+
+ // removes sequence, if found
+ void Remove(const SamSequence& sequence);
+ void Remove(const std::string& sequenceName);
+
+ // removes multiple sequences
+ void Remove(const std::vector<SamSequence>& sequences);
+ void Remove(const std::vector<std::string>& sequenceNames);
+
+ // returns number of sequences in dictionary
+ int Size(void) const;
+
+ // retrieves a modifiable reference to the SamSequence object associated with this name
+ SamSequence& operator[](const std::string& sequenceName);
+
+ // retrieve STL-compatible iterators
+ public:
+ SamSequenceIterator Begin(void); // returns iterator to begin()
+ SamSequenceConstIterator Begin(void) const; // returns const_iterator to begin()
+ SamSequenceConstIterator ConstBegin(void) const; // returns const_iterator to begin()
+ SamSequenceIterator End(void); // returns iterator to end()
+ SamSequenceConstIterator End(void) const; // returns const_iterator to end()
+ SamSequenceConstIterator ConstEnd(void) const; // returns const_iterator to end()
+
+ // data members
+ private:
+ SamSequenceContainer m_data;
+ std::map<std::string, size_t> m_lookupData;
+};
+
+} // namespace BamTools
+
+#endif // SAM_SEQUENCE_DICTIONARY_H
+
diff --git a/bamtools/src/api/algorithms/Sort.h b/bamtools/src/api/algorithms/Sort.h
new file mode 100644
index 0000000..32902e1
--- /dev/null
+++ b/bamtools/src/api/algorithms/Sort.h
@@ -0,0 +1,335 @@
+// ***************************************************************************
+// Sort.h (c) 2009 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// All rights reserved.
+// ---------------------------------------------------------------------------
+// Last modified: 4 April 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides sorting functionality.
+// ***************************************************************************
+
+#ifndef ALGORITHMS_SORT_H
+#define ALGORITHMS_SORT_H
+
+#include "api/api_global.h"
+#include "api/BamAlignment.h"
+#include "api/BamReader.h"
+#include "api/BamMultiReader.h"
+#include <cassert>
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <vector>
+
+namespace BamTools {
+namespace Algorithms {
+
+/*! \struct BamTools::Algorithms::Sort
+ \brief Provides classes & methods related to sorting BamAlignments
+*/
+struct API_EXPORT Sort {
+
+ //! Provides explicit values for specifying desired sort ordering
+ enum Order { AscendingOrder = 0
+ , DescendingOrder
+ };
+
+ /*! \fn template<typename ElemType> static inline bool sort_helper(const Sort::Order& order, const ElemType& lhs, const ElemType& rhs)
+ \internal
+
+ Determines necessary STL function object depending on requested Sort::Order
+ */
+ template<typename ElemType>
+ static inline bool sort_helper(const Sort::Order& order, const ElemType& lhs, const ElemType& rhs) {
+ switch ( order ) {
+ case ( Sort::AscendingOrder ) : { std::less<ElemType> comp; return comp(lhs, rhs); }
+ case ( Sort::DescendingOrder ) : { std::greater<ElemType> comp; return comp(lhs, rhs); }
+ default : BT_ASSERT_UNREACHABLE;
+ }
+ return false; // <-- unreachable
+ }
+
+ //! Base class for our sorting function objects
+ typedef std::binary_function<BamAlignment, BamAlignment, bool> AlignmentSortBase;
+
+ /*! \struct BamTools::Algorithms::Sort::ByName
+ \brief Function object for comparing alignments by name
+
+ Default sort order is Sort::AscendingOrder.
+
+ \code
+ std::vector<BamAlignment> a;
+
+ // sort by name, in ascending order (the following two lines are equivalent):
+ std::sort( a.begin(), a.end(), Sort::ByName() );
+ std::sort( a.begin(), a.end(), Sort::ByName(Sort::AscendingOrder) );
+
+ // OR sort in descending order
+ std::sort( a.begin(), a.end(), Sort::ByName(Sort::DescendingOrder) );
+ \endcode
+ */
+ struct ByName : public AlignmentSortBase {
+
+ // ctor
+ ByName(const Sort::Order& order = Sort::AscendingOrder)
+ : m_order(order)
+ { }
+
+ // comparison function
+ bool operator()(const BamTools::BamAlignment& lhs, const BamTools::BamAlignment& rhs) {
+ return sort_helper(m_order, lhs.Name, rhs.Name);
+ }
+
+ // used by BamMultiReader internals
+ static inline bool UsesCharData(void) { return true; }
+
+ // data members
+ private:
+ const Sort::Order m_order;
+ };
+
+ /*! \struct BamTools::Algorithms::Sort::ByPosition
+ \brief Function object for comparing alignments by position
+
+ Default sort order is Sort::AscendingOrder.
+
+ \code
+ std::vector<BamAlignment> a;
+
+ // sort by position, in ascending order (the following two lines are equivalent):
+ std::sort( a.begin(), a.end(), Sort::ByPosition() );
+ std::sort( a.begin(), a.end(), Sort::ByPosition(Sort::AscendingOrder) );
+
+ // OR sort in descending order
+ std::sort( a.begin(), a.end(), Sort::ByPosition(Sort::DescendingOrder) );
+ \endcode
+ */
+ struct ByPosition : public AlignmentSortBase {
+
+ // ctor
+ ByPosition(const Sort::Order& order = Sort::AscendingOrder)
+ : m_order(order)
+ { }
+
+ // comparison function
+ bool operator()(const BamTools::BamAlignment& lhs, const BamTools::BamAlignment& rhs) {
+
+ // force unmapped aligmnents to end
+ if ( lhs.RefID == -1 ) return false;
+ if ( rhs.RefID == -1 ) return true;
+
+ // if on same reference, sort on position
+ if ( lhs.RefID == rhs.RefID )
+ return sort_helper(m_order, lhs.Position, rhs.Position);
+
+ // otherwise sort on reference ID
+ return sort_helper(m_order, lhs.RefID, rhs.RefID);
+ }
+
+ // used by BamMultiReader internals
+ static inline bool UsesCharData(void) { return false; }
+
+ // data members
+ private:
+ const Sort::Order m_order;
+ };
+
+ /*! \struct BamTools::Algorithms::Sort::ByTag
+ \brief Function object for comparing alignments by tag value
+
+ Default sort order is Sort::AscendingOrder.
+
+ \code
+ std::vector<BamAlignment> a;
+
+ // sort by edit distance, in ascending order (the following two lines are equivalent):
+ std::sort( a.begin(), a.end(), Sort::ByTag<int>("NM") );
+ std::sort( a.begin(), a.end(), Sort::ByTag<int>("NM", Sort::AscendingOrder) );
+
+ // OR sort in descending order
+ std::sort( a.begin(), a.end(), Sort::ByTag<int>("NM", Sort::DescendingOrder) );
+ \endcode
+ */
+ template<typename T>
+ struct ByTag : public AlignmentSortBase {
+
+ // ctor
+ ByTag(const std::string& tag,
+ const Sort::Order& order = Sort::AscendingOrder)
+ : m_tag(tag)
+ , m_order(order)
+ { }
+
+ // comparison function
+ bool operator()(const BamTools::BamAlignment& lhs, const BamTools::BamAlignment& rhs) {
+
+ // force alignments without tag to end
+ T lhsTagValue;
+ T rhsTagValue;
+ if ( !lhs.GetTag(m_tag, lhsTagValue) ) return false;
+ if ( !rhs.GetTag(m_tag, rhsTagValue) ) return true;
+
+ // otherwise compare on tag values
+ return sort_helper(m_order, lhsTagValue, rhsTagValue);
+ }
+
+ // used by BamMultiReader internals
+ static inline bool UsesCharData(void) { return true; }
+
+ // data members
+ private:
+ const std::string m_tag;
+ const Sort::Order m_order;
+ };
+
+ /*! \struct BamTools::Algorithms::Sort::Unsorted
+ \brief Placeholder function object
+
+ This function object exists purely to allow for dropping a "do not care" ordering
+ into methods, containers, etc that are designed to work with the other sorting objects.
+
+ \code
+ std::set<BamAlignment, Sort::ByName>; // STL set, ordered on alignment name
+ std::set<BamAlignment, Sort::Unsorted>; // STL set, unsorted (but probably insertion order)
+ \endcode
+ */
+ struct Unsorted : public AlignmentSortBase {
+
+ // comparison function
+ inline bool operator()(const BamTools::BamAlignment&, const BamTools::BamAlignment&) {
+ return false; // returning false tends to retain insertion order
+ }
+
+ // used by BamMultiReader internals
+ static inline bool UsesCharData(void) { return false; }
+ };
+
+ /*! Sorts a std::vector of alignments (in-place), using the provided compare function.
+
+ \code
+ std::vector<BamAlignemnt> a;
+ // populate data
+
+ // sort our alignment list by edit distance
+ Sort::SortAlignments(a, Sort::ByTag<int>("NM"));
+ \endcode
+
+ \param[in,out] data vector of alignments to be sorted
+ \param[in] comp comparison function object
+ */
+ template<typename Compare>
+ static inline void SortAlignments(std::vector<BamAlignment>& data,
+ const Compare& comp = Compare())
+ {
+ std::sort(data.begin(), data.end(), comp);
+ }
+
+ /*! Returns a sorted copy of the input alignments, using the provided compare function.
+
+ \code
+ std::vector<BamAlignemnt> a;
+ // populate data
+
+ // get a copy of our original data, sorted by edit distance (descending order)
+ std::vector<BamAligment> sortedData;
+ sortedData = Sort::SortAlignments(a, Sort::ByTag<int>("NM", Sort::DescendingOrder));
+ \endcode
+
+ \param[in] input vector of alignments to be sorted
+ \param[in] comp comparison function object
+ \return sorted copy of the input data
+ */
+ template<typename Compare>
+ static inline std::vector<BamAlignment> SortAlignments(const std::vector<BamAlignment>& input,
+ const Compare& comp = Compare())
+ {
+ std::vector<BamAlignment> output(input);
+ SortAlignments(output, comp);
+ return output;
+ }
+
+ /*! Reads a region of alignments from a position-sorted BAM file,
+ then sorts by the provided compare function
+
+ \code
+ BamReader reader;
+ // open BAM file & index file
+
+ BamRegion region;
+ // define a region of interest (i.e. a exon or some other feature)
+
+ // get all alignments covering that region, sorted by read group name
+ std::vector<BamAlignments> a;
+ a = Sort::GetSortedRegion(reader, region, Sort::ByTag<std::string>("RG"));
+ \endcode
+
+ \param[in] reader BamReader opened on desired BAM file
+ \param[in] region desired region-of-interest
+ \param[in] comp comparison function object
+ \return sorted vector of the region's alignments
+ */
+ template<typename Compare>
+ static std::vector<BamAlignment> GetSortedRegion(BamReader& reader,
+ const BamRegion& region,
+ const Compare& comp = Compare())
+ {
+ // return empty container if unable to find region
+ if ( !reader.IsOpen() ) return std::vector<BamAlignment>();
+ if ( !reader.SetRegion(region) ) return std::vector<BamAlignment>();
+
+ // iterate through region, grabbing alignments
+ BamAlignment al;
+ std::vector<BamAlignment> results;
+ while ( reader.GetNextAlignmentCore(al) )
+ results.push_back(al);
+
+ // sort & return alignments
+ SortAlignments(results, comp);
+ return results;
+ }
+
+ /*! Reads a region of alignments from position-sorted BAM files,
+ then sorts by the provided compare function
+
+ \code
+ BamMultiReader reader;
+ // open BAM files & index files
+
+ BamRegion region;
+ // define a region of interest (i.e. a exon or some other feature)
+
+ // get all alignments covering that region, sorted by read group name
+ std::vector<BamAlignments> a;
+ a = Sort::GetSortedRegion(reader, region, Sort::ByTag<std::string>("RG"));
+ \endcode
+
+ \param[in] reader BamMultiReader opened on desired BAM files
+ \param[in] region desired region-of-interest
+ \param[in] comp comparison function object
+ \return sorted vector of the region's alignments
+ */
+ template<typename Compare>
+ static std::vector<BamAlignment> GetSortedRegion(BamMultiReader& reader,
+ const BamRegion& region,
+ const Compare& comp = Compare())
+ {
+ // return empty container if unable to find region
+ if ( !reader.HasOpenReaders() ) return std::vector<BamAlignment>();
+ if ( !reader.SetRegion(region) ) return std::vector<BamAlignment>();
+
+ // iterate through region, grabbing alignments
+ BamAlignment al;
+ std::vector<BamAlignment> results;
+ while ( reader.GetNextAlignmentCore(al) )
+ results.push_back(al);
+
+ // sort & return alignments
+ SortAlignments(results, comp);
+ return results;
+ }
+};
+
+} // namespace Algorithms
+} // namespace BamTools
+
+#endif // ALGORITHMS_SORT_H
diff --git a/bamtools/src/api/api_global.h b/bamtools/src/api/api_global.h
new file mode 100644
index 0000000..f1c2353
--- /dev/null
+++ b/bamtools/src/api/api_global.h
@@ -0,0 +1,21 @@
+// ***************************************************************************
+// api_global.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 19 November 2010 (DB)
+// ---------------------------------------------------------------------------
+// Provides macros for exporting & importing BamTools API library symbols
+// ***************************************************************************
+
+#ifndef API_GLOBAL_H
+#define API_GLOBAL_H
+
+#include "shared/bamtools_global.h"
+
+#ifdef BAMTOOLS_API_LIBRARY
+# define API_EXPORT BAMTOOLS_LIBRARY_EXPORT
+#else
+# define API_EXPORT BAMTOOLS_LIBRARY_IMPORT
+#endif
+
+#endif // API_GLOBAL_H
diff --git a/bamtools/src/api/internal/CMakeLists.txt b/bamtools/src/api/internal/CMakeLists.txt
new file mode 100644
index 0000000..a96cd6f
--- /dev/null
+++ b/bamtools/src/api/internal/CMakeLists.txt
@@ -0,0 +1,25 @@
+# ==========================
+# BamTools CMakeLists.txt
+# (c) 2011 Derek Barnett
+#
+# src/api/internal
+# ==========================
+
+set( InternalDir "internal" )
+
+add_subdirectory( bam )
+add_subdirectory( index )
+add_subdirectory( io )
+add_subdirectory( sam )
+add_subdirectory( utils )
+
+set( InternalSources
+ ${InternalBamSources}
+ ${InternalIndexSources}
+ ${InternalIOSources}
+ ${InternalSamSources}
+ ${InternalUtilsSources}
+
+ PARENT_SCOPE # <-- leave this last
+ )
+
diff --git a/bamtools/src/api/internal/bam/BamHeader_p.cpp b/bamtools/src/api/internal/bam/BamHeader_p.cpp
new file mode 100644
index 0000000..aa3cdf7
--- /dev/null
+++ b/bamtools/src/api/internal/bam/BamHeader_p.cpp
@@ -0,0 +1,125 @@
+// ***************************************************************************
+// BamHeader_p.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 18 November 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides the basic functionality for handling BAM headers.
+// ***************************************************************************
+
+#include "api/BamAux.h"
+#include "api/BamConstants.h"
+#include "api/internal/bam/BamHeader_p.h"
+#include "api/internal/io/BgzfStream_p.h"
+#include "api/internal/utils/BamException_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cstdlib>
+#include <cstring>
+using namespace std;
+
+// ------------------------
+// static utility methods
+// ------------------------
+
+static inline
+bool isValidMagicNumber(const char* buffer) {
+ return ( strncmp(buffer, Constants::BAM_HEADER_MAGIC,
+ Constants::BAM_HEADER_MAGIC_LENGTH) == 0 );
+}
+
+// --------------------------
+// BamHeader implementation
+// --------------------------
+
+// ctor
+BamHeader::BamHeader(void) { }
+
+// dtor
+BamHeader::~BamHeader(void) { }
+
+// reads magic number from BGZF stream, returns true if valid
+void BamHeader::CheckMagicNumber(BgzfStream* stream) {
+
+ // try to read magic number
+ char buffer[Constants::BAM_HEADER_MAGIC_LENGTH];
+ const size_t numBytesRead = stream->Read(buffer, Constants::BAM_HEADER_MAGIC_LENGTH);
+ if ( numBytesRead != (int)Constants::BAM_HEADER_MAGIC_LENGTH )
+ throw BamException("BamHeader::CheckMagicNumber", "could not read magic number");
+
+ // validate magic number
+ if ( !isValidMagicNumber(buffer) )
+ throw BamException("BamHeader::CheckMagicNumber", "invalid magic number");
+}
+
+// clear SamHeader data
+void BamHeader::Clear(void) {
+ m_header.Clear();
+}
+
+// return true if SamHeader data is valid
+bool BamHeader::IsValid(void) const {
+ return m_header.IsValid();
+}
+
+// load BAM header ('magic number' and SAM header text) from BGZF stream
+void BamHeader::Load(BgzfStream* stream) {
+
+ // read & check magic number
+ CheckMagicNumber(stream);
+
+ // read header (length, then actual text)
+ uint32_t length(0);
+ ReadHeaderLength(stream, length);
+ ReadHeaderText(stream, length);
+}
+
+// reads SAM header text length from BGZF stream, stores it in @length
+void BamHeader::ReadHeaderLength(BgzfStream* stream, uint32_t& length) {
+
+ // read BAM header text length
+ char buffer[sizeof(uint32_t)];
+ const size_t numBytesRead = stream->Read(buffer, sizeof(uint32_t));
+ if ( numBytesRead != sizeof(uint32_t) )
+ throw BamException("BamHeader::ReadHeaderLength", "could not read header length");
+
+ // convert char buffer to length
+ length = BamTools::UnpackUnsignedInt(buffer);
+ if ( BamTools::SystemIsBigEndian() )
+ BamTools::SwapEndian_32(length);
+}
+
+// reads SAM header text from BGZF stream, stores in SamHeader object
+void BamHeader::ReadHeaderText(BgzfStream* stream, const uint32_t& length) {
+
+ // read header text
+ char* headerText = (char*)calloc(length + 1, 1);
+ const size_t bytesRead = stream->Read(headerText, length);
+
+ // if error reading, clean up buffer & throw
+ if ( bytesRead != length ) {
+ free(headerText);
+ throw BamException("BamHeader::ReadHeaderText", "could not read header text");
+ }
+
+ // otherwise, text was read OK
+ // store & cleanup
+ m_header.SetHeaderText( (string)((const char*)headerText) );
+ free(headerText);
+}
+
+// returns const-reference to SamHeader data object
+const SamHeader& BamHeader::ToConstSamHeader(void) const {
+ return m_header;
+}
+
+// returns *copy* of SamHeader data object
+SamHeader BamHeader::ToSamHeader(void) const {
+ return m_header;
+}
+
+// returns SAM-formatted string of header data
+string BamHeader::ToString(void) const {
+ return m_header.ToString();
+}
diff --git a/bamtools/src/api/internal/bam/BamHeader_p.h b/bamtools/src/api/internal/bam/BamHeader_p.h
new file mode 100644
index 0000000..22851d8
--- /dev/null
+++ b/bamtools/src/api/internal/bam/BamHeader_p.h
@@ -0,0 +1,71 @@
+// ***************************************************************************
+// BamHeader_p.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 18 November 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides the basic functionality for handling BAM headers.
+// ***************************************************************************
+
+#ifndef BAMHEADER_P_H
+#define BAMHEADER_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/SamHeader.h"
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class BgzfStream;
+
+class BamHeader {
+
+ // ctor & dtor
+ public:
+ BamHeader(void);
+ ~BamHeader(void);
+
+ // BamHeader interface
+ public:
+ // clear SamHeader data
+ void Clear(void);
+ // return true if SamHeader data is valid
+ bool IsValid(void) const;
+ // load BAM header ('magic number' and SAM header text) from BGZF stream
+ // returns true if all OK
+ void Load(BgzfStream* stream);
+ // returns (read-only) reference to SamHeader data object
+ const SamHeader& ToConstSamHeader(void) const;
+ // returns (editable) copy of SamHeader data object
+ SamHeader ToSamHeader(void) const;
+ // returns SAM-formatted string of header data
+ std::string ToString(void) const;
+
+ // internal methods
+ private:
+ // reads magic number from BGZF stream
+ void CheckMagicNumber(BgzfStream* stream);
+ // reads SAM header length from BGZF stream, stores it in @length
+ void ReadHeaderLength(BgzfStream* stream, uint32_t& length);
+ // reads SAM header text from BGZF stream, stores in SamHeader object
+ void ReadHeaderText(BgzfStream* stream, const uint32_t& length);
+
+ // data members
+ private:
+ SamHeader m_header;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMHEADER_P_H
diff --git a/bamtools/src/api/internal/bam/BamMultiMerger_p.h b/bamtools/src/api/internal/bam/BamMultiMerger_p.h
new file mode 100644
index 0000000..3000097
--- /dev/null
+++ b/bamtools/src/api/internal/bam/BamMultiMerger_p.h
@@ -0,0 +1,266 @@
+// ***************************************************************************
+// BamMultiMerger_p.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides merging functionality for BamMultiReader. At this point, supports
+// sorting results by (refId, position) or by read name.
+// ***************************************************************************
+
+#ifndef BAMMULTIMERGER_P_H
+#define BAMMULTIMERGER_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/BamAlignment.h"
+#include "api/BamReader.h"
+#include "api/algorithms/Sort.h"
+#include <deque>
+#include <functional>
+#include <set>
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+struct MergeItem {
+
+ // data members
+ BamReader* Reader;
+ BamAlignment* Alignment;
+
+ // ctors & dtor
+ MergeItem(BamReader* reader = 0,
+ BamAlignment* alignment = 0)
+ : Reader(reader)
+ , Alignment(alignment)
+ { }
+
+ MergeItem(const MergeItem& other)
+ : Reader(other.Reader)
+ , Alignment(other.Alignment)
+ { }
+
+ ~MergeItem(void) { }
+};
+
+template<typename Compare>
+struct MergeItemSorter : public std::binary_function<MergeItem, MergeItem, bool> {
+
+ public:
+ MergeItemSorter(const Compare& comp = Compare())
+ : m_comp(comp)
+ { }
+
+ bool operator()(const MergeItem& lhs, const MergeItem& rhs) {
+ const BamAlignment& l = *lhs.Alignment;
+ const BamAlignment& r = *rhs.Alignment;
+ return m_comp(l,r);
+ }
+
+ private:
+ Compare m_comp;
+};
+
+// pure ABC so we can just work polymorphically with any specific merger implementation
+class IMultiMerger {
+
+ public:
+ IMultiMerger(void) { }
+ virtual ~IMultiMerger(void) { }
+ public:
+ virtual void Add(MergeItem item) =0;
+ virtual void Clear(void) =0;
+ virtual const MergeItem& First(void) const =0;
+ virtual bool IsEmpty(void) const =0;
+ virtual void Remove(BamReader* reader) =0;
+ virtual int Size(void) const =0;
+ virtual MergeItem TakeFirst(void) =0;
+};
+
+// general merger
+template<typename Compare>
+class MultiMerger : public IMultiMerger {
+
+ public:
+ typedef Compare CompareType;
+ typedef MergeItemSorter<CompareType> MergeType;
+
+ public:
+ explicit MultiMerger(const Compare& comp = Compare())
+ : IMultiMerger()
+ , m_data( MergeType(comp) )
+ { }
+ ~MultiMerger(void) { }
+
+ public:
+ void Add(MergeItem item);
+ void Clear(void);
+ const MergeItem& First(void) const;
+ bool IsEmpty(void) const;
+ void Remove(BamReader* reader);
+ int Size(void) const;
+ MergeItem TakeFirst(void);
+
+ private:
+ typedef MergeItem ValueType;
+ typedef std::multiset<ValueType, MergeType> ContainerType;
+ typedef typename ContainerType::iterator DataIterator;
+ typedef typename ContainerType::const_iterator DataConstIterator;
+ ContainerType m_data;
+};
+
+template <typename Compare>
+inline void MultiMerger<Compare>::Add(MergeItem item) {
+
+ // N.B. - any future custom Compare types must define this method
+ // see algorithms/Sort.h
+
+ if ( CompareType::UsesCharData() )
+ item.Alignment->BuildCharData();
+ m_data.insert(item);
+}
+
+template <typename Compare>
+inline void MultiMerger<Compare>::Clear(void) {
+ m_data.clear();
+}
+
+template <typename Compare>
+inline const MergeItem& MultiMerger<Compare>::First(void) const {
+ const ValueType& entry = (*m_data.begin());
+ return entry;
+}
+
+template <typename Compare>
+inline bool MultiMerger<Compare>::IsEmpty(void) const {
+ return m_data.empty();
+}
+template <typename Compare>
+inline void MultiMerger<Compare>::Remove(BamReader* reader) {
+
+ if ( reader == 0 ) return;
+ const std::string& filenameToRemove = reader->GetFilename();
+
+ // iterate over readers in cache
+ DataIterator dataIter = m_data.begin();
+ DataIterator dataEnd = m_data.end();
+ for ( ; dataIter != dataEnd; ++dataIter ) {
+ const MergeItem& item = (*dataIter);
+ const BamReader* itemReader = item.Reader;
+ if ( itemReader == 0 ) continue;
+
+ // remove iterator on match
+ if ( itemReader->GetFilename() == filenameToRemove ) {
+ m_data.erase(dataIter);
+ return;
+ }
+ }
+}
+template <typename Compare>
+inline int MultiMerger<Compare>::Size(void) const {
+ return m_data.size();
+}
+
+template <typename Compare>
+inline MergeItem MultiMerger<Compare>::TakeFirst(void) {
+ DataIterator firstIter = m_data.begin();
+ MergeItem firstItem = (*firstIter);
+ m_data.erase(firstIter);
+ return firstItem;
+}
+
+// unsorted "merger"
+template<>
+class MultiMerger<Algorithms::Sort::Unsorted> : public IMultiMerger {
+
+ public:
+ explicit MultiMerger(const Algorithms::Sort::Unsorted& comp = Algorithms::Sort::Unsorted())
+ : IMultiMerger()
+ { }
+ ~MultiMerger(void) { }
+
+ public:
+ void Add(MergeItem item);
+ void Clear(void);
+ const MergeItem& First(void) const;
+ bool IsEmpty(void) const;
+ void Remove(BamReader* reader);
+ int Size(void) const;
+ MergeItem TakeFirst(void);
+
+ private:
+ typedef MergeItem ValueType;
+ typedef std::deque<ValueType> ContainerType;
+ typedef ContainerType::iterator DataIterator;
+ typedef ContainerType::const_iterator DataConstIterator;
+ ContainerType m_data;
+};
+
+inline
+void MultiMerger<Algorithms::Sort::Unsorted>::Add(MergeItem item) {
+ m_data.push_back(item);
+}
+
+inline
+void MultiMerger<Algorithms::Sort::Unsorted>::Clear(void) {
+ m_data.clear();
+}
+
+inline
+const MergeItem& MultiMerger<Algorithms::Sort::Unsorted>::First(void) const {
+ return m_data.front();
+}
+
+inline
+bool MultiMerger<Algorithms::Sort::Unsorted>::IsEmpty(void) const {
+ return m_data.empty();
+}
+
+inline
+void MultiMerger<Algorithms::Sort::Unsorted>::Remove(BamReader* reader) {
+
+ if ( reader == 0 ) return;
+ const std::string filenameToRemove = reader->GetFilename();
+
+ // iterate over readers in cache
+ DataIterator dataIter = m_data.begin();
+ DataIterator dataEnd = m_data.end();
+ for ( ; dataIter != dataEnd; ++dataIter ) {
+ const MergeItem& item = (*dataIter);
+ const BamReader* itemReader = item.Reader;
+ if ( itemReader == 0 ) continue;
+
+ // remove iterator on match
+ if ( itemReader->GetFilename() == filenameToRemove ) {
+ m_data.erase(dataIter);
+ return;
+ }
+ }
+}
+
+inline
+int MultiMerger<Algorithms::Sort::Unsorted>::Size(void) const {
+ return m_data.size();
+}
+
+inline
+MergeItem MultiMerger<Algorithms::Sort::Unsorted>::TakeFirst(void) {
+ MergeItem firstItem = m_data.front();
+ m_data.pop_front();
+ return firstItem;
+}
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMMULTIMERGER_P_H
diff --git a/bamtools/src/api/internal/bam/BamMultiReader_p.cpp b/bamtools/src/api/internal/bam/BamMultiReader_p.cpp
new file mode 100644
index 0000000..310d837
--- /dev/null
+++ b/bamtools/src/api/internal/bam/BamMultiReader_p.cpp
@@ -0,0 +1,872 @@
+// ***************************************************************************
+// BamMultiReader_p.cpp (c) 2010 Derek Barnett, Erik Garrison
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 24 July 2013 (DB)
+// ---------------------------------------------------------------------------
+// Functionality for simultaneously reading multiple BAM files
+// *************************************************************************
+
+#include "api/BamAlignment.h"
+#include "api/BamMultiReader.h"
+#include "api/SamConstants.h"
+#include "api/algorithms/Sort.h"
+#include "api/internal/bam/BamMultiReader_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <sstream>
+using namespace std;
+
+// ctor
+BamMultiReaderPrivate::BamMultiReaderPrivate(void)
+ : m_alignmentCache(0)
+ , m_hasUserMergeOrder(false)
+ , m_mergeOrder(BamMultiReader::RoundRobinMerge)
+{ }
+
+// dtor
+BamMultiReaderPrivate::~BamMultiReaderPrivate(void) {
+ Close();
+}
+
+// close all BAM files
+bool BamMultiReaderPrivate::Close(void) {
+
+ m_errorString.clear();
+
+ if ( CloseFiles(Filenames()) )
+ return true;
+ else {
+ const string currentError = m_errorString;
+ const string message = string("error encountered while closing all files: \n\t") + currentError;
+ SetErrorString("BamMultiReader::Close", message);
+ return false;
+ }
+}
+
+// close requested BAM file
+bool BamMultiReaderPrivate::CloseFile(const string& filename) {
+
+ m_errorString.clear();
+
+ vector<string> filenames(1, filename);
+ if ( CloseFiles(filenames) )
+ return true;
+ else {
+ const string currentError = m_errorString;
+ const string message = string("error while closing file: ") + filename + "\n" + currentError;
+ SetErrorString("BamMultiReader::CloseFile", message);
+ return false;
+ }
+}
+
+// close requested BAM files
+bool BamMultiReaderPrivate::CloseFiles(const vector<string>& filenames) {
+
+ bool errorsEncountered = false;
+ m_errorString.clear();
+
+ // iterate over filenames
+ vector<string>::const_iterator filesIter = filenames.begin();
+ vector<string>::const_iterator filesEnd = filenames.end();
+ for ( ; filesIter != filesEnd; ++filesIter ) {
+ const string& filename = (*filesIter);
+ if ( filename.empty() ) continue;
+
+ // iterate over readers
+ vector<MergeItem>::iterator readerIter = m_readers.begin();
+ vector<MergeItem>::iterator readerEnd = m_readers.end();
+ for ( ; readerIter != readerEnd; ++readerIter ) {
+ MergeItem& item = (*readerIter);
+ BamReader* reader = item.Reader;
+ if ( reader == 0 ) continue;
+
+ // if reader matches requested filename
+ if ( reader->GetFilename() == filename ) {
+
+ // remove reader's entry from alignment cache
+ m_alignmentCache->Remove(reader);
+
+ // clean up reader & its alignment
+ if ( !reader->Close() ) {
+ m_errorString.append(1, '\t');
+ m_errorString.append(reader->GetErrorString());
+ m_errorString.append(1, '\n');
+ errorsEncountered = true;
+ }
+ delete reader;
+ reader = 0;
+
+ // delete reader's alignment entry
+ BamAlignment* alignment = item.Alignment;
+ delete alignment;
+ alignment = 0;
+
+ // remove reader from reader list
+ m_readers.erase(readerIter);
+
+ // on match, just go on to next filename
+ // (no need to keep looking and item iterator is invalid now anyway)
+ break;
+ }
+ }
+ }
+
+ // make sure we clean up properly if all readers were closed
+ if ( m_readers.empty() ) {
+
+ // clean up merger
+ if ( m_alignmentCache ) {
+ m_alignmentCache->Clear();
+ delete m_alignmentCache;
+ m_alignmentCache = 0;
+ }
+
+ // reset merge flags
+ m_hasUserMergeOrder = false;
+ m_mergeOrder = BamMultiReader::RoundRobinMerge;
+ }
+
+ // return whether all readers closed OK
+ return !errorsEncountered;
+}
+
+// creates index files for BAM files that don't have them
+bool BamMultiReaderPrivate::CreateIndexes(const BamIndex::IndexType& type) {
+
+ bool errorsEncountered = false;
+ m_errorString.clear();
+
+ // iterate over readers
+ vector<MergeItem>::iterator itemIter = m_readers.begin();
+ vector<MergeItem>::iterator itemEnd = m_readers.end();
+ for ( ; itemIter != itemEnd; ++itemIter ) {
+ MergeItem& item = (*itemIter);
+ BamReader* reader = item.Reader;
+ if ( reader == 0 ) continue;
+
+ // if reader doesn't have an index, create one
+ if ( !reader->HasIndex() ) {
+ if ( !reader->CreateIndex(type) ) {
+ m_errorString.append(1, '\t');
+ m_errorString.append(reader->GetErrorString());
+ m_errorString.append(1, '\n');
+ errorsEncountered = true;
+ }
+ }
+ }
+
+ // check for errors encountered before returning success/fail
+ if ( errorsEncountered ) {
+ const string currentError = m_errorString;
+ const string message = string("error while creating index files: ") + "\n" + currentError;
+ SetErrorString("BamMultiReader::CreateIndexes", message);
+ return false;
+ } else
+ return true;
+}
+
+IMultiMerger* BamMultiReaderPrivate::CreateAlignmentCache(void) {
+
+ // if no merge order set explicitly, use SAM header to lookup proper order
+ if ( !m_hasUserMergeOrder ) {
+
+ // fetch SamHeader from BAM files
+ SamHeader header = GetHeader();
+
+ // if BAM files are sorted by position
+ if ( header.SortOrder == Constants::SAM_HD_SORTORDER_COORDINATE )
+ m_mergeOrder = BamMultiReader::MergeByCoordinate;
+
+ // if BAM files are sorted by read name
+ else if ( header.SortOrder == Constants::SAM_HD_SORTORDER_QUERYNAME )
+ m_mergeOrder = BamMultiReader::MergeByName;
+
+ // otherwise, sorting is either "unknown" or marked as "unsorted"
+ else
+ m_mergeOrder = BamMultiReader::RoundRobinMerge;
+ }
+
+ // use current merge order to create proper 'multi-merger'
+ switch ( m_mergeOrder ) {
+
+ // merge BAM files by position
+ case BamMultiReader::MergeByCoordinate :
+ return new MultiMerger<Algorithms::Sort::ByPosition>();
+
+ // merge BAM files by read name
+ case BamMultiReader::MergeByName :
+ return new MultiMerger<Algorithms::Sort::ByName>();
+
+ // sorting is "unknown", "unsorted" or "ignored"... so use unsorted merger
+ case BamMultiReader::RoundRobinMerge :
+ return new MultiMerger<Algorithms::Sort::Unsorted>();
+
+ // unknown merge order, can't create merger
+ default:
+ return 0;
+ }
+}
+
+const vector<string> BamMultiReaderPrivate::Filenames(void) const {
+
+ // init filename container
+ vector<string> filenames;
+ filenames.reserve( m_readers.size() );
+
+ // iterate over readers
+ vector<MergeItem>::const_iterator itemIter = m_readers.begin();
+ vector<MergeItem>::const_iterator itemEnd = m_readers.end();
+ for ( ; itemIter != itemEnd; ++itemIter ) {
+ const MergeItem& item = (*itemIter);
+ const BamReader* reader = item.Reader;
+ if ( reader == 0 ) continue;
+
+ // store filename if not empty
+ const string& filename = reader->GetFilename();
+ if ( !filename.empty() )
+ filenames.push_back(filename);
+ }
+
+ // return result
+ return filenames;
+}
+
+string BamMultiReaderPrivate::GetErrorString(void) const {
+ return m_errorString;
+}
+
+SamHeader BamMultiReaderPrivate::GetHeader(void) const {
+ const string& text = GetHeaderText();
+ return SamHeader(text);
+}
+
+// makes a virtual, unified header for all the bam files in the multireader
+string BamMultiReaderPrivate::GetHeaderText(void) const {
+
+ // N.B. - right now, simply copies all header data from first BAM,
+ // and then appends RG's from other BAM files
+ // TODO: make this more intelligent wrt other header lines/fields
+
+ // if no readers open
+ const size_t numReaders = m_readers.size();
+ if ( numReaders == 0 ) return string();
+
+ // retrieve first reader's header
+ const MergeItem& firstItem = m_readers.front();
+ const BamReader* reader = firstItem.Reader;
+ if ( reader == 0 ) return string();
+ SamHeader mergedHeader = reader->GetHeader();
+
+ // iterate over any remaining readers (skipping the first)
+ for ( size_t i = 1; i < numReaders; ++i ) {
+ const MergeItem& item = m_readers.at(i);
+ const BamReader* reader = item.Reader;
+ if ( reader == 0 ) continue;
+
+ // retrieve current reader's header
+ const SamHeader currentHeader = reader->GetHeader();
+
+ // append current reader's RG entries to merged header
+ // N.B. - SamReadGroupDictionary handles duplicate-checking
+ mergedHeader.ReadGroups.Add(currentHeader.ReadGroups);
+
+ // TODO: merge anything else??
+ }
+
+ // return stringified header
+ return mergedHeader.ToString();
+}
+
+BamMultiReader::MergeOrder BamMultiReaderPrivate::GetMergeOrder(void) const {
+ return m_mergeOrder;
+}
+
+// get next alignment among all files
+bool BamMultiReaderPrivate::GetNextAlignment(BamAlignment& al) {
+ return PopNextCachedAlignment(al, true);
+}
+
+// get next alignment among all files without parsing character data from alignments
+bool BamMultiReaderPrivate::GetNextAlignmentCore(BamAlignment& al) {
+ return PopNextCachedAlignment(al, false);
+}
+
+// ---------------------------------------------------------------------------------------
+//
+// NB: The following GetReferenceX() functions assume that we have identical
+// references for all BAM files. We enforce this by invoking the
+// ValidateReaders() method to verify that our reference data is the same
+// across all files on Open - so we will not encounter a situation in which
+// there is a mismatch and we are still live.
+//
+// ---------------------------------------------------------------------------------------
+
+// returns the number of reference sequences
+int BamMultiReaderPrivate::GetReferenceCount(void) const {
+
+ // handle empty multireader
+ if ( m_readers.empty() ) return 0;
+
+ // return reference count from first reader
+ const MergeItem& item = m_readers.front();
+ const BamReader* reader = item.Reader;
+ if ( reader == 0 ) return 0;
+ else
+ return reader->GetReferenceCount();
+}
+
+// returns vector of reference objects
+const RefVector BamMultiReaderPrivate::GetReferenceData(void) const {
+
+ // handle empty multireader
+ if ( m_readers.empty() ) return RefVector();
+
+ // return reference data from first BamReader
+ const MergeItem& item = m_readers.front();
+ const BamReader* reader = item.Reader;
+ if ( reader == 0 ) return RefVector();
+ else
+ return reader->GetReferenceData();
+}
+
+// returns refID from reference name
+int BamMultiReaderPrivate::GetReferenceID(const string& refName) const {
+
+ // handle empty multireader
+ if ( m_readers.empty() ) return -1;
+
+ // return reference ID from first BamReader
+ const MergeItem& item = m_readers.front();
+ const BamReader* reader = item.Reader;
+ if ( reader == 0 ) return -1;
+ else
+ return reader->GetReferenceID(refName);
+}
+// ---------------------------------------------------------------------------------------
+
+// returns true if all readers have index data available
+// this is useful to indicate whether Jump() or SetRegion() are possible
+bool BamMultiReaderPrivate::HasIndexes(void) const {
+
+ // handle empty multireader
+ if ( m_readers.empty() )
+ return false;
+
+ bool result = true;
+
+ // iterate over readers
+ vector<MergeItem>::const_iterator readerIter = m_readers.begin();
+ vector<MergeItem>::const_iterator readerEnd = m_readers.end();
+ for ( ; readerIter != readerEnd; ++readerIter ) {
+ const MergeItem& item = (*readerIter);
+ const BamReader* reader = item.Reader;
+ if ( reader == 0 ) continue;
+
+ // see if current reader has index data
+ result &= reader->HasIndex();
+ }
+
+ return result;
+}
+
+// returns true if multireader has open readers
+bool BamMultiReaderPrivate::HasOpenReaders(void) {
+
+ // iterate over readers
+ vector<MergeItem>::const_iterator readerIter = m_readers.begin();
+ vector<MergeItem>::const_iterator readerEnd = m_readers.end();
+ for ( ; readerIter != readerEnd; ++readerIter ) {
+ const MergeItem& item = (*readerIter);
+ const BamReader* reader = item.Reader;
+ if ( reader == 0 ) continue;
+
+ // return true whenever an open reader is found
+ if ( reader->IsOpen() ) return true;
+ }
+
+ // no readers open
+ return false;
+}
+
+// performs random-access jump using (refID, position) as a left-bound
+bool BamMultiReaderPrivate::Jump(int refID, int position) {
+
+ // NB: While it may make sense to track readers in which we can
+ // successfully Jump, in practice a failure of Jump means "no
+ // alignments here." It makes sense to simply accept the failure,
+ // UpdateAlignments(), and continue.
+
+ // iterate over readers
+ vector<MergeItem>::iterator readerIter = m_readers.begin();
+ vector<MergeItem>::iterator readerEnd = m_readers.end();
+ for ( ; readerIter != readerEnd; ++readerIter ) {
+ MergeItem& item = (*readerIter);
+ BamReader* reader = item.Reader;
+ if ( reader == 0 ) continue;
+
+ // jump in each BamReader to position of interest
+ reader->Jump(refID, position);
+ }
+
+ // returns status of cache update
+ return UpdateAlignmentCache();
+}
+
+// locate (& load) index files for BAM readers that don't already have one loaded
+bool BamMultiReaderPrivate::LocateIndexes(const BamIndex::IndexType& preferredType) {
+
+ bool errorsEncountered = false;
+ m_errorString.clear();
+
+ // iterate over readers
+ vector<MergeItem>::iterator readerIter = m_readers.begin();
+ vector<MergeItem>::iterator readerEnd = m_readers.end();
+ for ( ; readerIter != readerEnd; ++readerIter ) {
+ MergeItem& item = (*readerIter);
+ BamReader* reader = item.Reader;
+ if ( reader == 0 ) continue;
+
+ // if reader has no index, try to locate one
+ if ( !reader->HasIndex() ) {
+ if ( !reader->LocateIndex(preferredType) ) {
+ m_errorString.append(1, '\t');
+ m_errorString.append(reader->GetErrorString());
+ m_errorString.append(1, '\n');
+ errorsEncountered = true;
+ }
+ }
+ }
+
+ // check for errors encountered before returning success/fail
+ if ( errorsEncountered ) {
+ const string currentError = m_errorString;
+ const string message = string("error while locating index files: ") + "\n" + currentError;
+ SetErrorString("BamMultiReader::LocatingIndexes", message);
+ return false;
+ } else
+ return true;
+}
+
+// opens BAM files
+bool BamMultiReaderPrivate::Open(const vector<string>& filenames) {
+
+ m_errorString.clear();
+
+ // put all current readers back at beginning (refreshes alignment cache)
+ if ( !Rewind() ) {
+ const string currentError = m_errorString;
+ const string message = string("unable to rewind existing readers: \n\t") + currentError;
+ SetErrorString("BamMultiReader::Open", message);
+ return false;
+ }
+
+ // iterate over filenames
+ bool errorsEncountered = false;
+ vector<string>::const_iterator filenameIter = filenames.begin();
+ vector<string>::const_iterator filenameEnd = filenames.end();
+ for ( ; filenameIter != filenameEnd; ++filenameIter ) {
+ const string& filename = (*filenameIter);
+ if ( filename.empty() ) continue;
+
+ // attempt to open BamReader
+ BamReader* reader = new BamReader;
+ const bool readerOpened = reader->Open(filename);
+
+ // if opened OK, store it
+ if ( readerOpened )
+ m_readers.push_back( MergeItem(reader, new BamAlignment) );
+
+ // otherwise store error & clean up invalid reader
+ else {
+ m_errorString.append(1, '\t');
+ m_errorString += string("unable to open file: ") + filename;
+ m_errorString.append(1, '\n');
+ errorsEncountered = true;
+
+ delete reader;
+ reader = 0;
+ }
+ }
+
+ // check for errors while opening
+ if ( errorsEncountered ) {
+ const string currentError = m_errorString;
+ const string message = string("unable to open all files: \t\n") + currentError;
+ SetErrorString("BamMultiReader::Open", message);
+ return false;
+ }
+
+ // check for BAM file consistency
+ if ( !ValidateReaders() ) {
+ const string currentError = m_errorString;
+ const string message = string("unable to open inconsistent files: \t\n") + currentError;
+ SetErrorString("BamMultiReader::Open", message);
+ return false;
+ }
+
+ // update alignment cache
+ return UpdateAlignmentCache();
+}
+
+bool BamMultiReaderPrivate::OpenFile(const std::string& filename) {
+ vector<string> filenames(1, filename);
+ if ( Open(filenames) )
+ return true;
+ else {
+ const string currentError = m_errorString;
+ const string message = string("could not open file: ") + filename + "\n\t" + currentError;
+ SetErrorString("BamMultiReader::OpenFile", message);
+ return false;
+ }
+}
+
+bool BamMultiReaderPrivate::OpenIndexes(const vector<string>& indexFilenames) {
+
+ // TODO: This needs to be cleaner - should not assume same order.
+ // And either way, shouldn't start at first reader. Should start at
+ // first reader without an index?
+
+ // make sure same number of index filenames as readers
+ if ( m_readers.size() != indexFilenames.size() ) {
+ const string message("size of index file list does not match current BAM file count");
+ SetErrorString("BamMultiReader::OpenIndexes", message);
+ return false;
+ }
+
+ bool errorsEncountered = false;
+ m_errorString.clear();
+
+ // iterate over BamReaders
+ vector<string>::const_iterator indexFilenameIter = indexFilenames.begin();
+ vector<string>::const_iterator indexFilenameEnd = indexFilenames.end();
+ vector<MergeItem>::iterator readerIter = m_readers.begin();
+ vector<MergeItem>::iterator readerEnd = m_readers.end();
+ for ( ; readerIter != readerEnd; ++readerIter ) {
+ MergeItem& item = (*readerIter);
+ BamReader* reader = item.Reader;
+
+ // open index filename on reader
+ if ( reader ) {
+ const string& indexFilename = (*indexFilenameIter);
+ if ( !reader->OpenIndex(indexFilename) ) {
+ m_errorString.append(1, '\t');
+ m_errorString += reader->GetErrorString();
+ m_errorString.append(1, '\n');
+ errorsEncountered = true;
+ }
+ }
+
+ // increment filename iterator, skip if no more index files to open
+ if ( ++indexFilenameIter == indexFilenameEnd )
+ break;
+ }
+
+ // return success/fail
+ if ( errorsEncountered ) {
+ const string currentError = m_errorString;
+ const string message = string("could not open all index files: \n\t") + currentError;
+ SetErrorString("BamMultiReader::OpenIndexes", message);
+ return false;
+ } else
+ return true;
+}
+
+bool BamMultiReaderPrivate::PopNextCachedAlignment(BamAlignment& al, const bool needCharData) {
+
+ // skip if no alignments available
+ if ( m_alignmentCache == 0 || m_alignmentCache->IsEmpty() )
+ return false;
+
+ // pop next merge item entry from cache
+ MergeItem item = m_alignmentCache->TakeFirst();
+ BamReader* reader = item.Reader;
+ BamAlignment* alignment = item.Alignment;
+ if ( reader == 0 || alignment == 0 )
+ return false;
+
+ // set char data if requested
+ if ( needCharData ) {
+ alignment->BuildCharData();
+ alignment->Filename = reader->GetFilename();
+ }
+
+ // store cached alignment into destination parameter (by copy)
+ al = *alignment;
+
+ // load next alignment from reader & store in cache
+ SaveNextAlignment(reader, alignment);
+ return true;
+}
+
+// returns BAM file pointers to beginning of alignment data & resets alignment cache
+bool BamMultiReaderPrivate::Rewind(void) {
+
+ // skip if no readers open
+ if ( m_readers.empty() )
+ return true;
+
+ // attempt to rewind files
+ if ( !RewindReaders() ) {
+ const string currentError = m_errorString;
+ const string message = string("could not rewind readers: \n\t") + currentError;
+ SetErrorString("BamMultiReader::Rewind", message);
+ return false;
+ }
+
+ // return status of cache update
+ return UpdateAlignmentCache();
+}
+
+// returns BAM file pointers to beginning of alignment data
+bool BamMultiReaderPrivate::RewindReaders(void) {
+
+ m_errorString.clear();
+ bool errorsEncountered = false;
+
+ // iterate over readers
+ vector<MergeItem>::iterator readerIter = m_readers.begin();
+ vector<MergeItem>::iterator readerEnd = m_readers.end();
+ for ( ; readerIter != readerEnd; ++readerIter ) {
+ MergeItem& item = (*readerIter);
+ BamReader* reader = item.Reader;
+ if ( reader == 0 ) continue;
+
+ // attempt rewind on BamReader
+ if ( !reader->Rewind() ) {
+ m_errorString.append(1, '\t');
+ m_errorString.append( reader->GetErrorString() );
+ m_errorString.append(1, '\n');
+ errorsEncountered = true;
+ }
+ }
+
+ return !errorsEncountered;
+}
+
+void BamMultiReaderPrivate::SaveNextAlignment(BamReader* reader, BamAlignment* alignment) {
+
+ // if can read alignment from reader, store in cache
+ //
+ // N.B. - lazy building of alignment's char data - populated only:
+ // automatically by alignment cache to maintain its sorting OR
+ // on demand from client call to future call to GetNextAlignment()
+
+ if ( reader->GetNextAlignmentCore(*alignment) )
+ m_alignmentCache->Add( MergeItem(reader, alignment) );
+}
+
+bool BamMultiReaderPrivate::SetExplicitMergeOrder(BamMultiReader::MergeOrder order) {
+
+ // set new merge flags
+ m_hasUserMergeOrder = true;
+ m_mergeOrder = order;
+
+ // remove any existing merger (storing any existing data sitting in the cache)
+ vector<MergeItem> currentCacheData;
+ if ( m_alignmentCache ) {
+ while ( !m_alignmentCache->IsEmpty() )
+ currentCacheData.push_back( m_alignmentCache->TakeFirst() );
+ delete m_alignmentCache;
+ m_alignmentCache = 0;
+ }
+
+ // create new cache using the new merge flags
+ m_alignmentCache = CreateAlignmentCache();
+ if ( m_alignmentCache == 0 ) {
+ SetErrorString("BamMultiReader::SetExplicitMergeOrder", "requested order is unrecognized");
+ return false;
+ }
+
+ // push current data onto new cache
+ vector<MergeItem>::const_iterator readerIter = currentCacheData.begin();
+ vector<MergeItem>::const_iterator readerEnd = currentCacheData.end();
+ for ( ; readerIter != readerEnd; ++readerIter ) {
+ const MergeItem& item = (*readerIter);
+ m_alignmentCache->Add(item);
+ }
+
+ // return success
+ return true;
+}
+
+void BamMultiReaderPrivate::SetErrorString(const string& where, const string& what) const {
+ static const string SEPARATOR = ": ";
+ m_errorString = where + SEPARATOR + what;
+}
+
+bool BamMultiReaderPrivate::SetRegion(const BamRegion& region) {
+
+ // NB: While it may make sense to track readers in which we can
+ // successfully SetRegion, In practice a failure of SetRegion means "no
+ // alignments here." It makes sense to simply accept the failure,
+ // UpdateAlignments(), and continue.
+
+ // iterate over alignments
+ vector<MergeItem>::iterator readerIter = m_readers.begin();
+ vector<MergeItem>::iterator readerEnd = m_readers.end();
+ for ( ; readerIter != readerEnd; ++readerIter ) {
+ MergeItem& item = (*readerIter);
+ BamReader* reader = item.Reader;
+ if ( reader == 0 ) continue;
+
+ // set region of interest
+ reader->SetRegion(region);
+ }
+
+ // return status of cache update
+ return UpdateAlignmentCache();
+}
+
+// updates our alignment cache
+bool BamMultiReaderPrivate::UpdateAlignmentCache(void) {
+
+ // create alignment cache if not created yet
+ if ( m_alignmentCache == 0 ) {
+ m_alignmentCache = CreateAlignmentCache();
+ if ( m_alignmentCache == 0 ) {
+ SetErrorString("BamMultiReader::UpdateAlignmentCache", "unable to create new alignment cache");
+ return false;
+ }
+ }
+
+ // clear any prior cache data
+ m_alignmentCache->Clear();
+
+ // iterate over readers
+ vector<MergeItem>::iterator readerIter = m_readers.begin();
+ vector<MergeItem>::iterator readerEnd = m_readers.end();
+ for ( ; readerIter != readerEnd; ++readerIter ) {
+ MergeItem& item = (*readerIter);
+ BamReader* reader = item.Reader;
+ BamAlignment* alignment = item.Alignment;
+ if ( reader == 0 || alignment == 0 ) continue;
+
+ // save next alignment from each reader in cache
+ SaveNextAlignment(reader, alignment);
+ }
+
+ // if we get here, ok
+ return true;
+}
+
+// ValidateReaders checks that all the readers point to BAM files representing
+// alignments against the same set of reference sequences, and that the
+// sequences are identically ordered. If these checks fail the operation of
+// the multireader is undefined, so we force program exit.
+bool BamMultiReaderPrivate::ValidateReaders(void) const {
+
+ m_errorString.clear();
+
+ // skip if 0 or 1 readers opened
+ if ( m_readers.empty() || (m_readers.size() == 1) )
+ return true;
+
+ // retrieve first reader
+ const MergeItem& firstItem = m_readers.front();
+ const BamReader* firstReader = firstItem.Reader;
+ if ( firstReader == 0 ) return false;
+
+ // retrieve first reader's header data
+ const SamHeader& firstReaderHeader = firstReader->GetHeader();
+ const string& firstReaderSortOrder = firstReaderHeader.SortOrder;
+
+ // retrieve first reader's reference data
+ const RefVector& firstReaderRefData = firstReader->GetReferenceData();
+ const int firstReaderRefCount = firstReader->GetReferenceCount();
+ const int firstReaderRefSize = firstReaderRefData.size();
+
+ // iterate over all readers
+ vector<MergeItem>::const_iterator readerIter = m_readers.begin();
+ vector<MergeItem>::const_iterator readerEnd = m_readers.end();
+ for ( ; readerIter != readerEnd; ++readerIter ) {
+ const MergeItem& item = (*readerIter);
+ BamReader* reader = item.Reader;
+ if ( reader == 0 ) continue;
+
+ // get current reader's header data
+ const SamHeader& currentReaderHeader = reader->GetHeader();
+ const string& currentReaderSortOrder = currentReaderHeader.SortOrder;
+
+ // check compatible sort order
+ if ( currentReaderSortOrder != firstReaderSortOrder ) {
+ const string message = string("mismatched sort order in ") + reader->GetFilename() +
+ ", expected " + firstReaderSortOrder +
+ ", but found " + currentReaderSortOrder;
+ SetErrorString("BamMultiReader::ValidateReaders", message);
+ return false;
+ }
+
+ // get current reader's reference data
+ const RefVector currentReaderRefData = reader->GetReferenceData();
+ const int currentReaderRefCount = reader->GetReferenceCount();
+ const int currentReaderRefSize = currentReaderRefData.size();
+
+ // init reference data iterators
+ RefVector::const_iterator firstRefIter = firstReaderRefData.begin();
+ RefVector::const_iterator firstRefEnd = firstReaderRefData.end();
+ RefVector::const_iterator currentRefIter = currentReaderRefData.begin();
+
+ // compare reference counts from BamReader ( & container size, in case of BR error)
+ if ( (currentReaderRefCount != firstReaderRefCount) ||
+ (firstReaderRefSize != currentReaderRefSize) )
+ {
+ stringstream s("");
+ s << "mismatched reference count in " << reader->GetFilename()
+ << ", expected " << firstReaderRefCount
+ << ", but found " << currentReaderRefCount;
+ SetErrorString("BamMultiReader::ValidateReaders", s.str());
+ return false;
+ }
+
+ // this will be ok; we just checked above that we have identically-sized sets of references
+ // here we simply check if they are all, in fact, equal in content
+ while ( firstRefIter != firstRefEnd ) {
+ const RefData& firstRef = (*firstRefIter);
+ const RefData& currentRef = (*currentRefIter);
+
+ // compare reference name & length
+ if ( (firstRef.RefName != currentRef.RefName) ||
+ (firstRef.RefLength != currentRef.RefLength) )
+ {
+ stringstream s("");
+ s << "mismatched references found in" << reader->GetFilename()
+ << "expected: " << endl;
+
+ // print first reader's reference data
+ RefVector::const_iterator refIter = firstReaderRefData.begin();
+ RefVector::const_iterator refEnd = firstReaderRefData.end();
+ for ( ; refIter != refEnd; ++refIter ) {
+ const RefData& entry = (*refIter);
+ stringstream s("");
+ s << entry.RefName << " " << endl;
+ }
+
+ s << "but found: " << endl;
+
+ // print current reader's reference data
+ refIter = currentReaderRefData.begin();
+ refEnd = currentReaderRefData.end();
+ for ( ; refIter != refEnd; ++refIter ) {
+ const RefData& entry = (*refIter);
+ s << entry.RefName << " " << entry.RefLength << endl;
+ }
+
+ SetErrorString("BamMultiReader::ValidateReaders", s.str());
+ return false;
+ }
+
+ // update iterators
+ ++firstRefIter;
+ ++currentRefIter;
+ }
+ }
+
+ // if we get here, everything checks out
+ return true;
+}
diff --git a/bamtools/src/api/internal/bam/BamMultiReader_p.h b/bamtools/src/api/internal/bam/BamMultiReader_p.h
new file mode 100644
index 0000000..3a7a0b2
--- /dev/null
+++ b/bamtools/src/api/internal/bam/BamMultiReader_p.h
@@ -0,0 +1,105 @@
+// ***************************************************************************
+// BamMultiReader_p.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 14 January 2013 (DB)
+// ---------------------------------------------------------------------------
+// Functionality for simultaneously reading multiple BAM files
+// *************************************************************************
+
+#ifndef BAMMULTIREADER_P_H
+#define BAMMULTIREADER_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/SamHeader.h"
+#include "api/BamMultiReader.h"
+#include "api/internal/bam/BamMultiMerger_p.h"
+#include <string>
+#include <vector>
+
+namespace BamTools {
+namespace Internal {
+
+class BamMultiReaderPrivate {
+
+ // typedefs
+ public:
+ typedef std::pair<BamReader*, BamAlignment*> ReaderAlignment;
+
+ // constructor / destructor
+ public:
+ BamMultiReaderPrivate(void);
+ ~BamMultiReaderPrivate(void);
+
+ // public interface
+ public:
+
+ // file operations
+ bool Close(void);
+ bool CloseFile(const std::string& filename);
+ const std::vector<std::string> Filenames(void) const;
+ bool Jump(int refID, int position = 0);
+ bool Open(const std::vector<std::string>& filenames);
+ bool OpenFile(const std::string& filename);
+ bool Rewind(void);
+ bool SetRegion(const BamRegion& region);
+
+ // access alignment data
+ BamMultiReader::MergeOrder GetMergeOrder(void) const;
+ bool GetNextAlignment(BamAlignment& al);
+ bool GetNextAlignmentCore(BamAlignment& al);
+ bool HasOpenReaders(void);
+ bool SetExplicitMergeOrder(BamMultiReader::MergeOrder order);
+
+ // access auxiliary data
+ SamHeader GetHeader(void) const;
+ std::string GetHeaderText(void) const;
+ int GetReferenceCount(void) const;
+ const BamTools::RefVector GetReferenceData(void) const;
+ int GetReferenceID(const std::string& refName) const;
+
+ // BAM index operations
+ bool CreateIndexes(const BamIndex::IndexType& type = BamIndex::STANDARD);
+ bool HasIndexes(void) const;
+ bool LocateIndexes(const BamIndex::IndexType& preferredType = BamIndex::STANDARD);
+ bool OpenIndexes(const std::vector<std::string>& indexFilenames);
+
+ // error handling
+ std::string GetErrorString(void) const;
+
+ // 'internal' methods
+ public:
+
+ bool CloseFiles(const std::vector<std::string>& filenames);
+ IMultiMerger* CreateAlignmentCache(void);
+ bool PopNextCachedAlignment(BamAlignment& al, const bool needCharData);
+ bool RewindReaders(void);
+ void SaveNextAlignment(BamReader* reader, BamAlignment* alignment);
+ void SetErrorString(const std::string& where, const std::string& what) const; //
+ bool UpdateAlignmentCache(void);
+ bool ValidateReaders(void) const;
+
+ // data members
+ public:
+ std::vector<MergeItem> m_readers;
+ IMultiMerger* m_alignmentCache;
+
+ bool m_hasUserMergeOrder;
+ BamMultiReader::MergeOrder m_mergeOrder;
+
+ mutable std::string m_errorString;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMMULTIREADER_P_H
diff --git a/bamtools/src/api/internal/bam/BamRandomAccessController_p.cpp b/bamtools/src/api/internal/bam/BamRandomAccessController_p.cpp
new file mode 100644
index 0000000..848fafd
--- /dev/null
+++ b/bamtools/src/api/internal/bam/BamRandomAccessController_p.cpp
@@ -0,0 +1,289 @@
+// ***************************************************************************
+// BamRandomAccessController_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 25 October 2011(DB)
+// ---------------------------------------------------------------------------
+// Manages random access operations in a BAM file
+// **************************************************************************
+
+#include "api/BamIndex.h"
+#include "api/internal/bam/BamRandomAccessController_p.h"
+#include "api/internal/bam/BamReader_p.h"
+#include "api/internal/index/BamIndexFactory_p.h"
+#include "api/internal/utils/BamException_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cassert>
+#include <sstream>
+using namespace std;
+
+BamRandomAccessController::BamRandomAccessController(void)
+ : m_index(0)
+ , m_hasAlignmentsInRegion(true)
+{ }
+
+BamRandomAccessController::~BamRandomAccessController(void) {
+ Close();
+}
+
+void BamRandomAccessController::AdjustRegion(const int& referenceCount) {
+
+ // skip if no index available
+ if ( m_index == 0 )
+ return;
+
+ // see if any references in region have alignments
+ m_hasAlignmentsInRegion = false;
+ int currentId = m_region.LeftRefID;
+ const int rightBoundRefId = ( m_region.isRightBoundSpecified() ? m_region.RightRefID : referenceCount - 1 );
+ while ( currentId <= rightBoundRefId ) {
+ m_hasAlignmentsInRegion = m_index->HasAlignments(currentId);
+ if ( m_hasAlignmentsInRegion ) break;
+ ++currentId;
+ }
+
+ // if no data found on any reference in region
+ if ( !m_hasAlignmentsInRegion )
+ return;
+
+ // if left bound of desired region had no data, use first reference that had data
+ // otherwise, leave requested region as-is
+ if ( currentId != m_region.LeftRefID ) {
+ m_region.LeftRefID = currentId;
+ m_region.LeftPosition = 0;
+ }
+}
+
+// returns alignments' "RegionState": { Before|Overlaps|After } current region
+BamRandomAccessController::RegionState
+BamRandomAccessController::AlignmentState(const BamAlignment& alignment) const {
+
+ // if region has no left bound at all
+ if ( !m_region.isLeftBoundSpecified() )
+ return OverlapsRegion;
+
+ // handle unmapped reads - return AFTER region to halt processing
+ if ( alignment.RefID == -1 )
+ return AfterRegion;
+
+ // if alignment is on any reference before left bound reference
+ if ( alignment.RefID < m_region.LeftRefID )
+ return BeforeRegion;
+
+ // if alignment is on left bound reference
+ else if ( alignment.RefID == m_region.LeftRefID ) {
+
+ // if alignment starts at or after left bound position
+ if ( alignment.Position >= m_region.LeftPosition) {
+
+ if ( m_region.isRightBoundSpecified() && // right bound is specified AND
+ m_region.LeftRefID == m_region.RightRefID && // left & right bounds on same reference AND
+ alignment.Position >= m_region.RightPosition ) // alignment starts on or after right bound position
+ return AfterRegion;
+
+ // otherwise, alignment overlaps region
+ else return OverlapsRegion;
+ }
+
+ // alignment starts before left bound position
+ else {
+
+ // if alignment overlaps left bound position
+ if ( alignment.GetEndPosition() > m_region.LeftPosition )
+ return OverlapsRegion;
+ else
+ return BeforeRegion;
+ }
+ }
+
+ // otherwise alignment is on a reference after left bound reference
+ else {
+
+ // if region has a right bound
+ if ( m_region.isRightBoundSpecified() ) {
+
+ // alignment is on any reference between boundaries
+ if ( alignment.RefID < m_region.RightRefID )
+ return OverlapsRegion;
+
+ // alignment is on any reference after right boundary
+ else if ( alignment.RefID > m_region.RightRefID )
+ return AfterRegion;
+
+ // alignment is on right bound reference
+ else {
+
+ // if alignment starts before right bound position
+ if ( alignment.Position < m_region.RightPosition )
+ return OverlapsRegion;
+ else
+ return AfterRegion;
+ }
+ }
+
+ // otherwise, alignment starts after left bound and there is no right bound given
+ else return OverlapsRegion;
+ }
+}
+
+void BamRandomAccessController::Close(void) {
+ ClearIndex();
+ ClearRegion();
+}
+
+void BamRandomAccessController::ClearIndex(void) {
+ if ( m_index ) {
+ delete m_index;
+ m_index = 0;
+ }
+}
+
+void BamRandomAccessController::ClearRegion(void) {
+ m_region.clear();
+ m_hasAlignmentsInRegion = true;
+}
+
+bool BamRandomAccessController::CreateIndex(BamReaderPrivate* reader,
+ const BamIndex::IndexType& type)
+{
+ // skip if reader is invalid
+ assert(reader);
+ if ( !reader->IsOpen() ) {
+ SetErrorString("BamRandomAccessController::CreateIndex",
+ "cannot create index for unopened reader");
+ return false;
+ }
+
+ // create new index of requested type
+ BamIndex* newIndex = BamIndexFactory::CreateIndexOfType(type, reader);
+ if ( newIndex == 0 ) {
+ stringstream s("");
+ s << "could not create index of type: " << type;
+ SetErrorString("BamRandomAccessController::CreateIndex", s.str());
+ return false;
+ }
+
+ // attempt to build index from current BamReader file
+ if ( !newIndex->Create() ) {
+ const string indexError = newIndex->GetErrorString();
+ const string message = "could not create index: \n\t" + indexError;
+ SetErrorString("BamRandomAccessController::CreateIndex", message);
+ return false;
+ }
+
+ // save new index & return success
+ SetIndex(newIndex);
+ return true;
+}
+
+string BamRandomAccessController::GetErrorString(void) const {
+ return m_errorString;
+}
+
+bool BamRandomAccessController::HasIndex(void) const {
+ return ( m_index != 0 );
+}
+
+bool BamRandomAccessController::HasRegion(void) const {
+ return ( !m_region.isNull() );
+}
+
+bool BamRandomAccessController::IndexHasAlignmentsForReference(const int& refId) {
+ return m_index->HasAlignments(refId);
+}
+
+bool BamRandomAccessController::LocateIndex(BamReaderPrivate* reader,
+ const BamIndex::IndexType& preferredType)
+{
+ // look up index filename, deferring to preferredType if possible
+ assert(reader);
+ const string& indexFilename = BamIndexFactory::FindIndexFilename(reader->Filename(), preferredType);
+
+ // if no index file found (of any type)
+ if ( indexFilename.empty() ) {
+ const string message = string("could not find index file for:") + reader->Filename();
+ SetErrorString("BamRandomAccessController::LocateIndex", message);
+ return false;
+ }
+
+ // otherwise open & use index file that was found
+ return OpenIndex(indexFilename, reader);
+}
+
+bool BamRandomAccessController::OpenIndex(const string& indexFilename, BamReaderPrivate* reader) {
+
+ // attempt create new index of type based on filename
+ BamIndex* index = BamIndexFactory::CreateIndexFromFilename(indexFilename, reader);
+ if ( index == 0 ) {
+ const string message = string("could not open index file: ") + indexFilename;
+ SetErrorString("BamRandomAccessController::OpenIndex", message);
+ return false;
+ }
+
+ // attempt to load data from index file
+ if ( !index->Load(indexFilename) ) {
+ const string indexError = index->GetErrorString();
+ const string message = string("could not load index data from file: ") + indexFilename +
+ "\n\t" + indexError;
+ SetErrorString("BamRandomAccessController::OpenIndex", message);
+ return false;
+ }
+
+ // save new index & return success
+ SetIndex(index);
+ return true;
+}
+
+bool BamRandomAccessController::RegionHasAlignments(void) const {
+ return m_hasAlignmentsInRegion;
+}
+
+void BamRandomAccessController::SetErrorString(const string& where, const string& what) {
+ m_errorString = where + ": " + what;
+}
+
+void BamRandomAccessController::SetIndex(BamIndex* index) {
+ if ( m_index )
+ ClearIndex();
+ m_index = index;
+}
+
+bool BamRandomAccessController::SetRegion(const BamRegion& region, const int& referenceCount) {
+
+ // store region
+ m_region = region;
+
+ // cannot jump when no index is available
+ if ( !HasIndex() ) {
+ SetErrorString("BamRandomAccessController", "cannot jump if no index data available");
+ return false;
+ }
+
+ // adjust region as necessary to reflect where data actually begins
+ AdjustRegion(referenceCount);
+
+ // if no data present, return true
+ // * Not an error, but future attempts to access alignments in this region will not return data
+ // Returning true is useful in a BamMultiReader setting where some BAM files may
+ // lack alignments in regions where other files still have data available.
+ if ( !m_hasAlignmentsInRegion )
+ return true;
+
+ // return success/failure of jump to specified region,
+ //
+ // * Index::Jump() is allowed to modify the m_hasAlignmentsInRegion flag
+ // This covers 'corner case' where a region is requested that lies beyond the last
+ // alignment on a reference. If this occurs, any subsequent calls to GetNextAlignment[Core]
+ // will not return data. BamMultiReader will still be able to successfully pull alignments
+ // from a region from other files even if this one has no data.
+ if ( !m_index->Jump(m_region, &m_hasAlignmentsInRegion) ) {
+ const string indexError = m_index->GetErrorString();
+ const string message = string("could not set region\n\t") + indexError;
+ SetErrorString("BamRandomAccessController::OpenIndex", message);
+ return false;
+ }
+ else
+ return true;
+}
diff --git a/bamtools/src/api/internal/bam/BamRandomAccessController_p.h b/bamtools/src/api/internal/bam/BamRandomAccessController_p.h
new file mode 100644
index 0000000..9262a61
--- /dev/null
+++ b/bamtools/src/api/internal/bam/BamRandomAccessController_p.h
@@ -0,0 +1,94 @@
+// ***************************************************************************
+// BamRandomAccessController_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011(DB)
+// ---------------------------------------------------------------------------
+// Manages random access operations in a BAM file
+// ***************************************************************************
+
+#ifndef BAMRACONTROLLER_P_H
+#define BAMRACONTROLLER_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/BamAux.h"
+#include "api/BamIndex.h"
+
+namespace BamTools {
+
+class BamAlignment;
+
+namespace Internal {
+
+class BamReaderPrivate;
+
+class BamRandomAccessController {
+
+ // enums
+ public: enum RegionState { BeforeRegion = 0
+ , OverlapsRegion
+ , AfterRegion
+ };
+
+ // ctor & dtor
+ public:
+ BamRandomAccessController(void);
+ ~BamRandomAccessController(void);
+
+ // BamRandomAccessController interface
+ public:
+
+ // index methods
+ void ClearIndex(void);
+ bool CreateIndex(BamReaderPrivate* reader, const BamIndex::IndexType& type);
+ bool HasIndex(void) const;
+ bool IndexHasAlignmentsForReference(const int& refId);
+ bool LocateIndex(BamReaderPrivate* reader, const BamIndex::IndexType& preferredType);
+ bool OpenIndex(const std::string& indexFilename, BamReaderPrivate* reader);
+ void SetIndex(BamIndex* index);
+
+ // region methods
+ void ClearRegion(void);
+ bool HasRegion(void) const;
+ RegionState AlignmentState(const BamAlignment& alignment) const;
+ bool RegionHasAlignments(void) const;
+ bool SetRegion(const BamRegion& region, const int& referenceCount);
+
+ // general methods
+ void Close(void);
+ std::string GetErrorString(void) const;
+
+ // internal methods
+ private:
+ // adjusts requested region if necessary (depending on where data actually begins)
+ void AdjustRegion(const int& referenceCount);
+ // error-string handling
+ void SetErrorString(const std::string& where, const std::string& what);
+
+ // data members
+ private:
+
+ // index data
+ BamIndex* m_index; // owns the index, not a copy - responsible for deleting
+
+ // region data
+ BamRegion m_region;
+ bool m_hasAlignmentsInRegion;
+
+ // general data
+ std::string m_errorString;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMRACONTROLLER_P_H
diff --git a/bamtools/src/api/internal/bam/BamReader_p.cpp b/bamtools/src/api/internal/bam/BamReader_p.cpp
new file mode 100644
index 0000000..737d598
--- /dev/null
+++ b/bamtools/src/api/internal/bam/BamReader_p.cpp
@@ -0,0 +1,470 @@
+// ***************************************************************************
+// BamReader_p.cpp (c) 2009 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 18 November 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides the basic functionality for reading BAM files
+// ***************************************************************************
+
+#include "api/BamConstants.h"
+#include "api/BamReader.h"
+#include "api/IBamIODevice.h"
+#include "api/internal/bam/BamHeader_p.h"
+#include "api/internal/bam/BamRandomAccessController_p.h"
+#include "api/internal/bam/BamReader_p.h"
+#include "api/internal/index/BamStandardIndex_p.h"
+#include "api/internal/index/BamToolsIndex_p.h"
+#include "api/internal/io/BamDeviceFactory_p.h"
+#include "api/internal/utils/BamException_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <iterator>
+#include <vector>
+using namespace std;
+
+// constructor
+BamReaderPrivate::BamReaderPrivate(BamReader* parent)
+ : m_alignmentsBeginOffset(0)
+ , m_parent(parent)
+{
+ m_isBigEndian = BamTools::SystemIsBigEndian();
+}
+
+// destructor
+BamReaderPrivate::~BamReaderPrivate(void) {
+ Close();
+}
+
+// closes the BAM file
+bool BamReaderPrivate::Close(void) {
+
+ // clear BAM metadata
+ m_references.clear();
+ m_header.Clear();
+
+ // clear filename
+ m_filename.clear();
+
+ // close random access controller
+ m_randomAccessController.Close();
+
+ // if stream is open, attempt close
+ if ( IsOpen() ) {
+ try {
+ m_stream.Close();
+ } catch ( BamException& e ) {
+ const string streamError = e.what();
+ const string message = string("encountered error closing BAM file: \n\t") + streamError;
+ SetErrorString("BamReader::Close", message);
+ return false;
+ }
+ }
+
+ // return success
+ return true;
+}
+
+// creates an index file of requested type on current BAM file
+bool BamReaderPrivate::CreateIndex(const BamIndex::IndexType& type) {
+
+ // skip if BAM file not open
+ if ( !IsOpen() ) {
+ SetErrorString("BamReader::CreateIndex", "cannot create index on unopened BAM file");
+ return false;
+ }
+
+ // attempt to create index
+ if ( m_randomAccessController.CreateIndex(this, type) )
+ return true;
+ else {
+ const string bracError = m_randomAccessController.GetErrorString();
+ const string message = string("could not create index: \n\t") + bracError;
+ SetErrorString("BamReader::CreateIndex", message);
+ return false;
+ }
+}
+
+// return path & filename of current BAM file
+const string BamReaderPrivate::Filename(void) const {
+ return m_filename;
+}
+
+const SamHeader& BamReaderPrivate::GetConstSamHeader(void) const {
+ return m_header.ToConstSamHeader();
+}
+
+string BamReaderPrivate::GetErrorString(void) const {
+ return m_errorString;
+}
+
+// return header data as std::string
+string BamReaderPrivate::GetHeaderText(void) const {
+ return m_header.ToString();
+}
+
+// return header data as SamHeader object
+SamHeader BamReaderPrivate::GetSamHeader(void) const {
+ return m_header.ToSamHeader();
+}
+
+// get next alignment (with character data fully parsed)
+bool BamReaderPrivate::GetNextAlignment(BamAlignment& alignment) {
+
+ // if valid alignment found
+ if ( GetNextAlignmentCore(alignment) ) {
+
+ // store alignment's "source" filename
+ alignment.Filename = m_filename;
+
+ // return success/failure of parsing char data
+ if ( alignment.BuildCharData() )
+ return true;
+ else {
+ const string alError = alignment.GetErrorString();
+ const string message = string("could not populate alignment data: \n\t") + alError;
+ SetErrorString("BamReader::GetNextAlignment", message);
+ return false;
+ }
+ }
+
+ // no valid alignment found
+ return false;
+}
+
+// retrieves next available alignment core data (returns success/fail)
+// ** DOES NOT populate any character data fields (read name, bases, qualities, tag data, filename)
+// these can be accessed, if necessary, from the supportData
+// useful for operations requiring ONLY positional or other alignment-related information
+bool BamReaderPrivate::GetNextAlignmentCore(BamAlignment& alignment) {
+
+ // skip if stream not opened
+ if ( !m_stream.IsOpen() )
+ return false;
+
+ try {
+
+ // skip if region is set but has no alignments
+ if ( m_randomAccessController.HasRegion() &&
+ !m_randomAccessController.RegionHasAlignments() )
+ {
+ return false;
+ }
+
+ // if can't read next alignment
+ if ( !LoadNextAlignment(alignment) )
+ return false;
+
+ // check alignment's region-overlap state
+ BamRandomAccessController::RegionState state = m_randomAccessController.AlignmentState(alignment);
+
+ // if alignment starts after region, no need to keep reading
+ if ( state == BamRandomAccessController::AfterRegion )
+ return false;
+
+ // read until overlap is found
+ while ( state != BamRandomAccessController::OverlapsRegion ) {
+
+ // if can't read next alignment
+ if ( !LoadNextAlignment(alignment) )
+ return false;
+
+ // check alignment's region-overlap state
+ state = m_randomAccessController.AlignmentState(alignment);
+
+ // if alignment starts after region, no need to keep reading
+ if ( state == BamRandomAccessController::AfterRegion )
+ return false;
+ }
+
+ // if we get here, we found the next 'valid' alignment
+ // (e.g. overlaps current region if one was set, simply the next alignment if not)
+ alignment.SupportData.HasCoreOnly = true;
+ return true;
+
+ } catch ( BamException& e ) {
+ const string streamError = e.what();
+ const string message = string("encountered error reading BAM alignment: \n\t") + streamError;
+ SetErrorString("BamReader::GetNextAlignmentCore", message);
+ return false;
+ }
+}
+
+int BamReaderPrivate::GetReferenceCount(void) const {
+ return m_references.size();
+}
+
+const RefVector& BamReaderPrivate::GetReferenceData(void) const {
+ return m_references;
+}
+
+// returns RefID for given RefName (returns References.size() if not found)
+int BamReaderPrivate::GetReferenceID(const string& refName) const {
+
+ // retrieve names from reference data
+ vector<string> refNames;
+ RefVector::const_iterator refIter = m_references.begin();
+ RefVector::const_iterator refEnd = m_references.end();
+ for ( ; refIter != refEnd; ++refIter)
+ refNames.push_back( (*refIter).RefName );
+
+ // return 'index-of' refName (or -1 if not found)
+ int index = distance(refNames.begin(), find(refNames.begin(), refNames.end(), refName));
+ if ( index == (int)m_references.size() ) return -1;
+ else return index;
+}
+
+bool BamReaderPrivate::HasIndex(void) const {
+ return m_randomAccessController.HasIndex();
+}
+
+bool BamReaderPrivate::IsOpen(void) const {
+ return m_stream.IsOpen();
+}
+
+// load BAM header data
+void BamReaderPrivate::LoadHeaderData(void) {
+ m_header.Load(&m_stream);
+}
+
+// populates BamAlignment with alignment data under file pointer, returns success/fail
+bool BamReaderPrivate::LoadNextAlignment(BamAlignment& alignment) {
+
+ // read in the 'block length' value, make sure it's not zero
+ char buffer[sizeof(uint32_t)];
+ fill_n(buffer, sizeof(uint32_t), 0);
+ m_stream.Read(buffer, sizeof(uint32_t));
+ alignment.SupportData.BlockLength = BamTools::UnpackUnsignedInt(buffer);
+ if ( m_isBigEndian ) BamTools::SwapEndian_32(alignment.SupportData.BlockLength);
+ if ( alignment.SupportData.BlockLength == 0 )
+ return false;
+
+ // read in core alignment data, make sure the right size of data was read
+ char x[Constants::BAM_CORE_SIZE];
+ if ( m_stream.Read(x, Constants::BAM_CORE_SIZE) != Constants::BAM_CORE_SIZE )
+ return false;
+
+ // swap core endian-ness if necessary
+ if ( m_isBigEndian ) {
+ for ( unsigned int i = 0; i < Constants::BAM_CORE_SIZE; i+=sizeof(uint32_t) )
+ BamTools::SwapEndian_32p(&x[i]);
+ }
+
+ // set BamAlignment 'core' and 'support' data
+ alignment.RefID = BamTools::UnpackSignedInt(&x[0]);
+ alignment.Position = BamTools::UnpackSignedInt(&x[4]);
+
+ unsigned int tempValue = BamTools::UnpackUnsignedInt(&x[8]);
+ alignment.Bin = tempValue >> 16;
+ alignment.MapQuality = tempValue >> 8 & 0xff;
+ alignment.SupportData.QueryNameLength = tempValue & 0xff;
+
+ tempValue = BamTools::UnpackUnsignedInt(&x[12]);
+ alignment.AlignmentFlag = tempValue >> 16;
+ alignment.SupportData.NumCigarOperations = tempValue & 0xffff;
+
+ alignment.SupportData.QuerySequenceLength = BamTools::UnpackUnsignedInt(&x[16]);
+ alignment.MateRefID = BamTools::UnpackSignedInt(&x[20]);
+ alignment.MatePosition = BamTools::UnpackSignedInt(&x[24]);
+ alignment.InsertSize = BamTools::UnpackSignedInt(&x[28]);
+
+ // set BamAlignment length
+ alignment.Length = alignment.SupportData.QuerySequenceLength;
+
+ // read in character data - make sure proper data size was read
+ bool readCharDataOK = false;
+ const unsigned int dataLength = alignment.SupportData.BlockLength - Constants::BAM_CORE_SIZE;
+ RaiiBuffer allCharData(dataLength);
+
+ if ( m_stream.Read(allCharData.Buffer, dataLength) == dataLength ) {
+
+ // store 'allCharData' in supportData structure
+ alignment.SupportData.AllCharData.assign((const char*)allCharData.Buffer, dataLength);
+
+ // set success flag
+ readCharDataOK = true;
+
+ // save CIGAR ops
+ // need to calculate this here so that BamAlignment::GetEndPosition() performs correctly,
+ // even when GetNextAlignmentCore() is called
+ const unsigned int cigarDataOffset = alignment.SupportData.QueryNameLength;
+ uint32_t* cigarData = (uint32_t*)(allCharData.Buffer + cigarDataOffset);
+ CigarOp op;
+ alignment.CigarData.clear();
+ alignment.CigarData.reserve(alignment.SupportData.NumCigarOperations);
+ for ( unsigned int i = 0; i < alignment.SupportData.NumCigarOperations; ++i ) {
+
+ // swap endian-ness if necessary
+ if ( m_isBigEndian ) BamTools::SwapEndian_32(cigarData[i]);
+
+ // build CigarOp structure
+ op.Length = (cigarData[i] >> Constants::BAM_CIGAR_SHIFT);
+ op.Type = Constants::BAM_CIGAR_LOOKUP[ (cigarData[i] & Constants::BAM_CIGAR_MASK) ];
+
+ // save CigarOp
+ alignment.CigarData.push_back(op);
+ }
+ }
+
+ // return success/failure
+ return readCharDataOK;
+}
+
+// loads reference data from BAM file
+bool BamReaderPrivate::LoadReferenceData(void) {
+
+ // get number of reference sequences
+ char buffer[sizeof(uint32_t)];
+ m_stream.Read(buffer, sizeof(uint32_t));
+ uint32_t numberRefSeqs = BamTools::UnpackUnsignedInt(buffer);
+ if ( m_isBigEndian ) BamTools::SwapEndian_32(numberRefSeqs);
+ m_references.reserve((int)numberRefSeqs);
+
+ // iterate over all references in header
+ for ( unsigned int i = 0; i != numberRefSeqs; ++i ) {
+
+ // get length of reference name
+ m_stream.Read(buffer, sizeof(uint32_t));
+ uint32_t refNameLength = BamTools::UnpackUnsignedInt(buffer);
+ if ( m_isBigEndian ) BamTools::SwapEndian_32(refNameLength);
+ RaiiBuffer refName(refNameLength);
+
+ // get reference name and reference sequence length
+ m_stream.Read(refName.Buffer, refNameLength);
+ m_stream.Read(buffer, sizeof(int32_t));
+ int32_t refLength = BamTools::UnpackSignedInt(buffer);
+ if ( m_isBigEndian ) BamTools::SwapEndian_32(refLength);
+
+ // store data for reference
+ RefData aReference;
+ aReference.RefName = (string)((const char*)refName.Buffer);
+ aReference.RefLength = refLength;
+ m_references.push_back(aReference);
+ }
+
+ // return success
+ return true;
+}
+
+bool BamReaderPrivate::LocateIndex(const BamIndex::IndexType& preferredType) {
+
+ if ( m_randomAccessController.LocateIndex(this, preferredType) )
+ return true;
+ else {
+ const string bracError = m_randomAccessController.GetErrorString();
+ const string message = string("could not locate index: \n\t") + bracError;
+ SetErrorString("BamReader::LocateIndex", message);
+ return false;
+ }
+}
+
+// opens BAM file (and index)
+bool BamReaderPrivate::Open(const string& filename) {
+
+ try {
+
+ // make sure we're starting with fresh state
+ Close();
+
+ // open BgzfStream
+ m_stream.Open(filename, IBamIODevice::ReadOnly);
+
+ // load BAM metadata
+ LoadHeaderData();
+ LoadReferenceData();
+
+ // store filename & offset of first alignment
+ m_filename = filename;
+ m_alignmentsBeginOffset = m_stream.Tell();
+
+ // return success
+ return true;
+
+ } catch ( BamException& e ) {
+ const string error = e.what();
+ const string message = string("could not open file: ") + filename +
+ "\n\t" + error;
+ SetErrorString("BamReader::Open", message);
+ return false;
+ }
+}
+
+bool BamReaderPrivate::OpenIndex(const std::string& indexFilename) {
+
+ if ( m_randomAccessController.OpenIndex(indexFilename, this) )
+ return true;
+ else {
+ const string bracError = m_randomAccessController.GetErrorString();
+ const string message = string("could not open index: \n\t") + bracError;
+ SetErrorString("BamReader::OpenIndex", message);
+ return false;
+ }
+}
+
+// returns BAM file pointer to beginning of alignment data
+bool BamReaderPrivate::Rewind(void) {
+
+ // reset region
+ m_randomAccessController.ClearRegion();
+
+ // return status of seeking back to first alignment
+ if ( Seek(m_alignmentsBeginOffset) )
+ return true;
+ else {
+ const string currentError = m_errorString;
+ const string message = string("could not rewind: \n\t") + currentError;
+ SetErrorString("BamReader::Rewind", message);
+ return false;
+ }
+}
+
+bool BamReaderPrivate::Seek(const int64_t& position) {
+
+ // skip if BAM file not open
+ if ( !IsOpen() ) {
+ SetErrorString("BamReader::Seek", "cannot seek on unopened BAM file");
+ return false;
+ }
+
+ try {
+ m_stream.Seek(position);
+ return true;
+ }
+ catch ( BamException& e ) {
+ const string streamError = e.what();
+ const string message = string("could not seek in BAM file: \n\t") + streamError;
+ SetErrorString("BamReader::Seek", message);
+ return false;
+ }
+}
+
+void BamReaderPrivate::SetErrorString(const string& where, const string& what) {
+ static const string SEPARATOR = ": ";
+ m_errorString = where + SEPARATOR + what;
+}
+
+void BamReaderPrivate::SetIndex(BamIndex* index) {
+ m_randomAccessController.SetIndex(index);
+}
+
+// sets current region & attempts to jump to it
+// returns success/failure
+bool BamReaderPrivate::SetRegion(const BamRegion& region) {
+
+ if ( m_randomAccessController.SetRegion(region, m_references.size()) )
+ return true;
+ else {
+ const string bracError = m_randomAccessController.GetErrorString();
+ const string message = string("could not set region: \n\t") + bracError;
+ SetErrorString("BamReader::SetRegion", message);
+ return false;
+ }
+}
+
+int64_t BamReaderPrivate::Tell(void) const {
+ return m_stream.Tell();
+}
diff --git a/bamtools/src/api/internal/bam/BamReader_p.h b/bamtools/src/api/internal/bam/BamReader_p.h
new file mode 100644
index 0000000..a49ad2a
--- /dev/null
+++ b/bamtools/src/api/internal/bam/BamReader_p.h
@@ -0,0 +1,119 @@
+// ***************************************************************************
+// BamReader_p.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 18 November 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides the basic functionality for reading BAM files
+// ***************************************************************************
+
+#ifndef BAMREADER_P_H
+#define BAMREADER_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/BamAlignment.h"
+#include "api/BamIndex.h"
+#include "api/BamReader.h"
+#include "api/SamHeader.h"
+#include "api/internal/bam/BamHeader_p.h"
+#include "api/internal/bam/BamRandomAccessController_p.h"
+#include "api/internal/io/BgzfStream_p.h"
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class BamReaderPrivate {
+
+ // ctor & dtor
+ public:
+ BamReaderPrivate(BamReader* parent);
+ ~BamReaderPrivate(void);
+
+ // BamReader interface
+ public:
+
+ // file operations
+ bool Close(void);
+ const std::string Filename(void) const;
+ bool IsOpen(void) const;
+ bool Open(const std::string& filename);
+ bool Rewind(void);
+ bool SetRegion(const BamRegion& region);
+
+ // access alignment data
+ bool GetNextAlignment(BamAlignment& alignment);
+ bool GetNextAlignmentCore(BamAlignment& alignment);
+
+ // access auxiliary data
+ std::string GetHeaderText(void) const;
+ const SamHeader& GetConstSamHeader(void) const;
+ SamHeader GetSamHeader(void) const;
+ int GetReferenceCount(void) const;
+ const RefVector& GetReferenceData(void) const;
+ int GetReferenceID(const std::string& refName) const;
+
+ // index operations
+ bool CreateIndex(const BamIndex::IndexType& type);
+ bool HasIndex(void) const;
+ bool LocateIndex(const BamIndex::IndexType& preferredType);
+ bool OpenIndex(const std::string& indexFilename);
+ void SetIndex(BamIndex* index);
+
+ // error handling
+ std::string GetErrorString(void) const;
+ void SetErrorString(const std::string& where, const std::string& what);
+
+ // internal methods, but available as a BamReaderPrivate 'interface'
+ //
+ // these methods should only be used by BamTools::Internal classes
+ // (currently only used by the BamIndex subclasses)
+ public:
+ // retrieves header text from BAM file
+ void LoadHeaderData(void);
+ // retrieves BAM alignment under file pointer
+ // (does no overlap checking or character data parsing)
+ bool LoadNextAlignment(BamAlignment& alignment);
+ // builds reference data structure from BAM file
+ bool LoadReferenceData(void);
+ // seek reader to file position
+ bool Seek(const int64_t& position);
+ // return reader's file position
+ int64_t Tell(void) const;
+
+ // data members
+ public:
+
+ // general BAM file data
+ int64_t m_alignmentsBeginOffset;
+ std::string m_filename;
+ RefVector m_references;
+
+ // system data
+ bool m_isBigEndian;
+
+ // parent BamReader
+ BamReader* m_parent;
+
+ // BamReaderPrivate components
+ BamHeader m_header;
+ BamRandomAccessController m_randomAccessController;
+ BgzfStream m_stream;
+
+ // error handling
+ std::string m_errorString;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMREADER_P_H
diff --git a/bamtools/src/api/internal/bam/BamWriter_p.cpp b/bamtools/src/api/internal/bam/BamWriter_p.cpp
new file mode 100644
index 0000000..637bb7a
--- /dev/null
+++ b/bamtools/src/api/internal/bam/BamWriter_p.cpp
@@ -0,0 +1,475 @@
+// ***************************************************************************
+// BamWriter_p.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 18 November 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides the basic functionality for producing BAM files
+// ***************************************************************************
+
+#include "api/BamAlignment.h"
+#include "api/BamConstants.h"
+#include "api/IBamIODevice.h"
+#include "api/internal/bam/BamWriter_p.h"
+#include "api/internal/utils/BamException_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cstdlib>
+#include <cstring>
+using namespace std;
+
+// ctor
+BamWriterPrivate::BamWriterPrivate(void)
+ : m_isBigEndian( BamTools::SystemIsBigEndian() )
+{ }
+
+// dtor
+BamWriterPrivate::~BamWriterPrivate(void) {
+ Close();
+}
+
+// calculates minimum bin for a BAM alignment interval [begin, end)
+uint32_t BamWriterPrivate::CalculateMinimumBin(const int begin, int end) const {
+ --end;
+ if ( (begin >> 14) == (end >> 14) ) return 4681 + (begin >> 14);
+ if ( (begin >> 17) == (end >> 17) ) return 585 + (begin >> 17);
+ if ( (begin >> 20) == (end >> 20) ) return 73 + (begin >> 20);
+ if ( (begin >> 23) == (end >> 23) ) return 9 + (begin >> 23);
+ if ( (begin >> 26) == (end >> 26) ) return 1 + (begin >> 26);
+ return 0;
+}
+
+// closes the alignment archive
+void BamWriterPrivate::Close(void) {
+
+ // skip if file not open
+ if ( !IsOpen() ) return;
+
+ // close output stream
+ try {
+ m_stream.Close();
+ } catch ( BamException& e ) {
+ m_errorString = e.what();
+ }
+}
+
+// creates a cigar string from the supplied alignment
+void BamWriterPrivate::CreatePackedCigar(const vector<CigarOp>& cigarOperations, string& packedCigar) {
+
+ // initialize
+ const size_t numCigarOperations = cigarOperations.size();
+ packedCigar.resize(numCigarOperations * Constants::BAM_SIZEOF_INT);
+
+ // pack the cigar data into the string
+ unsigned int* pPackedCigar = (unsigned int*)packedCigar.data();
+
+ // iterate over cigar operations
+ vector<CigarOp>::const_iterator coIter = cigarOperations.begin();
+ vector<CigarOp>::const_iterator coEnd = cigarOperations.end();
+ for ( ; coIter != coEnd; ++coIter ) {
+
+ // store op in packedCigar
+ uint8_t cigarOp;
+ switch ( coIter->Type ) {
+ case (Constants::BAM_CIGAR_MATCH_CHAR) : cigarOp = Constants::BAM_CIGAR_MATCH; break;
+ case (Constants::BAM_CIGAR_INS_CHAR) : cigarOp = Constants::BAM_CIGAR_INS; break;
+ case (Constants::BAM_CIGAR_DEL_CHAR) : cigarOp = Constants::BAM_CIGAR_DEL; break;
+ case (Constants::BAM_CIGAR_REFSKIP_CHAR) : cigarOp = Constants::BAM_CIGAR_REFSKIP; break;
+ case (Constants::BAM_CIGAR_SOFTCLIP_CHAR) : cigarOp = Constants::BAM_CIGAR_SOFTCLIP; break;
+ case (Constants::BAM_CIGAR_HARDCLIP_CHAR) : cigarOp = Constants::BAM_CIGAR_HARDCLIP; break;
+ case (Constants::BAM_CIGAR_PAD_CHAR) : cigarOp = Constants::BAM_CIGAR_PAD; break;
+ case (Constants::BAM_CIGAR_SEQMATCH_CHAR) : cigarOp = Constants::BAM_CIGAR_SEQMATCH; break;
+ case (Constants::BAM_CIGAR_MISMATCH_CHAR) : cigarOp = Constants::BAM_CIGAR_MISMATCH; break;
+ default:
+ const string message = string("invalid CIGAR operation type") + coIter->Type;
+ throw BamException("BamWriter::CreatePackedCigar", message);
+ }
+
+ *pPackedCigar = coIter->Length << Constants::BAM_CIGAR_SHIFT | cigarOp;
+ pPackedCigar++;
+ }
+}
+
+// encodes the supplied query sequence into 4-bit notation
+void BamWriterPrivate::EncodeQuerySequence(const string& query, string& encodedQuery) {
+
+ // prepare the encoded query string
+ const size_t queryLength = query.size();
+ const size_t encodedQueryLength = static_cast<size_t>((queryLength+1)/2);
+ encodedQuery.resize(encodedQueryLength);
+ char* pEncodedQuery = (char*)encodedQuery.data();
+ const char* pQuery = (const char*)query.data();
+
+ // walk through original query sequence, encoding its bases
+ unsigned char nucleotideCode;
+ bool useHighWord = true;
+ while ( *pQuery ) {
+ switch ( *pQuery ) {
+ case (Constants::BAM_DNA_EQUAL) : nucleotideCode = Constants::BAM_BASECODE_EQUAL; break;
+ case (Constants::BAM_DNA_A) : nucleotideCode = Constants::BAM_BASECODE_A; break;
+ case (Constants::BAM_DNA_C) : nucleotideCode = Constants::BAM_BASECODE_C; break;
+ case (Constants::BAM_DNA_M) : nucleotideCode = Constants::BAM_BASECODE_M; break;
+ case (Constants::BAM_DNA_G) : nucleotideCode = Constants::BAM_BASECODE_G; break;
+ case (Constants::BAM_DNA_R) : nucleotideCode = Constants::BAM_BASECODE_R; break;
+ case (Constants::BAM_DNA_S) : nucleotideCode = Constants::BAM_BASECODE_S; break;
+ case (Constants::BAM_DNA_V) : nucleotideCode = Constants::BAM_BASECODE_V; break;
+ case (Constants::BAM_DNA_T) : nucleotideCode = Constants::BAM_BASECODE_T; break;
+ case (Constants::BAM_DNA_W) : nucleotideCode = Constants::BAM_BASECODE_W; break;
+ case (Constants::BAM_DNA_Y) : nucleotideCode = Constants::BAM_BASECODE_Y; break;
+ case (Constants::BAM_DNA_H) : nucleotideCode = Constants::BAM_BASECODE_H; break;
+ case (Constants::BAM_DNA_K) : nucleotideCode = Constants::BAM_BASECODE_K; break;
+ case (Constants::BAM_DNA_D) : nucleotideCode = Constants::BAM_BASECODE_D; break;
+ case (Constants::BAM_DNA_B) : nucleotideCode = Constants::BAM_BASECODE_B; break;
+ case (Constants::BAM_DNA_N) : nucleotideCode = Constants::BAM_BASECODE_N; break;
+ default:
+ const string message = string("invalid base: ") + *pQuery;
+ throw BamException("BamWriter::EncodeQuerySequence", message);
+ }
+
+ // pack the nucleotide code
+ if ( useHighWord ) {
+ *pEncodedQuery = nucleotideCode << 4;
+ useHighWord = false;
+ } else {
+ *pEncodedQuery |= nucleotideCode;
+ ++pEncodedQuery;
+ useHighWord = true;
+ }
+
+ // increment the query position
+ ++pQuery;
+ }
+}
+
+// returns a description of the last error that occurred
+std::string BamWriterPrivate::GetErrorString(void) const {
+ return m_errorString;
+}
+
+// returns whether BAM file is open for writing or not
+bool BamWriterPrivate::IsOpen(void) const {
+ return m_stream.IsOpen();
+}
+
+// opens the alignment archive
+bool BamWriterPrivate::Open(const string& filename,
+ const string& samHeaderText,
+ const RefVector& referenceSequences)
+{
+ try {
+
+ // open the BGZF file for writing
+ m_stream.Open(filename, IBamIODevice::WriteOnly);
+
+ // write BAM file 'metadata' components
+ WriteMagicNumber();
+ WriteSamHeaderText(samHeaderText);
+ WriteReferences(referenceSequences);
+
+ // return success
+ return true;
+
+ } catch ( BamException& e ) {
+ m_errorString = e.what();
+ return false;
+ }
+}
+
+// saves the alignment to the alignment archive
+bool BamWriterPrivate::SaveAlignment(const BamAlignment& al) {
+
+ try {
+
+ // if BamAlignment contains only the core data and a raw char data buffer
+ // (as a result of BamReader::GetNextAlignmentCore())
+ if ( al.SupportData.HasCoreOnly )
+ WriteCoreAlignment(al);
+
+ // otherwise, BamAlignment should contain character in the standard fields: Name, QueryBases, etc
+ // (resulting from BamReader::GetNextAlignment() *OR* being generated directly by client code)
+ else WriteAlignment(al);
+
+ // if we get here, everything OK
+ return true;
+
+ } catch ( BamException& e ) {
+ m_errorString = e.what();
+ return false;
+ }
+}
+
+void BamWriterPrivate::SetWriteCompressed(bool ok) {
+ // modifying compression is not allowed if BAM file is open
+ if ( !IsOpen() )
+ m_stream.SetWriteCompressed(ok);
+}
+
+void BamWriterPrivate::WriteAlignment(const BamAlignment& al) {
+
+ // calculate char lengths
+ const unsigned int nameLength = al.Name.size() + 1;
+ const unsigned int numCigarOperations = al.CigarData.size();
+ const unsigned int queryLength = ( (al.QueryBases == "*") ? 0 : al.QueryBases.size() );
+ const unsigned int tagDataLength = al.TagData.size();
+
+ // no way to tell if alignment's bin is already defined (there is no default, invalid value)
+ // so we'll go ahead calculate its bin ID before storing
+ const uint32_t alignmentBin = CalculateMinimumBin(al.Position, al.GetEndPosition());
+
+ // create our packed cigar string
+ string packedCigar;
+ CreatePackedCigar(al.CigarData, packedCigar);
+ const unsigned int packedCigarLength = packedCigar.size();
+
+ // encode the query
+ unsigned int encodedQueryLength = 0;
+ string encodedQuery;
+ if ( queryLength > 0 ) {
+ EncodeQuerySequence(al.QueryBases, encodedQuery);
+ encodedQueryLength = encodedQuery.size();
+ }
+
+ // write the block size
+ const unsigned int dataBlockSize = nameLength +
+ packedCigarLength +
+ encodedQueryLength +
+ queryLength + // here referring to quality length
+ tagDataLength;
+ unsigned int blockSize = Constants::BAM_CORE_SIZE + dataBlockSize;
+ if ( m_isBigEndian ) BamTools::SwapEndian_32(blockSize);
+ m_stream.Write((char*)&blockSize, Constants::BAM_SIZEOF_INT);
+
+ // assign the BAM core data
+ uint32_t buffer[Constants::BAM_CORE_BUFFER_SIZE];
+ buffer[0] = al.RefID;
+ buffer[1] = al.Position;
+ buffer[2] = (alignmentBin << 16) | (al.MapQuality << 8) | nameLength;
+ buffer[3] = (al.AlignmentFlag << 16) | numCigarOperations;
+ buffer[4] = queryLength;
+ buffer[5] = al.MateRefID;
+ buffer[6] = al.MatePosition;
+ buffer[7] = al.InsertSize;
+
+ // swap BAM core endian-ness, if necessary
+ if ( m_isBigEndian ) {
+ for ( int i = 0; i < 8; ++i )
+ BamTools::SwapEndian_32(buffer[i]);
+ }
+
+ // write the BAM core
+ m_stream.Write((char*)&buffer, Constants::BAM_CORE_SIZE);
+
+ // write the query name
+ m_stream.Write(al.Name.c_str(), nameLength);
+
+ // write the packed cigar
+ if ( m_isBigEndian ) {
+ char* cigarData = new char[packedCigarLength]();
+ memcpy(cigarData, packedCigar.data(), packedCigarLength);
+ if ( m_isBigEndian ) {
+ for ( size_t i = 0; i < packedCigarLength; ++i )
+ BamTools::SwapEndian_32p(&cigarData[i]);
+ }
+ m_stream.Write(cigarData, packedCigarLength);
+ delete[] cigarData; // TODO: cleanup on Write exception thrown?
+ }
+ else
+ m_stream.Write(packedCigar.data(), packedCigarLength);
+
+ if ( queryLength > 0 ) {
+
+ // write the encoded query sequence
+ m_stream.Write(encodedQuery.data(), encodedQueryLength);
+
+ // write the base qualities
+ char* pBaseQualities = new char[queryLength]();
+ if ( al.Qualities.empty() || ( al.Qualities.size() == 1 && al.Qualities[0] == '*' ) || al.Qualities[0] == (char)0xFF )
+ memset(pBaseQualities, 0xFF, queryLength); // if missing or '*', fill with invalid qual
+ else {
+ for ( size_t i = 0; i < queryLength; ++i )
+ pBaseQualities[i] = al.Qualities.at(i) - 33; // FASTQ ASCII -> phred score conversion
+ }
+ m_stream.Write(pBaseQualities, queryLength);
+ delete[] pBaseQualities;
+ }
+
+ // write the tag data
+ if ( m_isBigEndian ) {
+
+ char* tagData = new char[tagDataLength]();
+ memcpy(tagData, al.TagData.data(), tagDataLength);
+
+ size_t i = 0;
+ while ( i < tagDataLength ) {
+
+ i += Constants::BAM_TAG_TAGSIZE; // skip tag chars (e.g. "RG", "NM", etc.)
+ const char type = tagData[i]; // get tag type at position i
+ ++i;
+
+ switch ( type ) {
+
+ case(Constants::BAM_TAG_TYPE_ASCII) :
+ case(Constants::BAM_TAG_TYPE_INT8) :
+ case(Constants::BAM_TAG_TYPE_UINT8) :
+ ++i;
+ break;
+
+ case(Constants::BAM_TAG_TYPE_INT16) :
+ case(Constants::BAM_TAG_TYPE_UINT16) :
+ BamTools::SwapEndian_16p(&tagData[i]);
+ i += sizeof(uint16_t);
+ break;
+
+ case(Constants::BAM_TAG_TYPE_FLOAT) :
+ case(Constants::BAM_TAG_TYPE_INT32) :
+ case(Constants::BAM_TAG_TYPE_UINT32) :
+ BamTools::SwapEndian_32p(&tagData[i]);
+ i += sizeof(uint32_t);
+ break;
+
+ case(Constants::BAM_TAG_TYPE_HEX) :
+ case(Constants::BAM_TAG_TYPE_STRING) :
+ // no endian swapping necessary for hex-string/string data
+ while ( tagData[i] )
+ ++i;
+ // increment one more for null terminator
+ ++i;
+ break;
+
+ case(Constants::BAM_TAG_TYPE_ARRAY) :
+
+ {
+ // read array type
+ const char arrayType = tagData[i];
+ ++i;
+
+ // swap endian-ness of number of elements in place, then retrieve for loop
+ BamTools::SwapEndian_32p(&tagData[i]);
+ int32_t numElements;
+ memcpy(&numElements, &tagData[i], sizeof(uint32_t));
+ i += sizeof(uint32_t);
+
+ // swap endian-ness of array elements
+ for ( int j = 0; j < numElements; ++j ) {
+ switch (arrayType) {
+ case (Constants::BAM_TAG_TYPE_INT8) :
+ case (Constants::BAM_TAG_TYPE_UINT8) :
+ // no endian-swapping necessary
+ ++i;
+ break;
+ case (Constants::BAM_TAG_TYPE_INT16) :
+ case (Constants::BAM_TAG_TYPE_UINT16) :
+ BamTools::SwapEndian_16p(&tagData[i]);
+ i += sizeof(uint16_t);
+ break;
+ case (Constants::BAM_TAG_TYPE_FLOAT) :
+ case (Constants::BAM_TAG_TYPE_INT32) :
+ case (Constants::BAM_TAG_TYPE_UINT32) :
+ BamTools::SwapEndian_32p(&tagData[i]);
+ i += sizeof(uint32_t);
+ break;
+ default:
+ delete[] tagData;
+ const string message = string("invalid binary array type: ") + arrayType;
+ throw BamException("BamWriter::SaveAlignment", message);
+ }
+ }
+
+ break;
+ }
+
+ default :
+ delete[] tagData;
+ const string message = string("invalid tag type: ") + type;
+ throw BamException("BamWriter::SaveAlignment", message);
+ }
+ }
+
+ m_stream.Write(tagData, tagDataLength);
+ delete[] tagData; // TODO: cleanup on Write exception thrown?
+ }
+ else
+ m_stream.Write(al.TagData.data(), tagDataLength);
+}
+
+void BamWriterPrivate::WriteCoreAlignment(const BamAlignment& al) {
+
+ // write the block size
+ unsigned int blockSize = al.SupportData.BlockLength;
+ if ( m_isBigEndian ) BamTools::SwapEndian_32(blockSize);
+ m_stream.Write((char*)&blockSize, Constants::BAM_SIZEOF_INT);
+
+ // re-calculate bin (in case BamAlignment's position has been previously modified)
+ const uint32_t alignmentBin = CalculateMinimumBin(al.Position, al.GetEndPosition());
+
+ // assign the BAM core data
+ uint32_t buffer[Constants::BAM_CORE_BUFFER_SIZE];
+ buffer[0] = al.RefID;
+ buffer[1] = al.Position;
+ buffer[2] = (alignmentBin << 16) | (al.MapQuality << 8) | al.SupportData.QueryNameLength;
+ buffer[3] = (al.AlignmentFlag << 16) | al.SupportData.NumCigarOperations;
+ buffer[4] = al.SupportData.QuerySequenceLength;
+ buffer[5] = al.MateRefID;
+ buffer[6] = al.MatePosition;
+ buffer[7] = al.InsertSize;
+
+ // swap BAM core endian-ness, if necessary
+ if ( m_isBigEndian ) {
+ for ( int i = 0; i < 8; ++i )
+ BamTools::SwapEndian_32(buffer[i]);
+ }
+
+ // write the BAM core
+ m_stream.Write((char*)&buffer, Constants::BAM_CORE_SIZE);
+
+ // write the raw char data
+ m_stream.Write((char*)al.SupportData.AllCharData.data(),
+ al.SupportData.BlockLength-Constants::BAM_CORE_SIZE);
+}
+
+void BamWriterPrivate::WriteMagicNumber(void) {
+ // write BAM file 'magic number'
+ m_stream.Write(Constants::BAM_HEADER_MAGIC, Constants::BAM_HEADER_MAGIC_LENGTH);
+}
+
+void BamWriterPrivate::WriteReferences(const BamTools::RefVector& referenceSequences) {
+
+ // write the number of reference sequences
+ uint32_t numReferenceSequences = referenceSequences.size();
+ if ( m_isBigEndian ) BamTools::SwapEndian_32(numReferenceSequences);
+ m_stream.Write((char*)&numReferenceSequences, Constants::BAM_SIZEOF_INT);
+
+ // foreach reference sequence
+ RefVector::const_iterator rsIter = referenceSequences.begin();
+ RefVector::const_iterator rsEnd = referenceSequences.end();
+ for ( ; rsIter != rsEnd; ++rsIter ) {
+
+ // write the reference sequence name length (+1 for terminator)
+ const uint32_t actualNameLen = rsIter->RefName.size() + 1;
+ uint32_t maybeSwappedNameLen = actualNameLen;
+ if ( m_isBigEndian ) BamTools::SwapEndian_32(maybeSwappedNameLen);
+ m_stream.Write((char*)&maybeSwappedNameLen, Constants::BAM_SIZEOF_INT);
+
+ // write the reference sequence name
+ m_stream.Write(rsIter->RefName.c_str(), actualNameLen);
+
+ // write the reference sequence length
+ int32_t referenceLength = rsIter->RefLength;
+ if ( m_isBigEndian ) BamTools::SwapEndian_32(referenceLength);
+ m_stream.Write((char*)&referenceLength, Constants::BAM_SIZEOF_INT);
+ }
+}
+
+void BamWriterPrivate::WriteSamHeaderText(const std::string& samHeaderText) {
+
+ // write the SAM header text length
+ const uint32_t actualHeaderLen = samHeaderText.size();
+ uint32_t maybeSwappedHeaderLen = samHeaderText.size();
+ if ( m_isBigEndian ) BamTools::SwapEndian_32(maybeSwappedHeaderLen);
+ m_stream.Write((char*)&maybeSwappedHeaderLen, Constants::BAM_SIZEOF_INT);
+
+ // write the SAM header text
+ if ( actualHeaderLen > 0 )
+ m_stream.Write(samHeaderText.data(), actualHeaderLen);
+}
diff --git a/bamtools/src/api/internal/bam/BamWriter_p.h b/bamtools/src/api/internal/bam/BamWriter_p.h
new file mode 100644
index 0000000..d5bbe8d
--- /dev/null
+++ b/bamtools/src/api/internal/bam/BamWriter_p.h
@@ -0,0 +1,73 @@
+// ***************************************************************************
+// BamWriter_p.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 25 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides the basic functionality for producing BAM files
+// ***************************************************************************
+
+#ifndef BAMWRITER_P_H
+#define BAMWRITER_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/BamAux.h"
+#include "api/internal/io/BgzfStream_p.h"
+#include <string>
+#include <vector>
+
+namespace BamTools {
+
+class BamAlignment;
+
+namespace Internal {
+
+class BamWriterPrivate {
+
+ // ctor & dtor
+ public:
+ BamWriterPrivate(void);
+ ~BamWriterPrivate(void);
+
+ // interface methods
+ public:
+ void Close(void);
+ std::string GetErrorString(void) const;
+ bool IsOpen(void) const;
+ bool Open(const std::string& filename,
+ const std::string& samHeaderText,
+ const BamTools::RefVector& referenceSequences);
+ bool SaveAlignment(const BamAlignment& al);
+ void SetWriteCompressed(bool ok);
+
+ // 'internal' methods
+ public:
+ uint32_t CalculateMinimumBin(const int begin, int end) const;
+ void CreatePackedCigar(const std::vector<BamTools::CigarOp>& cigarOperations, std::string& packedCigar);
+ void EncodeQuerySequence(const std::string& query, std::string& encodedQuery);
+ void WriteAlignment(const BamAlignment& al);
+ void WriteCoreAlignment(const BamAlignment& al);
+ void WriteMagicNumber(void);
+ void WriteReferences(const BamTools::RefVector& referenceSequences);
+ void WriteSamHeaderText(const std::string& samHeaderText);
+
+ // data members
+ private:
+ BgzfStream m_stream;
+ bool m_isBigEndian;
+ std::string m_errorString;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMWRITER_P_H
diff --git a/bamtools/src/api/internal/bam/CMakeLists.txt b/bamtools/src/api/internal/bam/CMakeLists.txt
new file mode 100644
index 0000000..1bd2569
--- /dev/null
+++ b/bamtools/src/api/internal/bam/CMakeLists.txt
@@ -0,0 +1,19 @@
+# ==========================
+# BamTools CMakeLists.txt
+# (c) 2011 Derek Barnett
+#
+# src/api/internal/bam
+# ==========================
+
+set( InternalBamDir "${InternalDir}/bam" )
+
+set( InternalBamSources
+ ${InternalBamDir}/BamHeader_p.cpp
+ ${InternalBamDir}/BamMultiReader_p.cpp
+ ${InternalBamDir}/BamRandomAccessController_p.cpp
+ ${InternalBamDir}/BamReader_p.cpp
+ ${InternalBamDir}/BamWriter_p.cpp
+
+ PARENT_SCOPE # <-- leave this last
+ )
+
diff --git a/bamtools/src/api/internal/index/BamIndexFactory_p.cpp b/bamtools/src/api/internal/index/BamIndexFactory_p.cpp
new file mode 100644
index 0000000..ab7751f
--- /dev/null
+++ b/bamtools/src/api/internal/index/BamIndexFactory_p.cpp
@@ -0,0 +1,107 @@
+// ***************************************************************************
+// BamIndexFactory_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides interface for generating BamIndex implementations
+// ***************************************************************************
+
+#include "api/internal/index/BamIndexFactory_p.h"
+#include "api/internal/index/BamStandardIndex_p.h"
+#include "api/internal/index/BamToolsIndex_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+using namespace std;
+
+// generates index filename from BAM filename (depending on requested type)
+// if type is unknown, returns empty string
+const string BamIndexFactory::CreateIndexFilename(const string& bamFilename,
+ const BamIndex::IndexType& type)
+{
+ switch ( type ) {
+ case ( BamIndex::STANDARD ) : return ( bamFilename + BamStandardIndex::Extension() );
+ case ( BamIndex::BAMTOOLS ) : return ( bamFilename + BamToolsIndex::Extension() );
+ default :
+ return string();
+ }
+}
+
+// creates a new BamIndex object, depending on extension of @indexFilename
+BamIndex* BamIndexFactory::CreateIndexFromFilename(const string& indexFilename, BamReaderPrivate* reader) {
+
+ // get file extension from index filename, including dot (".EXT")
+ // if can't get file extension, return null index
+ const string extension = FileExtension(indexFilename);
+ if ( extension.empty() )
+ return 0;
+
+ // create index based on extension
+ if ( extension == BamStandardIndex::Extension() ) return new BamStandardIndex(reader);
+ else if ( extension == BamToolsIndex::Extension() ) return new BamToolsIndex(reader);
+ else
+ return 0;
+}
+
+// creates a new BamIndex, object of requested @type
+BamIndex* BamIndexFactory::CreateIndexOfType(const BamIndex::IndexType& type,
+ BamReaderPrivate* reader)
+{
+ switch ( type ) {
+ case ( BamIndex::STANDARD ) : return new BamStandardIndex(reader);
+ case ( BamIndex::BAMTOOLS ) : return new BamToolsIndex(reader);
+ default :
+ return 0;
+ }
+}
+
+// retrieves file extension (including '.')
+const string BamIndexFactory::FileExtension(const string& filename) {
+
+ // if filename cannot contain valid path + extension, return empty string
+ if ( filename.empty() || filename.length() <= 4 )
+ return string();
+
+ // look for last dot in filename
+ const size_t lastDotPosition = filename.find_last_of('.');
+
+ // if none found, return empty string
+ if ( lastDotPosition == string::npos )
+ return string();
+
+ // return substring from last dot position
+ return filename.substr(lastDotPosition);
+}
+
+// returns name of existing index file that corresponds to @bamFilename
+// will defer to @preferredType if possible, if not will attempt to load any supported type
+// returns empty string if not found
+const string BamIndexFactory::FindIndexFilename(const string& bamFilename,
+ const BamIndex::IndexType& preferredType)
+{
+ // skip if BAM filename provided is empty
+ if ( bamFilename.empty() )
+ return string();
+
+ // try to find index of preferred type first
+ // return index filename if found
+ string indexFilename = CreateIndexFilename(bamFilename, preferredType);
+ if ( !indexFilename.empty() )
+ return indexFilename;
+
+ // couldn't find preferred type, try the other supported types
+ // return index filename if found
+ if ( preferredType != BamIndex::STANDARD ) {
+ indexFilename = CreateIndexFilename(bamFilename, BamIndex::STANDARD);
+ if ( !indexFilename.empty() )
+ return indexFilename;
+ }
+ if ( preferredType != BamIndex::BAMTOOLS ) {
+ indexFilename = CreateIndexFilename(bamFilename, BamIndex::BAMTOOLS);
+ if ( !indexFilename.empty() )
+ return indexFilename;
+ }
+
+ // otherwise couldn't find any index matching this filename
+ return string();
+}
diff --git a/bamtools/src/api/internal/index/BamIndexFactory_p.h b/bamtools/src/api/internal/index/BamIndexFactory_p.h
new file mode 100644
index 0000000..4e4f1cf
--- /dev/null
+++ b/bamtools/src/api/internal/index/BamIndexFactory_p.h
@@ -0,0 +1,49 @@
+// ***************************************************************************
+// BamIndexFactory_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides interface for generating BamIndex implementations
+// ***************************************************************************
+
+#ifndef BAMINDEX_FACTORY_P_H
+#define BAMINDEX_FACTORY_P_H
+
+#include "api/BamIndex.h"
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class BamIndexFactory {
+
+ // static interface methods
+ public:
+ // creates a new BamIndex object, depending on extension of @indexFilename
+ static BamIndex* CreateIndexFromFilename(const std::string& indexFilename,
+ BamReaderPrivate* reader);
+ // creates a new BamIndex object, of requested @type
+ static BamIndex* CreateIndexOfType(const BamIndex::IndexType& type,
+ BamReaderPrivate* reader);
+ // returns name of existing index file that corresponds to @bamFilename
+ // will defer to @preferredType if possible
+ // if @preferredType not found, will attempt to load any supported index type
+ // returns empty string if no index file (of any type) is found
+ static const std::string FindIndexFilename(const std::string& bamFilename,
+ const BamIndex::IndexType& preferredType);
+
+ // internal methods
+ public:
+ // generates index filename from BAM filename (depending on requested type)
+ // if type is unknown, returns empty string
+ static const std::string CreateIndexFilename(const std::string& bamFilename,
+ const BamIndex::IndexType& type);
+ // retrieves file extension (including '.')
+ static const std::string FileExtension(const std::string& filename);
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMINDEX_FACTORY_P_H
diff --git a/bamtools/src/api/internal/index/BamStandardIndex_p.cpp b/bamtools/src/api/internal/index/BamStandardIndex_p.cpp
new file mode 100644
index 0000000..2606a98
--- /dev/null
+++ b/bamtools/src/api/internal/index/BamStandardIndex_p.cpp
@@ -0,0 +1,965 @@
+// ***************************************************************************
+// BamStandardIndex.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 25 May 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides index operations for the standardized BAM index format (".bai")
+// ***************************************************************************
+
+#include "api/BamAlignment.h"
+#include "api/internal/bam/BamReader_p.h"
+#include "api/internal/index/BamStandardIndex_p.h"
+#include "api/internal/io/BamDeviceFactory_p.h"
+#include "api/internal/utils/BamException_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <algorithm>
+#include <sstream>
+using namespace std;
+
+// -----------------------------------
+// static BamStandardIndex constants
+// -----------------------------------
+
+const int BamStandardIndex::MAX_BIN = 37450; // =(8^6-1)/7+1
+const int BamStandardIndex::BAM_LIDX_SHIFT = 14;
+const string BamStandardIndex::BAI_EXTENSION = ".bai";
+const char* const BamStandardIndex::BAI_MAGIC = "BAI\1";
+const int BamStandardIndex::SIZEOF_ALIGNMENTCHUNK = sizeof(uint64_t)*2;
+const int BamStandardIndex::SIZEOF_BINCORE = sizeof(uint32_t) + sizeof(int32_t);
+const int BamStandardIndex::SIZEOF_LINEAROFFSET = sizeof(uint64_t);
+
+// ----------------------------
+// RaiiWrapper implementation
+// ----------------------------
+
+BamStandardIndex::RaiiWrapper::RaiiWrapper(void)
+ : Device(0)
+ , Buffer(0)
+{ }
+
+BamStandardIndex::RaiiWrapper::~RaiiWrapper(void) {
+
+ if ( Device ) {
+ Device->Close();
+ delete Device;
+ Device = 0;
+ }
+
+ if ( Buffer ) {
+ delete[] Buffer;
+ Buffer = 0;
+ }
+}
+
+// ---------------------------------
+// BamStandardIndex implementation
+// ---------------------------------
+
+// ctor
+BamStandardIndex::BamStandardIndex(Internal::BamReaderPrivate* reader)
+ : BamIndex(reader)
+ , m_bufferLength(0)
+{
+ m_isBigEndian = BamTools::SystemIsBigEndian();
+}
+
+// dtor
+BamStandardIndex::~BamStandardIndex(void) {
+ CloseFile();
+}
+
+void BamStandardIndex::AdjustRegion(const BamRegion& region, uint32_t& begin, uint32_t& end) {
+
+ // retrieve references from reader
+ const RefVector& references = m_reader->GetReferenceData();
+
+ // LeftPosition cannot be greater than or equal to reference length
+ if ( region.LeftPosition >= references.at(region.LeftRefID).RefLength )
+ throw BamException("BamStandardIndex::AdjustRegion", "invalid region requested");
+
+ // set region 'begin'
+ begin = (unsigned int)region.LeftPosition;
+
+ // if right bound specified AND left&right bounds are on same reference
+ // OK to use right bound position as region 'end'
+ if ( region.isRightBoundSpecified() && ( region.LeftRefID == region.RightRefID ) )
+ end = (unsigned int)region.RightPosition;
+
+ // otherwise, set region 'end' to last reference base
+ else end = (unsigned int)references.at(region.LeftRefID).RefLength;
+}
+
+// [begin, end)
+void BamStandardIndex::CalculateCandidateBins(const uint32_t& begin,
+ const uint32_t& end,
+ set<uint16_t>& candidateBins)
+{
+ // initialize list, bin '0' is always a valid bin
+ candidateBins.insert(0);
+
+ // get rest of bins that contain this region
+ unsigned int k;
+ for (k = 1 + (begin>>26); k <= 1 + (end>>26); ++k) { candidateBins.insert(k); }
+ for (k = 9 + (begin>>23); k <= 9 + (end>>23); ++k) { candidateBins.insert(k); }
+ for (k = 73 + (begin>>20); k <= 73 + (end>>20); ++k) { candidateBins.insert(k); }
+ for (k = 585 + (begin>>17); k <= 585 + (end>>17); ++k) { candidateBins.insert(k); }
+ for (k = 4681 + (begin>>14); k <= 4681 + (end>>14); ++k) { candidateBins.insert(k); }
+}
+
+void BamStandardIndex::CalculateCandidateOffsets(const BaiReferenceSummary& refSummary,
+ const uint64_t& minOffset,
+ set<uint16_t>& candidateBins,
+ vector<int64_t>& offsets)
+{
+ // seek to first bin
+ Seek(refSummary.FirstBinFilePosition, SEEK_SET);
+
+ // iterate over reference bins
+ uint32_t binId;
+ int32_t numAlignmentChunks;
+ set<uint16_t>::iterator candidateBinIter;
+ for ( int i = 0; i < refSummary.NumBins; ++i ) {
+
+ // read bin contents (if successful, alignment chunks are now in m_buffer)
+ ReadBinIntoBuffer(binId, numAlignmentChunks);
+
+ // see if bin is a 'candidate bin'
+ candidateBinIter = candidateBins.find(binId);
+
+ // if not, move on to next bin
+ if ( candidateBinIter == candidateBins.end() )
+ continue;
+
+ // otherwise, check bin's contents against for overlap
+ else {
+
+ size_t offset = 0;
+ uint64_t chunkStart;
+ uint64_t chunkStop;
+
+ // iterate over alignment chunks
+ for ( int j = 0; j < numAlignmentChunks; ++j ) {
+
+ // read chunk start & stop from buffer
+ memcpy((char*)&chunkStart, m_resources.Buffer+offset, sizeof(uint64_t));
+ offset += sizeof(uint64_t);
+ memcpy((char*)&chunkStop, m_resources.Buffer+offset, sizeof(uint64_t));
+ offset += sizeof(uint64_t);
+
+ // swap endian-ness if necessary
+ if ( m_isBigEndian ) {
+ SwapEndian_64(chunkStart);
+ SwapEndian_64(chunkStop);
+ }
+
+ // store alignment chunk's start offset
+ // if its stop offset is larger than our 'minOffset'
+ if ( chunkStop >= minOffset )
+ offsets.push_back(chunkStart);
+ }
+
+ // 'pop' bin ID from candidate bins set
+ candidateBins.erase(candidateBinIter);
+
+ // quit if no more candidates
+ if ( candidateBins.empty() )
+ break;
+ }
+ }
+}
+
+uint64_t BamStandardIndex::CalculateMinOffset(const BaiReferenceSummary& refSummary,
+ const uint32_t& begin)
+{
+ // if no linear offsets exist, return 0
+ if ( refSummary.NumLinearOffsets == 0 )
+ return 0;
+
+ // if 'begin' starts beyond last linear offset, use the last linear offset as minimum
+ // else use the offset corresponding to the requested start position
+ const int shiftedBegin = begin>>BamStandardIndex::BAM_LIDX_SHIFT;
+ if ( shiftedBegin >= refSummary.NumLinearOffsets )
+ return LookupLinearOffset( refSummary, refSummary.NumLinearOffsets-1 );
+ else
+ return LookupLinearOffset( refSummary, shiftedBegin );
+}
+
+void BamStandardIndex::CheckBufferSize(char*& buffer,
+ unsigned int& bufferLength,
+ const unsigned int& requestedBytes)
+{
+ try {
+ if ( requestedBytes > bufferLength ) {
+ bufferLength = requestedBytes + 10;
+ delete[] buffer;
+ buffer = new char[bufferLength];
+ }
+ } catch ( std::bad_alloc& ) {
+ stringstream s("");
+ s << "out of memory when allocating " << requestedBytes << " bytes";
+ throw BamException("BamStandardIndex::CheckBufferSize", s.str());
+ }
+}
+
+void BamStandardIndex::CheckBufferSize(unsigned char*& buffer,
+ unsigned int& bufferLength,
+ const unsigned int& requestedBytes)
+{
+ try {
+ if ( requestedBytes > bufferLength ) {
+ bufferLength = requestedBytes + 10;
+ delete[] buffer;
+ buffer = new unsigned char[bufferLength];
+ }
+ } catch ( std::bad_alloc& ) {
+ stringstream s("");
+ s << "out of memory when allocating " << requestedBytes << " bytes";
+ throw BamException("BamStandardIndex::CheckBufferSize", s.str());
+ }
+}
+
+void BamStandardIndex::CheckMagicNumber(void) {
+
+ // check 'magic number' to see if file is BAI index
+ char magic[4];
+ const int64_t numBytesRead = m_resources.Device->Read(magic, sizeof(magic));
+ if ( numBytesRead != 4 )
+ throw BamException("BamStandardIndex::CheckMagicNumber", "could not read BAI magic number");
+
+ // compare to expected value
+ if ( strncmp(magic, BamStandardIndex::BAI_MAGIC, 4) != 0 )
+ throw BamException("BamStandardIndex::CheckMagicNumber", "invalid BAI magic number");
+}
+
+void BamStandardIndex::ClearReferenceEntry(BaiReferenceEntry& refEntry) {
+ refEntry.ID = -1;
+ refEntry.Bins.clear();
+ refEntry.LinearOffsets.clear();
+}
+
+void BamStandardIndex::CloseFile(void) {
+
+ // close file stream
+ if ( IsDeviceOpen() ) {
+ m_resources.Device->Close();
+ delete m_resources.Device;
+ m_resources.Device = 0;
+ }
+
+ // clear index file summary data
+ m_indexFileSummary.clear();
+
+ // clean up I/O buffer
+ delete[] m_resources.Buffer;
+ m_resources.Buffer = 0;
+ m_bufferLength = 0;
+}
+
+// builds index from associated BAM file & writes out to index file
+bool BamStandardIndex::Create(void) {
+
+ // skip if BamReader is invalid or not open
+ if ( m_reader == 0 || !m_reader->IsOpen() ) {
+ SetErrorString("BamStandardIndex::Create", "could not create index: reader is not open");
+ return false;
+ }
+
+ // rewind BamReader
+ if ( !m_reader->Rewind() ) {
+ const string readerError = m_reader->GetErrorString();
+ const string message = "could not create index: \n\t" + readerError;
+ SetErrorString("BamStandardIndex::Create", message);
+ return false;
+ }
+
+ try {
+
+ // open new index file (read & write)
+ string indexFilename = m_reader->Filename() + Extension();
+ OpenFile(indexFilename, IBamIODevice::ReadWrite);
+
+ // initialize BaiFileSummary with number of references
+ const int& numReferences = m_reader->GetReferenceCount();
+ ReserveForSummary(numReferences);
+
+ // initialize output file
+ WriteHeader();
+
+ // set up bin, ID, offset, & coordinate markers
+ const uint32_t defaultValue = 0xffffffffu;
+ uint32_t currentBin = defaultValue;
+ uint32_t lastBin = defaultValue;
+ int32_t currentRefID = defaultValue;
+ int32_t lastRefID = defaultValue;
+ uint64_t currentOffset = (uint64_t)m_reader->Tell();
+ uint64_t lastOffset = currentOffset;
+ int32_t lastPosition = defaultValue;
+
+ // iterate through alignments in BAM file
+ BamAlignment al;
+ BaiReferenceEntry refEntry;
+ while ( m_reader->LoadNextAlignment(al) ) {
+
+ // changed to new reference
+ if ( lastRefID != al.RefID ) {
+
+ // if not first reference, save previous reference data
+ if ( lastRefID != (int32_t)defaultValue ) {
+
+ SaveAlignmentChunkToBin(refEntry.Bins, currentBin, currentOffset, lastOffset);
+ WriteReferenceEntry(refEntry);
+ ClearReferenceEntry(refEntry);
+
+ // write any empty references between (but *NOT* including) lastRefID & al.RefID
+ for ( int i = lastRefID+1; i < al.RefID; ++i ) {
+ BaiReferenceEntry emptyEntry(i);
+ WriteReferenceEntry(emptyEntry);
+ }
+
+ // update bin markers
+ currentOffset = lastOffset;
+ currentBin = al.Bin;
+ lastBin = al.Bin;
+ currentRefID = al.RefID;
+ }
+
+ // otherwise, this is first pass
+ // be sure to write any empty references up to (but *NOT* including) current RefID
+ else {
+ for ( int i = 0; i < al.RefID; ++i ) {
+ BaiReferenceEntry emptyEntry(i);
+ WriteReferenceEntry(emptyEntry);
+ }
+ }
+
+ // update reference markers
+ refEntry.ID = al.RefID;
+ lastRefID = al.RefID;
+ lastBin = defaultValue;
+ }
+
+ // if lastPosition greater than current alignment position - file not sorted properly
+ else if ( lastPosition > al.Position ) {
+ stringstream s("");
+ s << "BAM file is not properly sorted by coordinate" << endl
+ << "Current alignment position: " << al.Position
+ << " < previous alignment position: " << lastPosition
+ << " on reference ID: " << al.RefID << endl;
+ SetErrorString("BamStandardIndex::Create", s.str());
+ return false;
+ }
+
+ // if alignment's ref ID is valid & its bin is not a 'leaf'
+ if ( (al.RefID >= 0) && (al.Bin < 4681) )
+ SaveLinearOffsetEntry(refEntry.LinearOffsets, al.Position, al.GetEndPosition(), lastOffset);
+
+ // changed to new BAI bin
+ if ( al.Bin != lastBin ) {
+
+ // if not first bin on reference, save previous bin data
+ if ( currentBin != defaultValue )
+ SaveAlignmentChunkToBin(refEntry.Bins, currentBin, currentOffset, lastOffset);
+
+ // update markers
+ currentOffset = lastOffset;
+ currentBin = al.Bin;
+ lastBin = al.Bin;
+ currentRefID = al.RefID;
+
+ // if invalid RefID, break out
+ if ( currentRefID < 0 )
+ break;
+ }
+
+ // make sure that current file pointer is beyond lastOffset
+ if ( m_reader->Tell() <= (int64_t)lastOffset ) {
+ SetErrorString("BamStandardIndex::Create", "calculating offsets failed");
+ return false;
+ }
+
+ // update lastOffset & lastPosition
+ lastOffset = m_reader->Tell();
+ lastPosition = al.Position;
+ }
+
+ // after finishing alignments, if any data was read, check:
+ if ( lastOffset != currentOffset ) {
+
+ // store last alignment chunk to its bin, then write last reference entry with data
+ SaveAlignmentChunkToBin(refEntry.Bins, currentBin, currentOffset, lastOffset);
+ WriteReferenceEntry(refEntry);
+ }
+
+ // then write any empty references remaining at end of file
+ for ( int i = currentRefID+1; i < numReferences; ++i ) {
+ BaiReferenceEntry emptyEntry(i);
+ WriteReferenceEntry(emptyEntry);
+ }
+
+ } catch ( BamException& e) {
+ m_errorString = e.what();
+ return false;
+ }
+
+ // rewind BamReader
+ if ( !m_reader->Rewind() ) {
+ const string readerError = m_reader->GetErrorString();
+ const string message = "could not create index: \n\t" + readerError;
+ SetErrorString("BamStandardIndex::Create", message);
+ return false;
+ }
+
+ // return success
+ return true;
+}
+
+// returns format's file extension
+const string BamStandardIndex::Extension(void) {
+ return BamStandardIndex::BAI_EXTENSION;
+}
+
+void BamStandardIndex::GetOffset(const BamRegion& region, int64_t& offset, bool* hasAlignmentsInRegion) {
+
+ // cannot calculate offsets if unknown/invalid reference ID requested
+ if ( region.LeftRefID < 0 || region.LeftRefID >= (int)m_indexFileSummary.size() )
+ throw BamException("BamStandardIndex::GetOffset", "invalid reference ID requested");
+
+ // retrieve index summary for left bound reference
+ const BaiReferenceSummary& refSummary = m_indexFileSummary.at(region.LeftRefID);
+
+ // set up region boundaries based on actual BamReader data
+ uint32_t begin;
+ uint32_t end;
+ AdjustRegion(region, begin, end);
+
+ // retrieve all candidate bin IDs for region
+ set<uint16_t> candidateBins;
+ CalculateCandidateBins(begin, end, candidateBins);
+
+ // use reference's linear offsets to calculate the minimum offset
+ // that must be considered to find overlap
+ const uint64_t& minOffset = CalculateMinOffset(refSummary, begin);
+
+ // attempt to use reference summary, minOffset, & candidateBins to calculate offsets
+ // no data should not be error, just bail
+ vector<int64_t> offsets;
+ CalculateCandidateOffsets(refSummary, minOffset, candidateBins, offsets);
+ if ( offsets.empty() )
+ return;
+
+ // ensure that offsets are sorted before processing
+ sort( offsets.begin(), offsets.end() );
+
+ // binary search for an overlapping block (may not be first one though)
+ BamAlignment al;
+ typedef vector<int64_t>::const_iterator OffsetConstIterator;
+ OffsetConstIterator offsetFirst = offsets.begin();
+ OffsetConstIterator offsetIter = offsetFirst;
+ OffsetConstIterator offsetLast = offsets.end();
+ iterator_traits<OffsetConstIterator>::difference_type count = distance(offsetFirst, offsetLast);
+ iterator_traits<OffsetConstIterator>::difference_type step;
+ while ( count > 0 ) {
+ offsetIter = offsetFirst;
+ step = count/2;
+ advance(offsetIter, step);
+
+ // attempt seek to candidate offset
+ const int64_t& candidateOffset = (*offsetIter);
+ if ( !m_reader->Seek(candidateOffset) ) {
+ const string readerError = m_reader->GetErrorString();
+ const string message = "could not seek in BAM file: \n\t" + readerError;
+ throw BamException("BamToolsIndex::GetOffset", message);
+ }
+
+ // load first available alignment, setting flag to true if data exists
+ *hasAlignmentsInRegion = m_reader->LoadNextAlignment(al);
+
+ // check alignment against region
+ if ( al.GetEndPosition() <= region.LeftPosition ) {
+ offsetFirst = ++offsetIter;
+ count -= step+1;
+ } else count = step;
+ }
+
+ // step back to the offset before the 'current offset' (to make sure we cover overlaps)
+ if ( offsetIter != offsets.begin() )
+ --offsetIter;
+ offset = (*offsetIter);
+}
+
+// returns whether reference has alignments or no
+bool BamStandardIndex::HasAlignments(const int& referenceID) const {
+ if ( referenceID < 0 || referenceID >= (int)m_indexFileSummary.size() )
+ return false;
+ const BaiReferenceSummary& refSummary = m_indexFileSummary.at(referenceID);
+ return ( refSummary.NumBins > 0 );
+}
+
+bool BamStandardIndex::IsDeviceOpen(void) const {
+ if ( m_resources.Device == 0 )
+ return false;
+ return m_resources.Device->IsOpen();
+}
+
+// attempts to use index data to jump to @region, returns success/fail
+// a "successful" jump indicates no error, but not whether this region has data
+// * thus, the method sets a flag to indicate whether there are alignments
+// available after the jump position
+bool BamStandardIndex::Jump(const BamRegion& region, bool* hasAlignmentsInRegion) {
+
+ // clear out flag
+ *hasAlignmentsInRegion = false;
+
+ // skip if invalid reader or not open
+ if ( m_reader == 0 || !m_reader->IsOpen() ) {
+ SetErrorString("BamStandardIndex::Jump", "could not jump: reader is not open");
+ return false;
+ }
+
+ // calculate nearest offset to jump to
+ int64_t offset;
+ try {
+ GetOffset(region, offset, hasAlignmentsInRegion);
+ } catch ( BamException& e ) {
+ m_errorString = e.what();
+ return false;
+ }
+
+ // if region has alignments, return success/fail of seeking there
+ if ( *hasAlignmentsInRegion )
+ return m_reader->Seek(offset);
+
+ // otherwise, simply return true (but hasAlignmentsInRegion flag has been set to false)
+ // (this is OK, BamReader will check this flag before trying to load data)
+ return true;
+}
+
+// loads existing data from file into memory
+bool BamStandardIndex::Load(const std::string& filename) {
+
+ try {
+
+ // attempt to open file (read-only)
+ OpenFile(filename, IBamIODevice::ReadOnly);
+
+ // validate format
+ CheckMagicNumber();
+
+ // load in-memory summary of index data
+ SummarizeIndexFile();
+
+ // return success
+ return true;
+
+ } catch ( BamException& e ) {
+ m_errorString = e.what();
+ return false;
+ }
+}
+
+uint64_t BamStandardIndex::LookupLinearOffset(const BaiReferenceSummary& refSummary, const int& index) {
+
+ // attempt seek to proper index file position
+ const int64_t linearOffsetFilePosition = (int64_t)refSummary.FirstLinearOffsetFilePosition +
+ index*BamStandardIndex::SIZEOF_LINEAROFFSET;
+ Seek(linearOffsetFilePosition, SEEK_SET);
+
+ // read linear offset from BAI file
+ uint64_t linearOffset;
+ ReadLinearOffset(linearOffset);
+ return linearOffset;
+}
+
+void BamStandardIndex::MergeAlignmentChunks(BaiAlignmentChunkVector& chunks) {
+
+ // skip if chunks are empty, nothing to merge
+ if ( chunks.empty() )
+ return;
+
+ // set up merged alignment chunk container
+ BaiAlignmentChunkVector mergedChunks;
+ mergedChunks.push_back( chunks[0] );
+
+ // iterate over chunks
+ int i = 0;
+ BaiAlignmentChunkVector::iterator chunkIter = chunks.begin();
+ BaiAlignmentChunkVector::iterator chunkEnd = chunks.end();
+ for ( ++chunkIter; chunkIter != chunkEnd; ++chunkIter) {
+
+ // get 'currentMergeChunk' based on numeric index
+ BaiAlignmentChunk& currentMergeChunk = mergedChunks[i];
+
+ // get sourceChunk based on source vector iterator
+ BaiAlignmentChunk& sourceChunk = (*chunkIter);
+
+ // if currentMergeChunk ends where sourceChunk starts, then merge the two
+ if ( currentMergeChunk.Stop>>16 == sourceChunk.Start>>16 )
+ currentMergeChunk.Stop = sourceChunk.Stop;
+
+ // otherwise
+ else {
+ // append sourceChunk after currentMergeChunk
+ mergedChunks.push_back(sourceChunk);
+
+ // update i, so the next iteration will consider the
+ // recently-appended sourceChunk as new mergeChunk candidate
+ ++i;
+ }
+ }
+
+ // saved newly-merged chunks into (parameter) chunks
+ chunks = mergedChunks;
+}
+
+void BamStandardIndex::OpenFile(const std::string& filename, IBamIODevice::OpenMode mode) {
+
+ // make sure any previous index file is closed
+ CloseFile();
+
+ m_resources.Device = BamDeviceFactory::CreateDevice(filename);
+ if ( m_resources.Device == 0 ) {
+ const string message = string("could not open file: ") + filename;
+ throw BamException("BamStandardIndex::OpenFile", message);
+ }
+
+ // attempt to open file
+ m_resources.Device->Open(mode);
+ if ( !IsDeviceOpen() ) {
+ const string message = string("could not open file: ") + filename;
+ throw BamException("BamStandardIndex::OpenFile", message);
+ }
+}
+
+void BamStandardIndex::ReadBinID(uint32_t& binId) {
+ const int64_t numBytesRead = m_resources.Device->Read((char*)&binId, sizeof(binId));
+ if ( m_isBigEndian ) SwapEndian_32(binId);
+ if ( numBytesRead != sizeof(binId) )
+ throw BamException("BamStandardIndex::ReadBinID", "could not read BAI bin ID");
+}
+
+void BamStandardIndex::ReadBinIntoBuffer(uint32_t& binId, int32_t& numAlignmentChunks) {
+
+ // read bin header
+ ReadBinID(binId);
+ ReadNumAlignmentChunks(numAlignmentChunks);
+
+ // read bin contents
+ const unsigned int bytesRequested = numAlignmentChunks*BamStandardIndex::SIZEOF_ALIGNMENTCHUNK;
+ ReadIntoBuffer(bytesRequested);
+}
+
+void BamStandardIndex::ReadIntoBuffer(const unsigned int& bytesRequested) {
+
+ // ensure that our buffer is big enough for request
+ BamStandardIndex::CheckBufferSize(m_resources.Buffer, m_bufferLength, bytesRequested);
+
+ // read from BAI file stream
+ const int64_t bytesRead = m_resources.Device->Read(m_resources.Buffer, bytesRequested);
+ if ( bytesRead != (int64_t)bytesRequested ) {
+ stringstream s("");
+ s << "expected to read: " << bytesRequested << " bytes, "
+ << "but instead read: " << bytesRead;
+ throw BamException("BamStandardIndex::ReadIntoBuffer", s.str());
+ }
+}
+
+void BamStandardIndex::ReadLinearOffset(uint64_t& linearOffset) {
+ const int64_t numBytesRead = m_resources.Device->Read((char*)&linearOffset, sizeof(linearOffset));
+ if ( m_isBigEndian ) SwapEndian_64(linearOffset);
+ if ( numBytesRead != sizeof(linearOffset) )
+ throw BamException("BamStandardIndex::ReadLinearOffset", "could not read BAI linear offset");
+}
+
+void BamStandardIndex::ReadNumAlignmentChunks(int& numAlignmentChunks) {
+ const int64_t numBytesRead = m_resources.Device->Read((char*)&numAlignmentChunks, sizeof(numAlignmentChunks));
+ if ( m_isBigEndian ) SwapEndian_32(numAlignmentChunks);
+ if ( numBytesRead != sizeof(numAlignmentChunks) )
+ throw BamException("BamStandardIndex::ReadNumAlignmentChunks", "could not read BAI chunk count");
+}
+
+void BamStandardIndex::ReadNumBins(int& numBins) {
+ const int64_t numBytesRead = m_resources.Device->Read((char*)&numBins, sizeof(numBins));
+ if ( m_isBigEndian ) SwapEndian_32(numBins);
+ if ( numBytesRead != sizeof(numBins) )
+ throw BamException("BamStandardIndex::ReadNumBins", "could not read BAI bin count");
+}
+
+void BamStandardIndex::ReadNumLinearOffsets(int& numLinearOffsets) {
+ const int64_t numBytesRead = m_resources.Device->Read((char*)&numLinearOffsets, sizeof(numLinearOffsets));
+ if ( m_isBigEndian ) SwapEndian_32(numLinearOffsets);
+ if ( numBytesRead != sizeof(numLinearOffsets) )
+ throw BamException("BamStandardIndex::ReadNumAlignmentChunks", "could not read BAI linear offset count");
+}
+
+void BamStandardIndex::ReadNumReferences(int& numReferences) {
+ const int64_t numBytesRead = m_resources.Device->Read((char*)&numReferences, sizeof(numReferences));
+ if ( m_isBigEndian ) SwapEndian_32(numReferences);
+ if ( numBytesRead != sizeof(numReferences) )
+ throw BamException("BamStandardIndex::ReadNumReferences", "could not read reference count");
+}
+
+void BamStandardIndex::ReserveForSummary(const int& numReferences) {
+ m_indexFileSummary.clear();
+ m_indexFileSummary.assign( numReferences, BaiReferenceSummary() );
+}
+
+void BamStandardIndex::SaveAlignmentChunkToBin(BaiBinMap& binMap,
+ const uint32_t& currentBin,
+ const uint64_t& currentOffset,
+ const uint64_t& lastOffset)
+{
+ // create new alignment chunk
+ BaiAlignmentChunk newChunk(currentOffset, lastOffset);
+
+ // if no entry exists yet for this bin, create one and store alignment chunk
+ BaiBinMap::iterator binIter = binMap.find(currentBin);
+ if ( binIter == binMap.end() ) {
+ BaiAlignmentChunkVector newChunks;
+ newChunks.push_back(newChunk);
+ binMap.insert( pair<uint32_t, BaiAlignmentChunkVector>(currentBin, newChunks));
+ }
+
+ // otherwise, just append alignment chunk
+ else {
+ BaiAlignmentChunkVector& binChunks = (*binIter).second;
+ binChunks.push_back( newChunk );
+ }
+}
+
+void BamStandardIndex::SaveBinsSummary(const int& refId, const int& numBins) {
+ BaiReferenceSummary& refSummary = m_indexFileSummary.at(refId);
+ refSummary.NumBins = numBins;
+ refSummary.FirstBinFilePosition = Tell();
+}
+
+void BamStandardIndex::SaveLinearOffsetEntry(BaiLinearOffsetVector& offsets,
+ const int& alignmentStartPosition,
+ const int& alignmentStopPosition,
+ const uint64_t& lastOffset)
+{
+ // get converted offsets
+ const int beginOffset = alignmentStartPosition >> BamStandardIndex::BAM_LIDX_SHIFT;
+ const int endOffset = (alignmentStopPosition - 1) >> BamStandardIndex::BAM_LIDX_SHIFT;
+
+ // resize vector if necessary
+ int oldSize = offsets.size();
+ int newSize = endOffset + 1;
+ if ( oldSize < newSize )
+ offsets.resize(newSize, 0);
+
+ // store offset
+ for( int i = beginOffset + 1; i <= endOffset; ++i ) {
+ if ( offsets[i] == 0 )
+ offsets[i] = lastOffset;
+ }
+}
+
+void BamStandardIndex::SaveLinearOffsetsSummary(const int& refId, const int& numLinearOffsets) {
+ BaiReferenceSummary& refSummary = m_indexFileSummary.at(refId);
+ refSummary.NumLinearOffsets = numLinearOffsets;
+ refSummary.FirstLinearOffsetFilePosition = Tell();
+}
+
+// seek to position in index file stream
+void BamStandardIndex::Seek(const int64_t& position, const int origin) {
+ if ( !m_resources.Device->Seek(position, origin) )
+ throw BamException("BamStandardIndex::Seek", "could not seek in BAI file");
+}
+
+void BamStandardIndex::SkipBins(const int& numBins) {
+ uint32_t binId;
+ int32_t numAlignmentChunks;
+ for (int i = 0; i < numBins; ++i)
+ ReadBinIntoBuffer(binId, numAlignmentChunks); // results & buffer ignored
+}
+
+void BamStandardIndex::SkipLinearOffsets(const int& numLinearOffsets) {
+ const unsigned int bytesRequested = numLinearOffsets*BamStandardIndex::SIZEOF_LINEAROFFSET;
+ ReadIntoBuffer(bytesRequested);
+}
+
+void BamStandardIndex::SortLinearOffsets(BaiLinearOffsetVector& linearOffsets) {
+ sort( linearOffsets.begin(), linearOffsets.end() );
+}
+
+void BamStandardIndex::SummarizeBins(BaiReferenceSummary& refSummary) {
+
+ // load number of bins
+ int numBins;
+ ReadNumBins(numBins);
+
+ // store bins summary for this reference
+ refSummary.NumBins = numBins;
+ refSummary.FirstBinFilePosition = Tell();
+
+ // skip this reference's bins
+ SkipBins(numBins);
+}
+
+void BamStandardIndex::SummarizeIndexFile(void) {
+
+ // load number of reference sequences
+ int numReferences;
+ ReadNumReferences(numReferences);
+
+ // initialize file summary data
+ ReserveForSummary(numReferences);
+
+ // iterate over reference entries
+ BaiFileSummary::iterator summaryIter = m_indexFileSummary.begin();
+ BaiFileSummary::iterator summaryEnd = m_indexFileSummary.end();
+ for ( int i = 0; summaryIter != summaryEnd; ++summaryIter, ++i )
+ SummarizeReference(*summaryIter);
+}
+
+void BamStandardIndex::SummarizeLinearOffsets(BaiReferenceSummary& refSummary) {
+
+ // load number of linear offsets
+ int numLinearOffsets;
+ ReadNumLinearOffsets(numLinearOffsets);
+
+ // store bin summary data for this reference
+ refSummary.NumLinearOffsets = numLinearOffsets;
+ refSummary.FirstLinearOffsetFilePosition = Tell();
+
+ // skip linear offsets in index file
+ SkipLinearOffsets(numLinearOffsets);
+}
+
+void BamStandardIndex::SummarizeReference(BaiReferenceSummary& refSummary) {
+ SummarizeBins(refSummary);
+ SummarizeLinearOffsets(refSummary);
+}
+
+// return position of file pointer in index file stream
+int64_t BamStandardIndex::Tell(void) const {
+ return m_resources.Device->Tell();
+}
+
+void BamStandardIndex::WriteAlignmentChunk(const BaiAlignmentChunk& chunk) {
+
+ // localize alignment chunk offsets
+ uint64_t start = chunk.Start;
+ uint64_t stop = chunk.Stop;
+
+ // swap endian-ness if necessary
+ if ( m_isBigEndian ) {
+ SwapEndian_64(start);
+ SwapEndian_64(stop);
+ }
+
+ // write to index file
+ int64_t numBytesWritten = 0;
+ numBytesWritten += m_resources.Device->Write((const char*)&start, sizeof(start));
+ numBytesWritten += m_resources.Device->Write((const char*)&stop, sizeof(stop));
+ if ( numBytesWritten != (sizeof(start)+sizeof(stop)) )
+ throw BamException("BamStandardIndex::WriteAlignmentChunk", "could not write BAI alignment chunk");
+}
+
+void BamStandardIndex::WriteAlignmentChunks(BaiAlignmentChunkVector& chunks) {
+
+ // make sure chunks are merged (simplified) before writing & saving summary
+ MergeAlignmentChunks(chunks);
+
+ // write chunks
+ int32_t chunkCount = chunks.size();
+ if ( m_isBigEndian ) SwapEndian_32(chunkCount);
+ const int64_t numBytesWritten = m_resources.Device->Write((const char*)&chunkCount, sizeof(chunkCount));
+ if ( numBytesWritten != sizeof(chunkCount) )
+ throw BamException("BamStandardIndex::WriteAlignmentChunks", "could not write BAI chunk count");
+
+ // iterate over chunks
+ BaiAlignmentChunkVector::const_iterator chunkIter = chunks.begin();
+ BaiAlignmentChunkVector::const_iterator chunkEnd = chunks.end();
+ for ( ; chunkIter != chunkEnd; ++chunkIter )
+ WriteAlignmentChunk( (*chunkIter) );
+}
+
+void BamStandardIndex::WriteBin(const uint32_t& binId, BaiAlignmentChunkVector& chunks) {
+
+ // write BAM bin ID
+ uint32_t binKey = binId;
+ if ( m_isBigEndian ) SwapEndian_32(binKey);
+ const int64_t numBytesWritten = m_resources.Device->Write((const char*)&binKey, sizeof(binKey));
+ if ( numBytesWritten != sizeof(binKey) )
+ throw BamException("BamStandardIndex::WriteBin", "could not write bin ID");
+
+ // write bin's alignment chunks
+ WriteAlignmentChunks(chunks);
+}
+
+void BamStandardIndex::WriteBins(const int& refId, BaiBinMap& bins) {
+
+ // write number of bins
+ int32_t binCount = bins.size();
+ if ( m_isBigEndian ) SwapEndian_32(binCount);
+ const int64_t numBytesWritten = m_resources.Device->Write((const char*)&binCount, sizeof(binCount));
+ if ( numBytesWritten != sizeof(binCount) )
+ throw BamException("BamStandardIndex::WriteBins", "could not write bin count");
+
+ // save summary for reference's bins
+ SaveBinsSummary(refId, bins.size());
+
+ // iterate over bins
+ BaiBinMap::iterator binIter = bins.begin();
+ BaiBinMap::iterator binEnd = bins.end();
+ for ( ; binIter != binEnd; ++binIter )
+ WriteBin( (*binIter).first, (*binIter).second );
+}
+
+void BamStandardIndex::WriteHeader(void) {
+
+ int64_t numBytesWritten = 0;
+
+ // write magic number
+ numBytesWritten += m_resources.Device->Write(BamStandardIndex::BAI_MAGIC, 4);
+
+ // write number of reference sequences
+ int32_t numReferences = m_indexFileSummary.size();
+ if ( m_isBigEndian ) SwapEndian_32(numReferences);
+ numBytesWritten += m_resources.Device->Write((const char*)&numReferences, sizeof(numReferences));
+
+ if ( numBytesWritten != sizeof(numReferences)+4 )
+ throw BamException("BamStandardIndex::WriteHeader", "could not write BAI header");
+}
+
+void BamStandardIndex::WriteLinearOffsets(const int& refId, BaiLinearOffsetVector& linearOffsets) {
+
+ // make sure linear offsets are sorted before writing & saving summary
+ SortLinearOffsets(linearOffsets);
+
+ int64_t numBytesWritten = 0;
+
+ // write number of linear offsets
+ int32_t offsetCount = linearOffsets.size();
+ if ( m_isBigEndian ) SwapEndian_32(offsetCount);
+ numBytesWritten += m_resources.Device->Write((const char*)&offsetCount, sizeof(offsetCount));
+
+ // save summary for reference's linear offsets
+ SaveLinearOffsetsSummary(refId, linearOffsets.size());
+
+ // iterate over linear offsets
+ BaiLinearOffsetVector::const_iterator offsetIter = linearOffsets.begin();
+ BaiLinearOffsetVector::const_iterator offsetEnd = linearOffsets.end();
+ for ( ; offsetIter != offsetEnd; ++offsetIter ) {
+
+ // write linear offset
+ uint64_t linearOffset = (*offsetIter);
+ if ( m_isBigEndian ) SwapEndian_64(linearOffset);
+ numBytesWritten += m_resources.Device->Write((const char*)&linearOffset, sizeof(linearOffset));
+ }
+
+ if ( numBytesWritten != (sizeof(offsetCount) + linearOffsets.size()*sizeof(uint64_t)) )
+ throw BamException("BamStandardIndex::WriteLinearOffsets", "could not write BAI linear offsets");
+}
+
+void BamStandardIndex::WriteReferenceEntry(BaiReferenceEntry& refEntry) {
+ WriteBins(refEntry.ID, refEntry.Bins);
+ WriteLinearOffsets(refEntry.ID, refEntry.LinearOffsets);
+}
diff --git a/bamtools/src/api/internal/index/BamStandardIndex_p.h b/bamtools/src/api/internal/index/BamStandardIndex_p.h
new file mode 100644
index 0000000..273d56e
--- /dev/null
+++ b/bamtools/src/api/internal/index/BamStandardIndex_p.h
@@ -0,0 +1,237 @@
+// ***************************************************************************
+// BamStandardIndex.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides index operations for the standardized BAM index format (".bai")
+// ***************************************************************************
+
+#ifndef BAM_STANDARD_INDEX_FORMAT_H
+#define BAM_STANDARD_INDEX_FORMAT_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/BamAux.h"
+#include "api/BamIndex.h"
+#include "api/IBamIODevice.h"
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+namespace BamTools {
+namespace Internal {
+
+// -----------------------------------------------------------------------------
+// BamStandardIndex data structures
+
+// defines start and end of a contiguous run of alignments
+struct BaiAlignmentChunk {
+
+ // data members
+ uint64_t Start;
+ uint64_t Stop;
+
+ // constructor
+ BaiAlignmentChunk(const uint64_t& start = 0,
+ const uint64_t& stop = 0)
+ : Start(start)
+ , Stop(stop)
+ { }
+};
+
+// comparison operator (for sorting)
+inline
+bool operator<(const BaiAlignmentChunk& lhs, const BaiAlignmentChunk& rhs) {
+ return lhs.Start < rhs.Start;
+}
+
+// convenience typedef for a list of all alignment 'chunks' in a BAI bin
+typedef std::vector<BaiAlignmentChunk> BaiAlignmentChunkVector;
+
+// convenience typedef for a map of all BAI bins in a reference (ID => chunks)
+typedef std::map<uint32_t, BaiAlignmentChunkVector> BaiBinMap;
+
+// convenience typedef for a list of all 'linear offsets' in a reference
+typedef std::vector<uint64_t> BaiLinearOffsetVector;
+
+// contains all fields necessary for building, loading, & writing
+// full BAI index data for a single reference
+struct BaiReferenceEntry {
+
+ // data members
+ int32_t ID;
+ BaiBinMap Bins;
+ BaiLinearOffsetVector LinearOffsets;
+
+ // ctor
+ BaiReferenceEntry(const int32_t& id = -1)
+ : ID(id)
+ { }
+};
+
+// provides (persistent) summary of BaiReferenceEntry's index data
+struct BaiReferenceSummary {
+
+ // data members
+ int NumBins;
+ int NumLinearOffsets;
+ uint64_t FirstBinFilePosition;
+ uint64_t FirstLinearOffsetFilePosition;
+
+ // ctor
+ BaiReferenceSummary(void)
+ : NumBins(0)
+ , NumLinearOffsets(0)
+ , FirstBinFilePosition(0)
+ , FirstLinearOffsetFilePosition(0)
+ { }
+};
+
+// convenience typedef for describing a full BAI index file summary
+typedef std::vector<BaiReferenceSummary> BaiFileSummary;
+
+// end BamStandardIndex data structures
+// -----------------------------------------------------------------------------
+
+class BamStandardIndex : public BamIndex {
+
+ // ctor & dtor
+ public:
+ BamStandardIndex(Internal::BamReaderPrivate* reader);
+ ~BamStandardIndex(void);
+
+ // BamIndex implementation
+ public:
+ // builds index from associated BAM file & writes out to index file
+ bool Create(void);
+ // returns whether reference has alignments or no
+ bool HasAlignments(const int& referenceID) const;
+ // attempts to use index data to jump to @region, returns success/fail
+ // a "successful" jump indicates no error, but not whether this region has data
+ // * thus, the method sets a flag to indicate whether there are alignments
+ // available after the jump position
+ bool Jump(const BamTools::BamRegion& region, bool* hasAlignmentsInRegion);
+ // loads existing data from file into memory
+ bool Load(const std::string& filename);
+ BamIndex::IndexType Type(void) const { return BamIndex::STANDARD; }
+ public:
+ // returns format's file extension
+ static const std::string Extension(void);
+
+ // internal methods
+ private:
+
+ // index file ops
+ void CheckMagicNumber(void);
+ void CloseFile(void);
+ bool IsDeviceOpen(void) const;
+ void OpenFile(const std::string& filename, IBamIODevice::OpenMode mode);
+ void Seek(const int64_t& position, const int origin);
+ int64_t Tell(void) const;
+
+ // BAI index building methods
+ void ClearReferenceEntry(BaiReferenceEntry& refEntry);
+ void SaveAlignmentChunkToBin(BaiBinMap& binMap,
+ const uint32_t& currentBin,
+ const uint64_t& currentOffset,
+ const uint64_t& lastOffset);
+ void SaveLinearOffsetEntry(BaiLinearOffsetVector& offsets,
+ const int& alignmentStartPosition,
+ const int& alignmentStopPosition,
+ const uint64_t& lastOffset);
+
+ // random-access methods
+ void AdjustRegion(const BamRegion& region, uint32_t& begin, uint32_t& end);
+ void CalculateCandidateBins(const uint32_t& begin,
+ const uint32_t& end,
+ std::set<uint16_t>& candidateBins);
+ void CalculateCandidateOffsets(const BaiReferenceSummary& refSummary,
+ const uint64_t& minOffset,
+ std::set<uint16_t>& candidateBins,
+ std::vector<int64_t>& offsets);
+ uint64_t CalculateMinOffset(const BaiReferenceSummary& refSummary, const uint32_t& begin);
+ void GetOffset(const BamRegion& region, int64_t& offset, bool* hasAlignmentsInRegion);
+ uint64_t LookupLinearOffset(const BaiReferenceSummary& refSummary, const int& index);
+
+ // BAI summary (create/load) methods
+ void ReserveForSummary(const int& numReferences);
+ void SaveBinsSummary(const int& refId, const int& numBins);
+ void SaveLinearOffsetsSummary(const int& refId, const int& numLinearOffsets);
+ void SkipBins(const int& numBins);
+ void SkipLinearOffsets(const int& numLinearOffsets);
+ void SummarizeBins(BaiReferenceSummary& refSummary);
+ void SummarizeIndexFile(void);
+ void SummarizeLinearOffsets(BaiReferenceSummary& refSummary);
+ void SummarizeReference(BaiReferenceSummary& refSummary);
+
+ // BAI full index input methods
+ void ReadBinID(uint32_t& binId);
+ void ReadBinIntoBuffer(uint32_t& binId, int32_t& numAlignmentChunks);
+ void ReadIntoBuffer(const unsigned int& bytesRequested);
+ void ReadLinearOffset(uint64_t& linearOffset);
+ void ReadNumAlignmentChunks(int& numAlignmentChunks);
+ void ReadNumBins(int& numBins);
+ void ReadNumLinearOffsets(int& numLinearOffsets);
+ void ReadNumReferences(int& numReferences);
+
+ // BAI full index output methods
+ void MergeAlignmentChunks(BaiAlignmentChunkVector& chunks);
+ void SortLinearOffsets(BaiLinearOffsetVector& linearOffsets);
+ void WriteAlignmentChunk(const BaiAlignmentChunk& chunk);
+ void WriteAlignmentChunks(BaiAlignmentChunkVector& chunks);
+ void WriteBin(const uint32_t& binId, BaiAlignmentChunkVector& chunks);
+ void WriteBins(const int& refId, BaiBinMap& bins);
+ void WriteHeader(void);
+ void WriteLinearOffsets(const int& refId, BaiLinearOffsetVector& linearOffsets);
+ void WriteReferenceEntry(BaiReferenceEntry& refEntry);
+
+ // data members
+ private:
+ bool m_isBigEndian;
+ BaiFileSummary m_indexFileSummary;
+
+ // our input buffer
+ unsigned int m_bufferLength;
+ struct RaiiWrapper {
+ IBamIODevice* Device;
+ char* Buffer;
+ RaiiWrapper(void);
+ ~RaiiWrapper(void);
+ };
+ RaiiWrapper m_resources;
+
+ // static methods
+ private:
+ // checks if the buffer is large enough to accomodate the requested size
+ static void CheckBufferSize(char*& buffer,
+ unsigned int& bufferLength,
+ const unsigned int& requestedBytes);
+ // checks if the buffer is large enough to accomodate the requested size
+ static void CheckBufferSize(unsigned char*& buffer,
+ unsigned int& bufferLength,
+ const unsigned int& requestedBytes);
+ // static constants
+ private:
+ static const int MAX_BIN;
+ static const int BAM_LIDX_SHIFT;
+ static const std::string BAI_EXTENSION;
+ static const char* const BAI_MAGIC;
+ static const int SIZEOF_ALIGNMENTCHUNK;
+ static const int SIZEOF_BINCORE;
+ static const int SIZEOF_LINEAROFFSET;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAM_STANDARD_INDEX_FORMAT_H
diff --git a/bamtools/src/api/internal/index/BamToolsIndex_p.cpp b/bamtools/src/api/internal/index/BamToolsIndex_p.cpp
new file mode 100644
index 0000000..bb09bc9
--- /dev/null
+++ b/bamtools/src/api/internal/index/BamToolsIndex_p.cpp
@@ -0,0 +1,642 @@
+// ***************************************************************************
+// BamToolsIndex.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides index operations for the BamTools index format (".bti")
+// ***************************************************************************
+
+#include "api/BamAlignment.h"
+#include "api/internal/bam/BamReader_p.h"
+#include "api/internal/index/BamToolsIndex_p.h"
+#include "api/internal/io/BamDeviceFactory_p.h"
+#include "api/internal/io/BgzfStream_p.h"
+#include "api/internal/utils/BamException_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <map>
+using namespace std;
+
+// --------------------------------
+// static BamToolsIndex constants
+// --------------------------------
+
+const uint32_t BamToolsIndex::DEFAULT_BLOCK_LENGTH = 1000;
+const string BamToolsIndex::BTI_EXTENSION = ".bti";
+const char* const BamToolsIndex::BTI_MAGIC = "BTI\1";
+const int BamToolsIndex::SIZEOF_BLOCK = sizeof(int32_t)*2 + sizeof(int64_t);
+
+// ----------------------------
+// RaiiWrapper implementation
+// ----------------------------
+
+BamToolsIndex::RaiiWrapper::RaiiWrapper(void)
+ : Device(0)
+{ }
+
+BamToolsIndex::RaiiWrapper::~RaiiWrapper(void) {
+ if ( Device ) {
+ Device->Close();
+ delete Device;
+ Device = 0;
+ }
+}
+
+// ------------------------------
+// BamToolsIndex implementation
+// ------------------------------
+
+// ctor
+BamToolsIndex::BamToolsIndex(Internal::BamReaderPrivate* reader)
+ : BamIndex(reader)
+ , m_blockSize(BamToolsIndex::DEFAULT_BLOCK_LENGTH)
+ , m_inputVersion(0)
+ , m_outputVersion(BTI_2_0) // latest version - used for writing new index files
+{
+ m_isBigEndian = BamTools::SystemIsBigEndian();
+}
+
+// dtor
+BamToolsIndex::~BamToolsIndex(void) {
+ CloseFile();
+}
+
+void BamToolsIndex::CheckMagicNumber(void) {
+
+ // read magic number
+ char magic[4];
+ const int64_t numBytesRead = m_resources.Device->Read(magic, 4);
+ if ( numBytesRead != 4 )
+ throw BamException("BamToolsIndex::CheckMagicNumber", "could not read BTI magic number");
+
+ // validate expected magic number
+ if ( strncmp(magic, BamToolsIndex::BTI_MAGIC, 4) != 0 )
+ throw BamException("BamToolsIndex::CheckMagicNumber", "invalid BTI magic number");
+}
+
+// check index file version, return true if OK
+void BamToolsIndex::CheckVersion(void) {
+
+ // read version from file
+ const int64_t numBytesRead = m_resources.Device->Read((char*)&m_inputVersion, sizeof(m_inputVersion));
+ if ( numBytesRead != sizeof(m_inputVersion) )
+ throw BamException("BamToolsIndex::CheckVersion", "could not read format version");
+ if ( m_isBigEndian ) SwapEndian_32(m_inputVersion);
+
+ // if version is negative, or zero
+ if ( m_inputVersion <= 0 )
+ throw BamException("BamToolsIndex::CheckVersion", "invalid format version");
+
+ // if version is newer than can be supported by this version of bamtools
+ else if ( m_inputVersion > m_outputVersion ) {
+ const string message = "unsupported format: this index was created by a newer version of BamTools. "
+ "Update your local version of BamTools to use the index file.";
+ throw BamException("BamToolsIndex::CheckVersion", message);
+ }
+
+ // ------------------------------------------------------------------
+ // check for deprecated, unsupported versions
+ // (the format had to be modified to accomodate a particular bug fix)
+
+ // Version 2.0: introduced support for half-open intervals, instead of the old closed intervals
+ // respondBy: throwing exception - we're not going to try to handle the old BTI files.
+ else if ( (Version)m_inputVersion < BamToolsIndex::BTI_2_0 ) {
+ const string message = "unsupported format: this version of the index may not properly handle "
+ "coordinate intervals. Please run 'bamtools index -bti -in yourData.bam' "
+ "to generate an up-to-date, fixed BTI file.";
+ throw BamException("BamToolsIndex::CheckVersion", message);
+ }
+}
+
+void BamToolsIndex::ClearReferenceEntry(BtiReferenceEntry& refEntry) {
+ refEntry.ID = -1;
+ refEntry.Blocks.clear();
+}
+
+void BamToolsIndex::CloseFile(void) {
+ if ( IsDeviceOpen() ) {
+ m_resources.Device->Close();
+ delete m_resources.Device;
+ m_resources.Device = 0;
+ }
+ m_indexFileSummary.clear();
+}
+
+// builds index from associated BAM file & writes out to index file
+bool BamToolsIndex::Create(void) {
+
+ // skip if BamReader is invalid or not open
+ if ( m_reader == 0 || !m_reader->IsOpen() ) {
+ SetErrorString("BamToolsIndex::Create", "could not create index: reader is not open");
+ return false;
+ }
+
+ // rewind BamReader
+ if ( !m_reader->Rewind() ) {
+ const string readerError = m_reader->GetErrorString();
+ const string message = "could not create index: \n\t" + readerError;
+ SetErrorString("BamToolsIndex::Create", message);
+ return false;
+ }
+
+ try {
+ // open new index file (read & write)
+ const string indexFilename = m_reader->Filename() + Extension();
+ OpenFile(indexFilename, IBamIODevice::ReadWrite);
+
+ // initialize BtiFileSummary with number of references
+ const int& numReferences = m_reader->GetReferenceCount();
+ InitializeFileSummary(numReferences);
+
+ // intialize output file header
+ WriteHeader();
+
+ // index building markers
+ uint32_t currentBlockCount = 0;
+ int64_t currentAlignmentOffset = m_reader->Tell();
+ int32_t blockRefId = -1;
+ int32_t blockMaxEndPosition = -1;
+ int64_t blockStartOffset = currentAlignmentOffset;
+ int32_t blockStartPosition = -1;
+
+ // plow through alignments, storing index entries
+ BamAlignment al;
+ BtiReferenceEntry refEntry;
+ while ( m_reader->LoadNextAlignment(al) ) {
+
+ // if moved to new reference
+ if ( al.RefID != blockRefId ) {
+
+ // if first pass, check:
+ if ( currentBlockCount == 0 ) {
+
+ // write any empty references up to (but not including) al.RefID
+ for ( int i = 0; i < al.RefID; ++i )
+ WriteReferenceEntry( BtiReferenceEntry(i) );
+ }
+
+ // not first pass:
+ else {
+
+ // store previous BTI block data in reference entry
+ const BtiBlock block(blockMaxEndPosition, blockStartOffset, blockStartPosition);
+ refEntry.Blocks.push_back(block);
+
+ // write reference entry, then clear
+ WriteReferenceEntry(refEntry);
+ ClearReferenceEntry(refEntry);
+
+ // write any empty references between (but not including)
+ // the last blockRefID and current al.RefID
+ for ( int i = blockRefId+1; i < al.RefID; ++i )
+ WriteReferenceEntry( BtiReferenceEntry(i) );
+
+ // reset block count
+ currentBlockCount = 0;
+ }
+
+ // set ID for new reference entry
+ refEntry.ID = al.RefID;
+ }
+
+ // if beginning of block, update counters
+ if ( currentBlockCount == 0 ) {
+ blockRefId = al.RefID;
+ blockStartOffset = currentAlignmentOffset;
+ blockStartPosition = al.Position;
+ blockMaxEndPosition = al.GetEndPosition();
+ }
+
+ // increment block counter
+ ++currentBlockCount;
+
+ // check end position
+ const int32_t alignmentEndPosition = al.GetEndPosition();
+ if ( alignmentEndPosition > blockMaxEndPosition )
+ blockMaxEndPosition = alignmentEndPosition;
+
+ // if block is full, get offset for next block, reset currentBlockCount
+ if ( currentBlockCount == m_blockSize ) {
+
+ // store previous block data in reference entry
+ const BtiBlock block(blockMaxEndPosition, blockStartOffset, blockStartPosition);
+ refEntry.Blocks.push_back(block);
+
+ // update markers
+ blockStartOffset = m_reader->Tell();
+ currentBlockCount = 0;
+ }
+
+ // not the best name, but for the next iteration, this value will be the offset of the
+ // *current* alignment. this is necessary because we won't know if this next alignment
+ // is on a new reference until we actually read it
+ currentAlignmentOffset = m_reader->Tell();
+ }
+
+ // after finishing alignments, if any data was read, check:
+ if ( blockRefId >= 0 ) {
+
+ // store last BTI block data in reference entry
+ const BtiBlock block(blockMaxEndPosition, blockStartOffset, blockStartPosition);
+ refEntry.Blocks.push_back(block);
+
+ // write last reference entry, then clear
+ WriteReferenceEntry(refEntry);
+ ClearReferenceEntry(refEntry);
+
+ // then write any empty references remaining at end of file
+ for ( int i = blockRefId+1; i < numReferences; ++i )
+ WriteReferenceEntry( BtiReferenceEntry(i) );
+ }
+
+ } catch ( BamException& e ) {
+ m_errorString = e.what();
+ return false;
+ }
+
+ // rewind BamReader
+ if ( !m_reader->Rewind() ) {
+ const string readerError = m_reader->GetErrorString();
+ const string message = "could not create index: \n\t" + readerError;
+ SetErrorString("BamToolsIndex::Create", message);
+ return false;
+ }
+
+ // return success
+ return true;
+}
+
+// returns format's file extension
+const std::string BamToolsIndex::Extension(void) {
+ return BamToolsIndex::BTI_EXTENSION;
+}
+
+void BamToolsIndex::GetOffset(const BamRegion& region, int64_t& offset, bool* hasAlignmentsInRegion) {
+
+ // return false ref ID is not a valid index in file summary data
+ if ( region.LeftRefID < 0 || region.LeftRefID >= (int)m_indexFileSummary.size() )
+ throw BamException("BamToolsIndex::GetOffset", "invalid region requested");
+
+ // retrieve reference index data for left bound reference
+ BtiReferenceEntry refEntry(region.LeftRefID);
+ ReadReferenceEntry(refEntry);
+
+ // binary search for an overlapping block (may not be first one though)
+ bool found = false;
+ typedef BtiBlockVector::const_iterator BtiBlockConstIterator;
+ BtiBlockConstIterator blockFirst = refEntry.Blocks.begin();
+ BtiBlockConstIterator blockIter = blockFirst;
+ BtiBlockConstIterator blockLast = refEntry.Blocks.end();
+ iterator_traits<BtiBlockConstIterator>::difference_type count = distance(blockFirst, blockLast);
+ iterator_traits<BtiBlockConstIterator>::difference_type step;
+ while ( count > 0 ) {
+ blockIter = blockFirst;
+ step = count/2;
+ advance(blockIter, step);
+
+ const BtiBlock& block = (*blockIter);
+ if ( block.StartPosition <= region.RightPosition ) {
+ if ( block.MaxEndPosition > region.LeftPosition ) {
+ offset = block.StartOffset;
+ break;
+ }
+ blockFirst = ++blockIter;
+ count -= step+1;
+ }
+ else count = step;
+ }
+
+ // if we didn't search "off the end" of the blocks
+ if ( blockIter != blockLast ) {
+
+ // "walk back" until we've gone too far
+ while ( blockIter != blockFirst ) {
+ const BtiBlock& currentBlock = (*blockIter);
+
+ --blockIter;
+ const BtiBlock& previousBlock = (*blockIter);
+ if ( previousBlock.MaxEndPosition <= region.LeftPosition ) {
+ offset = currentBlock.StartOffset;
+ found = true;
+ break;
+ }
+ }
+
+ // if we walked all the way to first block, just return that and let the reader's
+ // region overlap parsing do the rest
+ if ( blockIter == blockFirst ) {
+ const BtiBlock& block = (*blockIter);
+ offset = block.StartOffset;
+ found = true;
+ }
+ }
+
+
+ // sets to false if blocks container is empty, or if no matching block could be found
+ *hasAlignmentsInRegion = found;
+}
+
+// returns whether reference has alignments or no
+bool BamToolsIndex::HasAlignments(const int& referenceID) const {
+ if ( referenceID < 0 || referenceID >= (int)m_indexFileSummary.size() )
+ return false;
+ const BtiReferenceSummary& refSummary = m_indexFileSummary.at(referenceID);
+ return ( refSummary.NumBlocks > 0 );
+}
+
+// pre-allocates space for each reference's summary data
+void BamToolsIndex::InitializeFileSummary(const int& numReferences) {
+ m_indexFileSummary.clear();
+ for ( int i = 0; i < numReferences; ++i )
+ m_indexFileSummary.push_back( BtiReferenceSummary() );
+}
+
+// returns true if the index stream is open
+bool BamToolsIndex::IsDeviceOpen(void) const {
+ if ( m_resources.Device == 0 )
+ return false;
+ return m_resources.Device->IsOpen();
+}
+
+// attempts to use index data to jump to @region, returns success/fail
+// a "successful" jump indicates no error, but not whether this region has data
+// * thus, the method sets a flag to indicate whether there are alignments
+// available after the jump position
+bool BamToolsIndex::Jump(const BamTools::BamRegion& region, bool* hasAlignmentsInRegion) {
+
+ // clear flag
+ *hasAlignmentsInRegion = false;
+
+ // skip if invalid reader or not open
+ if ( m_reader == 0 || !m_reader->IsOpen() ) {
+ SetErrorString("BamToolsIndex::Jump", "could not jump: reader is not open");
+ return false;
+ }
+
+ // make sure left-bound position is valid
+ const RefVector& references = m_reader->GetReferenceData();
+ if ( region.LeftPosition > references.at(region.LeftRefID).RefLength ) {
+ SetErrorString("BamToolsIndex::Jump", "could not create index: invalid region requested");
+ return false;
+ }
+
+ // calculate nearest offset to jump to
+ int64_t offset;
+ try {
+ GetOffset(region, offset, hasAlignmentsInRegion);
+ } catch ( BamException& e ) {
+ m_errorString = e.what();
+ return false;
+ }
+
+ // return success/failure of seek
+ return m_reader->Seek(offset);
+}
+
+// loads existing data from file into memory
+bool BamToolsIndex::Load(const std::string& filename) {
+
+ try {
+
+ // attempt to open file (read-only)
+ OpenFile(filename, IBamIODevice::ReadOnly);
+
+ // load metadata & generate in-memory summary
+ LoadHeader();
+ LoadFileSummary();
+
+ // return success
+ return true;
+
+ } catch ( BamException& e ) {
+ m_errorString = e.what();
+ return false;
+ }
+}
+
+void BamToolsIndex::LoadFileSummary(void) {
+
+ // load number of reference sequences
+ int numReferences;
+ LoadNumReferences(numReferences);
+
+ // initialize file summary data
+ InitializeFileSummary(numReferences);
+
+ // load summary for each reference
+ BtiFileSummary::iterator summaryIter = m_indexFileSummary.begin();
+ BtiFileSummary::iterator summaryEnd = m_indexFileSummary.end();
+ for ( ; summaryIter != summaryEnd; ++summaryIter )
+ LoadReferenceSummary(*summaryIter);
+}
+
+void BamToolsIndex::LoadHeader(void) {
+
+ // check BTI file metadata
+ CheckMagicNumber();
+ CheckVersion();
+
+ // use file's BTI block size to set member variable
+ const int64_t numBytesRead = m_resources.Device->Read((char*)&m_blockSize, sizeof(m_blockSize));
+ if ( m_isBigEndian ) SwapEndian_32(m_blockSize);
+ if ( numBytesRead != sizeof(m_blockSize) )
+ throw BamException("BamToolsIndex::LoadHeader", "could not read BTI block size");
+}
+
+void BamToolsIndex::LoadNumBlocks(int& numBlocks) {
+ const int64_t numBytesRead = m_resources.Device->Read((char*)&numBlocks, sizeof(numBlocks));
+ if ( m_isBigEndian ) SwapEndian_32(numBlocks);
+ if ( numBytesRead != sizeof(numBlocks) )
+ throw BamException("BamToolsIndex::LoadNumBlocks", "could not read number of BTI blocks");
+}
+
+void BamToolsIndex::LoadNumReferences(int& numReferences) {
+ const int64_t numBytesRead = m_resources.Device->Read((char*)&numReferences, sizeof(numReferences));
+ if ( m_isBigEndian ) SwapEndian_32(numReferences);
+ if ( numBytesRead != sizeof(numReferences) )
+ throw BamException("BamToolsIndex::LoadNumReferences", "could not read number of references");
+}
+
+void BamToolsIndex::LoadReferenceSummary(BtiReferenceSummary& refSummary) {
+
+ // load number of blocks
+ int numBlocks;
+ LoadNumBlocks(numBlocks);
+
+ // store block summary data for this reference
+ refSummary.NumBlocks = numBlocks;
+ refSummary.FirstBlockFilePosition = Tell();
+
+ // skip reference's blocks
+ SkipBlocks(numBlocks);
+}
+
+void BamToolsIndex::OpenFile(const std::string& filename, IBamIODevice::OpenMode mode) {
+
+ // make sure any previous index file is closed
+ CloseFile();
+
+ m_resources.Device = BamDeviceFactory::CreateDevice(filename);
+ if ( m_resources.Device == 0 ) {
+ const string message = string("could not open file: ") + filename;
+ throw BamException("BamStandardIndex::OpenFile", message);
+ }
+
+ // attempt to open file
+ m_resources.Device->Open(mode);
+ if ( !IsDeviceOpen() ) {
+ const string message = string("could not open file: ") + filename;
+ throw BamException("BamToolsIndex::OpenFile", message);
+ }
+}
+
+void BamToolsIndex::ReadBlock(BtiBlock& block) {
+
+ // read in block data members
+ int64_t numBytesRead = 0;
+ numBytesRead += m_resources.Device->Read((char*)&block.MaxEndPosition, sizeof(block.MaxEndPosition));
+ numBytesRead += m_resources.Device->Read((char*)&block.StartOffset, sizeof(block.StartOffset));
+ numBytesRead += m_resources.Device->Read((char*)&block.StartPosition, sizeof(block.StartPosition));
+
+ // swap endian-ness if necessary
+ if ( m_isBigEndian ) {
+ SwapEndian_32(block.MaxEndPosition);
+ SwapEndian_64(block.StartOffset);
+ SwapEndian_32(block.StartPosition);
+ }
+
+ // check block read ok
+ const int expectedBytes = sizeof(block.MaxEndPosition) +
+ sizeof(block.StartOffset) +
+ sizeof(block.StartPosition);
+ if ( numBytesRead != expectedBytes )
+ throw BamException("BamToolsIndex::ReadBlock", "could not read block");
+}
+
+void BamToolsIndex::ReadBlocks(const BtiReferenceSummary& refSummary, BtiBlockVector& blocks) {
+
+ // prep blocks container
+ blocks.clear();
+ blocks.reserve(refSummary.NumBlocks);
+
+ // skip to first block entry
+ Seek( refSummary.FirstBlockFilePosition, SEEK_SET );
+
+ // read & store block entries
+ BtiBlock block;
+ for ( int i = 0; i < refSummary.NumBlocks; ++i ) {
+ ReadBlock(block);
+ blocks.push_back(block);
+ }
+}
+
+void BamToolsIndex::ReadReferenceEntry(BtiReferenceEntry& refEntry) {
+
+ // return false if refId not valid index in file summary structure
+ if ( refEntry.ID < 0 || refEntry.ID >= (int)m_indexFileSummary.size() )
+ throw BamException("BamToolsIndex::ReadReferenceEntry", "invalid reference requested");
+
+ // use index summary to assist reading the reference's BTI blocks
+ const BtiReferenceSummary& refSummary = m_indexFileSummary.at(refEntry.ID);
+ ReadBlocks(refSummary, refEntry.Blocks);
+}
+
+void BamToolsIndex::Seek(const int64_t& position, const int origin) {
+ if ( !m_resources.Device->Seek(position, origin) )
+ throw BamException("BamToolsIndex::Seek", "could not seek in BAI file");
+}
+
+void BamToolsIndex::SkipBlocks(const int& numBlocks) {
+ Seek( numBlocks*BamToolsIndex::SIZEOF_BLOCK, SEEK_CUR );
+}
+
+int64_t BamToolsIndex::Tell(void) const {
+ return m_resources.Device->Tell();
+}
+
+void BamToolsIndex::WriteBlock(const BtiBlock& block) {
+
+ // copy entry data
+ int32_t maxEndPosition = block.MaxEndPosition;
+ int64_t startOffset = block.StartOffset;
+ int32_t startPosition = block.StartPosition;
+
+ // swap endian-ness if necessary
+ if ( m_isBigEndian ) {
+ SwapEndian_32(maxEndPosition);
+ SwapEndian_64(startOffset);
+ SwapEndian_32(startPosition);
+ }
+
+ // write the reference index entry
+ int64_t numBytesWritten = 0;
+ numBytesWritten += m_resources.Device->Write((const char*)&maxEndPosition, sizeof(maxEndPosition));
+ numBytesWritten += m_resources.Device->Write((const char*)&startOffset, sizeof(startOffset));
+ numBytesWritten += m_resources.Device->Write((const char*)&startPosition, sizeof(startPosition));
+
+ // check block written ok
+ const int expectedBytes = sizeof(maxEndPosition) +
+ sizeof(startOffset) +
+ sizeof(startPosition);
+ if ( numBytesWritten != expectedBytes )
+ throw BamException("BamToolsIndex::WriteBlock", "could not write BTI block");
+}
+
+void BamToolsIndex::WriteBlocks(const BtiBlockVector& blocks) {
+ BtiBlockVector::const_iterator blockIter = blocks.begin();
+ BtiBlockVector::const_iterator blockEnd = blocks.end();
+ for ( ; blockIter != blockEnd; ++blockIter )
+ WriteBlock(*blockIter);
+}
+
+void BamToolsIndex::WriteHeader(void) {
+
+ int64_t numBytesWritten = 0 ;
+
+ // write BTI index format 'magic number'
+ numBytesWritten += m_resources.Device->Write(BamToolsIndex::BTI_MAGIC, 4);
+
+ // write BTI index format version
+ int32_t currentVersion = (int32_t)m_outputVersion;
+ if ( m_isBigEndian ) SwapEndian_32(currentVersion);
+ numBytesWritten += m_resources.Device->Write((const char*)&currentVersion, sizeof(currentVersion));
+
+ // write block size
+ uint32_t blockSize = m_blockSize;
+ if ( m_isBigEndian ) SwapEndian_32(blockSize);
+ numBytesWritten += m_resources.Device->Write((const char*)&blockSize, sizeof(blockSize));
+
+ // write number of references
+ int32_t numReferences = m_indexFileSummary.size();
+ if ( m_isBigEndian ) SwapEndian_32(numReferences);
+ numBytesWritten += m_resources.Device->Write((const char*)&numReferences, sizeof(numReferences));
+
+ // check header written ok
+ const int expectedBytes = 4 +
+ sizeof(currentVersion) +
+ sizeof(blockSize) +
+ sizeof(numReferences);
+ if ( numBytesWritten != expectedBytes )
+ throw BamException("BamToolsIndex::WriteHeader", "could not write BTI header");
+}
+
+void BamToolsIndex::WriteReferenceEntry(const BtiReferenceEntry& refEntry) {
+
+ // write number of blocks this reference
+ uint32_t numBlocks = refEntry.Blocks.size();
+ if ( m_isBigEndian ) SwapEndian_32(numBlocks);
+ const int64_t numBytesWritten = m_resources.Device->Write((const char*)&numBlocks, sizeof(numBlocks));
+ if ( numBytesWritten != sizeof(numBlocks) )
+ throw BamException("BamToolsIndex::WriteReferenceEntry", "could not write number of blocks");
+
+ // write actual block entries
+ WriteBlocks(refEntry.Blocks);
+}
diff --git a/bamtools/src/api/internal/index/BamToolsIndex_p.h b/bamtools/src/api/internal/index/BamToolsIndex_p.h
new file mode 100644
index 0000000..c1e1aa0
--- /dev/null
+++ b/bamtools/src/api/internal/index/BamToolsIndex_p.h
@@ -0,0 +1,186 @@
+// ***************************************************************************
+// BamToolsIndex.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides index operations for the BamTools index format (".bti")
+// ***************************************************************************
+
+#ifndef BAMTOOLS_INDEX_FORMAT_H
+#define BAMTOOLS_INDEX_FORMAT_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/BamAux.h"
+#include "api/BamIndex.h"
+#include "api/IBamIODevice.h"
+#include <map>
+#include <string>
+#include <vector>
+
+namespace BamTools {
+namespace Internal {
+
+// contains data for each 'block' in a BTI index
+struct BtiBlock {
+
+ // data members
+ int32_t MaxEndPosition;
+ int64_t StartOffset;
+ int32_t StartPosition;
+
+ // ctor
+ BtiBlock(const int32_t& maxEndPosition = 0,
+ const int64_t& startOffset = 0,
+ const int32_t& startPosition = 0)
+ : MaxEndPosition(maxEndPosition)
+ , StartOffset(startOffset)
+ , StartPosition(startPosition)
+ { }
+};
+
+// convenience typedef for describing a a list of BTI blocks on a reference
+typedef std::vector<BtiBlock> BtiBlockVector;
+
+// contains all fields necessary for building, loading, & writing
+// full BTI index data for a single reference
+struct BtiReferenceEntry {
+
+ // data members
+ int32_t ID;
+ BtiBlockVector Blocks;
+
+ // ctor
+ BtiReferenceEntry(const int& id = -1)
+ : ID(id)
+ { }
+};
+
+// provides (persistent) summary of BtiReferenceEntry's index data
+struct BtiReferenceSummary {
+
+ // data members
+ int NumBlocks;
+ uint64_t FirstBlockFilePosition;
+
+ // ctor
+ BtiReferenceSummary(void)
+ : NumBlocks(0)
+ , FirstBlockFilePosition(0)
+ { }
+};
+
+// convenience typedef for describing a full BTI index file summary
+typedef std::vector<BtiReferenceSummary> BtiFileSummary;
+
+class BamToolsIndex : public BamIndex {
+
+ // keep a list of any supported versions here
+ // (might be useful later to handle any 'legacy' versions if the format changes)
+ // listed for example like: BTI_1_0 = 1, BTI_1_1 = 2, BTI_1_2 = 3, BTI_2_0 = 4, and so on
+ //
+ // so a change introduced in BTI_1_2 may be handled from then on by:
+ //
+ // if ( indexVersion >= BTI_1_2 )
+ // do something new
+ // else
+ // do the old thing
+ enum Version { BTI_1_0 = 1
+ , BTI_1_1
+ , BTI_1_2
+ , BTI_2_0
+ };
+
+ // ctor & dtor
+ public:
+ BamToolsIndex(Internal::BamReaderPrivate* reader);
+ ~BamToolsIndex(void);
+
+ // BamIndex implementation
+ public:
+ // builds index from associated BAM file & writes out to index file
+ bool Create(void);
+ // returns whether reference has alignments or no
+ bool HasAlignments(const int& referenceID) const;
+ // attempts to use index data to jump to @region, returns success/fail
+ // a "successful" jump indicates no error, but not whether this region has data
+ // * thus, the method sets a flag to indicate whether there are alignments
+ // available after the jump position
+ bool Jump(const BamTools::BamRegion& region, bool* hasAlignmentsInRegion);
+ // loads existing data from file into memory
+ bool Load(const std::string& filename);
+ BamIndex::IndexType Type(void) const { return BamIndex::BAMTOOLS; }
+ public:
+ // returns format's file extension
+ static const std::string Extension(void);
+
+ // internal methods
+ private:
+
+ // index file ops
+ void CheckMagicNumber(void);
+ void CheckVersion(void);
+ void CloseFile(void);
+ bool IsDeviceOpen(void) const;
+ void OpenFile(const std::string& filename, IBamIODevice::OpenMode mode);
+ void Seek(const int64_t& position, const int origin);
+ int64_t Tell(void) const;
+
+ // index-creation methods
+ void ClearReferenceEntry(BtiReferenceEntry& refEntry);
+ void WriteBlock(const BtiBlock& block);
+ void WriteBlocks(const BtiBlockVector& blocks);
+ void WriteHeader(void);
+ void WriteReferenceEntry(const BtiReferenceEntry& refEntry);
+
+ // random-access methods
+ void GetOffset(const BamRegion& region, int64_t& offset, bool* hasAlignmentsInRegion);
+ void ReadBlock(BtiBlock& block);
+ void ReadBlocks(const BtiReferenceSummary& refSummary, BtiBlockVector& blocks);
+ void ReadReferenceEntry(BtiReferenceEntry& refEntry);
+
+ // BTI summary data methods
+ void InitializeFileSummary(const int& numReferences);
+ void LoadFileSummary(void);
+ void LoadHeader(void);
+ void LoadNumBlocks(int& numBlocks);
+ void LoadNumReferences(int& numReferences);
+ void LoadReferenceSummary(BtiReferenceSummary& refSummary);
+ void SkipBlocks(const int& numBlocks);
+
+ // data members
+ private:
+ bool m_isBigEndian;
+ BtiFileSummary m_indexFileSummary;
+ uint32_t m_blockSize;
+ int32_t m_inputVersion; // Version is serialized as int
+ Version m_outputVersion;
+
+ struct RaiiWrapper {
+ IBamIODevice* Device;
+ RaiiWrapper(void);
+ ~RaiiWrapper(void);
+ };
+ RaiiWrapper m_resources;
+
+ // static constants
+ private:
+ static const uint32_t DEFAULT_BLOCK_LENGTH;
+ static const std::string BTI_EXTENSION;
+ static const char* const BTI_MAGIC;
+ static const int SIZEOF_BLOCK;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMTOOLS_INDEX_FORMAT_H
diff --git a/bamtools/src/api/internal/index/CMakeLists.txt b/bamtools/src/api/internal/index/CMakeLists.txt
new file mode 100644
index 0000000..d6a7df6
--- /dev/null
+++ b/bamtools/src/api/internal/index/CMakeLists.txt
@@ -0,0 +1,17 @@
+# ==========================
+# BamTools CMakeLists.txt
+# (c) 2011 Derek Barnett
+#
+# src/api/internal/index
+# ==========================
+
+set( InternalIndexDir "${InternalDir}/index" )
+
+set( InternalIndexSources
+ ${InternalIndexDir}/BamIndexFactory_p.cpp
+ ${InternalIndexDir}/BamStandardIndex_p.cpp
+ ${InternalIndexDir}/BamToolsIndex_p.cpp
+
+ PARENT_SCOPE # <-- leave this last
+)
+
diff --git a/bamtools/src/api/internal/io/BamDeviceFactory_p.cpp b/bamtools/src/api/internal/io/BamDeviceFactory_p.cpp
new file mode 100644
index 0000000..f9c7694
--- /dev/null
+++ b/bamtools/src/api/internal/io/BamDeviceFactory_p.cpp
@@ -0,0 +1,37 @@
+// ***************************************************************************
+// BamDeviceFactory_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 25 September 2011 (DB)
+// ---------------------------------------------------------------------------
+// Creates built-in concrete implementations of IBamIODevices
+// ***************************************************************************
+
+#include "api/internal/io/BamDeviceFactory_p.h"
+#include "api/internal/io/BamFile_p.h"
+#include "api/internal/io/BamFtp_p.h"
+#include "api/internal/io/BamHttp_p.h"
+#include "api/internal/io/BamPipe_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <iostream>
+using namespace std;
+
+IBamIODevice* BamDeviceFactory::CreateDevice(const string& source) {
+
+ // check for requested pipe
+ if ( source == "-" || source == "stdin" || source == "stdout" )
+ return new BamPipe;
+
+ // check for HTTP prefix
+ if ( source.find("http://") == 0 )
+ return new BamHttp(source);
+
+ // check for FTP prefix
+ if ( source.find("ftp://") == 0 )
+ return new BamFtp(source);
+
+ // otherwise assume a "normal" file
+ return new BamFile(source);
+}
diff --git a/bamtools/src/api/internal/io/BamDeviceFactory_p.h b/bamtools/src/api/internal/io/BamDeviceFactory_p.h
new file mode 100644
index 0000000..1d48533
--- /dev/null
+++ b/bamtools/src/api/internal/io/BamDeviceFactory_p.h
@@ -0,0 +1,37 @@
+// ***************************************************************************
+// BamDeviceFactory_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Creates built-in concrete implementations of IBamIODevices
+// ***************************************************************************
+
+#ifndef BAMDEVICEFACTORY_P_H
+#define BAMDEVICEFACTORY_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/IBamIODevice.h"
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class BamDeviceFactory {
+ public:
+ static IBamIODevice* CreateDevice(const std::string& source);
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMDEVICEFACTORY_P_H
diff --git a/bamtools/src/api/internal/io/BamFile_p.cpp b/bamtools/src/api/internal/io/BamFile_p.cpp
new file mode 100644
index 0000000..990d9bf
--- /dev/null
+++ b/bamtools/src/api/internal/io/BamFile_p.cpp
@@ -0,0 +1,69 @@
+// ***************************************************************************
+// BamFile_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides BAM file-specific IO behavior
+// ***************************************************************************
+
+#include "api/internal/io/BamFile_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cstdio>
+#include <iostream>
+using namespace std;
+
+BamFile::BamFile(const string& filename)
+ : ILocalIODevice()
+ , m_filename(filename)
+{ }
+
+BamFile::~BamFile(void) { }
+
+void BamFile::Close(void) {
+ if ( IsOpen() ) {
+ m_filename.clear();
+ ILocalIODevice::Close();
+ }
+}
+
+bool BamFile::IsRandomAccess(void) const {
+ return true;
+}
+
+bool BamFile::Open(const IBamIODevice::OpenMode mode) {
+
+ // make sure we're starting with a fresh file stream
+ Close();
+
+ // attempt to open FILE* depending on requested openmode
+ if ( mode == IBamIODevice::ReadOnly )
+ m_stream = fopen(m_filename.c_str(), "rb");
+ else if ( mode == IBamIODevice::WriteOnly )
+ m_stream = fopen(m_filename.c_str(), "wb");
+ else if ( mode == IBamIODevice::ReadWrite )
+ m_stream = fopen(m_filename.c_str(), "w+b");
+ else {
+ SetErrorString("BamFile::Open", "unknown open mode requested");
+ return false;
+ }
+
+ // check that we obtained a valid FILE*
+ if ( m_stream == 0 ) {
+ const string message_base = string("could not open file handle for ");
+ const string message = message_base + ( (m_filename.empty()) ? "empty filename" : m_filename );
+ SetErrorString("BamFile::Open", message);
+ return false;
+ }
+
+ // store current IO mode & return success
+ m_mode = mode;
+ return true;
+}
+
+bool BamFile::Seek(const int64_t& position, const int origin) {
+ BT_ASSERT_X( m_stream, "BamFile::Seek() - null stream" );
+ return ( fseek64(m_stream, position, origin) == 0 );
+}
diff --git a/bamtools/src/api/internal/io/BamFile_p.h b/bamtools/src/api/internal/io/BamFile_p.h
new file mode 100644
index 0000000..ed61813
--- /dev/null
+++ b/bamtools/src/api/internal/io/BamFile_p.h
@@ -0,0 +1,51 @@
+// ***************************************************************************
+// BamFile_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides BAM file-specific IO behavior
+// ***************************************************************************
+
+#ifndef BAMFILE_P_H
+#define BAMFILE_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/internal/io/ILocalIODevice_p.h"
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class BamFile : public ILocalIODevice {
+
+ // ctor & dtor
+ public:
+ BamFile(const std::string& filename);
+ ~BamFile(void);
+
+ // ILocalIODevice implementation
+ public:
+ void Close(void);
+ bool IsRandomAccess(void) const;
+ bool Open(const IBamIODevice::OpenMode mode);
+ bool Seek(const int64_t& position, const int origin = SEEK_SET);
+
+ // data members
+ private:
+ std::string m_filename;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMFILE_P_H
diff --git a/bamtools/src/api/internal/io/BamFtp_p.cpp b/bamtools/src/api/internal/io/BamFtp_p.cpp
new file mode 100644
index 0000000..b851401
--- /dev/null
+++ b/bamtools/src/api/internal/io/BamFtp_p.cpp
@@ -0,0 +1,490 @@
+// ***************************************************************************
+// BamFtp_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 8 December 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides reading/writing of BAM files on FTP server
+// ***************************************************************************
+
+#include "api/BamAux.h"
+#include "api/internal/io/BamFtp_p.h"
+#include "api/internal/io/TcpSocket_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cctype>
+#include <cstdlib>
+#include <sstream>
+#include <vector>
+using namespace std;
+
+namespace BamTools {
+namespace Internal {
+
+// -----------
+// constants
+// -----------
+
+static const uint16_t FTP_PORT = 21;
+static const string FTP_PREFIX = "ftp://";
+static const size_t FTP_PREFIX_LENGTH = 6;
+static const string FTP_NEWLINE = "\r\n";
+
+static const string DEFAULT_USER = "anonymous";
+static const string DEFAULT_PASS = "anonymous@";
+
+static const string ABOR_CMD = "ABOR";
+static const string USER_CMD = "USER";
+static const string PASS_CMD = "PASS";
+static const string PASV_CMD = "PASV";
+static const string REIN_CMD = "REIN";
+static const string REST_CMD = "REST";
+static const string RETR_CMD = "RETR";
+static const string TYPE_CMD = "TYPE";
+
+static const char CMD_SEPARATOR = ' ';
+static const char HOST_SEPARATOR = '/';
+static const char IP_SEPARATOR = '.';
+
+static const char MULTILINE_CONTINUE = '-';
+
+static const char PASV_REPLY_PREFIX = '(';
+static const char PASV_REPLY_SEPARATOR = ',';
+static const char PASV_REPLY_SUFFIX = ')';
+
+// -----------------
+// utility methods
+// -----------------
+
+static inline
+vector<string> split(const string& source, const char delim) {
+
+ stringstream ss(source);
+ string field;
+ vector<string> fields;
+
+ while ( getline(ss, field, delim) )
+ fields.push_back(field);
+ return fields;
+}
+
+static inline
+bool startsWith(const string& source, const string& pattern) {
+ return ( source.find(pattern) == 0 );
+}
+
+static inline
+string toLower(const string& s) {
+ string out;
+ const size_t sSize = s.size();
+ out.resize(sSize);
+ for ( size_t i = 0; i < sSize; ++i )
+ out[i] = tolower(s[i]);
+ return out;
+}
+
+} // namespace Internal
+} // namespace BamTools
+
+// -----------------------
+// BamFtp implementation
+// -----------------------
+
+BamFtp::BamFtp(const string& url)
+ : IBamIODevice()
+ , m_commandSocket(new TcpSocket)
+ , m_dataSocket(new TcpSocket)
+ , m_port(FTP_PORT)
+ , m_dataPort(0)
+ , m_username(DEFAULT_USER)
+ , m_password(DEFAULT_PASS)
+ , m_isUrlParsed(false)
+ , m_filePosition(-1)
+{
+ ParseUrl(url);
+}
+
+BamFtp::~BamFtp(void) {
+
+ // close connection & clean up
+ Close();
+ if ( m_commandSocket )
+ delete m_commandSocket;
+ if ( m_dataSocket )
+ delete m_dataSocket;
+}
+
+void BamFtp::Close(void) {
+
+ // disconnect socket
+ m_commandSocket->DisconnectFromHost();
+ m_dataSocket->DisconnectFromHost();
+
+ // reset state - necessary??
+ m_isUrlParsed = false;
+ m_filePosition = -1;
+ m_username = DEFAULT_USER;
+ m_password = DEFAULT_PASS;
+ m_dataHostname.clear();
+ m_dataPort = 0;
+}
+
+bool BamFtp::ConnectCommandSocket(void) {
+
+ BT_ASSERT_X(m_commandSocket, "null command socket?");
+
+ // connect to FTP server
+ if ( !m_commandSocket->ConnectToHost(m_hostname, m_port, m_mode) ) {
+ SetErrorString("BamFtp::ConnectCommandSocket", "could not connect to host - ");
+ return false;
+ }
+
+ // receive initial reply from host
+ if ( !ReceiveReply() ) {
+ Close();
+ return false;
+ }
+
+ // send USER command
+ string userCommand = USER_CMD + CMD_SEPARATOR + m_username + FTP_NEWLINE;
+ if ( !SendCommand(userCommand, true) ) {
+ Close();
+ return false;
+ }
+
+ // send PASS command
+ string passwordCommand = PASS_CMD + CMD_SEPARATOR + m_password + FTP_NEWLINE;
+ if ( !SendCommand(passwordCommand, true) ) {
+ Close();
+ return false;
+ }
+
+ // send TYPE command
+ string typeCommand = TYPE_CMD + CMD_SEPARATOR + 'I' + FTP_NEWLINE;
+ if ( !SendCommand(typeCommand, true) ) {
+ Close();
+ return false;
+ }
+
+ // return success
+ return true;
+}
+
+bool BamFtp::ConnectDataSocket(void) {
+
+ // failure if can't connect to command socket first
+ if ( !m_commandSocket->IsConnected() ) {
+ if ( !ConnectCommandSocket() )
+ return false;
+ }
+
+ // make sure we're starting with a fresh data channel
+ if ( m_dataSocket->IsConnected() )
+ m_dataSocket->DisconnectFromHost();
+
+ // send passive connection command
+ const string passiveCommand = PASV_CMD + FTP_NEWLINE;
+ if ( !SendCommand(passiveCommand, true) ) {
+ // TODO: set error string
+ return false;
+ }
+
+ // retrieve passive connection port
+ if ( !ParsePassiveResponse() ) {
+ // TODO: set error string
+ return false;
+ }
+
+ // set up restart command (tell server where to start fetching bytes from)
+ if ( m_filePosition >= 0 ) {
+
+ stringstream fpStream("");
+ fpStream << m_filePosition;
+ string restartCommand = REST_CMD + CMD_SEPARATOR + fpStream.str() + FTP_NEWLINE;
+ if ( !SendCommand(restartCommand, true) ) {
+ // TODO: set error string
+ return false;
+ }
+ }
+
+ // main file retrieval request
+ string retrieveCommand = RETR_CMD + CMD_SEPARATOR + m_filename + FTP_NEWLINE;
+ if ( !SendCommand(retrieveCommand, false) ) {
+ // TODO: set error string
+ return false;
+ }
+
+ // make data channel connection
+ if ( !m_dataSocket->ConnectToHost(m_dataHostname, m_dataPort) ) {
+ // TODO: set error string
+ return false;
+ }
+
+ // fetch intial reply from server
+ if ( !ReceiveReply() ) {
+ // TODO: set error string
+ m_dataSocket->DisconnectFromHost();
+ return false;
+ }
+
+ // make sure we have reply code 150 (all good)
+ if ( !startsWith(m_response, "150") ) {
+ // TODO: set error string
+ m_dataSocket->DisconnectFromHost();
+ return false;
+ }
+
+ // return success
+ return true;
+}
+
+bool BamFtp::IsOpen(void) const {
+ return IBamIODevice::IsOpen() && m_isUrlParsed;
+}
+
+bool BamFtp::IsRandomAccess(void) const {
+ return true;
+}
+
+bool BamFtp::Open(const IBamIODevice::OpenMode mode) {
+
+ // BamFtp only supports read-only access
+ if ( mode != IBamIODevice::ReadOnly ) {
+ SetErrorString("BamFtp::Open", "writing on this device is not supported");
+ return false;
+ }
+
+ // initialize basic valid state
+ m_mode = mode;
+ m_filePosition = 0;
+
+ // attempt connection to command & data sockets
+ return ( ConnectCommandSocket() && ConnectDataSocket() );
+}
+
+bool BamFtp::ParsePassiveResponse(void) {
+
+ // fail if empty
+ if ( m_response.empty() )
+ return false;
+
+ // find parentheses
+ const size_t leftParenFound = m_response.find(PASV_REPLY_PREFIX);
+ const size_t rightParenFound = m_response.find(PASV_REPLY_SUFFIX);
+ if ( leftParenFound == string::npos || rightParenFound == string::npos )
+ return false;
+
+ // grab everything between ( should be "h1,h2,h3,h4,p1,p2" )
+ string::const_iterator responseBegin = m_response.begin();
+ const string hostAndPort(responseBegin+leftParenFound+1, responseBegin+rightParenFound);
+
+ // parse into string fields
+ vector<string> fields = split(hostAndPort, PASV_REPLY_SEPARATOR);
+ if ( fields.size() != 6 )
+ return false;
+
+ // fetch passive connection IP
+ m_dataHostname = fields[0] + IP_SEPARATOR +
+ fields[1] + IP_SEPARATOR +
+ fields[2] + IP_SEPARATOR +
+ fields[3];
+
+ // fetch passive connection port
+ const uint8_t portUpper = static_cast<uint8_t>(atoi(fields[4].c_str()));
+ const uint8_t portLower = static_cast<uint8_t>(atoi(fields[5].c_str()));
+ m_dataPort = ( portUpper<<8 ) + portLower;
+
+ // return success
+ return true;
+}
+
+void BamFtp::ParseUrl(const string& url) {
+
+ // clear flag to start
+ m_isUrlParsed = false;
+
+ // make sure url starts with "ftp://", case-insensitive
+ string tempUrl(url);
+ toLower(tempUrl);
+ const size_t prefixFound = tempUrl.find(FTP_PREFIX);
+ if ( prefixFound == string::npos )
+ return;
+
+ // find end of host name portion (first '/' hit after the prefix)
+ const size_t firstSlashFound = tempUrl.find(HOST_SEPARATOR, FTP_PREFIX_LENGTH);
+ if ( firstSlashFound == string::npos ) {
+ ; // no slash found... no filename given along with host?
+ }
+
+ // fetch hostname
+ string hostname = tempUrl.substr(FTP_PREFIX_LENGTH, (firstSlashFound - FTP_PREFIX_LENGTH));
+ m_hostname = hostname;
+ m_port = FTP_PORT;
+
+ // store remainder of URL as filename (must be non-empty)
+ string filename = tempUrl.substr(firstSlashFound);
+ if ( filename.empty() )
+ return;
+ m_filename = filename;
+
+ // set parsed OK flag
+ m_isUrlParsed = true;
+}
+
+int64_t BamFtp::Read(char* data, const unsigned int numBytes) {
+
+ // if BamHttp not in a valid state
+ if ( !IsOpen() )
+ return -1;
+
+ // read until hit desired @numBytes
+ int64_t bytesReadSoFar = 0;
+ while ( bytesReadSoFar < numBytes ) {
+
+ // calculate number of bytes we're going to try to read this iteration
+ const size_t remainingBytes = ( numBytes - bytesReadSoFar );
+
+ // if either disconnected somehow, or (more likely) we have seeked since last read
+ if ( !m_dataSocket->IsConnected() ) {
+ if ( !ConnectDataSocket() ) {
+ // TODO: set error string
+ return -1;
+ }
+ }
+
+ // read bytes from data socket
+ const int64_t socketBytesRead = ReadDataSocket(data+bytesReadSoFar, remainingBytes);
+ if ( socketBytesRead < 0 ) // error
+ return -1;
+ else if ( socketBytesRead == 0 ) // EOF
+ return bytesReadSoFar;
+ bytesReadSoFar += socketBytesRead;
+ m_filePosition += socketBytesRead;
+ }
+
+ // return actual number bytes successfully read
+ return bytesReadSoFar;
+}
+
+int64_t BamFtp::ReadCommandSocket(char* data, const unsigned int maxNumBytes) {
+ return m_commandSocket->Read(data, maxNumBytes);
+}
+
+int64_t BamFtp::ReadDataSocket(char* data, const unsigned int maxNumBytes) {
+ return m_dataSocket->Read(data, maxNumBytes);
+}
+
+bool BamFtp::ReceiveReply(void) {
+
+ // failure if not connected
+ if ( !m_commandSocket->IsConnected() ) {
+ SetErrorString("BamFtp::ReceiveReply()", "command socket not connected");
+ return false;
+ }
+
+ m_response.clear();
+
+ // read header data (& discard for now)
+ bool headerEnd = false;
+ while ( !headerEnd ) {
+
+ const string headerLine = m_commandSocket->ReadLine();
+ m_response += headerLine;
+
+ // if line is of form 'xyz ', quit reading lines
+ if ( (headerLine.length() >= 4 ) &&
+ isdigit(headerLine[0]) &&
+ isdigit(headerLine[1]) &&
+ isdigit(headerLine[2]) &&
+ ( headerLine[3] != MULTILINE_CONTINUE )
+ )
+ {
+ headerEnd = true;
+ }
+ }
+
+ // return success, depending on response
+ if ( m_response.empty() ) {
+ SetErrorString("BamFtp::ReceiveReply", "error reading server reply");
+ return false;
+ }
+ return true;
+}
+
+bool BamFtp::Seek(const int64_t& position, const int origin) {
+
+ // if FTP device not in a valid state
+ if ( !IsOpen() ) {
+ // TODO: set error string
+ return false;
+ }
+
+ // ----------------------
+ // UGLY !! but works??
+ // ----------------------
+ // disconnect from server
+ m_dataSocket->DisconnectFromHost();
+ m_commandSocket->DisconnectFromHost();
+
+ // update file position & return success
+ if ( origin == SEEK_CUR )
+ m_filePosition += position;
+ else if ( origin == SEEK_SET)
+ m_filePosition = position;
+ else {
+ // TODO: set error string
+ return false;
+ }
+ return true;
+}
+
+bool BamFtp::SendCommand(const string& command, bool waitForReply) {
+
+ // failure if not connected
+ if ( !m_commandSocket->IsConnected() ) {
+ SetErrorString("BamFtp::SendCommand", "command socket not connected");
+ return false;
+ }
+
+ // write command to 'command socket'
+ if ( WriteCommandSocket(command.c_str(), command.length()) == -1 ) {
+ SetErrorString("BamFtp::SendCommand", "error writing to socket");
+ // get actual error from command socket??
+ return false;
+ }
+
+ // if we sent a command that receives a response
+ if ( waitForReply )
+ return ReceiveReply();
+
+ // return success
+ return true;
+}
+
+int64_t BamFtp::Tell(void) const {
+ return ( IsOpen() ? m_filePosition : -1 );
+}
+
+int64_t BamFtp::Write(const char* data, const unsigned int numBytes) {
+ (void)data;
+ (void)numBytes;
+ BT_ASSERT_X(false, "BamFtp::Write : write-mode not supported on this device");
+ SetErrorString("BamFtp::Write", "write-mode not supported on this device");
+ return -1;
+}
+
+int64_t BamFtp::WriteCommandSocket(const char* data, const unsigned int numBytes) {
+ if ( !m_commandSocket->IsConnected() )
+ return -1;
+ m_commandSocket->ClearBuffer();
+ return m_commandSocket->Write(data, numBytes);
+}
+
+int64_t BamFtp::WriteDataSocket(const char* data, const unsigned int numBytes) {
+ (void)data;
+ (void)numBytes;
+ BT_ASSERT_X(false, "BamFtp::WriteDataSocket: write-mode not supported on this device");
+ SetErrorString("BamFtp::Write", "write-mode not supported on this device");
+ return -1;
+}
diff --git a/bamtools/src/api/internal/io/BamFtp_p.h b/bamtools/src/api/internal/io/BamFtp_p.h
new file mode 100644
index 0000000..11f549c
--- /dev/null
+++ b/bamtools/src/api/internal/io/BamFtp_p.h
@@ -0,0 +1,91 @@
+// ***************************************************************************
+// BamFtp_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides reading/writing of BAM files on FTP server
+// ***************************************************************************
+
+#ifndef BAMFTP_P_H
+#define BAMFTP_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/IBamIODevice.h"
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class TcpSocket;
+
+class BamFtp : public IBamIODevice {
+
+ // ctor & dtor
+ public:
+ BamFtp(const std::string& url);
+ ~BamFtp(void);
+
+ // IBamIODevice implementation
+ public:
+ void Close(void);
+ bool IsOpen(void) const;
+ bool IsRandomAccess(void) const;
+ bool Open(const IBamIODevice::OpenMode mode);
+ int64_t Read(char* data, const unsigned int numBytes);
+ bool Seek(const int64_t& position, const int origin = SEEK_SET);
+ int64_t Tell(void) const;
+ int64_t Write(const char* data, const unsigned int numBytes);
+
+ // internal methods
+ private:
+ bool ConnectCommandSocket(void);
+ bool ConnectDataSocket(void);
+ bool ParsePassiveResponse(void);
+ void ParseUrl(const std::string& url);
+ int64_t ReadCommandSocket(char* data, const unsigned int numBytes);
+ int64_t ReadDataSocket(char* data, const unsigned int numBytes);
+ bool ReceiveReply(void);
+ bool SendCommand(const std::string& command, bool waitForReply);
+ int64_t WriteCommandSocket(const char* data, const unsigned int numBytes);
+ int64_t WriteDataSocket(const char* data, const unsigned int numBytes);
+
+ // data members
+ private:
+
+ // our main sockets
+ TcpSocket* m_commandSocket;
+ TcpSocket* m_dataSocket;
+
+ // our connection data
+ std::string m_hostname;
+ uint16_t m_port;
+ std::string m_dataHostname;
+ uint16_t m_dataPort;
+ std::string m_filename;
+
+ std::string m_username;
+ std::string m_password;
+
+ std::string m_response;
+
+ // internal state flags
+ bool m_isUrlParsed;
+
+ // file position
+ int64_t m_filePosition;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMFTP_P_H
diff --git a/bamtools/src/api/internal/io/BamHttp_p.cpp b/bamtools/src/api/internal/io/BamHttp_p.cpp
new file mode 100644
index 0000000..b089172
--- /dev/null
+++ b/bamtools/src/api/internal/io/BamHttp_p.cpp
@@ -0,0 +1,544 @@
+// ***************************************************************************
+// BamHttp_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 24 July 2013 (DB)
+// ---------------------------------------------------------------------------
+// Provides reading/writing of BAM files on HTTP server
+// ***************************************************************************
+
+#include "api/BamAux.h"
+#include "api/internal/io/BamHttp_p.h"
+#include "api/internal/io/HttpHeader_p.h"
+#include "api/internal/io/TcpSocket_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cassert>
+#include <cctype>
+#include <cstdlib>
+#include <algorithm>
+#include <sstream>
+using namespace std;
+
+namespace BamTools {
+namespace Internal {
+
+// -----------
+// constants
+// -----------
+
+static const string HTTP_PORT = "80";
+static const string HTTP_PREFIX = "http://";
+static const size_t HTTP_PREFIX_LENGTH = 7;
+
+static const string DOUBLE_NEWLINE = "\n\n";
+
+static const string GET_METHOD = "GET";
+static const string HEAD_METHOD = "HEAD";
+static const string HOST_HEADER = "Host";
+static const string RANGE_HEADER = "Range";
+static const string BYTES_PREFIX = "bytes=";
+static const string CONTENT_LENGTH_HEADER = "Content-Length";
+
+static const char HOST_SEPARATOR = '/';
+static const char PROXY_SEPARATOR = ':';
+
+// -----------------
+// utility methods
+// -----------------
+
+static inline
+bool endsWith(const string& source, const string& pattern) {
+ return ( source.find(pattern) == (source.length() - pattern.length()) );
+}
+
+static inline
+string toLower(const string& s) {
+ string out;
+ const size_t sSize = s.size();
+ out.reserve(sSize);
+ for ( size_t i = 0; i < sSize; ++i )
+ out[i] = tolower(s[i]);
+ return out;
+}
+
+} // namespace Internal
+} // namespace BamTools
+
+// ------------------------
+// BamHttp implementation
+// ------------------------
+
+BamHttp::BamHttp(const string& url)
+ : IBamIODevice()
+ , m_socket(new TcpSocket)
+ , m_port(HTTP_PORT)
+ , m_request(0)
+ , m_response(0)
+ , m_isUrlParsed(false)
+ , m_filePosition(-1)
+ , m_fileEndPosition(-1)
+ , m_rangeEndPosition(-1)
+{
+ ParseUrl(url);
+}
+
+BamHttp::~BamHttp(void) {
+
+ // close connection & clean up
+ Close();
+ if ( m_socket )
+ delete m_socket;
+}
+
+void BamHttp::ClearResponse(void) {
+ if ( m_response ) {
+ delete m_response;
+ m_response = 0;
+ }
+}
+
+void BamHttp::Close(void) {
+
+ // disconnect socket & clear related resources
+ DisconnectSocket();
+
+ // reset state
+ m_isUrlParsed = false;
+ m_filePosition = -1;
+ m_fileEndPosition = -1;
+ m_rangeEndPosition = -1;
+ m_mode = IBamIODevice::NotOpen;
+}
+
+bool BamHttp::ConnectSocket(void) {
+
+ BT_ASSERT_X(m_socket, "null socket?");
+
+ // any state checks, etc?
+ if ( !m_socket->ConnectToHost(m_hostname, m_port, m_mode) ) {
+ SetErrorString("BamHttp::ConnectSocket", m_socket->GetErrorString());
+ return false;
+ }
+
+ // return success
+ return true;
+}
+
+void BamHttp::DisconnectSocket(void) {
+
+ // disconnect socket & clean up
+ m_socket->DisconnectFromHost();
+ ClearResponse();
+ if ( m_request ) {
+ delete m_request;
+ m_request = 0;
+ }
+}
+
+bool BamHttp::EnsureSocketConnection(void) {
+ if ( m_socket->IsConnected() )
+ return true;
+ return ConnectSocket();
+}
+
+bool BamHttp::IsOpen(void) const {
+ return IBamIODevice::IsOpen() && m_isUrlParsed;
+}
+
+bool BamHttp::IsRandomAccess(void) const {
+ return true;
+}
+
+bool BamHttp::Open(const IBamIODevice::OpenMode mode) {
+
+ // BamHttp only supports read-only access
+ if ( mode != IBamIODevice::ReadOnly ) {
+ SetErrorString("BamHttp::Open", "writing on this device is not supported");
+ return false;
+ }
+ m_mode = mode;
+
+ // attempt connection to socket
+ if ( !ConnectSocket() ) {
+ SetErrorString("BamHttp::Open", m_socket->GetErrorString());
+ return false;
+ }
+
+ // initialize our file positions
+ m_filePosition = 0;
+ m_fileEndPosition = 0;
+ m_rangeEndPosition = 0;
+
+ // attempt to send initial request (just 'HEAD' to check connection)
+ if ( !SendHeadRequest() ) {
+ SetErrorString("BamHttp::Open", m_socket->GetErrorString());
+ return false;
+ }
+
+ // clear response from HEAD request, not needed
+ ClearResponse();
+
+ // return success
+ return true;
+}
+
+void BamHttp::ParseUrl(const string& url) {
+
+ // clear flag to start
+ m_isUrlParsed = false;
+
+ // make sure url starts with "http://", case-insensitive
+ string tempUrl(url);
+ toLower(tempUrl);
+ const size_t prefixFound = tempUrl.find(HTTP_PREFIX);
+ if ( prefixFound == string::npos )
+ return;
+
+ // find end of host name portion (first '/' hit after the prefix)
+ const size_t firstSlashFound = tempUrl.find(HOST_SEPARATOR, HTTP_PREFIX_LENGTH);
+ if ( firstSlashFound == string::npos ) {
+ ; // no slash found... no filename given along with host?
+ }
+
+ // fetch hostname (check for proxy port)
+ string hostname = tempUrl.substr(HTTP_PREFIX_LENGTH, (firstSlashFound - HTTP_PREFIX_LENGTH));
+ const size_t colonFound = hostname.find(PROXY_SEPARATOR);
+ if ( colonFound != string::npos ) {
+ ; // TODO: handle proxy port (later, just skip for now)
+ } else {
+ m_hostname = hostname;
+ m_port = HTTP_PORT;
+ }
+
+ // store remainder of URL as filename (must be non-empty)
+ string filename = tempUrl.substr(firstSlashFound);
+ if ( filename.empty() )
+ return;
+ m_filename = filename;
+
+ // set parsed OK flag
+ m_isUrlParsed = true;
+}
+
+int64_t BamHttp::Read(char* data, const unsigned int numBytes) {
+
+ // if BamHttp not in a valid state
+ if ( !IsOpen() )
+ return -1;
+
+ int64_t numBytesReadSoFar = 0;
+ while ( numBytesReadSoFar < numBytes ) {
+
+ const size_t remaining = static_cast<size_t>( numBytes - numBytesReadSoFar );
+
+ // if we're not holding a valid GET reponse, get one
+ if ( m_response == 0 ) {
+ if ( !SendGetRequest(remaining) )
+ return -1;
+ }
+ BT_ASSERT_X(m_response, "null HTTP response");
+
+ // check response status code
+ const int statusCode = m_response->GetStatusCode();
+
+ // if we receieved full file contents in response
+ if ( statusCode == 200 ) {
+
+ // try to read 'remaining' bytes from socket
+ const int64_t socketBytesRead = ReadFromSocket(data+numBytesReadSoFar, remaining);
+
+ // if error
+ if ( socketBytesRead < 0 ) {
+ SetErrorString("BamHttp::Read", m_socket->GetErrorString());
+ return -1;
+ }
+
+ // EOF
+ else if ( socketBytesRead == 0 )
+ return numBytesReadSoFar;
+
+ // update counters
+ numBytesReadSoFar += socketBytesRead;
+ m_filePosition += socketBytesRead;
+
+ }
+
+ // else if we received a range of bytes in response
+ else if ( statusCode == 206 ) {
+
+ // if we've exhausted the last request
+ if ( m_filePosition == m_rangeEndPosition ) {
+ if ( !SendGetRequest(remaining) )
+ return -1;
+ }
+
+ else {
+
+ // try to read 'remaining' bytes from socket
+ const int64_t socketBytesRead = ReadFromSocket(data+numBytesReadSoFar, remaining);
+
+ // if error
+ if ( socketBytesRead < 0 ) {
+ SetErrorString("BamHttp::Read", m_socket->GetErrorString());
+ return -1;
+ }
+
+ // maybe EOF
+ else if ( socketBytesRead == 0 ) {
+
+ // if we know we're not at end position, fire off a new request
+ if ( m_fileEndPosition > 0 && m_filePosition < m_fileEndPosition ) {
+ if ( !SendGetRequest() )
+ return -1;
+ } else
+ return numBytesReadSoFar;
+ }
+
+ // update counters
+ numBytesReadSoFar += socketBytesRead;
+ m_filePosition += socketBytesRead;
+ }
+ }
+
+
+ // else some other HTTP status
+ else {
+ SetErrorString("BamHttp::Read", "unsupported status code in response");
+ return -1;
+ }
+ }
+
+ // return actual number of bytes read
+ return numBytesReadSoFar;
+}
+
+int64_t BamHttp::ReadFromSocket(char* data, const unsigned int maxNumBytes) {
+ return m_socket->Read(data, maxNumBytes);
+}
+
+bool BamHttp::ReceiveResponse(void) {
+
+ // fetch header, up until double new line
+ string responseHeader;
+ do {
+
+ // make sure we can read a line
+ if ( !m_socket->WaitForReadLine() )
+ return false;
+
+ // read line & append to full header
+ const string headerLine = m_socket->ReadLine();
+ responseHeader += headerLine;
+
+ } while ( !endsWith(responseHeader, DOUBLE_NEWLINE) );
+
+ // sanity check
+ if ( responseHeader.empty() ) {
+ SetErrorString("BamHttp::ReceiveResponse", "empty HTTP response");
+ Close();
+ return false;
+ }
+
+ // create response from header text
+ m_response = new HttpResponseHeader(responseHeader);
+ if ( !m_response->IsValid() ) {
+ SetErrorString("BamHttp::ReceiveResponse", "could not parse HTTP response");
+ Close();
+ return false;
+ }
+
+ // if we get here, success
+ return true;
+}
+
+bool BamHttp::Seek(const int64_t& position, const int origin) {
+
+ // if HTTP device not in a valid state
+ if ( !IsOpen() ) {
+ SetErrorString("BamHttp::Seek", "cannot seek on unopen connection");
+ return false;
+ }
+
+ // reset the connection
+ DisconnectSocket();
+ if ( !ConnectSocket() ) {
+ SetErrorString("BamHttp::Seek", m_socket->GetErrorString());
+ return false;
+ }
+
+ // udpate file position
+ switch ( origin ) {
+ case SEEK_CUR : m_filePosition += position; break;
+ case SEEK_SET : m_filePosition = position; break;
+ default :
+ SetErrorString("BamHttp::Seek", "unsupported seek origin");
+ return false;
+ }
+
+ // return success
+ return true;
+}
+
+bool BamHttp::SendGetRequest(const size_t numBytes) {
+
+ // clear previous data
+ ClearResponse();
+ if ( m_request )
+ delete m_request;
+ m_socket->ClearBuffer();
+
+ // make sure we're connected
+ if ( !EnsureSocketConnection() )
+ return false;
+
+ // create range string
+ const int64_t endPosition = m_filePosition + std::max(static_cast<size_t>(0x10000), numBytes);
+ stringstream range("");
+ range << BYTES_PREFIX << m_filePosition << '-' << endPosition;
+
+ // create request
+ m_request = new HttpRequestHeader(GET_METHOD, m_filename);
+ m_request->SetField(HOST_HEADER, m_hostname);
+ m_request->SetField(RANGE_HEADER, range.str());
+
+ // send request
+ const string requestHeader = m_request->ToString();
+ const size_t headerSize = requestHeader.size();
+ if ( WriteToSocket(requestHeader.c_str(), headerSize) != headerSize ) {
+ SetErrorString("BamHttp::SendHeadRequest", m_socket->GetErrorString());
+ return false;
+ }
+
+ // ensure clean buffer
+ m_socket->ClearBuffer();
+
+ // wait for response
+ if ( !ReceiveResponse() ) {
+ SetErrorString("BamHttp::SendGetRequest", m_socket->GetErrorString());
+ Close();
+ return false;
+ }
+ BT_ASSERT_X(m_response, "BamHttp::SendGetRequest : null HttpResponse");
+ BT_ASSERT_X(m_response->IsValid(), "BamHttp::SendGetRequest : invalid HttpResponse");
+
+ // check response status code
+ const int statusCode = m_response->GetStatusCode();
+ switch ( statusCode ) {
+
+ // ranged response, as requested
+ case 206 :
+ // get content length if available
+ if ( m_response->ContainsKey(CONTENT_LENGTH_HEADER) ) {
+ const string contentLengthString = m_response->GetValue(CONTENT_LENGTH_HEADER);
+ m_rangeEndPosition = m_filePosition + atoi( contentLengthString.c_str() );
+ }
+ return true;
+
+ // full contents, not range
+ case 200 :
+ {
+ // skip up to current file position
+ RaiiBuffer tmp(0x8000);
+ int64_t numBytesRead = 0;
+ while ( numBytesRead < m_filePosition ) {
+
+ // read data from response
+ const int64_t remaining = m_filePosition - numBytesRead;
+ const size_t bytesToRead = static_cast<size_t>( (remaining > 0x8000) ? 0x8000 : remaining );
+ const int64_t socketBytesRead = ReadFromSocket(tmp.Buffer, bytesToRead);
+
+ // if error
+ if ( socketBytesRead < 0 ) {
+ SetErrorString("BamHttp::SendGetRequest", m_socket->GetErrorString());
+ Close();
+ return false;
+ }
+
+ // else if EOF
+ else if ( socketBytesRead == 0 && m_socket->BufferBytesAvailable() == 0 )
+ break;
+
+ // update byte counter
+ numBytesRead += socketBytesRead;
+ }
+
+ // return success
+ return ( numBytesRead == m_filePosition);
+ }
+
+ // any other status codes
+ default:
+ break;
+ }
+
+ // fail on unexpected status code
+ SetErrorString("BamHttp::SendGetRequest", "unsupported status code in response");
+ Close();
+ return false;
+}
+
+bool BamHttp::SendHeadRequest(void) {
+
+ // ensure clean slate
+ ClearResponse();
+ if ( m_request )
+ delete m_request;
+ m_socket->ClearBuffer();
+
+ // make sure we're connected
+ if ( !EnsureSocketConnection() )
+ return false;
+
+ // create request
+ m_request = new HttpRequestHeader(HEAD_METHOD, m_filename);
+ m_request->SetField(HOST_HEADER, m_hostname);
+
+ // send request
+ const string requestHeader = m_request->ToString();
+ const size_t headerSize = requestHeader.size();
+ if ( WriteToSocket(requestHeader.c_str(), headerSize) != headerSize ) {
+ SetErrorString("BamHttp::SendHeadRequest", m_socket->GetErrorString());
+ return false;
+ }
+
+ m_socket->ClearBuffer();
+
+ // wait for response from server
+ if ( !ReceiveResponse() ) {
+ SetErrorString("BamHttp::SendHeadRequest", m_socket->GetErrorString());
+ Close();
+ return false;
+ }
+ BT_ASSERT_X(m_response, "BamHttp::SendHeadRequest : null HttpResponse");
+ BT_ASSERT_X(m_response->IsValid(), "BamHttp::SendHeadRequest : invalid HttpResponse");
+
+ // get content length if available
+ if ( m_response->ContainsKey(CONTENT_LENGTH_HEADER) ) {
+ const string contentLengthString = m_response->GetValue(CONTENT_LENGTH_HEADER);
+ m_fileEndPosition = atoi( contentLengthString.c_str() ) - 1;
+ }
+
+ // return whether we found any errors
+ return m_socket->GetError() == TcpSocket::NoError;
+}
+
+int64_t BamHttp::Tell(void) const {
+ return ( IsOpen() ? m_filePosition : -1 );
+}
+
+int64_t BamHttp::Write(const char* data, const unsigned int numBytes) {
+ (void)data;
+ (void)numBytes;
+ BT_ASSERT_X(false, "BamHttp::Write : write-mode not supported on this device");
+ SetErrorString("BamHttp::Write", "write-mode not supported on this device");
+ return -1;
+}
+
+int64_t BamHttp::WriteToSocket(const char* data, const unsigned int numBytes) {
+ if ( !m_socket->IsConnected() )
+ return -1;
+ m_socket->ClearBuffer();
+ return m_socket->Write(data, numBytes);
+}
diff --git a/bamtools/src/api/internal/io/BamHttp_p.h b/bamtools/src/api/internal/io/BamHttp_p.h
new file mode 100644
index 0000000..cbbc95c
--- /dev/null
+++ b/bamtools/src/api/internal/io/BamHttp_p.h
@@ -0,0 +1,91 @@
+// ***************************************************************************
+// BamHttp_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides reading/writing of BAM files on HTTP server
+// ***************************************************************************
+
+#ifndef BAMHTTP_P_H
+#define BAMHTTP_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/IBamIODevice.h"
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class HttpRequestHeader;
+class HttpResponseHeader;
+class TcpSocket;
+
+class BamHttp : public IBamIODevice {
+
+ // ctor & dtor
+ public:
+ BamHttp(const std::string& url);
+ ~BamHttp(void);
+
+ // IBamIODevice implementation
+ public:
+ void Close(void);
+ bool IsOpen(void) const;
+ bool IsRandomAccess(void) const;
+ bool Open(const IBamIODevice::OpenMode mode);
+ int64_t Read(char* data, const unsigned int numBytes);
+ bool Seek(const int64_t& position, const int origin = SEEK_SET);
+ int64_t Tell(void) const;
+ int64_t Write(const char* data, const unsigned int numBytes);
+
+ // internal methods
+ private:
+ void ClearResponse(void);
+ bool ConnectSocket(void);
+ void DisconnectSocket(void);
+ bool EnsureSocketConnection(void);
+ void ParseUrl(const std::string& url);
+ int64_t ReadFromSocket(char* data, const unsigned int numBytes);
+ bool ReceiveResponse(void);
+ bool SendGetRequest(const size_t numBytes = 0x10000);
+ bool SendHeadRequest(void);
+ int64_t WriteToSocket(const char* data, const unsigned int numBytes);
+
+ // data members
+ private:
+
+ // our main socket
+ TcpSocket* m_socket;
+
+ // our connection data
+ std::string m_hostname;
+ std::string m_port;
+ std::string m_filename;
+
+ // our last (active) request & response info
+ HttpRequestHeader* m_request;
+ HttpResponseHeader* m_response;
+
+ // internal state flags
+ bool m_isUrlParsed;
+
+ // file position
+ int64_t m_filePosition;
+ int64_t m_fileEndPosition;
+ int64_t m_rangeEndPosition;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMHTTP_P_H
diff --git a/bamtools/src/api/internal/io/BamPipe_p.cpp b/bamtools/src/api/internal/io/BamPipe_p.cpp
new file mode 100644
index 0000000..6af4af1
--- /dev/null
+++ b/bamtools/src/api/internal/io/BamPipe_p.cpp
@@ -0,0 +1,69 @@
+// ***************************************************************************
+// BamPipe_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 18 October 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides BAM pipe-specific IO behavior
+// ***************************************************************************
+
+#include "api/internal/io/BamPipe_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cstdio>
+#include <iostream>
+using namespace std;
+
+BamPipe::BamPipe(void) : ILocalIODevice() { }
+
+BamPipe::~BamPipe(void) { }
+
+bool BamPipe::IsRandomAccess(void) const {
+ return false;
+}
+
+bool BamPipe::Open(const IBamIODevice::OpenMode mode) {
+
+ // make sure we're starting with a fresh pipe
+ Close();
+
+ // open stdin/stdout depending on requested openmode
+#if defined( SYSTEM_NODEJS ) && SYSTEM_NODEJS == 1
+ if ( mode == IBamIODevice::ReadOnly )
+ m_stream = stdin;
+ else if ( mode == IBamIODevice::WriteOnly )
+ m_stream = stdout;
+#else
+ if ( mode == IBamIODevice::ReadOnly )
+ m_stream = freopen(0, "rb", stdin);
+ else if ( mode == IBamIODevice::WriteOnly )
+ m_stream = freopen(0, "wb", stdout);
+#endif // SYSTEM_NODEJS
+
+ else {
+ const string errorType = string( (mode == IBamIODevice::ReadWrite) ? "unsupported"
+ : "unknown" );
+ const string message = errorType + " open mode requested";
+ SetErrorString("BamPipe::Open", message);
+ return false;
+ }
+
+ // check that we obtained a valid FILE*
+ if ( m_stream == 0 ) {
+ const string message_base = string("could not open handle on ");
+ const string message = message_base + ( (mode == IBamIODevice::ReadOnly) ? "stdin"
+ : "stdout" );
+ SetErrorString("BamPipe::Open", message);
+ return false;
+ }
+
+ // store current IO mode & return success
+ m_mode = mode;
+ return true;
+}
+
+bool BamPipe::Seek(const int64_t&, const int) {
+ SetErrorString("BamPipe::Seek", "random access not allowed in FIFO pipe");
+ return false;
+}
diff --git a/bamtools/src/api/internal/io/BamPipe_p.h b/bamtools/src/api/internal/io/BamPipe_p.h
new file mode 100644
index 0000000..1a95cc7
--- /dev/null
+++ b/bamtools/src/api/internal/io/BamPipe_p.h
@@ -0,0 +1,46 @@
+// ***************************************************************************
+// BamPipe_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides BAM pipe-specific IO behavior
+// ***************************************************************************
+
+#ifndef BAMPIPE_P_H
+#define BAMPIPE_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/internal/io/ILocalIODevice_p.h"
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class BamPipe : public ILocalIODevice {
+
+ // ctor & dtor
+ public:
+ BamPipe(void);
+ ~BamPipe(void);
+
+ // IBamIODevice implementation
+ public:
+ bool IsRandomAccess(void) const;
+ bool Open(const IBamIODevice::OpenMode mode);
+ bool Seek(const int64_t& position, const int origin = SEEK_SET);
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMPIPE_P_H
diff --git a/bamtools/src/api/internal/io/BgzfStream_p.cpp b/bamtools/src/api/internal/io/BgzfStream_p.cpp
new file mode 100644
index 0000000..1f4e0d8
--- /dev/null
+++ b/bamtools/src/api/internal/io/BgzfStream_p.cpp
@@ -0,0 +1,469 @@
+// ***************************************************************************
+// BgzfStream_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 17 January 2012(DB)
+// ---------------------------------------------------------------------------
+// Based on BGZF routines developed at the Broad Institute.
+// Provides the basic functionality for reading & writing BGZF files
+// Replaces the old BGZF.* files to avoid clashing with other toolkits
+// ***************************************************************************
+
+#include "api/BamAux.h"
+#include "api/BamConstants.h"
+#include "api/internal/io/BamDeviceFactory_p.h"
+#include "api/internal/io/BgzfStream_p.h"
+#include "api/internal/utils/BamException_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include "zlib.h"
+
+#include <cstring>
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+using namespace std;
+
+// ---------------------------
+// BgzfStream implementation
+// ---------------------------
+
+// constructor
+BgzfStream::BgzfStream(void)
+ : m_blockLength(0)
+ , m_blockOffset(0)
+ , m_blockAddress(0)
+ , m_isWriteCompressed(true)
+ , m_device(0)
+ , m_uncompressedBlock(Constants::BGZF_DEFAULT_BLOCK_SIZE)
+ , m_compressedBlock(Constants::BGZF_MAX_BLOCK_SIZE)
+{ }
+
+// destructor
+BgzfStream::~BgzfStream(void) {
+ Close();
+}
+
+// checks BGZF block header
+bool BgzfStream::CheckBlockHeader(char* header) {
+ return (header[0] == Constants::GZIP_ID1 &&
+ header[1] == Constants::GZIP_ID2 &&
+ header[2] == Z_DEFLATED &&
+ (header[3] & Constants::FLG_FEXTRA) != 0 &&
+ BamTools::UnpackUnsignedShort(&header[10]) == Constants::BGZF_XLEN &&
+ header[12] == Constants::BGZF_ID1 &&
+ header[13] == Constants::BGZF_ID2 &&
+ BamTools::UnpackUnsignedShort(&header[14]) == Constants::BGZF_LEN );
+}
+
+// closes BGZF file
+void BgzfStream::Close(void) {
+
+ // skip if no device open
+ if ( m_device == 0 ) return;
+
+ // if writing to file, flush the current BGZF block,
+ // then write an empty block (as EOF marker)
+ if ( m_device->IsOpen() && (m_device->Mode() == IBamIODevice::WriteOnly) ) {
+ FlushBlock();
+ const size_t blockLength = DeflateBlock(0);
+ m_device->Write(m_compressedBlock.Buffer, blockLength);
+ }
+
+ // close device
+ m_device->Close();
+ delete m_device;
+ m_device = 0;
+
+ // ensure our buffers are cleared out
+ m_uncompressedBlock.Clear();
+ m_compressedBlock.Clear();
+
+ // reset state
+ m_blockLength = 0;
+ m_blockOffset = 0;
+ m_blockAddress = 0;
+ m_isWriteCompressed = true;
+}
+
+// compresses the current block
+size_t BgzfStream::DeflateBlock(int32_t blockLength) {
+
+ // initialize the gzip header
+ char* buffer = m_compressedBlock.Buffer;
+ memset(buffer, 0, 18);
+ buffer[0] = Constants::GZIP_ID1;
+ buffer[1] = Constants::GZIP_ID2;
+ buffer[2] = Constants::CM_DEFLATE;
+ buffer[3] = Constants::FLG_FEXTRA;
+ buffer[9] = Constants::OS_UNKNOWN;
+ buffer[10] = Constants::BGZF_XLEN;
+ buffer[12] = Constants::BGZF_ID1;
+ buffer[13] = Constants::BGZF_ID2;
+ buffer[14] = Constants::BGZF_LEN;
+
+ // set compression level
+ const int compressionLevel = ( m_isWriteCompressed ? Z_DEFAULT_COMPRESSION : 0 );
+
+ // loop to retry for blocks that do not compress enough
+ int inputLength = blockLength;
+ size_t compressedLength = 0;
+ const unsigned int bufferSize = Constants::BGZF_MAX_BLOCK_SIZE;
+
+ while ( true ) {
+
+ // initialize zstream values
+ z_stream zs;
+ zs.zalloc = NULL;
+ zs.zfree = NULL;
+ zs.next_in = (Bytef*)m_uncompressedBlock.Buffer;
+ zs.avail_in = inputLength;
+ zs.next_out = (Bytef*)&buffer[Constants::BGZF_BLOCK_HEADER_LENGTH];
+ zs.avail_out = bufferSize -
+ Constants::BGZF_BLOCK_HEADER_LENGTH -
+ Constants::BGZF_BLOCK_FOOTER_LENGTH;
+
+ // initialize the zlib compression algorithm
+ int status = deflateInit2(&zs,
+ compressionLevel,
+ Z_DEFLATED,
+ Constants::GZIP_WINDOW_BITS,
+ Constants::Z_DEFAULT_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY);
+ if ( status != Z_OK )
+ throw BamException("BgzfStream::DeflateBlock", "zlib deflateInit2 failed");
+
+ // compress the data
+ status = deflate(&zs, Z_FINISH);
+
+ // if not at stream end
+ if ( status != Z_STREAM_END ) {
+
+ deflateEnd(&zs);
+
+ // there was not enough space available in buffer
+ // try to reduce the input length & re-start loop
+ if ( status == Z_OK ) {
+ inputLength -= 1024;
+ if ( inputLength < 0 )
+ throw BamException("BgzfStream::DeflateBlock", "input reduction failed");
+ continue;
+ }
+
+ throw BamException("BgzfStream::DeflateBlock", "zlib deflate failed");
+ }
+
+ // finalize the compression routine
+ status = deflateEnd(&zs);
+ if ( status != Z_OK )
+ throw BamException("BgzfStream::DeflateBlock", "zlib deflateEnd failed");
+
+ // update compressedLength
+ compressedLength = zs.total_out +
+ Constants::BGZF_BLOCK_HEADER_LENGTH +
+ Constants::BGZF_BLOCK_FOOTER_LENGTH;
+ if ( compressedLength > Constants::BGZF_MAX_BLOCK_SIZE )
+ throw BamException("BgzfStream::DeflateBlock", "deflate overflow");
+
+ // quit while loop
+ break;
+ }
+
+ // store the compressed length
+ BamTools::PackUnsignedShort(&buffer[16], static_cast<uint16_t>(compressedLength - 1));
+
+ // store the CRC32 checksum
+ uint32_t crc = crc32(0, NULL, 0);
+ crc = crc32(crc, (Bytef*)m_uncompressedBlock.Buffer, inputLength);
+ BamTools::PackUnsignedInt(&buffer[compressedLength - 8], crc);
+ BamTools::PackUnsignedInt(&buffer[compressedLength - 4], inputLength);
+
+ // ensure that we have less than a block of data left
+ int remaining = blockLength - inputLength;
+ if ( remaining > 0 ) {
+ if ( remaining > inputLength )
+ throw BamException("BgzfStream::DeflateBlock", "after deflate, remainder too large");
+ memcpy(m_uncompressedBlock.Buffer, m_uncompressedBlock.Buffer + inputLength, remaining);
+ }
+
+ // update block data
+ m_blockOffset = remaining;
+
+ // return result
+ return compressedLength;
+}
+
+// flushes the data in the BGZF block
+void BgzfStream::FlushBlock(void) {
+
+ BT_ASSERT_X( m_device, "BgzfStream::FlushBlock() - attempting to flush to null device" );
+
+ // flush all of the remaining blocks
+ while ( m_blockOffset > 0 ) {
+
+ // compress the data block
+ const size_t blockLength = DeflateBlock(m_blockOffset);
+
+ // flush the data to our output device
+ const int64_t numBytesWritten = m_device->Write(m_compressedBlock.Buffer, blockLength);
+
+ // check for device error
+ if ( numBytesWritten < 0 ) {
+ const string message = string("device error: ") + m_device->GetErrorString();
+ throw BamException("BgzfStream::FlushBlock", message);
+ }
+
+ // check that we wrote expected numBytes
+ if ( numBytesWritten != static_cast<int64_t>(blockLength) ) {
+ stringstream s("");
+ s << "expected to write " << blockLength
+ << " bytes during flushing, but wrote " << numBytesWritten;
+ throw BamException("BgzfStream::FlushBlock", s.str());
+ }
+
+ // update block data
+ m_blockAddress += blockLength;
+ }
+}
+
+// decompresses the current block
+size_t BgzfStream::InflateBlock(const size_t& blockLength) {
+
+ // setup zlib stream object
+ z_stream zs;
+ zs.zalloc = NULL;
+ zs.zfree = NULL;
+ zs.next_in = (Bytef*)m_compressedBlock.Buffer + 18;
+ zs.avail_in = blockLength - 16;
+ zs.next_out = (Bytef*)m_uncompressedBlock.Buffer;
+ zs.avail_out = Constants::BGZF_DEFAULT_BLOCK_SIZE;
+
+ // initialize
+ int status = inflateInit2(&zs, Constants::GZIP_WINDOW_BITS);
+ if ( status != Z_OK )
+ throw BamException("BgzfStream::InflateBlock", "zlib inflateInit failed");
+
+ // decompress
+ status = inflate(&zs, Z_FINISH);
+ if ( status != Z_STREAM_END ) {
+ inflateEnd(&zs);
+ throw BamException("BgzfStream::InflateBlock", "zlib inflate failed");
+ }
+
+ // finalize
+ status = inflateEnd(&zs);
+ if ( status != Z_OK ) {
+ inflateEnd(&zs);
+ throw BamException("BgzfStream::InflateBlock", "zlib inflateEnd failed");
+ }
+
+ // return result
+ return zs.total_out;
+}
+
+bool BgzfStream::IsOpen(void) const {
+ if ( m_device == 0 )
+ return false;
+ return m_device->IsOpen();
+}
+
+void BgzfStream::Open(const string& filename, const IBamIODevice::OpenMode mode) {
+
+ // close current device if necessary
+ Close();
+ BT_ASSERT_X( (m_device == 0), "BgzfStream::Open() - unable to properly close previous IO device" );
+
+ // retrieve new IO device depending on filename
+ m_device = BamDeviceFactory::CreateDevice(filename);
+ BT_ASSERT_X( m_device, "BgzfStream::Open() - unable to create IO device from filename" );
+
+ // if device fails to open
+ if ( !m_device->Open(mode) ) {
+ const string deviceError = m_device->GetErrorString();
+ const string message = string("could not open BGZF stream: \n\t") + deviceError;
+ throw BamException("BgzfStream::Open", message);
+ }
+}
+
+// reads BGZF data into a byte buffer
+size_t BgzfStream::Read(char* data, const size_t dataLength) {
+
+ if ( dataLength == 0 )
+ return 0;
+
+ // if stream not open for reading
+ BT_ASSERT_X( m_device, "BgzfStream::Read() - trying to read from null device");
+ if ( !m_device->IsOpen() || (m_device->Mode() != IBamIODevice::ReadOnly) )
+ return 0;
+
+ // read blocks as needed until desired data length is retrieved
+ char* output = data;
+ size_t numBytesRead = 0;
+ while ( numBytesRead < dataLength ) {
+
+ // determine bytes available in current block
+ int bytesAvailable = m_blockLength - m_blockOffset;
+
+ // read (and decompress) next block if needed
+ if ( bytesAvailable <= 0 ) {
+ ReadBlock();
+ bytesAvailable = m_blockLength - m_blockOffset;
+ if ( bytesAvailable <= 0 )
+ break;
+ }
+
+ // copy data from uncompressed source buffer into data destination buffer
+ const size_t copyLength = min( (dataLength-numBytesRead), (size_t)bytesAvailable );
+ memcpy(output, m_uncompressedBlock.Buffer + m_blockOffset, copyLength);
+
+ // update counters
+ m_blockOffset += copyLength;
+ output += copyLength;
+ numBytesRead += copyLength;
+ }
+
+ // update block data
+ if ( m_blockOffset == m_blockLength ) {
+ m_blockAddress = m_device->Tell();
+ m_blockOffset = 0;
+ m_blockLength = 0;
+ }
+
+ // return actual number of bytes read
+ return numBytesRead;
+}
+
+// reads a BGZF block
+void BgzfStream::ReadBlock(void) {
+
+ BT_ASSERT_X( m_device, "BgzfStream::ReadBlock() - trying to read from null IO device");
+
+ // store block's starting address
+ const int64_t blockAddress = m_device->Tell();
+
+ // read block header from file
+ char header[Constants::BGZF_BLOCK_HEADER_LENGTH];
+ int64_t numBytesRead = m_device->Read(header, Constants::BGZF_BLOCK_HEADER_LENGTH);
+
+ // check for device error
+ if ( numBytesRead < 0 ) {
+ const string message = string("device error: ") + m_device->GetErrorString();
+ throw BamException("BgzfStream::ReadBlock", message);
+ }
+
+ // if block header empty
+ if ( numBytesRead == 0 ) {
+ m_blockLength = 0;
+ return;
+ }
+
+ // if block header invalid size
+ if ( numBytesRead != static_cast<int8_t>(Constants::BGZF_BLOCK_HEADER_LENGTH) )
+ throw BamException("BgzfStream::ReadBlock", "invalid block header size");
+
+ // validate block header contents
+ if ( !BgzfStream::CheckBlockHeader(header) )
+ throw BamException("BgzfStream::ReadBlock", "invalid block header contents");
+
+ // copy header contents to compressed buffer
+ const size_t blockLength = BamTools::UnpackUnsignedShort(&header[16]) + 1;
+ memcpy(m_compressedBlock.Buffer, header, Constants::BGZF_BLOCK_HEADER_LENGTH);
+
+ // read remainder of block
+ const size_t remaining = blockLength - Constants::BGZF_BLOCK_HEADER_LENGTH;
+ numBytesRead = m_device->Read(&m_compressedBlock.Buffer[Constants::BGZF_BLOCK_HEADER_LENGTH], remaining);
+
+ // check for device error
+ if ( numBytesRead < 0 ) {
+ const string message = string("device error: ") + m_device->GetErrorString();
+ throw BamException("BgzfStream::ReadBlock", message);
+ }
+
+ // check that we read in expected numBytes
+ if ( numBytesRead != static_cast<int64_t>(remaining) )
+ throw BamException("BgzfStream::ReadBlock", "could not read data from block");
+
+ // decompress block data
+ const size_t newBlockLength = InflateBlock(blockLength);
+
+ // update block data
+ if ( m_blockLength != 0 )
+ m_blockOffset = 0;
+ m_blockAddress = blockAddress;
+ m_blockLength = newBlockLength;
+}
+
+// seek to position in BGZF file
+void BgzfStream::Seek(const int64_t& position) {
+
+ BT_ASSERT_X( m_device, "BgzfStream::Seek() - trying to seek on null IO device");
+
+ // skip if device is not open
+ if ( !IsOpen() ) return;
+
+ // determine adjusted offset & address
+ int blockOffset = (position & 0xFFFF);
+ int64_t blockAddress = (position >> 16) & 0xFFFFFFFFFFFFLL;
+
+ // attempt seek in file
+ if ( m_device->IsRandomAccess() && m_device->Seek(blockAddress) ) {
+
+ // update block data & return success
+ m_blockLength = 0;
+ m_blockAddress = blockAddress;
+ m_blockOffset = blockOffset;
+ }
+ else {
+ stringstream s("");
+ s << "unable to seek to position: " << position;
+ throw BamException("BgzfStream::Seek", s.str());
+ }
+}
+
+void BgzfStream::SetWriteCompressed(bool ok) {
+ m_isWriteCompressed = ok;
+}
+
+// get file position in BGZF file
+int64_t BgzfStream::Tell(void) const {
+ if ( !IsOpen() )
+ return 0;
+ return ( (m_blockAddress << 16) | (m_blockOffset & 0xFFFF) );
+}
+
+// writes the supplied data into the BGZF buffer
+size_t BgzfStream::Write(const char* data, const size_t dataLength) {
+
+ BT_ASSERT_X( m_device, "BgzfStream::Write() - trying to write to null IO device");
+ BT_ASSERT_X( (m_device->Mode() == IBamIODevice::WriteOnly),
+ "BgzfStream::Write() - trying to write to non-writable IO device");
+
+ // skip if file not open for writing
+ if ( !IsOpen() )
+ return 0;
+
+ // write blocks as needed til all data is written
+ size_t numBytesWritten = 0;
+ const char* input = data;
+ const size_t blockLength = Constants::BGZF_DEFAULT_BLOCK_SIZE;
+ while ( numBytesWritten < dataLength ) {
+
+ // copy data contents to uncompressed output buffer
+ unsigned int copyLength = min(blockLength - m_blockOffset, dataLength - numBytesWritten);
+ char* buffer = m_uncompressedBlock.Buffer;
+ memcpy(buffer + m_blockOffset, input, copyLength);
+
+ // update counter
+ m_blockOffset += copyLength;
+ input += copyLength;
+ numBytesWritten += copyLength;
+
+ // flush (& compress) output buffer when full
+ if ( m_blockOffset == static_cast<int32_t>(blockLength) )
+ FlushBlock();
+ }
+
+ // return actual number of bytes written
+ return numBytesWritten;
+}
diff --git a/bamtools/src/api/internal/io/BgzfStream_p.h b/bamtools/src/api/internal/io/BgzfStream_p.h
new file mode 100644
index 0000000..a386c1a
--- /dev/null
+++ b/bamtools/src/api/internal/io/BgzfStream_p.h
@@ -0,0 +1,93 @@
+// ***************************************************************************
+// BgzfStream_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 17 January 2012(DB)
+// ---------------------------------------------------------------------------
+// Based on BGZF routines developed at the Broad Institute.
+// Provides the basic functionality for reading & writing BGZF files
+// Replaces the old BGZF.* files to avoid clashing with other toolkits
+// ***************************************************************************
+
+#ifndef BGZFSTREAM_P_H
+#define BGZFSTREAM_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/api_global.h"
+#include "api/BamAux.h"
+#include "api/IBamIODevice.h"
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class BgzfStream {
+
+ // constructor & destructor
+ public:
+ BgzfStream(void);
+ ~BgzfStream(void);
+
+ // main interface methods
+ public:
+ // closes BGZF file
+ void Close(void);
+ // returns true if BgzfStream open for IO
+ bool IsOpen(void) const;
+ // opens the BGZF file
+ void Open(const std::string& filename, const IBamIODevice::OpenMode mode);
+ // reads BGZF data into a byte buffer
+ size_t Read(char* data, const size_t dataLength);
+ // seek to position in BGZF file
+ void Seek(const int64_t& position);
+ // sets IO device (closes previous, if any, but does not attempt to open)
+ void SetIODevice(IBamIODevice* device);
+ // enable/disable compressed output
+ void SetWriteCompressed(bool ok);
+ // get file position in BGZF file
+ int64_t Tell(void) const;
+ // writes the supplied data into the BGZF buffer
+ size_t Write(const char* data, const size_t dataLength);
+
+ // internal methods
+ private:
+ // compresses the current block
+ size_t DeflateBlock(int32_t blockLength);
+ // flushes the data in the BGZF block
+ void FlushBlock(void);
+ // de-compresses the current block
+ size_t InflateBlock(const size_t& blockLength);
+ // reads a BGZF block
+ void ReadBlock(void);
+
+ // static 'utility' methods
+ public:
+ // checks BGZF block header
+ static bool CheckBlockHeader(char* header);
+
+ // data members
+ public:
+ int32_t m_blockLength;
+ int32_t m_blockOffset;
+ int64_t m_blockAddress;
+
+ bool m_isWriteCompressed;
+ IBamIODevice* m_device;
+
+ RaiiBuffer m_uncompressedBlock;
+ RaiiBuffer m_compressedBlock;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BGZFSTREAM_P_H
diff --git a/bamtools/src/api/internal/io/ByteArray_p.cpp b/bamtools/src/api/internal/io/ByteArray_p.cpp
new file mode 100644
index 0000000..5f54c83
--- /dev/null
+++ b/bamtools/src/api/internal/io/ByteArray_p.cpp
@@ -0,0 +1,111 @@
+// ***************************************************************************
+// ByteArray_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides a dynamic, variable-length byte buffer
+// ***************************************************************************
+
+#include "api/internal/io/ByteArray_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cstdlib>
+#include <cstring>
+using namespace std;
+
+// --------------------------
+// ByteArray implementation
+// --------------------------
+
+ByteArray::ByteArray(void)
+ : m_data()
+{ }
+
+ByteArray::ByteArray(const string& value)
+ : m_data(value.begin(), value.end())
+{ }
+
+ByteArray::ByteArray(const vector<char>& value)
+ : m_data(value)
+{ }
+
+ByteArray::ByteArray(const char* value, size_t n) {
+ const string s(value, n);
+ m_data.assign(s.begin(), s.end());
+}
+
+ByteArray::ByteArray(const ByteArray& other)
+ : m_data(other.m_data)
+{ }
+
+ByteArray::~ByteArray(void) { }
+
+ByteArray& ByteArray::operator=(const ByteArray& other) {
+ m_data = other.m_data;
+ return *this;
+}
+
+void ByteArray::Clear(void) {
+ m_data.clear();
+}
+
+const char* ByteArray::ConstData(void) const {
+ return &m_data[0];
+}
+
+char* ByteArray::Data(void) {
+ return &m_data[0];
+}
+
+const char& ByteArray::operator[](size_t i) const {
+ return m_data[i];
+}
+
+char& ByteArray::operator[](size_t i) {
+ return m_data[i];
+}
+
+size_t ByteArray::IndexOf(const char c, const size_t from, const size_t to) const {
+ const size_t size = ( (to == 0 ) ? m_data.size() : to );
+ for ( size_t i = from; i < size; ++i ) {
+ if ( m_data.at(i) == c )
+ return i;
+ }
+ return m_data.size();
+}
+
+ByteArray& ByteArray::Remove(size_t from, size_t n) {
+
+ // if 'from' outside range, just return
+ const size_t originalSize = m_data.size();
+ if ( from >= originalSize )
+ return *this;
+
+ // if asked to clip from 'from' to end (or beyond), simply resize
+ if ( from + n >= originalSize )
+ Resize(from);
+
+ // otherwise, shift data & resize
+ else {
+ memmove( &m_data[from], &m_data[from+n], (originalSize-from-n) );
+ Resize(originalSize - n);
+ }
+
+ // return reference to modified byte array
+ return *this;
+}
+
+void ByteArray::Resize(size_t n) {
+ m_data.resize(n, 0);
+}
+
+size_t ByteArray::Size(void) const {
+ return m_data.size();
+}
+
+void ByteArray::Squeeze(void) {
+ vector<char> t(m_data);
+ t.swap(m_data);
+}
diff --git a/bamtools/src/api/internal/io/ByteArray_p.h b/bamtools/src/api/internal/io/ByteArray_p.h
new file mode 100644
index 0000000..7e95f6e
--- /dev/null
+++ b/bamtools/src/api/internal/io/ByteArray_p.h
@@ -0,0 +1,69 @@
+// ***************************************************************************
+// ByteArray_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides a dynamic, variable-length byte buffer
+// ***************************************************************************
+
+#ifndef BYTEARRAY_P_H
+#define BYTEARRAY_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/api_global.h"
+#include <string>
+#include <vector>
+
+namespace BamTools {
+namespace Internal {
+
+// provides a wrapper around a byte vector
+class ByteArray {
+
+ // ctors & dtor
+ public:
+ ByteArray(void);
+ ByteArray(const std::string& value);
+ ByteArray(const std::vector<char>& value);
+ ByteArray(const char* value, size_t n);
+ ByteArray(const ByteArray& other);
+ ~ByteArray(void);
+
+ ByteArray& operator=(const ByteArray& other);
+
+ // ByteArray interface
+ public:
+
+ // data access
+ const char* ConstData(void) const;
+ char* Data(void);
+ const char& operator[](size_t i) const;
+ char& operator[](size_t i);
+
+ // byte array manipulation
+ void Clear(void);
+ size_t IndexOf(const char c, const size_t from = 0, const size_t to = 0) const;
+ ByteArray& Remove(size_t from, size_t n);
+ void Resize(size_t n);
+ size_t Size(void) const;
+ void Squeeze(void);
+
+ // data members
+ private:
+ std::vector<char> m_data;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BYTEARRAY_P_H
diff --git a/bamtools/src/api/internal/io/CMakeLists.txt b/bamtools/src/api/internal/io/CMakeLists.txt
new file mode 100644
index 0000000..28153d5
--- /dev/null
+++ b/bamtools/src/api/internal/io/CMakeLists.txt
@@ -0,0 +1,48 @@
+# ==========================
+# BamTools CMakeLists.txt
+# (c) 2011 Derek Barnett
+#
+# src/api/internal/io
+# ==========================
+
+set( InternalIODir "${InternalDir}/io" )
+
+#--------------------------
+# platform-independent IO
+#--------------------------
+set( CommonIOSources
+ ${InternalIODir}/BamDeviceFactory_p.cpp
+ ${InternalIODir}/BamFile_p.cpp
+ ${InternalIODir}/BamFtp_p.cpp
+ ${InternalIODir}/BamHttp_p.cpp
+ ${InternalIODir}/BamPipe_p.cpp
+ ${InternalIODir}/BgzfStream_p.cpp
+ ${InternalIODir}/ByteArray_p.cpp
+ ${InternalIODir}/HostAddress_p.cpp
+ ${InternalIODir}/HostInfo_p.cpp
+ ${InternalIODir}/HttpHeader_p.cpp
+ ${InternalIODir}/ILocalIODevice_p.cpp
+ ${InternalIODir}/RollingBuffer_p.cpp
+ ${InternalIODir}/TcpSocket_p.cpp
+ ${InternalIODir}/TcpSocketEngine_p.cpp
+)
+
+#------------------------
+# platform-dependent IO
+#------------------------
+if( WIN32 )
+ set( PlatformIOSources ${InternalIODir}/TcpSocketEngine_win_p.cpp )
+else()
+ set( PlatformIOSources ${InternalIODir}/TcpSocketEngine_unix_p.cpp )
+endif()
+
+#---------------------------
+# make build-specific list
+#---------------------------
+set( InternalIOSources
+ ${CommonIOSources}
+ ${PlatformIOSources}
+
+ PARENT_SCOPE # <-- leave this last
+)
+
diff --git a/bamtools/src/api/internal/io/HostAddress_p.cpp b/bamtools/src/api/internal/io/HostAddress_p.cpp
new file mode 100644
index 0000000..5c42c5b
--- /dev/null
+++ b/bamtools/src/api/internal/io/HostAddress_p.cpp
@@ -0,0 +1,396 @@
+// ***************************************************************************
+// HostAddress_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 8 December 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides a generic IP address container
+// ***************************************************************************
+
+#include "api/internal/io/HostAddress_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cctype>
+#include <cstdlib>
+#include <sstream>
+#include <vector>
+using namespace std;
+
+// ------------------------
+// static utility methods
+// ------------------------
+
+namespace BamTools {
+namespace Internal {
+
+// split a string into fields, on delimiter character
+static inline
+vector<string> Split(const string& source, char delim) {
+ stringstream ss(source);
+ string field;
+ vector<string> fields;
+ while ( getline(ss, field, delim) )
+ fields.push_back(field);
+ return fields;
+}
+
+// return number of occurrences of @pattern in @source
+static inline
+uint8_t CountHits(const string& source, const string& pattern) {
+
+ uint8_t count(0);
+ size_t found = source.find(pattern);
+ while ( found != string::npos ) {
+ ++count;
+ found = source.find(pattern, found+1);
+ }
+ return count;
+}
+
+static
+bool ParseIp4(const string& address, uint32_t& maybeIp4 ) {
+
+ // split IP address into string fields
+ vector<string> addressFields = Split(address, '.');
+ if ( addressFields.size() != 4 )
+ return false;
+
+ // convert each field to integer value
+ uint32_t ipv4(0);
+ for ( uint8_t i = 0; i < 4; ++i ) {
+
+ const string& field = addressFields.at(i);
+ const size_t fieldSize = field.size();
+ for ( size_t j = 0; j < fieldSize; ++j ) {
+ if ( !isdigit(field[j]) )
+ return false;
+ }
+
+ int value = atoi( addressFields.at(i).c_str() );
+ if ( value < 0 || value > 255 )
+ return false;
+
+ // append byte value
+ ipv4 <<= 8;
+ ipv4 += value;
+ }
+
+ // store 32-bit IP address & return success
+ maybeIp4 = ipv4;
+ return true;
+}
+
+static
+bool ParseIp6(const string& address, uint8_t* maybeIp6 ) {
+
+ string tmp = address;
+
+ // look for '%' char (if found, lop off that part of address)
+ // we're going to ignore any link-local zone index, for now at least
+ const size_t percentFound = tmp.rfind('%');
+ if ( percentFound != string::npos )
+ tmp = tmp.substr(0, percentFound);
+
+ // split IP address into string fields
+ vector<string> fields = Split(tmp, ':');
+ const uint8_t numFields = fields.size();
+ if ( numFields < 3 || numFields > 8 )
+ return false;
+
+ // get number of '::' separators
+ const uint8_t numColonColons = CountHits(tmp, "::");
+ if ( numFields == 8 && numColonColons > 1 )
+ return false;
+
+ // check valid IPv6 'compression'
+ // must be valid 'pure' IPv6 or mixed IPv4/6 notation
+ const size_t dotFound = tmp.find('.');
+ const bool isMixed = ( dotFound != string::npos );
+ if ( numColonColons != 1 && (numFields < (isMixed ? 7 : 8)) )
+ return false;
+
+ // iterate over provided fields
+ size_t index = 16;
+ size_t fillCount = 9 - numFields;
+ for ( int8_t i = numFields - 1; i >= 0; --i ) {
+ if ( index == 0 )
+ return false;
+ const string& field = fields.at(i);
+
+ // if field empty
+ if ( field.empty() ) {
+
+ // if last field empty
+ if ( i == numFields - 1 ) {
+ const string& previousField = fields.at(i-1);
+ if ( previousField.empty() )
+ return false;
+ maybeIp6[--index] = 0;
+ maybeIp6[--index] = 0;
+ }
+
+ // if first field empty
+ else if ( i == 0 ) {
+ // make sure ':' isn't first character
+ const string& nextField = fields.at(i+1);
+ if ( nextField.empty() ) return false;
+ maybeIp6[--index] = 0;
+ maybeIp6[--index] = 0;
+ }
+
+ // fill in 'compressed' 0s
+ else {
+ for ( uint8_t j = 0; j < fillCount; ++j ) {
+ if ( index == 0 ) return false;
+ maybeIp6[--index] = 0;
+ maybeIp6[--index] = 0;
+ }
+ }
+ }
+
+ // field has data
+ else {
+ uint32_t value = static_cast<uint32_t>( strtoul(field.c_str(), 0, 16) );
+
+ if ( value <= 0xffff ) {
+ maybeIp6[--index] = value & 0xff;
+ maybeIp6[--index] = (value >> 8) & 0xff;
+ }
+
+ // possible mixed IPv4/6 notation
+ else {
+
+ // mixed field must be last
+ if ( i != numFields - 1 )
+ return false;
+
+ // parse the IPv4 section
+ uint32_t maybeIp4;
+ if ( !ParseIp4(field, maybeIp4) )
+ return false;
+
+ // store IPv4 fields in IPv6 container
+ maybeIp6[--index] = maybeIp4 & 0xff;
+ maybeIp6[--index] = (maybeIp4 >> 8) & 0xff;
+ maybeIp6[--index] = (maybeIp4 >> 16) & 0xff;
+ maybeIp6[--index] = (maybeIp4 >> 24) & 0xff;
+ --fillCount;
+ }
+ }
+ }
+
+ // should have parsed OK, return success
+ return true;
+}
+
+} // namespace Internal
+} // namespace BamTools
+
+// ----------------------------
+// HostAddress implementation
+// ----------------------------
+
+HostAddress::HostAddress(void)
+ : m_protocol(HostAddress::UnknownNetworkProtocol)
+ , m_ip4Address(0)
+ , m_hasIpAddress(true)
+{ }
+
+HostAddress::HostAddress(const uint32_t ip4Address)
+ : m_protocol(HostAddress::UnknownNetworkProtocol)
+ , m_ip4Address(0)
+ , m_hasIpAddress(true)
+{
+ SetAddress(ip4Address);
+}
+
+HostAddress::HostAddress(const uint8_t* ip6Address)
+ : m_protocol(HostAddress::UnknownNetworkProtocol)
+ , m_ip4Address(0)
+ , m_hasIpAddress(true)
+{
+ SetAddress(ip6Address);
+}
+
+HostAddress::HostAddress(const IPv6Address& ip6Address)
+ : m_protocol(HostAddress::UnknownNetworkProtocol)
+ , m_ip4Address(0)
+ , m_hasIpAddress(true)
+{
+ SetAddress(ip6Address);
+}
+
+HostAddress::HostAddress(const std::string& address)
+ : m_protocol(HostAddress::UnknownNetworkProtocol)
+ , m_ip4Address(0)
+{
+ SetAddress(address);
+}
+
+HostAddress::HostAddress(const HostAddress& other)
+ : m_protocol(other.m_protocol)
+ , m_ip4Address(other.m_ip4Address)
+ , m_ip6Address(other.m_ip6Address)
+ , m_ipString(other.m_ipString)
+ , m_hasIpAddress(other.m_hasIpAddress)
+{ }
+
+HostAddress::~HostAddress(void) { }
+
+bool HostAddress::operator==(const HostAddress& other) const {
+
+ // if self is IPv4
+ if ( m_protocol == HostAddress::IPv4Protocol ) {
+ return ( other.m_protocol == HostAddress::IPv4Protocol &&
+ m_ip4Address == other.m_ip4Address
+ );
+ }
+
+ // if self is IPv6
+ else if ( m_protocol == HostAddress::IPv6Protocol ) {
+ return ( other.m_protocol == HostAddress::IPv6Protocol &&
+ memcmp(&m_ip6Address, &other.m_ip6Address, sizeof(IPv6Address)) == 0
+ );
+ }
+
+ // otherwise compare protocols
+ else return m_protocol == other.m_protocol;
+}
+
+bool HostAddress::operator<(const HostAddress& other) const {
+
+ // if self is IPv4
+ if ( m_protocol == HostAddress::IPv4Protocol ) {
+ if ( other.m_protocol == HostAddress::IPv4Protocol )
+ return m_ip4Address < m_ip4Address;
+ }
+
+ // if self is IPv6
+ else if ( m_protocol == HostAddress::IPv6Protocol ) {
+ if ( other.m_protocol == HostAddress::IPv6Protocol )
+ return (memcmp(&m_ip6Address, &other.m_ip6Address, sizeof(IPv6Address)) < 0);
+ }
+
+ // otherwise compare protocol types
+ return m_protocol < other.m_protocol;
+}
+
+void HostAddress::Clear(void) {
+
+ m_protocol = HostAddress::UnknownNetworkProtocol;
+ m_ip4Address = 0;
+ memset(&m_ip6Address, 0, sizeof(IPv6Address));
+ m_ipString.clear();
+
+ // this may feel funny, but cleared IP (equivalent to '0.0.0.0') is technically valid
+ // and that's not really what this flag is checking anyway
+ //
+ // this flag is false *iff* the string passed in is a 'plain-text' hostname (www.foo.bar)
+ m_hasIpAddress = true;
+}
+
+bool HostAddress::HasIPAddress(void) const {
+ return m_hasIpAddress;
+}
+
+bool HostAddress::IsNull(void) const {
+ return m_protocol == HostAddress::UnknownNetworkProtocol;
+}
+
+uint32_t HostAddress::GetIPv4Address(void) const {
+ return m_ip4Address;
+}
+
+IPv6Address HostAddress::GetIPv6Address(void) const {
+ return m_ip6Address;
+}
+
+std::string HostAddress::GetIPString(void) const {
+
+ stringstream ss("");
+
+ // IPv4 format
+ if ( m_protocol == HostAddress::IPv4Protocol ) {
+ ss << ( (m_ip4Address>>24) & 0xff ) << '.'
+ << ( (m_ip4Address>>16) & 0xff ) << '.'
+ << ( (m_ip4Address>> 8) & 0xff ) << '.'
+ << ( m_ip4Address & 0xff );
+
+ }
+
+ // IPv6 format
+ else if ( m_protocol == HostAddress::IPv6Protocol ) {
+ for ( uint8_t i = 0; i < 8; ++i ) {
+ if ( i != 0 )
+ ss << ':';
+ ss << hex << ( (uint16_t(m_ip6Address[2*i]) << 8) |
+ (uint16_t(m_ip6Address[2*i+1]))
+ );
+ }
+ }
+
+ // return result (empty string if unknown protocol)
+ return ss.str();
+}
+
+HostAddress::NetworkProtocol HostAddress::GetProtocol(void) const {
+ return m_protocol;
+}
+
+bool HostAddress::ParseAddress(void) {
+
+ // all IPv6 addresses should have a ':'
+ string s = m_ipString;
+ size_t found = s.find(':');
+ if ( found != string::npos ) {
+ // try parse IP6 address
+ uint8_t maybeIp6[16];
+ if ( ParseIp6(s, maybeIp6) ) {
+ SetAddress(maybeIp6);
+ m_protocol = HostAddress::IPv6Protocol;
+ return true;
+ }
+ }
+
+ // all IPv4 addresses should have a '.'
+ found = s.find('.');
+ if ( found != string::npos ) {
+ uint32_t maybeIp4(0);
+ if ( ParseIp4(s, maybeIp4) ) {
+ SetAddress(maybeIp4);
+ m_protocol = HostAddress::IPv4Protocol;
+ return true;
+ }
+ }
+
+ // else likely just a plain-text host name "www.foo.bar"
+ // will need to look up IP address info later
+ m_protocol = HostAddress::UnknownNetworkProtocol;
+ return false;
+}
+
+void HostAddress::SetAddress(const uint32_t ip4Address) {
+ m_ip4Address = ip4Address;
+ m_protocol = HostAddress::IPv4Protocol;
+ m_hasIpAddress = true;
+}
+
+void HostAddress::SetAddress(const uint8_t* ip6Address) {
+ for ( uint8_t i = 0; i < 16; ++i )
+ m_ip6Address[i] = ip6Address[i];
+ m_protocol = HostAddress::IPv6Protocol;
+ m_hasIpAddress = true;
+}
+
+void HostAddress::SetAddress(const IPv6Address& ip6Address) {
+ m_ip6Address = ip6Address;
+ m_ip4Address = 0;
+ m_protocol = HostAddress::IPv6Protocol;
+ m_hasIpAddress = true;
+}
+
+void HostAddress::SetAddress(const std::string& address) {
+ m_ipString = address;
+ m_hasIpAddress = ParseAddress();
+}
diff --git a/bamtools/src/api/internal/io/HostAddress_p.h b/bamtools/src/api/internal/io/HostAddress_p.h
new file mode 100644
index 0000000..4c1b360
--- /dev/null
+++ b/bamtools/src/api/internal/io/HostAddress_p.h
@@ -0,0 +1,100 @@
+// ***************************************************************************
+// HostAddress_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides a generic IP address container
+// ***************************************************************************
+
+#ifndef HOSTADDRESS_P_H
+#define HOSTADDRESS_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/api_global.h"
+#include <cstring>
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+struct IPv6Address {
+
+ // ctor
+ inline IPv6Address(void) { memset(&data, 0, sizeof(uint8_t)*16); }
+
+ // data access (no bounds checking)
+ inline uint8_t& operator[](size_t index) { return data[index]; }
+ inline uint8_t operator[](size_t index) const { return data[index]; }
+
+ // data
+ uint8_t data[16];
+};
+
+class HostAddress {
+
+ // enums
+ public:
+ enum NetworkProtocol { UnknownNetworkProtocol = -1
+ , IPv4Protocol = 0
+ , IPv6Protocol
+ };
+
+ // ctors & dtor
+ public:
+ HostAddress(void);
+ explicit HostAddress(const uint32_t ip4Address);
+ explicit HostAddress(const uint8_t* ip6Address);
+ explicit HostAddress(const IPv6Address& ip6Address);
+ explicit HostAddress(const std::string& address);
+ HostAddress(const HostAddress& other);
+ ~HostAddress(void);
+
+ // HostAddress interface
+ public:
+ void Clear(void);
+ bool HasIPAddress(void) const; // returns whether string address could be converted to IP address
+ bool IsNull(void) const;
+
+ uint32_t GetIPv4Address(void) const;
+ IPv6Address GetIPv6Address(void) const;
+ std::string GetIPString(void) const;
+ HostAddress::NetworkProtocol GetProtocol(void) const;
+
+ void SetAddress(const uint32_t ip4Address);
+ void SetAddress(const uint8_t* ip6Address);
+ void SetAddress(const IPv6Address& ip6Address);
+ void SetAddress(const std::string& address);
+
+ // HostAddress comparison operators
+ public:
+ bool operator==(const HostAddress& other) const;
+ bool operator!=(const HostAddress& other) const { return !( operator==(other) ); }
+ bool operator<(const HostAddress& other) const;
+
+ // internal methods
+ private:
+ bool ParseAddress(void);
+
+ // data members
+ private:
+ HostAddress::NetworkProtocol m_protocol;
+ uint32_t m_ip4Address;
+ IPv6Address m_ip6Address;
+ std::string m_ipString;
+ bool m_hasIpAddress; // true until string passed in, then signifies whether string was an IP
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // HOSTADDRESS_P_H
diff --git a/bamtools/src/api/internal/io/HostInfo_p.cpp b/bamtools/src/api/internal/io/HostInfo_p.cpp
new file mode 100644
index 0000000..40b1047
--- /dev/null
+++ b/bamtools/src/api/internal/io/HostInfo_p.cpp
@@ -0,0 +1,224 @@
+// ***************************************************************************
+// HostInfo_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 8 December 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides DNS lookup functionality for hostname & its discovered addresses
+// ***************************************************************************
+
+#include "api/internal/io/HostInfo_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+// platorm-specifics
+#ifdef _WIN32
+# include "api/internal/io/NetWin_p.h"
+#else
+# include "api/internal/io/NetUnix_p.h"
+#endif
+
+// standard C++ includes
+#include <cstdlib>
+#include <cstring>
+#include <set>
+using namespace std;
+
+// -------------------------
+// HostInfo implementation
+// -------------------------
+
+HostInfo::HostInfo(void)
+ : m_error(HostInfo::NoError)
+{ }
+
+HostInfo::HostInfo(const HostInfo& other)
+ : m_hostName(other.m_hostName)
+ , m_addresses(other.m_addresses)
+ , m_error(other.m_error)
+ , m_errorString(other.m_errorString)
+{ }
+
+HostInfo::~HostInfo(void) { }
+
+vector<HostAddress> HostInfo::Addresses(void) const {
+ return m_addresses;
+}
+
+HostInfo::ErrorType HostInfo::GetError(void) const {
+ return m_error;
+}
+
+string HostInfo::GetErrorString(void) const {
+ return m_errorString;
+}
+
+string HostInfo::HostName(void) const {
+ return m_hostName;
+}
+
+void HostInfo::SetAddresses(const std::vector<HostAddress>& addresses) {
+ m_addresses = addresses;
+}
+
+void HostInfo::SetError(const HostInfo::ErrorType error) {
+ m_error = error;
+}
+
+void HostInfo::SetErrorString(const std::string& errorString) {
+ m_errorString = errorString;
+}
+
+void HostInfo::SetHostName(const string& name) {
+ m_hostName = name;
+}
+
+// ---------------------------------
+// HostInfo::Lookup(host, port)
+// - the real "heavy-lifter" here
+// ---------------------------------
+
+HostInfo HostInfo::Lookup(const string& hostname, const string& port) {
+
+ HostInfo result;
+ result.SetHostName(hostname);
+ set<HostAddress> uniqueAddresses;
+
+#ifdef _WIN32
+ WindowsSockInit init;
+#endif
+
+ HostAddress address;
+ address.SetAddress(hostname);
+
+ // if hostname is an IP string ('0.0.0.0' or IPv6 format)
+ // do reverse lookup for host domain name
+ //
+ // TODO: might just remove this... not sure if proper 'hostname' from IP string is needed
+ //
+ // so far, haven't been able to successfully fetch a domain name with reverse DNS
+ // getnameinfo() on test sites just returns original IP string. BUT this is likely a rare
+ // case that client code tries to use an IP string and the connection should work fine
+ // anyway. GetHostName() just won't quite show what I was hoping for. :(
+ if ( address.HasIPAddress() ) {
+
+ const uint16_t portNum = static_cast<uint16_t>( atoi(port.c_str()) );
+
+ sockaddr_in sa4;
+ sockaddr_in6 sa6;
+ sockaddr* sa = 0;
+ BT_SOCKLEN_T saSize = 0;
+
+ // IPv4
+ if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
+ sa = (sockaddr*)&sa4;
+ saSize = sizeof(sa4);
+ memset(&sa4, 0, sizeof(sa4));
+ sa4.sin_family = AF_INET;
+ sa4.sin_addr.s_addr = htonl(address.GetIPv4Address());
+ sa4.sin_port = htons(portNum);
+ }
+
+ // IPv6
+ else if ( address.GetProtocol() == HostAddress::IPv4Protocol ){
+ sa = (sockaddr*)&sa6;
+ saSize = sizeof(sa6);
+ memset(&sa6, 0, sizeof(sa6));
+ sa6.sin6_family = AF_INET6;
+ memcpy(sa6.sin6_addr.s6_addr, address.GetIPv6Address().data, sizeof(sa6.sin6_addr.s6_addr));
+ sa6.sin6_port = htons(portNum);
+ }
+
+ // unknown (should be unreachable)
+ else BT_ASSERT_X(false, "HostInfo::Lookup: unknown network protocol");
+
+ // lookup name for IP
+ char hbuf[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ if ( sa && (getnameinfo(sa, saSize, hbuf, sizeof(hbuf), serv, sizeof(serv), 0) == 0) )
+ result.SetHostName(string(hbuf));
+
+ // if no domain name found, just use the original address's IP string
+ if ( result.HostName().empty() )
+ result.SetHostName(address.GetIPString());
+
+ // store address in HostInfo
+ uniqueAddresses.insert(address);
+ }
+
+ // otherwise, hostname is a domain name ('www.foo.bar')
+ // do 'normal' lookup
+ else {
+
+ // setup address lookup 'hints'
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; // allow either IPv4 or IPv6
+ hints.ai_socktype = SOCK_STREAM; // for TCP
+ hints.ai_protocol = IPPROTO_TCP;
+
+ // fetch addresses for requested hostname/port
+ addrinfo* res;
+ int status = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &res );
+
+ // if everything OK
+ if ( status == 0 ) {
+
+ // iterate over all IP addresses found
+ addrinfo* p = res;
+ for ( ; p != NULL; p = p->ai_next ) {
+
+ // IPv4
+ if ( p->ai_family == AF_INET ) {
+ sockaddr_in* ipv4 = (sockaddr_in*)p->ai_addr;
+ HostAddress a( ntohl(ipv4->sin_addr.s_addr) );
+ uniqueAddresses.insert(a);
+ }
+
+ // IPv6
+ else if ( p->ai_family == AF_INET6 ) {
+ sockaddr_in6* ipv6 = (sockaddr_in6*)p->ai_addr;
+ HostAddress a(ipv6->sin6_addr.s6_addr);
+ uniqueAddresses.insert(a);
+ }
+ }
+
+ // if we iterated, but no addresses were stored
+ if ( uniqueAddresses.empty() && (p == NULL) ) {
+ result.SetError(HostInfo::UnknownError);
+ result.SetErrorString("HostInfo: unknown address types found");
+ }
+ }
+
+ // handle error cases
+ else if (
+#ifndef _WIN32
+ status == EAI_NONAME
+ || status == EAI_FAIL
+# ifdef EAI_NODATA
+ || status == EAI_NODATA // officially deprecated, but just in case we happen to hit it
+# endif // EAI_NODATA
+
+#else // _WIN32
+ WSAGetLastError() == WSAHOST_NOT_FOUND
+ || WSAGetLastError() == WSANO_DATA
+ || WSAGetLastError() == WSANO_RECOVERY
+#endif // _WIN32
+ )
+ {
+ result.SetError(HostInfo::HostNotFound);
+ result.SetErrorString("HostInfo: host not found");
+ }
+ else {
+ result.SetError(HostInfo::UnknownError);
+ result.SetErrorString("HostInfo: unknown error encountered");
+ }
+
+ // cleanup
+ freeaddrinfo(res);
+ }
+
+ // store fetched addresses (converting set -> vector) in result & return
+ result.SetAddresses( vector<HostAddress>(uniqueAddresses.begin(), uniqueAddresses.end()) );
+ return result;
+}
diff --git a/bamtools/src/api/internal/io/HostInfo_p.h b/bamtools/src/api/internal/io/HostInfo_p.h
new file mode 100644
index 0000000..ad03d37
--- /dev/null
+++ b/bamtools/src/api/internal/io/HostInfo_p.h
@@ -0,0 +1,76 @@
+// ***************************************************************************
+// HostInfo_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides DNS lookup functionality for hostname/IP addresses
+// ***************************************************************************
+
+#ifndef HOSTINFO_P_H
+#define HOSTINFO_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/internal/io/HostAddress_p.h"
+#include <string>
+#include <vector>
+
+namespace BamTools {
+namespace Internal {
+
+class HostInfo {
+
+ public:
+ enum ErrorType { NoError = 0
+ , HostNotFound
+ , UnknownError
+ };
+
+ // ctors & dtor
+ public:
+ HostInfo(void);
+ HostInfo(const HostInfo& other);
+ ~HostInfo(void);
+
+ // HostInfo interface
+ public:
+ std::string HostName(void) const;
+ void SetHostName(const std::string& name);
+
+ std::vector<HostAddress> Addresses(void) const;
+ void SetAddresses(const std::vector<HostAddress>& addresses);
+
+ HostInfo::ErrorType GetError(void) const;
+ std::string GetErrorString(void) const;
+
+ // internal methods
+ private:
+ void SetError(const HostInfo::ErrorType error);
+ void SetErrorString(const std::string& errorString);
+
+ // static methods
+ public:
+ static HostInfo Lookup(const std::string& hostname,
+ const std::string& port);
+
+ // data members
+ private:
+ std::string m_hostName;
+ std::vector<HostAddress> m_addresses;
+ HostInfo::ErrorType m_error;
+ std::string m_errorString;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // HOSTINFO_P_H
diff --git a/bamtools/src/api/internal/io/HttpHeader_p.cpp b/bamtools/src/api/internal/io/HttpHeader_p.cpp
new file mode 100644
index 0000000..156df26
--- /dev/null
+++ b/bamtools/src/api/internal/io/HttpHeader_p.cpp
@@ -0,0 +1,395 @@
+// ***************************************************************************
+// HttpHeader_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 13 January 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides a generic interface for parsing/generating HTTP headers, along
+// with specialized request & response header types
+// ***************************************************************************
+
+#include "api/internal/io/HttpHeader_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cstdlib>
+#include <sstream>
+#include <vector>
+using namespace std;
+
+namespace BamTools {
+
+// -----------
+// constants
+// -----------
+
+namespace Constants {
+
+static const char CAR_RET_CHAR = '\r';
+static const char COLON_CHAR = ':';
+static const char DOT_CHAR = '.';
+static const char NEWLINE_CHAR = '\n';
+static const char SPACE_CHAR = ' ';
+static const char TAB_CHAR = '\t';
+
+static const string FIELD_NEWLINE = "\r\n";
+static const string FIELD_SEPARATOR = ": ";
+static const string HTTP_STRING = "HTTP/";
+
+} // namespace Constants
+
+// ------------------------
+// static utility methods
+// ------------------------
+
+namespace Internal {
+
+static inline
+bool IsSpace(const char c) {
+ const int n = static_cast<int>(c);
+ return ( n== 0 || (n <= 13 && n >= 9) );
+}
+
+// split on hitting single char delim
+static vector<string> Split(const string& source, const char delim) {
+ stringstream ss(source);
+ string field;
+ vector<string> fields;
+ while ( getline(ss, field, delim) )
+ fields.push_back(field);
+ return fields;
+}
+
+static string Trim(const string& source) {
+
+ // skip if empty string
+ if ( source.empty() )
+ return source;
+
+ // fetch string data
+ const char* s = source.data(); // ignoring null-term on purpose
+ const size_t size = source.size();
+ size_t start = 0;
+ size_t end = size-1;
+
+ // skip if no spaces at start or end
+ if ( !IsSpace(s[start]) && !IsSpace( s[end] ) )
+ return source;
+
+ // remove leading whitespace
+ while ( (start != end) && IsSpace(s[start]) )
+ ++start;
+
+ // remove trailing whitespace
+ if ( start <= end ) {
+ while ( end && IsSpace(s[end]) )
+ --end;
+ }
+
+ // return result
+ return string(s + start, (end-start) + 1);
+}
+
+} // namespace Internal
+} // namespace BamTools
+
+// ---------------------------
+// HttpHeader implementation
+// ---------------------------
+
+HttpHeader::HttpHeader(void)
+ : m_isValid(true)
+ , m_majorVersion(1)
+ , m_minorVersion(1)
+{ }
+
+HttpHeader::HttpHeader(const string& s)
+ : m_isValid(true)
+ , m_majorVersion(1)
+ , m_minorVersion(1)
+{
+ Parse(s);
+}
+
+HttpHeader::~HttpHeader(void) { }
+
+bool HttpHeader::ContainsKey(const string& key) const {
+ return ( m_fields.find(key) != m_fields.end() );
+}
+
+int HttpHeader::GetMajorVersion(void) const {
+ return m_majorVersion;
+}
+
+int HttpHeader::GetMinorVersion(void) const {
+ return m_minorVersion;
+}
+
+string HttpHeader::GetValue(const string& key) {
+ if ( ContainsKey(key) )
+ return m_fields[key];
+ else return string();
+}
+
+bool HttpHeader::IsValid(void) const {
+ return m_isValid;
+}
+
+void HttpHeader::Parse(const string& s) {
+
+ // trim whitespace from input string
+ const string trimmed = Trim(s);
+
+ // split into list of header lines
+ vector<string> rawFields = Split(trimmed, Constants::NEWLINE_CHAR);
+
+ // prep our 'cleaned' fields container
+ vector<string> cleanFields;
+ cleanFields.reserve(rawFields.size());
+
+ // remove any empty fields and clean any trailing windows-style carriage returns ('\r')
+ vector<string>::iterator rawFieldIter = rawFields.begin();
+ vector<string>::iterator rawFieldEnd = rawFields.end();
+ for ( ; rawFieldIter != rawFieldEnd; ++rawFieldIter ) {
+ string& field = (*rawFieldIter);
+
+ // skip empty fields
+ if ( field.empty() )
+ continue;
+
+ // remove carriage returns
+ const size_t fieldSize = field.size();
+ if ( field[fieldSize-1] == Constants::CAR_RET_CHAR )
+ field.resize(fieldSize-1);
+
+ // store cleaned field
+ cleanFields.push_back(field);
+ }
+
+ // skip add'l processing if nothing here
+ if ( cleanFields.empty() )
+ return;
+
+ // parse header lines
+ int lineNumber = 0;
+ vector<string>::const_iterator fieldIter = cleanFields.begin();
+ vector<string>::const_iterator fieldEnd = cleanFields.end();
+ for ( ; fieldIter != fieldEnd; ++fieldIter, ++lineNumber ) {
+ if ( !ParseLine( (*fieldIter), lineNumber ) ) {
+ m_isValid = false;
+ return;
+ }
+ }
+}
+
+bool HttpHeader::ParseLine(const string& line, int) {
+
+ // find colon position, return failure if not found
+ const size_t colonFound = line.find(Constants::COLON_CHAR);
+ if ( colonFound == string::npos )
+ return false;
+
+ // store key/value (without leading/trailing whitespace) & return success
+ const string key = Trim(line.substr(0, colonFound));
+ const string value = Trim(line.substr(colonFound+1));
+ m_fields[key] = value;
+ return true;
+}
+
+void HttpHeader::RemoveField(const string& key) {
+ m_fields.erase(key);
+}
+
+void HttpHeader::SetField(const string& key, const string& value) {
+ m_fields[key] = value;
+}
+
+void HttpHeader::SetValid(bool ok) {
+ m_isValid = ok;
+}
+
+void HttpHeader::SetVersion(int major, int minor) {
+ m_majorVersion = major;
+ m_minorVersion = minor;
+}
+
+string HttpHeader::ToString(void) const {
+ string result("");
+ if ( m_isValid ) {
+ map<string, string>::const_iterator fieldIter = m_fields.begin();
+ map<string, string>::const_iterator fieldEnd = m_fields.end();
+ for ( ; fieldIter != fieldEnd; ++fieldIter ) {
+ const string& key = (*fieldIter).first;
+ const string& value = (*fieldIter).second;
+ const string& line = key + Constants::FIELD_SEPARATOR +
+ value + Constants::FIELD_NEWLINE;
+ result += line;
+ }
+ }
+ return result;
+}
+
+// ----------------------------------
+// HttpRequestHeader implementation
+// ----------------------------------
+
+HttpRequestHeader::HttpRequestHeader(const string& method,
+ const string& resource,
+ int majorVersion,
+ int minorVersion)
+ : HttpHeader()
+ , m_method(method)
+ , m_resource(resource)
+{
+ SetVersion(majorVersion, minorVersion);
+}
+
+HttpRequestHeader::~HttpRequestHeader(void) { }
+
+string HttpRequestHeader::GetMethod(void) const {
+ return m_method;
+}
+
+string HttpRequestHeader::GetResource(void) const {
+ return m_resource;
+}
+
+bool HttpRequestHeader::ParseLine(const string& line, int lineNumber) {
+
+ // if not 'request line', just let base class parse
+ if ( lineNumber != 0 )
+ return HttpHeader::ParseLine(line, lineNumber);
+
+ // fail if empty line
+ if ( line.empty() )
+ return false;
+
+ // walk through request line, storing positions
+ // GET /path/to/resource HTTP/1.1
+ // ^ ^^ ^^
+ const size_t foundMethod = line.find_first_not_of(Constants::SPACE_CHAR); // skip any leading whitespace
+ if ( foundMethod == string::npos ) return false;
+ const size_t foundFirstSpace = line.find(Constants::SPACE_CHAR, foundMethod+1);
+ if ( foundFirstSpace == string::npos ) return false;
+ const size_t foundResource = line.find_first_not_of(Constants::SPACE_CHAR, foundFirstSpace+1);
+ if ( foundResource == string::npos ) return false;
+ const size_t foundSecondSpace = line.find(Constants::SPACE_CHAR, foundResource+1);
+ if ( foundSecondSpace == string::npos ) return false;
+ const size_t foundVersion= line.find_first_not_of(Constants::SPACE_CHAR, foundSecondSpace+1);
+ if ( foundVersion == string::npos ) return false;
+
+ // parse out method & resource
+ m_method = line.substr(foundMethod, foundFirstSpace - foundMethod);
+ m_resource = line.substr(foundResource, foundSecondSpace - foundResource);
+
+ // parse out version numbers
+ const string temp = line.substr(foundVersion);
+ if ( (temp.find(Constants::HTTP_STRING) != 0) || (temp.size() != 8) )
+ return false;
+ const int major = static_cast<int>(temp.at(5) - '0');
+ const int minor = static_cast<int>(temp.at(7) - '0');
+ SetVersion(major, minor);
+
+ // if we get here, return success
+ return true;
+}
+
+string HttpRequestHeader::ToString(void) const {
+ stringstream request("");
+ request << m_method << Constants::SPACE_CHAR
+ << m_resource << Constants::SPACE_CHAR
+ << Constants::HTTP_STRING << GetMajorVersion() << Constants::DOT_CHAR << GetMinorVersion()
+ << Constants::FIELD_NEWLINE
+ << HttpHeader::ToString()
+ << Constants::FIELD_NEWLINE;
+ return request.str();
+}
+
+// -----------------------------------
+// HttpResponseHeader implementation
+// -----------------------------------
+
+HttpResponseHeader::HttpResponseHeader(const int statusCode,
+ const string& reason,
+ int majorVersion,
+ int minorVersion)
+
+ : HttpHeader()
+ , m_statusCode(statusCode)
+ , m_reason(reason)
+{
+ SetVersion(majorVersion, minorVersion);
+}
+
+HttpResponseHeader::HttpResponseHeader(const string& s)
+ : HttpHeader()
+ , m_statusCode(0)
+{
+ Parse(s);
+}
+
+HttpResponseHeader::~HttpResponseHeader(void) { }
+
+string HttpResponseHeader::GetReason(void) const {
+ return m_reason;
+}
+
+int HttpResponseHeader::GetStatusCode(void) const {
+ return m_statusCode;
+}
+
+bool HttpResponseHeader::ParseLine(const string& line, int lineNumber) {
+
+ // if not 'status line', just let base class
+ if ( lineNumber != 0 )
+ return HttpHeader::ParseLine(line, lineNumber);
+
+ // fail if empty line
+ if ( line.empty() )
+ return false;
+
+ // walk through status line, storing positions
+ // HTTP/1.1 200 OK
+ // ^ ^^ ^^
+
+ const size_t foundVersion = line.find_first_not_of(Constants::SPACE_CHAR); // skip any leading whitespace
+ if ( foundVersion == string::npos ) return false;
+ const size_t foundFirstSpace = line.find(Constants::SPACE_CHAR, foundVersion+1);
+ if ( foundFirstSpace == string::npos ) return false;
+ const size_t foundStatusCode = line.find_first_not_of(Constants::SPACE_CHAR, foundFirstSpace+1);
+ if ( foundStatusCode == string::npos ) return false;
+ const size_t foundSecondSpace = line.find(Constants::SPACE_CHAR, foundStatusCode+1);
+ if ( foundSecondSpace == string::npos ) return false;
+ const size_t foundReason= line.find_first_not_of(Constants::SPACE_CHAR, foundSecondSpace+1);
+ if ( foundReason == string::npos ) return false;
+
+ // parse version numbers
+ string temp = line.substr(foundVersion, foundFirstSpace - foundVersion);
+ if ( (temp.find(Constants::HTTP_STRING) != 0) || (temp.size() != 8) )
+ return false;
+ const int major = static_cast<int>(temp.at(5) - '0');
+ const int minor = static_cast<int>(temp.at(7) - '0');
+ SetVersion(major, minor);
+
+ // parse status code
+ temp = line.substr(foundStatusCode, foundSecondSpace - foundStatusCode);
+ if ( temp.size() != 3 ) return false;
+ m_statusCode = atoi( temp.c_str() );
+
+ // reason phrase should be everything else left
+ m_reason = line.substr(foundReason);
+
+ // if we get here, return success
+ return true;
+}
+
+string HttpResponseHeader::ToString(void) const {
+ stringstream response("");
+ response << Constants::HTTP_STRING << GetMajorVersion() << Constants::DOT_CHAR << GetMinorVersion()
+ << Constants::SPACE_CHAR << m_statusCode
+ << Constants::SPACE_CHAR << m_reason
+ << Constants::FIELD_NEWLINE
+ << HttpHeader::ToString()
+ << Constants::FIELD_NEWLINE;
+ return response.str();
+}
diff --git a/bamtools/src/api/internal/io/HttpHeader_p.h b/bamtools/src/api/internal/io/HttpHeader_p.h
new file mode 100644
index 0000000..6b838ff
--- /dev/null
+++ b/bamtools/src/api/internal/io/HttpHeader_p.h
@@ -0,0 +1,132 @@
+// ***************************************************************************
+// HttpHeader_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 13 January 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides a generic interface for parsing/generating HTTP headers, along
+// with specialized request & response header types
+// ***************************************************************************
+
+#ifndef HTTP_HEADER_P_H
+#define HTTP_HEADER_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/api_global.h"
+#include <map>
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class HttpHeader {
+
+ // ctors & dtor
+ public:
+ HttpHeader(void);
+ HttpHeader(const std::string& s);
+ virtual ~HttpHeader(void);
+
+ // HttpHeader interface
+ public:
+
+ // header field=>value access
+ bool ContainsKey(const std::string& key) const;
+ std::string GetValue(const std::string& key);
+ void RemoveField(const std::string& key);
+ void SetField(const std::string& key, const std::string& value);
+
+ // get formatted header string
+ virtual std::string ToString(void) const;
+
+ // query HTTP version used
+ int GetMajorVersion(void) const;
+ int GetMinorVersion(void) const;
+
+ // see if header was parsed OK
+ bool IsValid(void) const;
+
+ // internal methods
+ protected:
+ void Parse(const std::string& s);
+ virtual bool ParseLine(const std::string& line, int lineNumber);
+ void SetValid(bool ok);
+ void SetVersion(int major, int minor);
+
+ // data members
+ private:
+ std::map<std::string, std::string> m_fields;
+
+ bool m_isValid; // should usually be true, only false if error processing a header line
+ int m_majorVersion;
+ int m_minorVersion;
+};
+
+class HttpRequestHeader : public HttpHeader {
+
+ // ctor & dtor
+ public:
+ HttpRequestHeader(const std::string& method, // "GET", "HEAD", ...
+ const std::string& resource, // filename
+ int majorVersion = 1, // version info
+ int minorVersion = 1);
+ ~HttpRequestHeader(void);
+
+ // HttpRequestHeader interface
+ public:
+ std::string GetMethod(void) const;
+ std::string GetResource(void) const;
+
+ // HttpHeader implementation
+ public:
+ std::string ToString(void) const;
+ protected:
+ bool ParseLine(const std::string& line, int lineNumber);
+
+ // data members
+ private:
+ std::string m_method;
+ std::string m_resource;
+};
+
+class HttpResponseHeader : public HttpHeader {
+
+ // ctor & dtor
+ public:
+ HttpResponseHeader(const int statusCode, // 200, 404, etc
+ const std::string& reason = std::string(), // 'reason phrase' for code
+ int majorVersion = 1, // version info
+ int minorVersion = 1);
+ HttpResponseHeader(const std::string& s);
+ ~HttpResponseHeader(void);
+
+ // HttpRequestHeader interface
+ public:
+ std::string GetReason(void) const;
+ int GetStatusCode(void) const;
+
+ // HttpHeader implementation
+ public:
+ std::string ToString(void) const;
+ protected:
+ bool ParseLine(const std::string& line, int lineNumber);
+
+ // data members
+ private:
+ int m_statusCode;
+ std::string m_reason;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // HTTP_HEADER_P_H
diff --git a/bamtools/src/api/internal/io/ILocalIODevice_p.cpp b/bamtools/src/api/internal/io/ILocalIODevice_p.cpp
new file mode 100644
index 0000000..d515728
--- /dev/null
+++ b/bamtools/src/api/internal/io/ILocalIODevice_p.cpp
@@ -0,0 +1,56 @@
+// ***************************************************************************
+// ILocalIODevice_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 27 July 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides shared behavior for files & pipes
+// ***************************************************************************
+
+#include "api/internal/io/ILocalIODevice_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cstdio>
+using namespace std;
+
+ILocalIODevice::ILocalIODevice(void)
+ : IBamIODevice()
+ , m_stream(0)
+{ }
+
+ILocalIODevice::~ILocalIODevice(void) {
+ Close();
+}
+
+void ILocalIODevice::Close(void) {
+
+ // skip if not open
+ if ( !IsOpen() )
+ return;
+
+ // flush & close FILE*
+ fflush(m_stream);
+ fclose(m_stream);
+ m_stream = 0;
+
+ // reset other device state
+ m_mode = IBamIODevice::NotOpen;
+}
+
+int64_t ILocalIODevice::Read(char* data, const unsigned int numBytes) {
+ BT_ASSERT_X( m_stream, "ILocalIODevice::Read: trying to read from null stream" );
+ BT_ASSERT_X( (m_mode & IBamIODevice::ReadOnly), "ILocalIODevice::Read: device not in read-able mode");
+ return static_cast<int64_t>( fread(data, sizeof(char), numBytes, m_stream) );
+}
+
+int64_t ILocalIODevice::Tell(void) const {
+ BT_ASSERT_X( m_stream, "ILocalIODevice::Tell: trying to get file position fromnull stream" );
+ return ftell64(m_stream);
+}
+
+int64_t ILocalIODevice::Write(const char* data, const unsigned int numBytes) {
+ BT_ASSERT_X( m_stream, "ILocalIODevice::Write: tryint to write to null stream" );
+ BT_ASSERT_X( (m_mode & IBamIODevice::WriteOnly), "ILocalIODevice::Write: device not in write-able mode" );
+ return static_cast<int64_t>( fwrite(data, sizeof(char), numBytes, m_stream) );
+}
diff --git a/bamtools/src/api/internal/io/ILocalIODevice_p.h b/bamtools/src/api/internal/io/ILocalIODevice_p.h
new file mode 100644
index 0000000..cf01f90
--- /dev/null
+++ b/bamtools/src/api/internal/io/ILocalIODevice_p.h
@@ -0,0 +1,50 @@
+// ***************************************************************************
+// ILocalIODevice_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides shared behavior for files & pipes
+// ***************************************************************************
+
+#ifndef ILOCALIODEVICE_P_H
+#define ILOCALIODEVICE_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/IBamIODevice.h"
+
+namespace BamTools {
+namespace Internal {
+
+class ILocalIODevice : public IBamIODevice {
+
+ // ctor & dtor
+ public:
+ ILocalIODevice(void);
+ virtual ~ILocalIODevice(void);
+
+ // IBamIODevice implementation
+ public:
+ virtual void Close(void);
+ virtual int64_t Read(char* data, const unsigned int numBytes);
+ virtual int64_t Tell(void) const;
+ virtual int64_t Write(const char* data, const unsigned int numBytes);
+
+ // data members
+ protected:
+ FILE* m_stream;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // ILOCALIODEVICE_P_H
diff --git a/bamtools/src/api/internal/io/NetUnix_p.h b/bamtools/src/api/internal/io/NetUnix_p.h
new file mode 100644
index 0000000..8cf75f8
--- /dev/null
+++ b/bamtools/src/api/internal/io/NetUnix_p.h
@@ -0,0 +1,39 @@
+// ***************************************************************************
+// NetUnix_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides common networking-related includes, etc. for all UNIX-like systems
+// ***************************************************************************
+
+#ifndef NETUNIX_P_H
+#define NETUNIX_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef _WIN32 // <-- source files only include the proper Net*_p.h, but this is a double-check
+
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#ifndef BT_SOCKLEN_T
+# define BT_SOCKLEN_T socklen_t
+#endif
+
+#endif // _WIN32
+#endif // NETUNIX_P_H
diff --git a/bamtools/src/api/internal/io/NetWin_p.h b/bamtools/src/api/internal/io/NetWin_p.h
new file mode 100644
index 0000000..3796e01
--- /dev/null
+++ b/bamtools/src/api/internal/io/NetWin_p.h
@@ -0,0 +1,60 @@
+// ***************************************************************************
+// NetWin_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 8 December 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides common networking-related includes, etc. for Windows systems
+//
+// Note: requires Windows XP or later
+// ***************************************************************************
+
+#ifndef NETWIN_P_H
+#define NETWIN_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#ifdef _WIN32 // <-- source files only include the proper Net*_p.h, but this is a double-check
+
+#include <winsock2.h> // <-- should bring 'windows.h' along with it
+#include <Ws2tcpip.h>
+
+#ifndef BT_SOCKLEN_T
+# define BT_SOCKLEN_T int
+#endif
+
+#ifdef _MSC_VER
+# pragma comment(lib, "ws2_32.lib")
+#endif
+
+namespace BamTools {
+namespace Internal {
+
+// use RAII to ensure WSA is initialized
+class WindowsSockInit {
+ public:
+ WindowsSockInit(void) {
+ WSAData wsadata;
+ WSAStartup(MAKEWORD(2,2), &wsadata); // catch error ?
+ }
+
+ ~WindowsSockInit(void) {
+ WSACleanup();
+ }
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // _WIN32
+
+#endif // NETWIN_P_H
+
diff --git a/bamtools/src/api/internal/io/RollingBuffer_p.cpp b/bamtools/src/api/internal/io/RollingBuffer_p.cpp
new file mode 100644
index 0000000..c712b57
--- /dev/null
+++ b/bamtools/src/api/internal/io/RollingBuffer_p.cpp
@@ -0,0 +1,314 @@
+// ***************************************************************************
+// RollingBuffer_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 8 December 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides a dynamic I/O FIFO byte queue, which removes bytes as they are
+// read from the front of the buffer and grows to accept bytes being written
+// to buffer end.
+//
+// implementation note: basically a 'smart' wrapper around 1..* ByteArrays
+// ***************************************************************************
+
+#include "api/internal/io/RollingBuffer_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <climits>
+#include <cstring>
+#include <algorithm>
+#include <string>
+using namespace std;
+
+// ------------------------------
+// RollingBuffer implementation
+// ------------------------------
+
+RollingBuffer::RollingBuffer(size_t growth)
+ : m_bufferGrowth(growth)
+{
+ // buffer always contains at least 1 (maybe empty) byte array
+ m_data.push_back( ByteArray() );
+
+ // set cleared state
+ Clear();
+}
+
+RollingBuffer::~RollingBuffer(void) { }
+
+size_t RollingBuffer::BlockSize(void) const {
+
+ // if only one byte array in buffer <- needed?
+ if ( m_tailBufferIndex == 0 )
+ return m_tail - m_head;
+
+ // otherwise return remaining num bytes in first array
+ const ByteArray& first = m_data.front();
+ return first.Size() - m_head;
+}
+
+bool RollingBuffer::CanReadLine(void) const {
+ return IndexOf('\n') != string::npos;
+}
+
+void RollingBuffer::Chop(size_t n) {
+
+ // update buffer size
+ if ( n > m_totalBufferSize )
+ m_totalBufferSize = 0;
+ else
+ m_totalBufferSize -= n;
+
+ // loop until target case hit
+ for ( ; ; ) {
+
+ // if only one array, decrement tail
+ if ( m_tailBufferIndex == 0 ) {
+ m_tail -= n;
+
+ // if all data chopped
+ if ( m_tail <= m_head ) {
+ m_head = 0;
+ m_tail = 0;
+ }
+ return;
+ }
+
+ // if there's room in last byte array to 'chop', just decrement tail
+ if ( n <= m_tail ) {
+ m_tail -= n;
+ return;
+ }
+
+ // otherwise we're going to overlap our internal byte arrays
+ // reduce our chop amount by the amount of data in the last byte array
+ n -= m_tail;
+
+ // remove last byte array & set tail to it's end
+ m_data.pop_back();
+ --m_tailBufferIndex;
+ m_tail = m_data.at(m_tailBufferIndex).Size();
+ }
+
+ // if buffer is now empty, reset state & clear up memory
+ if ( IsEmpty() )
+ Clear();
+}
+
+void RollingBuffer::Clear(void) {
+
+ // remove all byte arrays (except first)
+ m_data.erase( m_data.begin()+1, m_data.end() );
+
+ // clear out first byte array
+ m_data[0].Resize(0);
+ m_data[0].Squeeze();
+
+ // reset index & size markers
+ m_head = 0;
+ m_tail = 0;
+ m_tailBufferIndex = 0;
+ m_totalBufferSize = 0;
+}
+
+void RollingBuffer::Free(size_t n) {
+
+ // update buffer size
+ if ( n > m_totalBufferSize )
+ m_totalBufferSize = 0;
+ else
+ m_totalBufferSize -= n;
+
+ // loop until target case hit
+ for ( ; ; ) {
+
+ const size_t blockSize = BlockSize();
+
+ // if there's room in current array
+ if ( n < blockSize ) {
+
+ // shift 'head' over @n bytes
+ m_head += n;
+
+ // check for emptied, single byte array
+ if ( m_head == m_tail && m_tailBufferIndex == 0 ) {
+ m_head = 0;
+ m_tail = 0;
+ }
+
+ break;
+ }
+
+ // otherwise we need to check next byte array
+ // first update amount to remove
+ n -= blockSize;
+
+ // special case - there was only 1 array
+ if ( m_data.size() == 1 ) {
+ if ( m_data.at(0).Size() != m_bufferGrowth )
+ m_data[0].Resize(m_bufferGrowth);
+ m_head = 0;
+ m_tail = 0;
+ m_tailBufferIndex = 0;
+ break;
+ }
+
+ // otherwise, remove first array and move to next iteration
+ m_data.pop_front();
+ --m_tailBufferIndex;
+ m_head = 0;
+ }
+
+ // if buffer is now empty, reset state & clear up memory
+ if ( IsEmpty() )
+ Clear();
+}
+
+size_t RollingBuffer::IndexOf(char c) const {
+
+ // skip processing if empty buffer
+ if ( IsEmpty() )
+ return string::npos;
+
+ size_t index(0);
+
+ // iterate over byte arrays
+ const size_t numBuffers = m_data.size();
+ for ( size_t i = 0; i < numBuffers; ++i ) {
+ const ByteArray& current = m_data.at(i);
+
+ // if on first array, use head; else 0
+ const size_t start = ( (i==0) ? m_head : 0 );
+
+ // if on last array, set end; else use current byte array size
+ const size_t end = ( (i==m_tailBufferIndex) ? m_tail : current.Size());
+
+ // look through this iteration's byte array for @c
+ const char* p = current.ConstData()+start;
+ for ( size_t j = start; j < end; ++j ) {
+ if ( *p++ == c )
+ return index;
+ ++index;
+ }
+ }
+
+ // no match found
+ return string::npos;
+}
+
+bool RollingBuffer::IsEmpty(void) const {
+ return (m_tailBufferIndex == 0) && (m_tail == 0);
+}
+
+size_t RollingBuffer::Read(char* dest, size_t max) {
+
+ size_t bytesToRead = std::min(Size(), max);
+ size_t bytesReadSoFar = 0;
+
+ while ( bytesReadSoFar < bytesToRead ) {
+ const char* readPtr = ReadPointer();
+ size_t blockBytes = std::min( (bytesToRead - bytesReadSoFar), BlockSize() );
+ if ( dest )
+ memcpy(dest+bytesReadSoFar, readPtr, blockBytes);
+ bytesReadSoFar += blockBytes;
+ Free(blockBytes);
+ }
+
+ return bytesReadSoFar;
+}
+
+size_t RollingBuffer::ReadLine(char* dest, size_t max) {
+
+ // if we can't read line or if max is 0
+ if ( !CanReadLine() || max == 0 )
+ return 0;
+
+ // otherwise, read until we hit newline
+ size_t bytesReadSoFar = 0;
+ bool finished = false;
+ while ( !finished ) {
+
+ const size_t index = IndexOf('\n');
+ const char* readPtr = ReadPointer();
+ size_t bytesToRead = std::min( (index+1)-bytesReadSoFar, BlockSize() );
+ bytesToRead = std::min( bytesToRead, (max-1)-bytesReadSoFar );
+ memcpy(dest+bytesReadSoFar, readPtr, bytesToRead);
+ bytesReadSoFar += bytesToRead;
+ Free(bytesToRead);
+
+ if ( !((bytesReadSoFar < index+1) && (bytesReadSoFar < max-1)) )
+ finished = true;
+ }
+
+ // null terminate 'dest' & return numBytesRead
+ dest[bytesReadSoFar] = '\0';
+ return bytesReadSoFar;
+}
+
+const char* RollingBuffer::ReadPointer(void) const {
+
+ // return null if empty buffer
+ if ( m_data.empty() )
+ return 0;
+
+ // otherwise return pointer to current position
+ const ByteArray& first = m_data.front();
+ return first.ConstData() + m_head;
+}
+
+char* RollingBuffer::Reserve(size_t n) {
+
+ // if empty buffer
+ if ( m_totalBufferSize == 0 ) {
+ m_data[0].Resize( std::max(m_bufferGrowth, n) );
+ m_totalBufferSize += n;
+ m_tail = n;
+ return m_data[m_tailBufferIndex].Data();
+ }
+
+ // increment buffer's byte count
+ m_totalBufferSize += n;
+
+ // if buffer already contains enough space to fit @n more bytes
+ if ( (m_tail + n) <= m_data.at(m_tailBufferIndex).Size() ) {
+
+ // fetch write pointer at current 'tail', increment tail by @n & return
+ char* ptr = m_data[m_tailBufferIndex].Data(); //+ m_tail;
+ m_tail += n;
+ return ptr;
+ }
+
+ // if last byte array isn't half full
+ if ( m_tail < m_data.at(m_tailBufferIndex).Size()/2 ) {
+
+ // we'll allow simple resize
+ m_data[m_tailBufferIndex].Resize(m_tail + n);
+
+ // fetch write pointer at current 'tail', increment tail by @n & return
+ char* ptr = m_data[m_tailBufferIndex].Data(); //+ m_tail;
+ m_tail += n;
+ return ptr;
+ }
+
+ // otherwise, shrink last byte array to current used size
+ m_data[m_tailBufferIndex].Resize(m_tail);
+
+ // then append new byte array
+ m_data.push_back( ByteArray() );
+ ++m_tailBufferIndex;
+ m_data[m_tailBufferIndex].Resize( std::max(m_bufferGrowth, n) );
+ m_tail = n;
+
+ // return write-able pointer on new array
+ return m_data[m_tailBufferIndex].Data();
+}
+
+size_t RollingBuffer::Size(void) const {
+ return m_totalBufferSize;
+}
+
+void RollingBuffer::Write(const char* src, size_t n) {
+ char* writePtr = Reserve(n);
+ memcpy(writePtr, src, n);
+}
diff --git a/bamtools/src/api/internal/io/RollingBuffer_p.h b/bamtools/src/api/internal/io/RollingBuffer_p.h
new file mode 100644
index 0000000..55550c0
--- /dev/null
+++ b/bamtools/src/api/internal/io/RollingBuffer_p.h
@@ -0,0 +1,87 @@
+// ***************************************************************************
+// RollingBuffer_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 7 December 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides a dynamic I/O FIFO byte queue, which removes bytes as they are
+// read from the front of the buffer and grows to accept bytes being written
+// to buffer end.
+//
+// implementation note: basically a 'smart' wrapper around 1..* ByteArrays
+// ***************************************************************************
+
+#ifndef ROLLINGBUFFER_P_H
+#define ROLLINGBUFFER_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/api_global.h"
+#include "api/internal/io/ByteArray_p.h"
+#include <deque>
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class RollingBuffer {
+
+ // ctors & dtor
+ public:
+ RollingBuffer(size_t growth);
+ ~RollingBuffer(void);
+
+ // RollingBuffer interface
+ public:
+
+ // returns current buffer size
+ size_t BlockSize(void) const;
+ // checks buffer for new line
+ bool CanReadLine(void) const;
+ // frees @n bytes from end of buffer
+ void Chop(size_t n);
+ // clears entire buffer structure
+ void Clear(void);
+ // frees @n bytes from front of buffer
+ void Free(size_t n);
+ // checks buffer for @c
+ size_t IndexOf(char c) const;
+ // returns whether buffer contains data
+ bool IsEmpty(void) const;
+ // reads up to @maxLen bytes into @dest
+ // returns exactly how many bytes were read from buffer
+ size_t Read(char* dest, size_t max);
+ // reads until newline (or up to @maxLen bytes)
+ // returns exactly how many bytes were read from buffer
+ size_t ReadLine(char* dest, size_t max);
+ // returns a C-fxn compatible char* to byte data
+ const char* ReadPointer(void) const;
+ // ensures that buffer contains space for @n incoming bytes, returns write-able char*
+ char* Reserve(size_t n);
+ // returns current number of bytes stored in buffer
+ size_t Size(void) const;
+ // reserves space for @n bytes, then appends contents of @src to buffer
+ void Write(const char* src, size_t n);
+
+ // data members
+ private:
+ size_t m_head; // index into current data (next char)
+ size_t m_tail; // index into last data position
+ size_t m_tailBufferIndex; // m_data::size() - 1
+ size_t m_totalBufferSize; // total buffer size
+ size_t m_bufferGrowth; // new buffers are typically initialized with this size
+ std::deque<ByteArray> m_data; // basic 'buffer of buffers'
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // ROLLINGBUFFER_P_H
diff --git a/bamtools/src/api/internal/io/TcpSocketEngine_p.cpp b/bamtools/src/api/internal/io/TcpSocketEngine_p.cpp
new file mode 100644
index 0000000..65587b2
--- /dev/null
+++ b/bamtools/src/api/internal/io/TcpSocketEngine_p.cpp
@@ -0,0 +1,196 @@
+// ***************************************************************************
+// TcpSocketEngine_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 8 December 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides low-level implementation of TCP I/O
+// ***************************************************************************
+
+// N.B. - this file contains the top-level, platform-independent logic. "Native" methods
+// are called as needed from the TcpSocketEngine_<X>.cpp files. Selection of the proper
+// native method file should have been handled at build-time by CMake.
+
+#include "api/internal/io/HostInfo_p.h"
+#include "api/internal/io/TcpSocketEngine_p.h"
+
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+TcpSocketEngine::TcpSocketEngine(void)
+ : m_socketDescriptor(-1)
+// , m_localPort(0)
+ , m_remotePort(0)
+ , m_socketError(TcpSocket::UnknownSocketError)
+ , m_socketState(TcpSocket::UnconnectedState)
+{ }
+
+TcpSocketEngine::TcpSocketEngine(const TcpSocketEngine& other)
+ : m_socketDescriptor(other.m_socketDescriptor)
+// , m_localAddress(other.m_localAddress)
+ , m_remoteAddress(other.m_remoteAddress)
+// , m_localPort(other.m_localPort)
+ , m_remotePort(other.m_remotePort)
+ , m_socketError(other.m_socketError)
+ , m_socketState(other.m_socketState)
+ , m_errorString(other.m_errorString)
+{ }
+
+TcpSocketEngine::~TcpSocketEngine(void) {
+ Close();
+}
+
+void TcpSocketEngine::Close(void) {
+
+ // close socket if we have valid FD
+ if ( m_socketDescriptor != -1 ) {
+ nativeClose();
+ m_socketDescriptor = -1;
+ }
+
+ // reset state
+ m_socketState = TcpSocket::UnconnectedState;
+// m_localAddress.Clear();
+ m_remoteAddress.Clear();
+// m_localPort = 0;
+ m_remotePort = 0;
+}
+
+bool TcpSocketEngine::Connect(const HostAddress& address, const uint16_t port) {
+
+ // return failure if invalid FD or already connected
+ if ( !IsValid() || (m_socketState == TcpSocket::ConnectedState) ) {
+ // TODO: set error string
+ return false;
+ }
+
+ // attempt to connect to host address on requested port
+ if ( !nativeConnect(address, port) ) {
+ // TODO: set error string
+ return false;
+ }
+
+ // if successful, store remote host address port & return success
+ // TODO: (later) fetch proxied remote & local host/port here
+ m_remoteAddress = address;
+ m_remotePort = port;
+ return true;
+}
+
+std::string TcpSocketEngine::GetErrorString(void) const {
+ return m_errorString;
+}
+
+//HostAddress TcpSocketEngine::GetLocalAddress(void) const {
+// return m_localAddress;
+//}
+
+//uint16_t TcpSocketEngine::GetLocalPort(void) const {
+// return m_localPort;
+//}
+
+HostAddress TcpSocketEngine::GetRemoteAddress(void) const {
+ return m_remoteAddress;
+}
+
+uint16_t TcpSocketEngine::GetRemotePort(void) const {
+ return m_remotePort;
+}
+
+int TcpSocketEngine::GetSocketDescriptor(void) const {
+ return m_socketDescriptor;
+}
+
+TcpSocket::SocketError TcpSocketEngine::GetSocketError(void) {
+ return m_socketError;
+}
+
+TcpSocket::SocketState TcpSocketEngine::GetSocketState(void) {
+ return m_socketState;
+}
+
+bool TcpSocketEngine::Initialize(HostAddress::NetworkProtocol protocol) {
+
+ // close current socket if we have one open
+ if ( IsValid() )
+ Close();
+
+ // attempt to create new socket
+ return nativeCreateSocket(protocol);
+}
+
+bool TcpSocketEngine::IsValid(void) const {
+ return (m_socketDescriptor != -1);
+}
+
+int64_t TcpSocketEngine::NumBytesAvailable(void) const {
+
+ // return 0 if socket FD is invalid
+ if ( !IsValid() ) {
+ // TODO: set error string
+ return -1;
+ }
+
+ // otherwise check socket to see how much is ready
+ return nativeNumBytesAvailable();
+}
+
+int64_t TcpSocketEngine::Read(char* dest, size_t max) {
+
+ // return failure if can't read
+ if ( !IsValid() || (m_socketState != TcpSocket::ConnectedState) )
+ return -1;
+
+ // otherwise return number of bytes read
+ return nativeRead(dest, max);
+}
+
+bool TcpSocketEngine::WaitForRead(int msec, bool* timedOut) {
+
+ // reset timedOut flag
+ *timedOut = false;
+
+ // need to wait for our socket to be ready to read
+ const int ret = nativeSelect(msec, true);
+
+ // if timed out
+ if ( ret == 0 ) {
+ *timedOut = true;
+ m_socketError = TcpSocket::SocketTimeoutError;
+ m_errorString = "socket timed out";
+ }
+
+ // return if any sockets available for reading
+ return ( ret > 0 );
+}
+
+bool TcpSocketEngine::WaitForWrite(int msec, bool* timedOut) {
+
+ // reset timedOut flag
+ *timedOut = false;
+
+ // need to wait for our socket to be ready to write
+ const int ret = nativeSelect(msec, false);
+
+ // if timed out
+ if ( ret == 0 ) {
+ *timedOut = true;
+ m_socketError = TcpSocket::SocketTimeoutError;
+ m_errorString = "socket timed out";
+ }
+
+ // return if any sockets available for reading
+ return ( ret > 0 );
+}
+
+int64_t TcpSocketEngine::Write(const char* data, size_t length) {
+
+ // return failure if can't write
+ if ( !IsValid() || (m_socketState != TcpSocket::ConnectedState) ) {
+ // TODO: set error string
+ return -1;
+ }
+
+ // otherwise return number of bytes written
+ return nativeWrite(data, length);
+}
diff --git a/bamtools/src/api/internal/io/TcpSocketEngine_p.h b/bamtools/src/api/internal/io/TcpSocketEngine_p.h
new file mode 100644
index 0000000..9218278
--- /dev/null
+++ b/bamtools/src/api/internal/io/TcpSocketEngine_p.h
@@ -0,0 +1,103 @@
+// ***************************************************************************
+// TcpSocketEngine_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 8 December 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides low-level implementation of TCP I/O
+// ***************************************************************************
+
+#ifndef TCPSOCKETENGINE_P_H
+#define TCPSOCKETENGINE_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/internal/io/HostAddress_p.h"
+#include "api/internal/io/TcpSocket_p.h"
+
+#ifdef _WIN32
+# include "api/internal/io/NetWin_p.h"
+#endif
+
+namespace BamTools {
+namespace Internal {
+
+struct TcpSocketEngine {
+
+ // ctors & dtor
+ public:
+ TcpSocketEngine(void);
+ TcpSocketEngine(const TcpSocketEngine& other);
+ ~TcpSocketEngine(void);
+
+ // TcpSocketEngine interface
+ public:
+
+ // connection-related methods
+ void Close(void);
+ bool Connect(const HostAddress& address, const uint16_t port);
+ bool Initialize(HostAddress::NetworkProtocol protocol);
+ bool IsValid(void) const;
+
+ // IO-related methods
+ int64_t NumBytesAvailable(void) const;
+ int64_t Read(char* dest, size_t max);
+ int64_t Write(const char* data, size_t length);
+
+ bool WaitForRead(int msec, bool* timedOut);
+ bool WaitForWrite(int msec, bool* timedOut);
+
+ // query connection state
+// HostAddress GetLocalAddress(void) const;
+// uint16_t GetLocalPort(void) const;
+ HostAddress GetRemoteAddress(void) const;
+ uint16_t GetRemotePort(void) const;
+
+ int GetSocketDescriptor(void) const;
+ TcpSocket::SocketError GetSocketError(void);
+ TcpSocket::SocketState GetSocketState(void);
+
+ std::string GetErrorString(void) const;
+
+ // platform-dependent internal methods
+ // provided in the corresponding TcpSocketEngine_<OS>_p.cpp
+ private:
+ void nativeClose(void);
+ bool nativeConnect(const HostAddress& address, const uint16_t port);
+ bool nativeCreateSocket(HostAddress::NetworkProtocol protocol);
+ void nativeDisconnect(void);
+ int64_t nativeNumBytesAvailable(void) const;
+ int64_t nativeRead(char* dest, size_t max);
+ int nativeSelect(int msecs, bool isRead) const;
+ int64_t nativeWrite(const char* data, size_t length);
+
+ // data members
+ private:
+ int m_socketDescriptor;
+
+// HostAddress m_localAddress;
+ HostAddress m_remoteAddress;
+// uint16_t m_localPort;
+ uint16_t m_remotePort;
+
+ TcpSocket::SocketError m_socketError;
+ TcpSocket::SocketState m_socketState;
+ std::string m_errorString;
+
+#ifdef _WIN32
+ WindowsSockInit m_win;
+#endif
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // TCPSOCKETENGINE_P_H
diff --git a/bamtools/src/api/internal/io/TcpSocketEngine_unix_p.cpp b/bamtools/src/api/internal/io/TcpSocketEngine_unix_p.cpp
new file mode 100644
index 0000000..cf598af
--- /dev/null
+++ b/bamtools/src/api/internal/io/TcpSocketEngine_unix_p.cpp
@@ -0,0 +1,216 @@
+// ***************************************************************************
+// TcpSocketEngine_unix_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 15 November 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides low-level implementation of TCP I/O for all UNIX-like systems
+// ***************************************************************************
+
+#include "api/internal/io/TcpSocketEngine_p.h"
+#include "api/internal/io/NetUnix_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#ifdef SUN_OS
+#include <sys/filio.h>
+#endif
+
+#include <cerrno>
+#include <ctime>
+#include <iostream>
+using namespace std;
+
+// ------------------------
+// static utility methods
+// ------------------------
+
+namespace BamTools {
+namespace Internal {
+
+} // namespace Internal
+} // namespace BamTools
+
+// --------------------------------
+// TcpSocketEngine implementation
+// --------------------------------
+
+void TcpSocketEngine::nativeClose(void) {
+ close(m_socketDescriptor);
+}
+
+bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) {
+
+ // setup connection parameters from address/port
+ sockaddr_in sockAddrIPv4;
+ sockaddr_in6 sockAddrIPv6;
+ sockaddr* sockAddrPtr = 0;
+ BT_SOCKLEN_T sockAddrSize = 0;
+
+ // IPv6
+ if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
+
+ memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
+ sockAddrIPv6.sin6_family = AF_INET6;
+ sockAddrIPv6.sin6_port = htons(port);
+
+ IPv6Address ip6 = address.GetIPv6Address();
+ memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
+
+ sockAddrSize = sizeof(sockAddrIPv6);
+ sockAddrPtr = (sockaddr*)&sockAddrIPv6;
+ }
+
+ // IPv4
+ else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
+
+ memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
+ sockAddrIPv4.sin_family = AF_INET;
+ sockAddrIPv4.sin_port = htons(port);
+ sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address());
+
+ sockAddrSize = sizeof(sockAddrIPv4);
+ sockAddrPtr = (sockaddr*)&sockAddrIPv4;
+ }
+
+ // unknown (should be unreachable)
+ else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
+
+ // attempt connection
+ int connectResult = connect(m_socketDescriptor, sockAddrPtr, sockAddrSize);
+
+ // if failed, handle error
+ if ( connectResult == -1 ) {
+
+ // ensure state is set before checking errno
+ m_socketState = TcpSocket::UnconnectedState;
+
+ // set error type/message depending on errno
+ switch ( errno ) { // <-- potential thread issues later? but can't get error type from connectResult
+
+ case EISCONN:
+ m_socketState = TcpSocket::ConnectedState; // socket was already connected
+ break;
+ case ECONNREFUSED:
+ case EINVAL:
+ m_socketError = TcpSocket::ConnectionRefusedError;
+ m_errorString = "connection refused";
+ break;
+ case ETIMEDOUT:
+ m_socketError = TcpSocket::NetworkError;
+ m_errorString = "connection timed out";
+ break;
+ case EHOSTUNREACH:
+ m_socketError = TcpSocket::NetworkError;
+ m_errorString = "host unreachable";
+ break;
+ case ENETUNREACH:
+ m_socketError = TcpSocket::NetworkError;
+ m_errorString = "network unreachable";
+ break;
+ case EADDRINUSE:
+ m_socketError = TcpSocket::SocketResourceError;
+ m_errorString = "address already in use";
+ break;
+ case EACCES:
+ case EPERM:
+ m_socketError = TcpSocket::SocketAccessError;
+ m_errorString = "permission denied";
+ break;
+ default:
+ break;
+ }
+
+ // double check that we're not in 'connected' state; if so, return failure
+ if ( m_socketState != TcpSocket::ConnectedState )
+ return false;
+ }
+
+ // otherwise, we should be good
+ // update state & return success
+ m_socketState = TcpSocket::ConnectedState;
+ return true;
+}
+
+bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
+
+ // get protocol value for requested protocol type
+ const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6
+ : AF_INET );
+
+ // attempt to create socket
+ int socketFd = socket(protocolNum, SOCK_STREAM, IPPROTO_TCP);
+
+ // if we fetched an invalid socket descriptor
+ if ( socketFd <= 0 ) {
+
+ // see what error we got
+ switch ( errno ) {
+ case EPROTONOSUPPORT:
+ case EAFNOSUPPORT:
+ case EINVAL:
+ m_socketError = TcpSocket::UnsupportedSocketOperationError;
+ m_errorString = "protocol not supported";
+ break;
+ case ENFILE:
+ case EMFILE:
+ case ENOBUFS:
+ case ENOMEM:
+ m_socketError = TcpSocket::SocketResourceError;
+ m_errorString = "out of resources";
+ break;
+ case EACCES:
+ m_socketError = TcpSocket::SocketAccessError;
+ m_errorString = "permission denied";
+ break;
+ default:
+ break;
+ }
+
+ // return failure
+ return false;
+ }
+
+ // otherwise, store our socket FD & return success
+ m_socketDescriptor = socketFd;
+ return true;
+}
+
+int64_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
+
+ // fetch number of bytes, return 0 on error
+ int numBytes(0);
+ if ( ioctl(m_socketDescriptor, FIONREAD, (char*)&numBytes) < 0 )
+ return -1;
+ return static_cast<int64_t>(numBytes);
+}
+
+int64_t TcpSocketEngine::nativeRead(char* dest, size_t max) {
+ const ssize_t ret = read(m_socketDescriptor, dest, max);
+ return static_cast<int64_t>(ret);
+}
+
+// negative value for msecs will block (forever) until ready
+int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
+
+ // set up FD set
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(m_socketDescriptor, &fds);
+
+ // setup our timeout
+ timeval tv;
+ tv.tv_sec = msecs / 1000;
+ tv.tv_usec = (msecs % 1000) * 1000;
+
+ // do 'select'
+ if ( isRead )
+ return select(m_socketDescriptor + 1, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
+ else
+ return select(m_socketDescriptor + 1, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
+}
+
+int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
+ const ssize_t writtenBytes = write(m_socketDescriptor, data, length);
+ return static_cast<int64_t>(writtenBytes);
+}
diff --git a/bamtools/src/api/internal/io/TcpSocketEngine_win_p.cpp b/bamtools/src/api/internal/io/TcpSocketEngine_win_p.cpp
new file mode 100644
index 0000000..c4d9b47
--- /dev/null
+++ b/bamtools/src/api/internal/io/TcpSocketEngine_win_p.cpp
@@ -0,0 +1,241 @@
+// ***************************************************************************
+// TcpSocketEngine_win_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 8 December 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides low-level implementation of TCP I/O for all Windows systems
+// ***************************************************************************
+
+#include "api/internal/io/TcpSocketEngine_p.h"
+#include "api/internal/io/NetWin_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cstring>
+#include <iostream>
+#include <sstream>
+using namespace std;
+
+// --------------------------------
+// TcpSocketEngine implementation
+// --------------------------------
+
+void TcpSocketEngine::nativeClose(void) {
+ closesocket(m_socketDescriptor);
+}
+
+bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) {
+
+ // setup connection parameters from address/port
+ sockaddr_in sockAddrIPv4;
+ sockaddr_in6 sockAddrIPv6;
+ sockaddr* sockAddrPtr = 0;
+ BT_SOCKLEN_T sockAddrSize = 0;
+
+ // IPv6
+ if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
+
+ memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
+ sockAddrIPv6.sin6_family = AF_INET6;
+ sockAddrIPv6.sin6_port = htons(port);
+
+ IPv6Address ip6 = address.GetIPv6Address();
+ memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
+
+ sockAddrSize = sizeof(sockAddrIPv6);
+ sockAddrPtr = (sockaddr*)&sockAddrIPv6;
+ }
+
+ // IPv4
+ else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
+
+ memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
+ sockAddrIPv4.sin_family = AF_INET;
+ sockAddrIPv4.sin_port = htons(port);
+ sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address());
+
+ sockAddrSize = sizeof(sockAddrIPv4);
+ sockAddrPtr = (sockaddr*)&sockAddrIPv4;
+ }
+
+ // unknown (should be unreachable)
+ else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
+
+ // attempt conenction
+ const int connectResult = WSAConnect(m_socketDescriptor, sockAddrPtr, sockAddrSize, 0, 0, 0, 0);
+
+ // if failed, handle error
+ if ( connectResult == SOCKET_ERROR ) {
+
+ // ensure state is set before checking error code
+ m_socketState = TcpSocket::UnconnectedState;
+
+ // set error type/message depending on errorCode
+ const int errorCode = WSAGetLastError();
+ switch ( errorCode ) {
+ case WSANOTINITIALISED:
+ m_socketError = TcpSocket::UnknownSocketError;
+ m_errorString = "Windows socket functionality not properly initialized";
+ break;
+ case WSAEISCONN:
+ m_socketState = TcpSocket::ConnectedState; // socket already connected
+ break;
+ case WSAECONNREFUSED:
+ case WSAEINVAL:
+ m_socketError = TcpSocket::ConnectionRefusedError;
+ m_errorString = "connection refused";
+ break;
+ case WSAETIMEDOUT:
+ m_socketError = TcpSocket::NetworkError;
+ m_errorString = "connection timed out";
+ break;
+ case WSAEHOSTUNREACH:
+ m_socketError = TcpSocket::NetworkError;
+ m_errorString = "host unreachable";
+ break;
+ case WSAENETUNREACH:
+ m_socketError = TcpSocket::NetworkError;
+ m_errorString = "network unreachable";
+ break;
+ case WSAEADDRINUSE:
+ m_socketError = TcpSocket::SocketResourceError;
+ m_errorString = "address already in use";
+ break;
+ case WSAEACCES:
+ m_socketError = TcpSocket::SocketAccessError;
+ m_errorString = "permission denied";
+ break;
+ default:
+ break;
+ }
+
+ // double check that we're not in 'connected' state; if so, return failure
+ if ( m_socketState != TcpSocket::ConnectedState )
+ return false;
+ }
+
+ // otherwise, we should be good
+ // update state & return success
+ m_socketState = TcpSocket::ConnectedState;
+ return true;
+}
+
+bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
+
+ // get protocol value for requested protocol type
+ const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6 : AF_INET );
+
+ // attempt to create socket
+ SOCKET socketFd = WSASocket(protocolNum, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
+
+ // if we fetched an invalid socket descriptor
+ if ( socketFd == INVALID_SOCKET ) {
+
+ // set error type/message depending on error code
+ const int errorCode = WSAGetLastError();
+ switch ( errorCode ) {
+ case WSANOTINITIALISED:
+ m_socketError = TcpSocket::UnknownSocketError;
+ m_errorString = "Windows socket functionality not properly initialized";
+ break;
+ case WSAEAFNOSUPPORT:
+ case WSAESOCKTNOSUPPORT:
+ case WSAEPROTOTYPE:
+ case WSAEINVAL:
+ m_socketError = TcpSocket::UnsupportedSocketOperationError;
+ m_errorString = "protocol not supported";
+ break;
+ case WSAEMFILE:
+ case WSAENOBUFS:
+ m_socketError = TcpSocket::SocketResourceError;
+ m_errorString = "out of resources";
+ break;
+ default:
+ m_socketError = TcpSocket::UnknownSocketError;
+ stringstream errStream("");
+ errStream << "WSA ErrorCode: " << errorCode;
+ m_errorString = errStream.str();
+ break;
+ }
+
+ // return failure
+ return false;
+ }
+
+ // otherwise, store our socket FD & return success
+ m_socketDescriptor = static_cast<int>(socketFd);
+ return true;
+}
+
+int64_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
+
+ int64_t numBytes(0);
+ int64_t dummy(0);
+ DWORD bytesWritten(0);
+
+ const int ioctlResult = WSAIoctl( m_socketDescriptor, FIONREAD
+ , &dummy, sizeof(dummy)
+ , &numBytes, sizeof(numBytes)
+ , &bytesWritten, 0, 0
+ );
+ return ( ioctlResult == SOCKET_ERROR ? -1 : numBytes );
+}
+
+int64_t TcpSocketEngine::nativeRead(char* dest, size_t max) {
+
+ // skip if invalid socket
+ if ( !IsValid() )
+ return -1;
+
+ // set up our WSA output buffer
+ WSABUF buf;
+ buf.buf = dest;
+ buf.len = max;
+
+ // attempt to read bytes
+ DWORD flags = 0;
+ DWORD bytesRead = 0;
+ const int readResult = WSARecv(m_socketDescriptor, &buf, 1, &bytesRead, &flags, 0, 0);
+ if ( readResult == SOCKET_ERROR )
+ return -1;
+
+ // return number of bytes read
+ return static_cast<int64_t>(bytesRead);
+}
+
+// negative value for msecs will block (forever) until
+int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
+
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(m_socketDescriptor, &fds);
+
+ timeval tv;
+ tv.tv_sec = msecs / 1000;
+ tv.tv_usec = (msecs % 1000) * 1000;
+
+ // do 'select'
+ if ( isRead )
+ return select(0, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
+ else
+ return select(0, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
+}
+
+int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
+
+ // setup our WSA write buffer
+ WSABUF buf;
+ buf.buf = (char*)data;
+ buf.len = length;
+
+ // attempt to write bytes
+ DWORD flags = 0;
+ DWORD bytesWritten = 0;
+ const int writeResult = WSASend(m_socketDescriptor, &buf, 1, &bytesWritten, flags, 0, 0);
+ if ( writeResult == SOCKET_ERROR )
+ return -1;
+
+ // return number of bytes written
+ return static_cast<int64_t>(bytesWritten);
+}
diff --git a/bamtools/src/api/internal/io/TcpSocket_p.cpp b/bamtools/src/api/internal/io/TcpSocket_p.cpp
new file mode 100644
index 0000000..d390932
--- /dev/null
+++ b/bamtools/src/api/internal/io/TcpSocket_p.cpp
@@ -0,0 +1,430 @@
+// ***************************************************************************
+// TcpSocket_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 5 January 2012 (DB)
+// ---------------------------------------------------------------------------
+// Provides basic TCP I/O interface
+// ***************************************************************************
+
+#include "api/internal/io/ByteArray_p.h"
+#include "api/internal/io/TcpSocket_p.h"
+#include "api/internal/io/TcpSocketEngine_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <algorithm>
+#include <climits>
+#include <sstream>
+#include <vector>
+using namespace std;
+
+// ------------------------------------
+// static utility methods & constants
+// ------------------------------------
+
+namespace BamTools {
+namespace Internal {
+
+// constants
+static const size_t DEFAULT_BUFFER_SIZE = 0x10000;
+
+} // namespace Internal
+} // namespace BamTools
+
+// --------------------------
+// TcpSocket implementation
+// --------------------------
+
+TcpSocket::TcpSocket(void)
+ : m_mode(IBamIODevice::NotOpen)
+// , m_localPort(0)
+ , m_remotePort(0)
+ , m_engine(0)
+ , m_cachedSocketDescriptor(-1)
+ , m_readBuffer(DEFAULT_BUFFER_SIZE)
+ , m_error(TcpSocket::NoError)
+ , m_state(TcpSocket::UnconnectedState)
+{ }
+
+TcpSocket::~TcpSocket(void) {
+ if ( m_state == TcpSocket::ConnectedState )
+ DisconnectFromHost();
+}
+
+size_t TcpSocket::BufferBytesAvailable(void) const {
+ return m_readBuffer.Size();
+}
+
+bool TcpSocket::CanReadLine(void) const {
+ return m_readBuffer.CanReadLine();
+}
+
+void TcpSocket::ClearBuffer(void) {
+ m_readBuffer.Clear();
+}
+
+bool TcpSocket::ConnectImpl(const HostInfo& hostInfo,
+ const std::string& port,
+ IBamIODevice::OpenMode mode)
+{
+ // skip if we're already connected
+ if ( m_state == TcpSocket::ConnectedState ) {
+ m_error = TcpSocket::SocketResourceError;
+ m_errorString = "socket already connected";
+ return false;
+ }
+
+ // reset socket state
+ m_hostName = hostInfo.HostName();
+ m_mode = mode;
+ m_state = TcpSocket::UnconnectedState;
+ m_error = TcpSocket::NoError;
+// m_localPort = 0;
+ m_remotePort = 0;
+// m_localAddress.Clear();
+ m_remoteAddress.Clear();
+ m_readBuffer.Clear();
+
+ // fetch candidate addresses for requested host
+ vector<HostAddress> addresses = hostInfo.Addresses();
+ if ( addresses.empty() ) {
+ m_error = TcpSocket::HostNotFoundError;
+ m_errorString = "no IP addresses found for host";
+ return false;
+ }
+
+ // convert port string to integer
+ stringstream ss(port);
+ uint16_t portNumber(0);
+ ss >> portNumber;
+
+ // iterate through adddresses
+ vector<HostAddress>::const_iterator addrIter = addresses.begin();
+ vector<HostAddress>::const_iterator addrEnd = addresses.end();
+ for ( ; addrIter != addrEnd; ++addrIter) {
+ const HostAddress& addr = (*addrIter);
+
+ // try to initialize socket engine with this address
+ if ( !InitializeSocketEngine(addr.GetProtocol()) ) {
+ // failure to initialize is OK here
+ // we'll just try the next available address
+ continue;
+ }
+
+ // attempt actual connection
+ if ( m_engine->Connect(addr, portNumber) ) {
+
+ // if connection successful, update our state & return true
+ m_mode = mode;
+// m_localAddress = m_engine->GetLocalAddress();
+// m_localPort = m_engine->GetLocalPort();
+ m_remoteAddress = m_engine->GetRemoteAddress();
+ m_remotePort = m_engine->GetRemotePort();
+ m_cachedSocketDescriptor = m_engine->GetSocketDescriptor();
+ m_state = TcpSocket::ConnectedState;
+ return true;
+ }
+ }
+
+ // if we get here, no connection could be made
+ m_error = TcpSocket::HostNotFoundError;
+ m_errorString = "could not connect to any host addresses";
+ return false;
+}
+
+bool TcpSocket::ConnectToHost(const string& hostName,
+ uint16_t port,
+ IBamIODevice::OpenMode mode)
+{
+ stringstream ss("");
+ ss << port;
+ return ConnectToHost(hostName, ss.str(), mode);
+
+}
+
+bool TcpSocket::ConnectToHost(const string& hostName,
+ const string& port,
+ IBamIODevice::OpenMode mode)
+{
+ // create new address object with requested host name
+ HostAddress hostAddress;
+ hostAddress.SetAddress(hostName);
+
+ HostInfo info;
+ // if host name was IP address ("x.x.x.x" or IPv6 format)
+ // otherwise host name was 'plain-text' ("www.foo.bar")
+ // we need to look up IP address(es)
+ if ( hostAddress.HasIPAddress() )
+ info.SetAddresses( vector<HostAddress>(1, hostAddress) );
+ else
+ info = HostInfo::Lookup(hostName, port);
+
+ // attempt connection on requested port
+ return ConnectImpl(info, port, mode);
+}
+
+void TcpSocket::DisconnectFromHost(void) {
+
+ // close socket engine & delete
+ if ( m_state == TcpSocket::ConnectedState )
+ ResetSocketEngine();
+
+ // reset connection state
+// m_localPort = 0;
+ m_remotePort = 0;
+// m_localAddress.Clear();
+ m_remoteAddress.Clear();
+ m_hostName.clear();
+ m_cachedSocketDescriptor = -1;
+
+ // for future, make sure there's outgoing data that needs to be flushed
+ m_readBuffer.Clear();
+}
+
+TcpSocket::SocketError TcpSocket::GetError(void) const {
+ return m_error;
+}
+
+std::string TcpSocket::GetErrorString(void) const {
+ return m_errorString;
+}
+
+std::string TcpSocket::GetHostName(void) const {
+ return m_hostName;
+}
+
+//HostAddress TcpSocket::GetLocalAddress(void) const {
+// return m_localAddress;
+//}
+
+//uint16_t TcpSocket::GetLocalPort(void) const {
+// return m_localPort;
+//}
+
+HostAddress TcpSocket::GetRemoteAddress(void) const {
+ return m_remoteAddress;
+}
+
+uint16_t TcpSocket::GetRemotePort(void) const {
+ return m_remotePort;
+}
+
+TcpSocket::SocketState TcpSocket::GetState(void) const {
+ return m_state;
+}
+
+bool TcpSocket::InitializeSocketEngine(HostAddress::NetworkProtocol protocol) {
+ ResetSocketEngine();
+ m_engine = new TcpSocketEngine;
+ return m_engine->Initialize(protocol);
+}
+
+bool TcpSocket::IsConnected(void) const {
+ if ( m_engine == 0 )
+ return false;
+ return ( m_engine->IsValid() && (m_state == TcpSocket::ConnectedState) );
+}
+
+// may be read in a look until desired data amount has been read
+// returns: number of bytes read, or -1 if error
+int64_t TcpSocket::Read(char* data, const unsigned int numBytes) {
+
+ // if we have data in buffer, just return it
+ if ( !m_readBuffer.IsEmpty() ) {
+ const size_t bytesRead = m_readBuffer.Read(data, numBytes);
+ return static_cast<int64_t>(bytesRead);
+ }
+
+ // otherwise, we'll need to fetch data from socket
+ // first make sure we have a valid socket engine
+ if ( m_engine == 0 ) {
+ // TODO: set error string/state?
+ return -1;
+ }
+
+ // fetch data from socket, return 0 for success, -1 for failure
+ // since this should be called in a loop,
+ // we'll pull the actual bytes from the buffer on next iteration
+ const int64_t socketBytesRead = ReadFromSocket();
+ if ( socketBytesRead < 0 ) {
+ // TODO: set error string/state ?
+ return -1;
+ }
+
+ // we should have data now in buffer, try to fetch requested amount
+ // if nothing in buffer, we will return 0 bytes read (signals EOF reached)
+ const size_t numBytesRead = m_readBuffer.Read(data, numBytes);
+ return static_cast<int64_t>(numBytesRead);
+}
+
+int64_t TcpSocket::ReadFromSocket(void) {
+
+ // check for any socket engine errors
+ if ( !m_engine->IsValid() ) {
+ m_errorString = "TcpSocket::ReadFromSocket - socket disconnected";
+ ResetSocketEngine();
+ return -1;
+ }
+
+ // wait for ready read
+ bool timedOut;
+ const bool isReadyRead = m_engine->WaitForRead(5000, &timedOut);
+
+ // if not ready
+ if ( !isReadyRead ) {
+
+ // if we simply timed out
+ if ( timedOut ) {
+ // TODO: get add'l error info from engine ?
+ m_errorString = "TcpSocket::ReadFromSocket - timed out waiting for ready read";
+ }
+
+ // otherwise, there was some other error
+ else {
+ // TODO: get add'l error info from engine ?
+ m_errorString = "TcpSocket::ReadFromSocket - encountered error while waiting for ready read";
+ }
+
+ // return failure
+ return -1;
+ }
+
+ // get number of bytes available from socket
+ const int64_t bytesToRead = m_engine->NumBytesAvailable();
+ if ( bytesToRead < 0 ) {
+ // TODO: get add'l error info from engine ?
+ m_errorString = "TcpSocket::ReadFromSocket - encountered error while determining numBytesAvailable";
+ return -1;
+ }
+
+ // make space in buffer & read from socket
+ char* buffer = m_readBuffer.Reserve(bytesToRead);
+ const int64_t numBytesRead = m_engine->Read(buffer, bytesToRead);
+ if ( numBytesRead == -1 ) {
+ // TODO: get add'l error info from engine ?
+ m_errorString = "TcpSocket::ReadFromSocket - encountered error while reading bytes";
+ }
+
+ // return number of bytes actually read
+ return numBytesRead;
+}
+
+string TcpSocket::ReadLine(int64_t max) {
+
+ // prep result byte buffer
+ ByteArray result;
+ size_t bufferMax = ((max > static_cast<int64_t>(UINT_MAX))
+ ? UINT_MAX : static_cast<size_t>(max));
+ result.Resize(bufferMax);
+
+ // read data
+ int64_t readBytes(0);
+ if ( result.Size() == 0 ) {
+
+ if ( bufferMax == 0 )
+ bufferMax = UINT_MAX;
+
+ result.Resize(1);
+
+ int64_t readResult;
+ do {
+ result.Resize( static_cast<size_t>(min(bufferMax, result.Size() + DEFAULT_BUFFER_SIZE)) );
+ readResult = ReadLine(result.Data()+readBytes, result.Size()-readBytes);
+ if ( readResult > 0 || readBytes == 0 )
+ readBytes += readResult;
+ } while ( readResult == DEFAULT_BUFFER_SIZE && result[static_cast<size_t>(readBytes-1)] != '\n' );
+
+ } else
+ readBytes = ReadLine(result.Data(), result.Size());
+
+ // clean up byte buffer
+ if ( readBytes <= 0 )
+ result.Clear();
+ else
+ result.Resize(static_cast<size_t>(readBytes));
+
+ // return byte buffer as string
+ return string( result.ConstData(), result.Size() );
+}
+
+int64_t TcpSocket::ReadLine(char* dest, size_t max) {
+
+ // wait for buffer to contain line contents
+ if ( !WaitForReadLine() ) {
+ m_errorString = "TcpSocket::ReadLine - error waiting for read line";
+ return -1;
+ }
+
+ // leave room for null term
+ if ( max < 2 )
+ return -1;
+ --max;
+
+ // read from buffer, handle newlines
+ int64_t readSoFar = m_readBuffer.ReadLine(dest, max);
+ if ( readSoFar && dest[readSoFar-1] == '\n' ) {
+
+ // adjust for windows-style '\r\n'
+ if ( readSoFar > 1 && dest[readSoFar-2] == '\r') {
+ --readSoFar;
+ dest[readSoFar-1] = '\n';
+ }
+ }
+
+ // null terminate & return number of bytes read
+ dest[readSoFar] = '\0';
+ return readSoFar;
+}
+
+void TcpSocket::ResetSocketEngine(void) {
+
+ // shut down socket engine
+ if ( m_engine ) {
+ m_engine->Close();
+ delete m_engine;
+ m_engine = 0;
+ }
+
+ // reset our state & cached socket handle
+ m_state = TcpSocket::UnconnectedState;
+ m_cachedSocketDescriptor = -1;
+}
+
+bool TcpSocket::WaitForReadLine(void) {
+
+ // wait until we can read a line (will return immediately if already capable)
+ while ( !CanReadLine() ) {
+ if ( !ReadFromSocket() )
+ return false;
+ }
+
+ // if we get here, success
+ return true;
+}
+
+int64_t TcpSocket::Write(const char* data, const unsigned int numBytes) {
+
+ // single-shot attempt at write (not buffered, just try to shove the data through socket)
+ // this method purely exists to send 'small' HTTP requests/FTP commands from client to server
+
+ // wait for our socket to be write-able
+ bool timedOut;
+ const bool isReadyWrite = m_engine->WaitForWrite(3000, &timedOut);
+
+ // if ready, return number of bytes written
+ if ( isReadyWrite )
+ return m_engine->Write(data, numBytes);
+
+ // otherwise, socket not ready for writing
+ // set error string depending on reason & return failure
+ if ( !timedOut ) {
+ // TODO: get add'l error info from engine ??
+ m_errorString = "TcpSocket::Write - timed out waiting for ready-write";
+ }
+ else {
+ // TODO: get add'l error info from engine ??
+ m_errorString = "TcpSocket::Write - error encountered while waiting for ready-write";
+ }
+ return -1;
+}
diff --git a/bamtools/src/api/internal/io/TcpSocket_p.h b/bamtools/src/api/internal/io/TcpSocket_p.h
new file mode 100644
index 0000000..2ad2dee
--- /dev/null
+++ b/bamtools/src/api/internal/io/TcpSocket_p.h
@@ -0,0 +1,128 @@
+// ***************************************************************************
+// TcpSocket_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 7 December 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides basic TCP I/O interface
+// ***************************************************************************
+
+#ifndef TCPSOCKET_P_H
+#define TCPSOCKET_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/IBamIODevice.h"
+#include "api/internal/io/HostInfo_p.h"
+#include "api/internal/io/RollingBuffer_p.h"
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class BamHttp;
+class TcpSocketEngine;
+
+class TcpSocket {
+
+ // enums
+ public:
+ enum SocketError { NoError = -2
+ , UnknownSocketError = -1
+ , ConnectionRefusedError = 0
+ , RemoteHostClosedError
+ , HostNotFoundError
+ , SocketAccessError
+ , SocketResourceError
+ , SocketTimeoutError
+ , NetworkError
+ , UnsupportedSocketOperationError
+ };
+
+ enum SocketState { UnconnectedState = 0
+ , ConnectedState
+ };
+
+ // ctor & dtor
+ public:
+ TcpSocket(void);
+ ~TcpSocket(void);
+
+ // TcpSocket interface
+ public:
+
+ // connection methods
+ bool ConnectToHost(const std::string& hostName,
+ const uint16_t port, // Connect("host", 80)
+ IBamIODevice::OpenMode mode = IBamIODevice::ReadOnly);
+ bool ConnectToHost(const std::string& hostName,
+ const std::string& port, // Connect("host", "80")
+ IBamIODevice::OpenMode mode = IBamIODevice::ReadOnly);
+ void DisconnectFromHost(void);
+ bool IsConnected(void) const;
+
+ // I/O methods
+ size_t BufferBytesAvailable(void) const;
+ bool CanReadLine(void) const;
+ void ClearBuffer(void); // force buffer to clear (not a 'flush', just a 'discard')
+ int64_t Read(char* data, const unsigned int numBytes);
+ std::string ReadLine(int64_t max = 0);
+ int64_t ReadLine(char* dest, size_t max);
+ bool WaitForReadLine(void);
+ int64_t Write(const char* data, const unsigned int numBytes);
+
+ // connection values
+ std::string GetHostName(void) const;
+// HostAddress GetLocalAddress(void) const;
+// uint16_t GetLocalPort(void) const;
+ HostAddress GetRemoteAddress(void) const;
+ uint16_t GetRemotePort(void) const;
+
+ // connection status
+ TcpSocket::SocketError GetError(void) const;
+ TcpSocket::SocketState GetState(void) const;
+ std::string GetErrorString(void) const;
+
+ // internal methods
+ private:
+ bool ConnectImpl(const HostInfo& hostInfo,
+ const std::string& port,
+ IBamIODevice::OpenMode mode);
+ bool InitializeSocketEngine(HostAddress::NetworkProtocol protocol);
+ int64_t ReadFromSocket(void);
+ void ResetSocketEngine(void);
+
+ // data members
+ private:
+ IBamIODevice::OpenMode m_mode;
+
+ std::string m_hostName;
+// uint16_t m_localPort;
+ uint16_t m_remotePort;
+// HostAddress m_localAddress;
+ HostAddress m_remoteAddress;
+
+ TcpSocketEngine* m_engine;
+ int m_cachedSocketDescriptor;
+
+ RollingBuffer m_readBuffer;
+
+ TcpSocket::SocketError m_error;
+ TcpSocket::SocketState m_state;
+ std::string m_errorString;
+
+ friend class BamHttp;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // TCPSOCKET_P_H
diff --git a/bamtools/src/api/internal/sam/CMakeLists.txt b/bamtools/src/api/internal/sam/CMakeLists.txt
new file mode 100644
index 0000000..2f303bd
--- /dev/null
+++ b/bamtools/src/api/internal/sam/CMakeLists.txt
@@ -0,0 +1,17 @@
+# ==========================
+# BamTools CMakeLists.txt
+# (c) 2011 Derek Barnett
+#
+# src/api/internal/sam
+# ==========================
+
+set( InternalSamDir "${InternalDir}/sam" )
+
+set( InternalSamSources
+ ${InternalSamDir}/SamFormatParser_p.cpp
+ ${InternalSamDir}/SamFormatPrinter_p.cpp
+ ${InternalSamDir}/SamHeaderValidator_p.cpp
+
+ PARENT_SCOPE # <-- leave this last
+)
+
diff --git a/bamtools/src/api/internal/sam/SamFormatParser_p.cpp b/bamtools/src/api/internal/sam/SamFormatParser_p.cpp
new file mode 100644
index 0000000..928cb5c
--- /dev/null
+++ b/bamtools/src/api/internal/sam/SamFormatParser_p.cpp
@@ -0,0 +1,202 @@
+// ***************************************************************************
+// SamFormatParser.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 8 December 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides functionality for parsing SAM header text into SamHeader object
+// ***************************************************************************
+
+#include "api/SamConstants.h"
+#include "api/SamHeader.h"
+#include "api/internal/sam/SamFormatParser_p.h"
+#include "api/internal/utils/BamException_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+using namespace std;
+
+SamFormatParser::SamFormatParser(SamHeader& header)
+ : m_header(header)
+{ }
+
+SamFormatParser::~SamFormatParser(void) { }
+
+void SamFormatParser::Parse(const string& headerText) {
+
+ // clear header's prior contents
+ m_header.Clear();
+
+ // empty header is OK, but skip processing
+ if ( headerText.empty() )
+ return;
+
+ // other wise parse SAM lines
+ istringstream headerStream(headerText);
+ string headerLine("");
+ while ( getline(headerStream, headerLine) )
+ ParseSamLine(headerLine);
+}
+
+void SamFormatParser::ParseSamLine(const string& line) {
+
+ // skip if line is not long enough to contain true values
+ if ( line.length() < 5 ) return;
+
+ // determine token at beginning of line
+ const string firstToken = line.substr(0,3);
+ const string restOfLine = line.substr(4);
+ if ( firstToken == Constants::SAM_HD_BEGIN_TOKEN) ParseHDLine(restOfLine);
+ else if ( firstToken == Constants::SAM_SQ_BEGIN_TOKEN) ParseSQLine(restOfLine);
+ else if ( firstToken == Constants::SAM_RG_BEGIN_TOKEN) ParseRGLine(restOfLine);
+ else if ( firstToken == Constants::SAM_PG_BEGIN_TOKEN) ParsePGLine(restOfLine);
+ else if ( firstToken == Constants::SAM_CO_BEGIN_TOKEN) ParseCOLine(restOfLine);
+}
+
+void SamFormatParser::ParseHDLine(const string& line) {
+
+ // split HD lines into tokens
+ vector<string> tokens = Split(line, Constants::SAM_TAB);
+
+ // iterate over tokens
+ vector<string>::const_iterator tokenIter = tokens.begin();
+ vector<string>::const_iterator tokenEnd = tokens.end();
+ for ( ; tokenIter != tokenEnd; ++tokenIter ) {
+
+ // get tag/value
+ const string tokenTag = (*tokenIter).substr(0,2);
+ const string tokenValue = (*tokenIter).substr(3);
+
+ // set header contents
+ if ( tokenTag == Constants::SAM_HD_VERSION_TAG ) m_header.Version = tokenValue;
+ else if ( tokenTag == Constants::SAM_HD_SORTORDER_TAG ) m_header.SortOrder = tokenValue;
+ else if ( tokenTag == Constants::SAM_HD_GROUPORDER_TAG ) m_header.GroupOrder = tokenValue;
+ }
+
+ // check for required tags
+ if ( !m_header.HasVersion() )
+ throw BamException("SamFormatParser::ParseHDLine", "@HD line is missing VN tag");
+}
+
+void SamFormatParser::ParseSQLine(const string& line) {
+
+ SamSequence seq;
+
+ // split SQ line into tokens
+ vector<string> tokens = Split(line, Constants::SAM_TAB);
+
+ // iterate over tokens
+ vector<string>::const_iterator tokenIter = tokens.begin();
+ vector<string>::const_iterator tokenEnd = tokens.end();
+ for ( ; tokenIter != tokenEnd; ++tokenIter ) {
+
+ // get tag/value
+ const string tokenTag = (*tokenIter).substr(0,2);
+ const string tokenValue = (*tokenIter).substr(3);
+
+ // set sequence contents
+ if ( tokenTag == Constants::SAM_SQ_NAME_TAG ) seq.Name = tokenValue;
+ else if ( tokenTag == Constants::SAM_SQ_LENGTH_TAG ) seq.Length = tokenValue;
+ else if ( tokenTag == Constants::SAM_SQ_ASSEMBLYID_TAG ) seq.AssemblyID = tokenValue;
+ else if ( tokenTag == Constants::SAM_SQ_CHECKSUM_TAG ) seq.Checksum = tokenValue;
+ else if ( tokenTag == Constants::SAM_SQ_SPECIES_TAG ) seq.Species = tokenValue;
+ else if ( tokenTag == Constants::SAM_SQ_URI_TAG ) seq.URI = tokenValue;
+ }
+
+ // check for required tags
+ if ( !seq.HasName() )
+ throw BamException("SamFormatParser::ParseSQLine", "@SQ line is missing SN tag");
+ if ( !seq.HasLength() )
+ throw BamException("SamFormatParser::ParseSQLine", "@SQ line is missing LN tag");
+
+ // store SAM sequence entry
+ m_header.Sequences.Add(seq);
+}
+
+void SamFormatParser::ParseRGLine(const string& line) {
+
+ SamReadGroup rg;
+
+ // split string into tokens
+ vector<string> tokens = Split(line, Constants::SAM_TAB);
+
+ // iterate over tokens
+ vector<string>::const_iterator tokenIter = tokens.begin();
+ vector<string>::const_iterator tokenEnd = tokens.end();
+ for ( ; tokenIter != tokenEnd; ++tokenIter ) {
+
+ // get token tag/value
+ const string tokenTag = (*tokenIter).substr(0,2);
+ const string tokenValue = (*tokenIter).substr(3);
+
+ // set read group contents
+ if ( tokenTag == Constants::SAM_RG_ID_TAG ) rg.ID = tokenValue;
+ else if ( tokenTag == Constants::SAM_RG_DESCRIPTION_TAG ) rg.Description = tokenValue;
+ else if ( tokenTag == Constants::SAM_RG_FLOWORDER_TAG ) rg.FlowOrder = tokenValue;
+ else if ( tokenTag == Constants::SAM_RG_KEYSEQUENCE_TAG ) rg.KeySequence = tokenValue;
+ else if ( tokenTag == Constants::SAM_RG_LIBRARY_TAG ) rg.Library = tokenValue;
+ else if ( tokenTag == Constants::SAM_RG_PLATFORMUNIT_TAG ) rg.PlatformUnit = tokenValue;
+ else if ( tokenTag == Constants::SAM_RG_PREDICTEDINSERTSIZE_TAG ) rg.PredictedInsertSize = tokenValue;
+ else if ( tokenTag == Constants::SAM_RG_PRODUCTIONDATE_TAG ) rg.ProductionDate = tokenValue;
+ else if ( tokenTag == Constants::SAM_RG_PROGRAM_TAG ) rg.Program = tokenValue;
+ else if ( tokenTag == Constants::SAM_RG_SAMPLE_TAG ) rg.Sample = tokenValue;
+ else if ( tokenTag == Constants::SAM_RG_SEQCENTER_TAG ) rg.SequencingCenter = tokenValue;
+ else if ( tokenTag == Constants::SAM_RG_SEQTECHNOLOGY_TAG ) rg.SequencingTechnology = tokenValue;
+ }
+
+ // check for required tags
+ if ( !rg.HasID() )
+ throw BamException("SamFormatParser::ParseRGLine", "@RG line is missing ID tag");
+
+ // store SAM read group entry
+ m_header.ReadGroups.Add(rg);
+}
+
+void SamFormatParser::ParsePGLine(const string& line) {
+
+ SamProgram pg;
+
+ // split string into tokens
+ vector<string> tokens = Split(line, Constants::SAM_TAB);
+
+ // iterate over tokens
+ vector<string>::const_iterator tokenIter = tokens.begin();
+ vector<string>::const_iterator tokenEnd = tokens.end();
+ for ( ; tokenIter != tokenEnd; ++tokenIter ) {
+
+ // get token tag/value
+ const string tokenTag = (*tokenIter).substr(0,2);
+ const string tokenValue = (*tokenIter).substr(3);
+
+ // set program record contents
+ if ( tokenTag == Constants::SAM_PG_ID_TAG ) pg.ID = tokenValue;
+ else if ( tokenTag == Constants::SAM_PG_NAME_TAG ) pg.Name = tokenValue;
+ else if ( tokenTag == Constants::SAM_PG_COMMANDLINE_TAG ) pg.CommandLine = tokenValue;
+ else if ( tokenTag == Constants::SAM_PG_PREVIOUSPROGRAM_TAG ) pg.PreviousProgramID = tokenValue;
+ else if ( tokenTag == Constants::SAM_PG_VERSION_TAG ) pg.Version = tokenValue;
+ }
+
+ // check for required tags
+ if ( !pg.HasID() )
+ throw BamException("SamFormatParser::ParsePGLine", "@PG line is missing ID tag");
+
+ // store SAM program entry
+ m_header.Programs.Add(pg);
+}
+
+void SamFormatParser::ParseCOLine(const string& line) {
+ // simply add line to comments list
+ m_header.Comments.push_back(line);
+}
+
+const vector<string> SamFormatParser::Split(const string& line, const char delim) {
+ vector<string> tokens;
+ stringstream lineStream(line);
+ string token;
+ while ( getline(lineStream, token, delim) )
+ tokens.push_back(token);
+ return tokens;
+}
diff --git a/bamtools/src/api/internal/sam/SamFormatParser_p.h b/bamtools/src/api/internal/sam/SamFormatParser_p.h
new file mode 100644
index 0000000..cf6d54c
--- /dev/null
+++ b/bamtools/src/api/internal/sam/SamFormatParser_p.h
@@ -0,0 +1,61 @@
+// ***************************************************************************
+// SamFormatParser.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 23 December 2010 (DB)
+// ---------------------------------------------------------------------------
+// Provides functionality for parsing SAM header text into SamHeader object
+// ***************************************************************************
+
+#ifndef SAM_FORMAT_PARSER_H
+#define SAM_FORMAT_PARSER_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include <string>
+#include <vector>
+
+namespace BamTools {
+
+class SamHeader;
+
+namespace Internal {
+
+class SamFormatParser {
+
+ // ctor & dtor
+ public:
+ SamFormatParser(BamTools::SamHeader& header);
+ ~SamFormatParser(void);
+
+ // parse text & populate header data
+ public:
+ void Parse(const std::string& headerText);
+
+ // internal methods
+ private:
+ void ParseSamLine(const std::string& line);
+ void ParseHDLine(const std::string& line);
+ void ParseSQLine(const std::string& line);
+ void ParseRGLine(const std::string& line);
+ void ParsePGLine(const std::string& line);
+ void ParseCOLine(const std::string& line);
+ const std::vector<std::string> Split(const std::string& line, const char delim);
+
+ // data members
+ private:
+ SamHeader& m_header;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // SAM_FORMAT_PARSER_H
diff --git a/bamtools/src/api/internal/sam/SamFormatPrinter_p.cpp b/bamtools/src/api/internal/sam/SamFormatPrinter_p.cpp
new file mode 100644
index 0000000..5a51a2f
--- /dev/null
+++ b/bamtools/src/api/internal/sam/SamFormatPrinter_p.cpp
@@ -0,0 +1,219 @@
+// ***************************************************************************
+// SamFormatPrinter.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 25 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides functionality for printing formatted SAM header to string
+// ***************************************************************************
+
+#include "api/SamConstants.h"
+#include "api/SamHeader.h"
+#include "api/internal/sam/SamFormatPrinter_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+using namespace std;
+
+// ------------------------
+// static utility methods
+// ------------------------
+
+static inline
+const string FormatTag(const string& tag, const string& value) {
+ return string(Constants::SAM_TAB + tag + Constants::SAM_COLON + value);
+}
+
+// ---------------------------------
+// SamFormatPrinter implementation
+// ---------------------------------
+
+SamFormatPrinter::SamFormatPrinter(const SamHeader& header)
+ : m_header(header)
+{ }
+
+SamFormatPrinter::~SamFormatPrinter(void) { }
+
+const string SamFormatPrinter::ToString(void) const {
+
+ // clear out stream
+ stringstream out("");
+
+ // generate formatted header text
+ PrintHD(out);
+ PrintSQ(out);
+ PrintRG(out);
+ PrintPG(out);
+ PrintCO(out);
+
+ // return result
+ return out.str();
+}
+
+void SamFormatPrinter::PrintHD(std::stringstream& out) const {
+
+ // if header has @HD data
+ if ( m_header.HasVersion() ) {
+
+ // @HD VN:<Version>
+ out << Constants::SAM_HD_BEGIN_TOKEN
+ << FormatTag(Constants::SAM_HD_VERSION_TAG, m_header.Version);
+
+ // SO:<SortOrder>
+ if ( m_header.HasSortOrder() )
+ out << FormatTag(Constants::SAM_HD_SORTORDER_TAG, m_header.SortOrder);
+
+ // GO:<GroupOrder>
+ if ( m_header.HasGroupOrder() )
+ out << FormatTag(Constants::SAM_HD_GROUPORDER_TAG, m_header.GroupOrder);
+
+ // newline
+ out << endl;
+ }
+}
+
+void SamFormatPrinter::PrintSQ(std::stringstream& out) const {
+
+ // iterate over sequence entries
+ SamSequenceConstIterator seqIter = m_header.Sequences.ConstBegin();
+ SamSequenceConstIterator seqEnd = m_header.Sequences.ConstEnd();
+ for ( ; seqIter != seqEnd; ++seqIter ) {
+ const SamSequence& seq = (*seqIter);
+
+ // @SQ SN:<Name> LN:<Length>
+ out << Constants::SAM_SQ_BEGIN_TOKEN
+ << FormatTag(Constants::SAM_SQ_NAME_TAG, seq.Name)
+ << FormatTag(Constants::SAM_SQ_LENGTH_TAG, seq.Length);
+
+ // AS:<AssemblyID>
+ if ( seq.HasAssemblyID() )
+ out << FormatTag(Constants::SAM_SQ_ASSEMBLYID_TAG, seq.AssemblyID);
+
+ // M5:<Checksum>
+ if ( seq.HasChecksum() )
+ out << FormatTag(Constants::SAM_SQ_CHECKSUM_TAG, seq.Checksum);
+
+ // SP:<Species>
+ if ( seq.HasSpecies() )
+ out << FormatTag(Constants::SAM_SQ_SPECIES_TAG, seq.Species);
+
+ // UR:<URI>
+ if ( seq.HasURI() )
+ out << FormatTag(Constants::SAM_SQ_URI_TAG, seq.URI);
+
+ // newline
+ out << endl;
+ }
+}
+
+void SamFormatPrinter::PrintRG(std::stringstream& out) const {
+
+ // iterate over read group entries
+ SamReadGroupConstIterator rgIter = m_header.ReadGroups.ConstBegin();
+ SamReadGroupConstIterator rgEnd = m_header.ReadGroups.ConstEnd();
+ for ( ; rgIter != rgEnd; ++rgIter ) {
+ const SamReadGroup& rg = (*rgIter);
+
+ // @RG ID:<ID>
+ out << Constants::SAM_RG_BEGIN_TOKEN
+ << FormatTag(Constants::SAM_RG_ID_TAG, rg.ID);
+
+ // CN:<SequencingCenter>
+ if ( rg.HasSequencingCenter() )
+ out << FormatTag(Constants::SAM_RG_SEQCENTER_TAG, rg.SequencingCenter);
+
+ // DS:<Description>
+ if ( rg.HasDescription() )
+ out << FormatTag(Constants::SAM_RG_DESCRIPTION_TAG, rg.Description);
+
+ // DT:<ProductionDate>
+ if ( rg.HasProductionDate() )
+ out << FormatTag(Constants::SAM_RG_PRODUCTIONDATE_TAG, rg.ProductionDate);
+
+ // FO:<FlowOrder>
+ if ( rg.HasFlowOrder() )
+ out << FormatTag(Constants::SAM_RG_FLOWORDER_TAG, rg.FlowOrder);
+
+ // KS:<KeySequence>
+ if ( rg.HasKeySequence() )
+ out << FormatTag(Constants::SAM_RG_KEYSEQUENCE_TAG, rg.KeySequence);
+
+ // LB:<Library>
+ if ( rg.HasLibrary() )
+ out << FormatTag(Constants::SAM_RG_LIBRARY_TAG, rg.Library);
+
+ // PG:<Program>
+ if ( rg.HasProgram() )
+ out << FormatTag(Constants::SAM_RG_PROGRAM_TAG, rg.Program);
+
+ // PI:<PredictedInsertSize>
+ if ( rg.HasPredictedInsertSize() )
+ out << FormatTag(Constants::SAM_RG_PREDICTEDINSERTSIZE_TAG, rg.PredictedInsertSize);
+
+ // PL:<SequencingTechnology>
+ if ( rg.HasSequencingTechnology() )
+ out << FormatTag(Constants::SAM_RG_SEQTECHNOLOGY_TAG, rg.SequencingTechnology);
+
+ // PU:<PlatformUnit>
+ if ( rg.HasPlatformUnit() )
+ out << FormatTag(Constants::SAM_RG_PLATFORMUNIT_TAG, rg.PlatformUnit);
+
+ // SM:<Sample>
+ if ( rg.HasSample() )
+ out << FormatTag(Constants::SAM_RG_SAMPLE_TAG, rg.Sample);
+
+ // newline
+ out << endl;
+ }
+}
+
+void SamFormatPrinter::PrintPG(std::stringstream& out) const {
+
+ // iterate over program record entries
+ SamProgramConstIterator pgIter = m_header.Programs.ConstBegin();
+ SamProgramConstIterator pgEnd = m_header.Programs.ConstEnd();
+ for ( ; pgIter != pgEnd; ++pgIter ) {
+ const SamProgram& pg = (*pgIter);
+
+ // @PG ID:<ID>
+ out << Constants::SAM_PG_BEGIN_TOKEN
+ << FormatTag(Constants::SAM_PG_ID_TAG, pg.ID);
+
+ // PN:<Name>
+ if ( pg.HasName() )
+ out << FormatTag(Constants::SAM_PG_NAME_TAG, pg.Name);
+
+ // CL:<CommandLine>
+ if ( pg.HasCommandLine() )
+ out << FormatTag(Constants::SAM_PG_COMMANDLINE_TAG, pg.CommandLine);
+
+ // PP:<PreviousProgramID>
+ if ( pg.HasPreviousProgramID() )
+ out << FormatTag(Constants::SAM_PG_PREVIOUSPROGRAM_TAG, pg.PreviousProgramID);
+
+ // VN:<Version>
+ if ( pg.HasVersion() )
+ out << FormatTag(Constants::SAM_PG_VERSION_TAG, pg.Version);
+
+ // newline
+ out << endl;
+ }
+}
+
+void SamFormatPrinter::PrintCO(std::stringstream& out) const {
+
+ // iterate over comments
+ vector<string>::const_iterator commentIter = m_header.Comments.begin();
+ vector<string>::const_iterator commentEnd = m_header.Comments.end();
+ for ( ; commentIter != commentEnd; ++commentIter ) {
+
+ // @CO <Comment>
+ out << Constants::SAM_CO_BEGIN_TOKEN
+ << Constants::SAM_TAB
+ << (*commentIter)
+ << endl;
+ }
+}
diff --git a/bamtools/src/api/internal/sam/SamFormatPrinter_p.h b/bamtools/src/api/internal/sam/SamFormatPrinter_p.h
new file mode 100644
index 0000000..ea29181
--- /dev/null
+++ b/bamtools/src/api/internal/sam/SamFormatPrinter_p.h
@@ -0,0 +1,59 @@
+// ***************************************************************************
+// SamFormatPrinter.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 6 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides functionality for printing formatted SAM header to string
+// ***************************************************************************
+
+#ifndef SAM_FORMAT_PRINTER_H
+#define SAM_FORMAT_PRINTER_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include <sstream>
+#include <string>
+
+namespace BamTools {
+
+class SamHeader;
+
+namespace Internal {
+
+class SamFormatPrinter {
+
+ // ctor & dtor
+ public:
+ SamFormatPrinter(const BamTools::SamHeader& header);
+ ~SamFormatPrinter(void);
+
+ // generates SAM-formatted string from header data
+ public:
+ const std::string ToString(void) const;
+
+ // internal methods
+ private:
+ void PrintHD(std::stringstream& out) const;
+ void PrintSQ(std::stringstream& out) const;
+ void PrintRG(std::stringstream& out) const;
+ void PrintPG(std::stringstream& out) const;
+ void PrintCO(std::stringstream& out) const;
+
+ // data members
+ private:
+ const SamHeader& m_header;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // SAM_FORMAT_PRINTER_H
diff --git a/bamtools/src/api/internal/sam/SamHeaderValidator_p.cpp b/bamtools/src/api/internal/sam/SamHeaderValidator_p.cpp
new file mode 100644
index 0000000..6bcb8a9
--- /dev/null
+++ b/bamtools/src/api/internal/sam/SamHeaderValidator_p.cpp
@@ -0,0 +1,524 @@
+// ***************************************************************************
+// SamHeaderValidator.cpp (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 25 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides functionality for validating SamHeader data
+// ***************************************************************************
+
+#include "api/SamConstants.h"
+#include "api/SamHeader.h"
+#include "api/internal/sam/SamHeaderValidator_p.h"
+#include "api/internal/sam/SamHeaderVersion_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cctype>
+#include <set>
+#include <sstream>
+using namespace std;
+
+// ------------------------
+// static utility methods
+// -------------------------
+
+static
+bool caseInsensitiveCompare(const string& lhs, const string& rhs) {
+
+ // can omit checking chars if lengths not equal
+ const int lhsLength = lhs.length();
+ const int rhsLength = rhs.length();
+ if ( lhsLength != rhsLength )
+ return false;
+
+ // do *basic* toupper checks on each string char's
+ for ( int i = 0; i < lhsLength; ++i ) {
+ if ( toupper( (int)lhs.at(i)) != toupper( (int)rhs.at(i)) )
+ return false;
+ }
+
+ // otherwise OK
+ return true;
+}
+
+// ------------------------------------------------------------------------
+// Allow validation rules to vary, as needed, between SAM header versions
+//
+// use SAM_VERSION_X_Y to tag important changes
+//
+// Together, they will allow for comparisons like:
+// if ( m_version < SAM_VERSION_2_0 ) {
+// // use some older rule
+// else
+// // use rule introduced with version 2.0
+
+static const SamHeaderVersion SAM_VERSION_1_0 = SamHeaderVersion(1,0);
+static const SamHeaderVersion SAM_VERSION_1_1 = SamHeaderVersion(1,1);
+static const SamHeaderVersion SAM_VERSION_1_2 = SamHeaderVersion(1,2);
+static const SamHeaderVersion SAM_VERSION_1_3 = SamHeaderVersion(1,3);
+static const SamHeaderVersion SAM_VERSION_1_4 = SamHeaderVersion(1,4);
+
+// TODO: This functionality is currently unused.
+// Make validation "version-aware."
+//
+// ------------------------------------------------------------------------
+
+const string SamHeaderValidator::ERROR_PREFIX = "ERROR: ";
+const string SamHeaderValidator::WARN_PREFIX = "WARNING: ";
+const string SamHeaderValidator::NEWLINE = "\n";
+
+SamHeaderValidator::SamHeaderValidator(const SamHeader& header)
+ : m_header(header)
+{ }
+
+SamHeaderValidator::~SamHeaderValidator(void) { }
+
+void SamHeaderValidator::AddError(const string& message) {
+ m_errorMessages.push_back(ERROR_PREFIX + message + NEWLINE);
+}
+
+void SamHeaderValidator::AddWarning(const string& message) {
+ m_warningMessages.push_back(WARN_PREFIX + message + NEWLINE);
+}
+
+void SamHeaderValidator::PrintErrorMessages(ostream& stream) {
+
+ // skip if no error messages
+ if ( m_errorMessages.empty() )
+ return;
+
+ // print error header line
+ stream << "* SAM header has " << m_errorMessages.size() << " errors:" << endl;
+
+ // print each error message
+ vector<string>::const_iterator errorIter = m_errorMessages.begin();
+ vector<string>::const_iterator errorEnd = m_errorMessages.end();
+ for ( ; errorIter != errorEnd; ++errorIter )
+ stream << (*errorIter);
+}
+
+void SamHeaderValidator::PrintMessages(ostream& stream) {
+ PrintErrorMessages(stream);
+ PrintWarningMessages(stream);
+}
+
+void SamHeaderValidator::PrintWarningMessages(ostream& stream) {
+
+ // skip if no warning messages
+ if ( m_warningMessages.empty() )
+ return;
+
+ // print warning header line
+ stream << "* SAM header has " << m_warningMessages.size() << " warnings:" << endl;
+
+ // print each warning message
+ vector<string>::const_iterator warnIter = m_warningMessages.begin();
+ vector<string>::const_iterator warnEnd = m_warningMessages.end();
+ for ( ; warnIter != warnEnd; ++warnIter )
+ stream << (*warnIter);
+}
+
+// entry point for validation
+bool SamHeaderValidator::Validate(void) {
+ bool isValid = true;
+ isValid &= ValidateMetadata();
+ isValid &= ValidateSequenceDictionary();
+ isValid &= ValidateReadGroupDictionary();
+ isValid &= ValidateProgramChain();
+ return isValid;
+}
+
+// check all SAM header 'metadata'
+bool SamHeaderValidator::ValidateMetadata(void) {
+ bool isValid = true;
+ isValid &= ValidateVersion();
+ isValid &= ValidateSortOrder();
+ isValid &= ValidateGroupOrder();
+ return isValid;
+}
+
+// check SAM header version tag
+bool SamHeaderValidator::ValidateVersion(void) {
+
+ const string& version = m_header.Version;
+
+ // warn if version not present
+ if ( version.empty() ) {
+ AddWarning("Version (VN) missing. Not required, but strongly recommended");
+ return true;
+ }
+
+ // invalid if version does not contain a period
+ const size_t periodFound = version.find(Constants::SAM_PERIOD);
+ if ( periodFound == string::npos ) {
+ AddError("Invalid version (VN) format: " + version);
+ return false;
+ }
+
+ // invalid if major version is empty or contains non-digits
+ const string majorVersion = version.substr(0, periodFound);
+ if ( majorVersion.empty() || !ContainsOnlyDigits(majorVersion) ) {
+ AddError("Invalid version (VN) format: " + version);
+ return false;
+ }
+
+ // invalid if major version is empty or contains non-digits
+ const string minorVersion = version.substr(periodFound + 1);
+ if ( minorVersion.empty() || !ContainsOnlyDigits(minorVersion) ) {
+ AddError("Invalid version (VN) format: " + version);
+ return false;
+ }
+
+ // TODO: check if version is not just syntactically OK,
+ // but is also a valid SAM version ( 1.0 .. CURRENT )
+
+ // all checked out this far, then version is OK
+ return true;
+}
+
+// assumes non-empty input string
+bool SamHeaderValidator::ContainsOnlyDigits(const string& s) {
+ const size_t nonDigitPosition = s.find_first_not_of(Constants::SAM_DIGITS);
+ return ( nonDigitPosition == string::npos ) ;
+}
+
+// validate SAM header sort order tag
+bool SamHeaderValidator::ValidateSortOrder(void) {
+
+ const string& sortOrder = m_header.SortOrder;
+
+ // warn if sort order not present
+ if ( sortOrder.empty() ) {
+ AddWarning("Sort order (SO) missing. Not required, but strongly recommended");
+ return true;
+ }
+
+ // if sort order is valid keyword
+ if ( sortOrder == Constants::SAM_HD_SORTORDER_COORDINATE ||
+ sortOrder == Constants::SAM_HD_SORTORDER_QUERYNAME ||
+ sortOrder == Constants::SAM_HD_SORTORDER_UNSORTED
+ )
+ {
+ return true;
+ }
+
+ // otherwise
+ AddError("Invalid sort order (SO): " + sortOrder);
+ return false;
+}
+
+// validate SAM header group order tag
+bool SamHeaderValidator::ValidateGroupOrder(void) {
+
+ const string& groupOrder = m_header.GroupOrder;
+
+ // if no group order, no problem, just return OK
+ if ( groupOrder.empty() )
+ return true;
+
+ // if group order is valid keyword
+ if ( groupOrder == Constants::SAM_HD_GROUPORDER_NONE ||
+ groupOrder == Constants::SAM_HD_GROUPORDER_QUERY ||
+ groupOrder == Constants::SAM_HD_GROUPORDER_REFERENCE
+ )
+ {
+ return true;
+ }
+
+ // otherwise
+ AddError("Invalid group order (GO): " + groupOrder);
+ return false;
+}
+
+// validate SAM header sequence dictionary
+bool SamHeaderValidator::ValidateSequenceDictionary(void) {
+
+ bool isValid = true;
+
+ // check for unique sequence names
+ isValid &= ContainsUniqueSequenceNames();
+
+ // iterate over sequences
+ const SamSequenceDictionary& sequences = m_header.Sequences;
+ SamSequenceConstIterator seqIter = sequences.ConstBegin();
+ SamSequenceConstIterator seqEnd = sequences.ConstEnd();
+ for ( ; seqIter != seqEnd; ++seqIter ) {
+ const SamSequence& seq = (*seqIter);
+ isValid &= ValidateSequence(seq);
+ }
+
+ // return validation state
+ return isValid;
+}
+
+// make sure all SQ names are unique
+bool SamHeaderValidator::ContainsUniqueSequenceNames(void) {
+
+ bool isValid = true;
+ set<string> sequenceNames;
+ set<string>::iterator nameIter;
+
+ // iterate over sequences
+ const SamSequenceDictionary& sequences = m_header.Sequences;
+ SamSequenceConstIterator seqIter = sequences.ConstBegin();
+ SamSequenceConstIterator seqEnd = sequences.ConstEnd();
+ for ( ; seqIter != seqEnd; ++seqIter ) {
+ const SamSequence& seq = (*seqIter);
+
+ // lookup sequence name
+ const string& name = seq.Name;
+ nameIter = sequenceNames.find(name);
+
+ // error if found (duplicate entry)
+ if ( nameIter != sequenceNames.end() ) {
+ AddError("Sequence name (SN): " + name + " is not unique");
+ isValid = false;
+ }
+
+ // otherwise ok, store name
+ sequenceNames.insert(name);
+ }
+
+ // return validation state
+ return isValid;
+}
+
+// validate SAM header sequence entry
+bool SamHeaderValidator::ValidateSequence(const SamSequence& seq) {
+ bool isValid = true;
+ isValid &= CheckNameFormat(seq.Name);
+ isValid &= CheckLengthInRange(seq.Length);
+ return isValid;
+}
+
+// check sequence name is valid format
+bool SamHeaderValidator::CheckNameFormat(const string& name) {
+
+ // invalid if name is empty
+ if ( name.empty() ) {
+ AddError("Sequence entry (@SQ) is missing SN tag");
+ return false;
+ }
+
+ // invalid if first character is a reserved char
+ const char firstChar = name.at(0);
+ if ( firstChar == Constants::SAM_EQUAL || firstChar == Constants::SAM_STAR ) {
+ AddError("Invalid sequence name (SN): " + name);
+ return false;
+ }
+ // otherwise OK
+ return true;
+}
+
+// check that sequence length is within accepted range
+bool SamHeaderValidator::CheckLengthInRange(const string& length) {
+
+ // invalid if empty
+ if ( length.empty() ) {
+ AddError("Sequence entry (@SQ) is missing LN tag");
+ return false;
+ }
+
+ // convert string length to numeric
+ stringstream lengthStream(length);
+ unsigned int sequenceLength;
+ lengthStream >> sequenceLength;
+
+ // invalid if length outside accepted range
+ if ( sequenceLength < Constants::SAM_SQ_LENGTH_MIN || sequenceLength > Constants::SAM_SQ_LENGTH_MAX ) {
+ AddError("Sequence length (LN): " + length + " out of range");
+ return false;
+ }
+
+ // otherwise OK
+ return true;
+}
+
+// validate SAM header read group dictionary
+bool SamHeaderValidator::ValidateReadGroupDictionary(void) {
+
+ bool isValid = true;
+
+ // check for unique read group IDs & platform units
+ isValid &= ContainsUniqueIDsAndPlatformUnits();
+
+ // iterate over read groups
+ const SamReadGroupDictionary& readGroups = m_header.ReadGroups;
+ SamReadGroupConstIterator rgIter = readGroups.ConstBegin();
+ SamReadGroupConstIterator rgEnd = readGroups.ConstEnd();
+ for ( ; rgIter != rgEnd; ++rgIter ) {
+ const SamReadGroup& rg = (*rgIter);
+ isValid &= ValidateReadGroup(rg);
+ }
+
+ // return validation state
+ return isValid;
+}
+
+// make sure RG IDs and platform units are unique
+bool SamHeaderValidator::ContainsUniqueIDsAndPlatformUnits(void) {
+
+ bool isValid = true;
+ set<string> readGroupIds;
+ set<string> platformUnits;
+ set<string>::iterator idIter;
+ set<string>::iterator puIter;
+
+ // iterate over sequences
+ const SamReadGroupDictionary& readGroups = m_header.ReadGroups;
+ SamReadGroupConstIterator rgIter = readGroups.ConstBegin();
+ SamReadGroupConstIterator rgEnd = readGroups.ConstEnd();
+ for ( ; rgIter != rgEnd; ++rgIter ) {
+ const SamReadGroup& rg = (*rgIter);
+
+ // --------------------------------
+ // check for unique ID
+
+ // lookup read group ID
+ const string& id = rg.ID;
+ idIter = readGroupIds.find(id);
+
+ // error if found (duplicate entry)
+ if ( idIter != readGroupIds.end() ) {
+ AddError("Read group ID (ID): " + id + " is not unique");
+ isValid = false;
+ }
+
+ // otherwise ok, store id
+ readGroupIds.insert(id);
+
+ // --------------------------------
+ // check for unique platform unit
+
+ // lookup platform unit
+ const string& pu = rg.PlatformUnit;
+ puIter = platformUnits.find(pu);
+
+ // error if found (duplicate entry)
+ if ( puIter != platformUnits.end() ) {
+ AddError("Platform unit (PU): " + pu + " is not unique");
+ isValid = false;
+ }
+
+ // otherwise ok, store platform unit
+ platformUnits.insert(pu);
+ }
+
+ // return validation state
+ return isValid;
+}
+
+// validate SAM header read group entry
+bool SamHeaderValidator::ValidateReadGroup(const SamReadGroup& rg) {
+ bool isValid = true;
+ isValid &= CheckReadGroupID(rg.ID);
+ isValid &= CheckSequencingTechnology(rg.SequencingTechnology);
+ return isValid;
+}
+
+// make sure RG ID exists
+bool SamHeaderValidator::CheckReadGroupID(const string& id) {
+
+ // invalid if empty
+ if ( id.empty() ) {
+ AddError("Read group entry (@RG) is missing ID tag");
+ return false;
+ }
+
+ // otherwise OK
+ return true;
+}
+
+// make sure RG sequencing tech is one of the accepted keywords
+bool SamHeaderValidator::CheckSequencingTechnology(const string& technology) {
+
+ // if no technology provided, no problem, just return OK
+ if ( technology.empty() )
+ return true;
+
+ // if technology is valid keyword
+ if ( caseInsensitiveCompare(technology, Constants::SAM_RG_SEQTECHNOLOGY_CAPILLARY) ||
+ caseInsensitiveCompare(technology, Constants::SAM_RG_SEQTECHNOLOGY_HELICOS) ||
+ caseInsensitiveCompare(technology, Constants::SAM_RG_SEQTECHNOLOGY_ILLUMINA) ||
+ caseInsensitiveCompare(technology, Constants::SAM_RG_SEQTECHNOLOGY_IONTORRENT) ||
+ caseInsensitiveCompare(technology, Constants::SAM_RG_SEQTECHNOLOGY_LS454) ||
+ caseInsensitiveCompare(technology, Constants::SAM_RG_SEQTECHNOLOGY_PACBIO) ||
+ caseInsensitiveCompare(technology, Constants::SAM_RG_SEQTECHNOLOGY_SOLID)
+ )
+ {
+ return true;
+ }
+
+ // otherwise
+ AddError("Invalid read group sequencing platform (PL): " + technology);
+ return false;
+}
+
+// validate the SAM header "program chain"
+bool SamHeaderValidator::ValidateProgramChain(void) {
+ bool isValid = true;
+ isValid &= ContainsUniqueProgramIds();
+ isValid &= ValidatePreviousProgramIds();
+ return isValid;
+}
+
+// make sure all PG IDs are unique
+bool SamHeaderValidator::ContainsUniqueProgramIds(void) {
+
+ bool isValid = true;
+ set<string> programIds;
+ set<string>::iterator pgIdIter;
+
+ // iterate over program records
+ const SamProgramChain& programs = m_header.Programs;
+ SamProgramConstIterator pgIter = programs.ConstBegin();
+ SamProgramConstIterator pgEnd = programs.ConstEnd();
+ for ( ; pgIter != pgEnd; ++pgIter ) {
+ const SamProgram& pg = (*pgIter);
+
+ // lookup program ID
+ const string& pgId = pg.ID;
+ pgIdIter = programIds.find(pgId);
+
+ // error if found (duplicate entry)
+ if ( pgIdIter != programIds.end() ) {
+ AddError("Program ID (ID): " + pgId + " is not unique");
+ isValid = false;
+ }
+
+ // otherwise ok, store ID
+ programIds.insert(pgId);
+ }
+
+ // return validation state
+ return isValid;
+}
+
+// make sure that any PP tags present point to existing @PG IDs
+bool SamHeaderValidator::ValidatePreviousProgramIds(void) {
+
+ bool isValid = true;
+
+ // iterate over program records
+ const SamProgramChain& programs = m_header.Programs;
+ SamProgramConstIterator pgIter = programs.ConstBegin();
+ SamProgramConstIterator pgEnd = programs.ConstEnd();
+ for ( ; pgIter != pgEnd; ++pgIter ) {
+ const SamProgram& pg = (*pgIter);
+
+ // ignore record for validation if PreviousProgramID is empty
+ const string& ppId = pg.PreviousProgramID;
+ if ( ppId.empty() )
+ continue;
+
+ // see if program "chain" contains an entry for ppId
+ if ( !programs.Contains(ppId) ) {
+ AddError("PreviousProgramID (PP): " + ppId + " is not a known ID");
+ isValid = false;
+ }
+ }
+
+ // return validation state
+ return isValid;
+}
diff --git a/bamtools/src/api/internal/sam/SamHeaderValidator_p.h b/bamtools/src/api/internal/sam/SamHeaderValidator_p.h
new file mode 100644
index 0000000..7d0c60a
--- /dev/null
+++ b/bamtools/src/api/internal/sam/SamHeaderValidator_p.h
@@ -0,0 +1,105 @@
+// ***************************************************************************
+// SamHeaderValidator.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 6 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides functionality for validating SamHeader data
+// ***************************************************************************
+
+#ifndef SAM_HEADER_VALIDATOR_P_H
+#define SAM_HEADER_VALIDATOR_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace BamTools {
+
+class SamHeader;
+class SamReadGroup;
+class SamSequence;
+
+namespace Internal {
+
+class SamHeaderValidator {
+
+ // ctor & dtor
+ public:
+ SamHeaderValidator(const SamHeader& header);
+ ~SamHeaderValidator(void);
+
+ // SamHeaderValidator interface
+ public:
+
+ // prints error & warning messages
+ void PrintMessages(std::ostream& stream);
+
+ // validates SamHeader data, returns true/false accordingly
+ bool Validate(void);
+
+ // internal methods
+ private:
+
+ // validate header metadata
+ bool ValidateMetadata(void);
+ bool ValidateVersion(void);
+ bool ContainsOnlyDigits(const std::string& s);
+ bool ValidateSortOrder(void);
+ bool ValidateGroupOrder(void);
+
+ // validate sequence dictionary
+ bool ValidateSequenceDictionary(void);
+ bool ContainsUniqueSequenceNames(void);
+ bool CheckNameFormat(const std::string& name);
+ bool ValidateSequence(const SamSequence& seq);
+ bool CheckLengthInRange(const std::string& length);
+
+ // validate read group dictionary
+ bool ValidateReadGroupDictionary(void);
+ bool ContainsUniqueIDsAndPlatformUnits(void);
+ bool ValidateReadGroup(const SamReadGroup& rg);
+ bool CheckReadGroupID(const std::string& id);
+ bool CheckSequencingTechnology(const std::string& technology);
+
+ // validate program data
+ bool ValidateProgramChain(void);
+ bool ContainsUniqueProgramIds(void);
+ bool ValidatePreviousProgramIds(void);
+
+ // error reporting
+ void AddError(const std::string& message);
+ void AddWarning(const std::string& message);
+ void PrintErrorMessages(std::ostream& stream);
+ void PrintWarningMessages(std::ostream& stream);
+
+ // data members
+ private:
+
+ // SamHeader being validated
+ const SamHeader& m_header;
+
+ // error reporting helpers
+ static const std::string ERROR_PREFIX;
+ static const std::string WARN_PREFIX;
+ static const std::string NEWLINE;
+
+ // error reporting messages
+ std::vector<std::string> m_errorMessages;
+ std::vector<std::string> m_warningMessages;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // SAM_HEADER_VALIDATOR_P_H
diff --git a/bamtools/src/api/internal/sam/SamHeaderVersion_p.h b/bamtools/src/api/internal/sam/SamHeaderVersion_p.h
new file mode 100644
index 0000000..4f85df0
--- /dev/null
+++ b/bamtools/src/api/internal/sam/SamHeaderVersion_p.h
@@ -0,0 +1,134 @@
+// ***************************************************************************
+// SamHeaderVersion.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides functionality for comparing SAM header versions
+// *************************************************************************
+
+#ifndef SAM_HEADERVERSION_P_H
+#define SAM_HEADERVERSION_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include "api/SamConstants.h"
+#include <sstream>
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class SamHeaderVersion {
+
+ // ctors & dtor
+ public:
+ SamHeaderVersion(void)
+ : m_majorVersion(0)
+ , m_minorVersion(0)
+ { }
+
+ explicit SamHeaderVersion(const std::string& version)
+ : m_majorVersion(0)
+ , m_minorVersion(0)
+ {
+ SetVersion(version);
+ }
+
+ SamHeaderVersion(const unsigned int& major, const unsigned int& minor)
+ : m_majorVersion(major)
+ , m_minorVersion(minor)
+ { }
+
+ ~SamHeaderVersion(void) {
+ m_majorVersion = 0;
+ m_minorVersion = 0;
+ }
+
+ // acess data
+ public:
+ unsigned int MajorVersion(void) const { return m_majorVersion; }
+ unsigned int MinorVersion(void) const { return m_minorVersion; }
+
+ void SetVersion(const std::string& version);
+ std::string ToString(void) const;
+
+ // data members
+ private:
+ unsigned int m_majorVersion;
+ unsigned int m_minorVersion;
+};
+
+inline
+void SamHeaderVersion::SetVersion(const std::string& version) {
+
+ // do nothing if version is empty
+ if ( !version.empty() ) {
+
+ std::stringstream versionStream("");
+
+ // do nothing if period not found
+ const size_t periodFound = version.find(Constants::SAM_PERIOD);
+ if ( periodFound != std::string::npos ) {
+
+ // store major version if non-empty and contains only digits
+ const std::string& majorVersion = version.substr(0, periodFound);
+ versionStream.str(majorVersion);
+ if ( !majorVersion.empty() ) {
+ const size_t nonDigitFound = majorVersion.find_first_not_of(Constants::SAM_DIGITS);
+ if ( nonDigitFound == std::string::npos )
+ versionStream >> m_majorVersion;
+ }
+
+ // store minor version if non-empty and contains only digits
+ const std::string& minorVersion = version.substr(periodFound + 1);
+ versionStream.str(minorVersion);
+ if ( !minorVersion.empty() ) {
+ const size_t nonDigitFound = minorVersion.find_first_not_of(Constants::SAM_DIGITS);
+ if ( nonDigitFound == std::string::npos )
+ versionStream >> m_minorVersion;
+ }
+ }
+ }
+}
+
+// -----------------------------------------------------
+// printing
+
+inline std::string SamHeaderVersion::ToString(void) const {
+ std::stringstream version;
+ version << m_majorVersion << Constants::SAM_PERIOD << m_minorVersion;
+ return version.str();
+}
+
+// -----------------------------------------------------
+// comparison operators
+
+inline bool operator==(const SamHeaderVersion& lhs, const SamHeaderVersion& rhs) {
+ return (lhs.MajorVersion() == rhs.MajorVersion()) &&
+ (lhs.MinorVersion() == rhs.MinorVersion());
+}
+
+inline bool operator<(const SamHeaderVersion& lhs, const SamHeaderVersion& rhs) {
+ if ( lhs.MajorVersion() == rhs.MajorVersion() )
+ return lhs.MinorVersion() < rhs.MinorVersion();
+ else
+ return lhs.MajorVersion() < rhs.MajorVersion();
+}
+
+inline bool operator> (const SamHeaderVersion& lhs, const SamHeaderVersion& rhs) { return rhs < lhs; }
+inline bool operator<=(const SamHeaderVersion& lhs, const SamHeaderVersion& rhs) { return !(lhs>rhs); }
+inline bool operator>=(const SamHeaderVersion& lhs, const SamHeaderVersion& rhs) { return !(lhs<rhs); }
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // SAM_HEADERVERSION_P_H
diff --git a/bamtools/src/api/internal/utils/BamException_p.cpp b/bamtools/src/api/internal/utils/BamException_p.cpp
new file mode 100644
index 0000000..103e34b
--- /dev/null
+++ b/bamtools/src/api/internal/utils/BamException_p.cpp
@@ -0,0 +1,15 @@
+// ***************************************************************************
+// BamException_p.cpp (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 25 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides a basic exception class for BamTools internals
+// ***************************************************************************
+
+#include "api/internal/utils/BamException_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+using namespace std;
+
+const string BamException::SEPARATOR = ": ";
diff --git a/bamtools/src/api/internal/utils/BamException_p.h b/bamtools/src/api/internal/utils/BamException_p.h
new file mode 100644
index 0000000..5199737
--- /dev/null
+++ b/bamtools/src/api/internal/utils/BamException_p.h
@@ -0,0 +1,51 @@
+// ***************************************************************************
+// BamException_p.h (c) 2011 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 6 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides a basic exception class for BamTools internals
+// ***************************************************************************
+
+#ifndef BAMEXCEPTION_P_H
+#define BAMEXCEPTION_P_H
+
+// -------------
+// W A R N I N G
+// -------------
+//
+// This file is not part of the BamTools API. It exists purely as an
+// implementation detail. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+
+#include <exception>
+#include <string>
+
+namespace BamTools {
+namespace Internal {
+
+class BamException : public std::exception {
+
+ public:
+ inline BamException(const std::string& where, const std::string& message)
+ : std::exception()
+ , m_errorString(where + SEPARATOR + message)
+ { }
+
+ inline ~BamException(void) throw() { }
+
+ inline const char* what(void) const throw() {
+ return m_errorString.c_str();
+ }
+
+ private:
+ std::string m_errorString;
+ static const std::string SEPARATOR;
+};
+
+} // namespace Internal
+} // namespace BamTools
+
+#endif // BAMEXCEPTION_P_H
diff --git a/bamtools/src/api/internal/utils/CMakeLists.txt b/bamtools/src/api/internal/utils/CMakeLists.txt
new file mode 100644
index 0000000..4b1e2c2
--- /dev/null
+++ b/bamtools/src/api/internal/utils/CMakeLists.txt
@@ -0,0 +1,15 @@
+# ==========================
+# BamTools CMakeLists.txt
+# (c) 2011 Derek Barnett
+#
+# src/api/internal/utils
+# ==========================
+
+set( InternalUtilsDir "${InternalDir}/utils" )
+
+set( InternalUtilsSources
+ ${InternalUtilsDir}/BamException_p.cpp
+
+ PARENT_SCOPE # <-- leave this last
+)
+
diff --git a/bamtools/src/shared/bamtools_global.h b/bamtools/src/shared/bamtools_global.h
new file mode 100644
index 0000000..e37bff6
--- /dev/null
+++ b/bamtools/src/shared/bamtools_global.h
@@ -0,0 +1,97 @@
+// ***************************************************************************
+// bamtools_global.h (c) 2010 Derek Barnett
+// Marth Lab, Department of Biology, Boston College
+// ---------------------------------------------------------------------------
+// Last modified: 10 October 2011 (DB)
+// ---------------------------------------------------------------------------
+// Provides the basic definitions for exporting & importing library symbols.
+// Also provides some platform-specific rules for definitions.
+// ***************************************************************************
+
+#ifndef BAMTOOLS_GLOBAL_H
+#define BAMTOOLS_GLOBAL_H
+
+/*! \brief Library export macro
+ \internal
+*/
+#ifndef BAMTOOLS_LIBRARY_EXPORT
+# if defined(WIN32)
+# define BAMTOOLS_LIBRARY_EXPORT __declspec(dllexport)
+# else
+# define BAMTOOLS_LIBRARY_EXPORT __attribute__((visibility("default")))
+# endif
+#endif // BAMTOOLS_LIBRARY_EXPORT
+
+/*! \brief Library import macro
+ \internal
+*/
+#ifndef BAMTOOLS_LIBRARY_IMPORT
+# if defined(WIN32)
+# define BAMTOOLS_LIBRARY_IMPORT __declspec(dllimport)
+# else
+# define BAMTOOLS_LIBRARY_IMPORT
+# endif
+#endif // BAMTOOLS_LIBRARY_IMPORT
+
+/*! \brief Platform-specific type definitions
+ \internal
+*/
+#ifndef BAMTOOLS_LFS
+#define BAMTOOLS_LFS
+# ifdef WIN32
+# define ftell64(a) _ftelli64(a)
+# define fseek64(a,b,c) _fseeki64(a,b,c)
+# else
+# define ftell64(a) ftello(a)
+# define fseek64(a,b,c) fseeko(a,b,c)
+# endif
+#endif // BAMTOOLS_LFS
+
+/*! \def ftell64(a)
+ \brief Platform-independent tell() operation.
+ \internal
+*/
+/*! \def fseek64(a,b,c)
+ \brief Platform-independent seek() operation.
+ \internal
+*/
+
+/*! \brief Platform-specific type definitions
+ \internal
+*/
+#ifndef BAMTOOLS_TYPES
+#define BAMTOOLS_TYPES
+# ifdef _MSC_VER
+ typedef char int8_t;
+ typedef unsigned char uint8_t;
+ typedef short int16_t;
+ typedef unsigned short uint16_t;
+ typedef int int32_t;
+ typedef unsigned int uint32_t;
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+# else
+# include <stdint.h>
+# endif
+#endif // BAMTOOLS_TYPES
+
+//! \internal
+inline void bamtools_noop(void) { }
+
+/*! \brief Assert definitions
+ \internal
+*/
+#ifndef BAMTOOLS_ASSERTS
+#define BAMTOOLS_ASSERTS
+# ifdef NDEBUG
+# define BT_ASSERT_UNREACHABLE bamtools_noop()
+# define BT_ASSERT_X( condition, message ) bamtools_noop()
+# else
+# include <cassert>
+# include <stdexcept>
+# define BT_ASSERT_UNREACHABLE assert( false )
+# define BT_ASSERT_X( condition, message ) if (!( condition )) { throw std::runtime_error( message ); }
+# endif
+#endif // BAMTOOLS_ASSERTS
+
+#endif // BAMTOOLS_GLOBAL_H
diff --git a/bamtools/src/third_party/CMakeLists.txt b/bamtools/src/third_party/CMakeLists.txt
new file mode 100644
index 0000000..ec965be
--- /dev/null
+++ b/bamtools/src/third_party/CMakeLists.txt
@@ -0,0 +1,9 @@
+# ==========================
+# BamTools CMakeLists.txt
+# (c) 2010 Derek Barnett
+#
+# src/third-party/
+# ==========================
+
+# list third-party subdirectories to build in
+add_subdirectory( jsoncpp )
diff --git a/bamtools/src/third_party/gtest-1.6.0/CHANGES b/bamtools/src/third_party/gtest-1.6.0/CHANGES
new file mode 100644
index 0000000..5919245
--- /dev/null
+++ b/bamtools/src/third_party/gtest-1.6.0/CHANGES
@@ -0,0 +1,130 @@
+Changes for 1.6.0:
+
+* New feature: ADD_FAILURE_AT() for reporting a test failure at the
+ given source location -- useful for writing testing utilities.
+* New feature: the universal value printer is moved from Google Mock
+ to Google Test.
+* New feature: type parameters and value parameters are reported in
+ the XML report now.
+* A gtest_disable_pthreads CMake option.
+* Colored output works in GNU Screen sessions now.
+* Parameters of value-parameterized tests are now printed in the
+ textual output.
+* Failures from ad hoc test assertions run before RUN_ALL_TESTS() are
+ now correctly reported.
+* Arguments of ASSERT_XY and EXPECT_XY no longer need to support << to
+ ostream.
+* More complete handling of exceptions.
+* GTEST_ASSERT_XY can be used instead of ASSERT_XY in case the latter
+ name is already used by another library.
+* --gtest_catch_exceptions is now true by default, allowing a test
+ program to continue after an exception is thrown.
+* Value-parameterized test fixtures can now derive from Test and
+ WithParamInterface<T> separately, easing conversion of legacy tests.
+* Death test messages are clearly marked to make them more
+ distinguishable from other messages.
+* Compatibility fixes for Android, Google Native Client, MinGW, HP UX,
+ PowerPC, Lucid autotools, libCStd, Sun C++, Borland C++ Builder (Code Gear),
+ IBM XL C++ (Visual Age C++), and C++0x.
+* Bug fixes and implementation clean-ups.
+* Potentially incompatible changes: disables the harmful 'make install'
+ command in autotools.
+
+Changes for 1.5.0:
+
+ * New feature: assertions can be safely called in multiple threads
+ where the pthreads library is available.
+ * New feature: predicates used inside EXPECT_TRUE() and friends
+ can now generate custom failure messages.
+ * New feature: Google Test can now be compiled as a DLL.
+ * New feature: fused source files are included.
+ * New feature: prints help when encountering unrecognized Google Test flags.
+ * Experimental feature: CMake build script (requires CMake 2.6.4+).
+ * Experimental feature: the Pump script for meta programming.
+ * double values streamed to an assertion are printed with enough precision
+ to differentiate any two different values.
+ * Google Test now works on Solaris and AIX.
+ * Build and test script improvements.
+ * Bug fixes and implementation clean-ups.
+
+ Potentially breaking changes:
+
+ * Stopped supporting VC++ 7.1 with exceptions disabled.
+ * Dropped support for 'make install'.
+
+Changes for 1.4.0:
+
+ * New feature: the event listener API
+ * New feature: test shuffling
+ * New feature: the XML report format is closer to junitreport and can
+ be parsed by Hudson now.
+ * New feature: when a test runs under Visual Studio, its failures are
+ integrated in the IDE.
+ * New feature: /MD(d) versions of VC++ projects.
+ * New feature: elapsed time for the tests is printed by default.
+ * New feature: comes with a TR1 tuple implementation such that Boost
+ is no longer needed for Combine().
+ * New feature: EXPECT_DEATH_IF_SUPPORTED macro and friends.
+ * New feature: the Xcode project can now produce static gtest
+ libraries in addition to a framework.
+ * Compatibility fixes for Solaris, Cygwin, minGW, Windows Mobile,
+ Symbian, gcc, and C++Builder.
+ * Bug fixes and implementation clean-ups.
+
+Changes for 1.3.0:
+
+ * New feature: death tests on Windows, Cygwin, and Mac.
+ * New feature: ability to use Google Test assertions in other testing
+ frameworks.
+ * New feature: ability to run disabled test via
+ --gtest_also_run_disabled_tests.
+ * New feature: the --help flag for printing the usage.
+ * New feature: access to Google Test flag values in user code.
+ * New feature: a script that packs Google Test into one .h and one
+ .cc file for easy deployment.
+ * New feature: support for distributing test functions to multiple
+ machines (requires support from the test runner).
+ * Bug fixes and implementation clean-ups.
+
+Changes for 1.2.1:
+
+ * Compatibility fixes for Linux IA-64 and IBM z/OS.
+ * Added support for using Boost and other TR1 implementations.
+ * Changes to the build scripts to support upcoming release of Google C++
+ Mocking Framework.
+ * Added Makefile to the distribution package.
+ * Improved build instructions in README.
+
+Changes for 1.2.0:
+
+ * New feature: value-parameterized tests.
+ * New feature: the ASSERT/EXPECT_(NON)FATAL_FAILURE(_ON_ALL_THREADS)
+ macros.
+ * Changed the XML report format to match JUnit/Ant's.
+ * Added tests to the Xcode project.
+ * Added scons/SConscript for building with SCons.
+ * Added src/gtest-all.cc for building Google Test from a single file.
+ * Fixed compatibility with Solaris and z/OS.
+ * Enabled running Python tests on systems with python 2.3 installed,
+ e.g. Mac OS X 10.4.
+ * Bug fixes.
+
+Changes for 1.1.0:
+
+ * New feature: type-parameterized tests.
+ * New feature: exception assertions.
+ * New feature: printing elapsed time of tests.
+ * Improved the robustness of death tests.
+ * Added an Xcode project and samples.
+ * Adjusted the output format on Windows to be understandable by Visual Studio.
+ * Minor bug fixes.
+
+Changes for 1.0.1:
+
+ * Added project files for Visual Studio 7.1.
+ * Fixed issues with compiling on Mac OS X.
+ * Fixed issues with compiling on Cygwin.
+
+Changes for 1.0.0:
+
+ * Initial Open Source release of Google Test
diff --git a/bamtools/src/third_party/gtest-1.6.0/CMakeLists.txt b/bamtools/src/third_party/gtest-1.6.0/CMakeLists.txt
new file mode 100644
index 0000000..0fe2654
--- /dev/null
+++ b/bamtools/src/third_party/gtest-1.6.0/CMakeLists.txt
@@ -0,0 +1,240 @@
+########################################################################
+# CMake build script for Google Test.
+#
+# To run the tests for Google Test itself on Linux, use 'make test' or
+# ctest. You can select which tests to run using 'ctest -R regex'.
+# For more options, run 'ctest --help'.
+
+# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to
+# make it prominent in the GUI.
+option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF)
+
+# When other libraries are using a shared version of runtime libraries,
+# Google Test also has to use one.
+option(
+ gtest_force_shared_crt
+ "Use shared (DLL) run-time lib even when Google Test is built as static lib."
+ OFF)
+
+option(gtest_build_tests "Build all of gtest's own tests." OFF)
+
+option(gtest_build_samples "Build gtest's sample programs." OFF)
+
+option(gtest_disable_pthreads "Disable uses of pthreads in gtest." OFF)
+
+# Defines pre_project_set_up_hermetic_build() and set_up_hermetic_build().
+include(cmake/hermetic_build.cmake OPTIONAL)
+
+if (COMMAND pre_project_set_up_hermetic_build)
+ pre_project_set_up_hermetic_build()
+endif()
+
+########################################################################
+#
+# Project-wide settings
+
+# Name of the project.
+#
+# CMake files in this project can refer to the root source directory
+# as ${gtest_SOURCE_DIR} and to the root binary directory as
+# ${gtest_BINARY_DIR}.
+# Language "C" is required for find_package(Threads).
+project(gtest CXX C)
+cmake_minimum_required(VERSION 2.6.2)
+
+if (COMMAND set_up_hermetic_build)
+ set_up_hermetic_build()
+endif()
+
+# Define helper functions and macros used by Google Test.
+include(cmake/internal_utils.cmake)
+
+config_compiler_and_linker() # Defined in internal_utils.cmake.
+
+# Where Google Test's .h files can be found.
+include_directories(
+ ${gtest_SOURCE_DIR}/include
+ ${gtest_SOURCE_DIR})
+
+# Where Google Test's libraries can be found.
+link_directories(${gtest_BINARY_DIR}/src)
+
+########################################################################
+#
+# Defines the gtest & gtest_main libraries. User tests should link
+# with one of them.
+
+# Google Test libraries. We build them using more strict warnings than what
+# are used for other targets, to ensure that gtest can be compiled by a user
+# aggressive about warnings.
+cxx_library(gtest "${cxx_strict}" src/gtest-all.cc)
+cxx_library(gtest_main "${cxx_strict}" src/gtest_main.cc)
+target_link_libraries(gtest_main gtest)
+
+########################################################################
+#
+# Samples on how to link user tests with gtest or gtest_main.
+#
+# They are not built by default. To build them, set the
+# gtest_build_samples option to ON. You can do it by running ccmake
+# or specifying the -Dbuild_gtest_samples=ON flag when running cmake.
+
+if (gtest_build_samples)
+ cxx_executable(sample1_unittest samples gtest_main samples/sample1.cc)
+ cxx_executable(sample2_unittest samples gtest_main samples/sample2.cc)
+ cxx_executable(sample3_unittest samples gtest_main)
+ cxx_executable(sample4_unittest samples gtest_main samples/sample4.cc)
+ cxx_executable(sample5_unittest samples gtest_main samples/sample1.cc)
+ cxx_executable(sample6_unittest samples gtest_main)
+ cxx_executable(sample7_unittest samples gtest_main)
+ cxx_executable(sample8_unittest samples gtest_main)
+ cxx_executable(sample9_unittest samples gtest)
+ cxx_executable(sample10_unittest samples gtest)
+endif()
+
+########################################################################
+#
+# Google Test's own tests.
+#
+# You can skip this section if you aren't interested in testing
+# Google Test itself.
+#
+# The tests are not built by default. To build them, set the
+# gtest_build_tests option to ON. You can do it by running ccmake
+# or specifying the -Dgtest_build_tests=ON flag when running cmake.
+
+if (gtest_build_tests)
+ # This must be set in the root directory for the tests to be run by
+ # 'make test' or ctest.
+ enable_testing()
+
+ ############################################################
+ # C++ tests built with standard compiler flags.
+
+ cxx_test(gtest-death-test_test gtest_main)
+ cxx_test(gtest_environment_test gtest)
+ cxx_test(gtest-filepath_test gtest_main)
+ cxx_test(gtest-linked_ptr_test gtest_main)
+ cxx_test(gtest-listener_test gtest_main)
+ cxx_test(gtest_main_unittest gtest_main)
+ cxx_test(gtest-message_test gtest_main)
+ cxx_test(gtest_no_test_unittest gtest)
+ cxx_test(gtest-options_test gtest_main)
+ cxx_test(gtest-param-test_test gtest
+ test/gtest-param-test2_test.cc)
+ cxx_test(gtest-port_test gtest_main)
+ cxx_test(gtest_pred_impl_unittest gtest_main)
+ cxx_test(gtest-printers_test gtest_main)
+ cxx_test(gtest_prod_test gtest_main
+ test/production.cc)
+ cxx_test(gtest_repeat_test gtest)
+ cxx_test(gtest_sole_header_test gtest_main)
+ cxx_test(gtest_stress_test gtest)
+ cxx_test(gtest-test-part_test gtest_main)
+ cxx_test(gtest_throw_on_failure_ex_test gtest)
+ cxx_test(gtest-typed-test_test gtest_main
+ test/gtest-typed-test2_test.cc)
+ cxx_test(gtest_unittest gtest_main)
+ cxx_test(gtest-unittest-api_test gtest)
+
+ ############################################################
+ # C++ tests built with non-standard compiler flags.
+
+ cxx_library(gtest_no_exception "${cxx_no_exception}"
+ src/gtest-all.cc)
+ cxx_library(gtest_main_no_exception "${cxx_no_exception}"
+ src/gtest-all.cc src/gtest_main.cc)
+ cxx_library(gtest_main_no_rtti "${cxx_no_rtti}"
+ src/gtest-all.cc src/gtest_main.cc)
+
+ cxx_test_with_flags(gtest-death-test_ex_nocatch_test
+ "${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=0"
+ gtest test/gtest-death-test_ex_test.cc)
+ cxx_test_with_flags(gtest-death-test_ex_catch_test
+ "${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=1"
+ gtest test/gtest-death-test_ex_test.cc)
+
+ cxx_test_with_flags(gtest_no_rtti_unittest "${cxx_no_rtti}"
+ gtest_main_no_rtti test/gtest_unittest.cc)
+
+ cxx_shared_library(gtest_dll "${cxx_default}"
+ src/gtest-all.cc src/gtest_main.cc)
+
+ cxx_executable_with_flags(gtest_dll_test_ "${cxx_default}"
+ gtest_dll test/gtest_all_test.cc)
+ set_target_properties(gtest_dll_test_
+ PROPERTIES
+ COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1")
+
+ if (NOT MSVC OR NOT MSVC_VERSION EQUAL 1600)
+ # The C++ Standard specifies tuple_element<int, class>.
+ # Yet MSVC 10's <utility> declares tuple_element<size_t, class>.
+ # That declaration conflicts with our own standard-conforming
+ # tuple implementation. Therefore using our own tuple with
+ # MSVC 10 doesn't compile.
+ cxx_library(gtest_main_use_own_tuple "${cxx_use_own_tuple}"
+ src/gtest-all.cc src/gtest_main.cc)
+
+ cxx_test_with_flags(gtest-tuple_test "${cxx_use_own_tuple}"
+ gtest_main_use_own_tuple test/gtest-tuple_test.cc)
+
+ cxx_test_with_flags(gtest_use_own_tuple_test "${cxx_use_own_tuple}"
+ gtest_main_use_own_tuple
+ test/gtest-param-test_test.cc test/gtest-param-test2_test.cc)
+ endif()
+
+ ############################################################
+ # Python tests.
+
+ cxx_executable(gtest_break_on_failure_unittest_ test gtest)
+ py_test(gtest_break_on_failure_unittest)
+
+ cxx_executable_with_flags(
+ gtest_catch_exceptions_no_ex_test_
+ "${cxx_no_exception}"
+ gtest_main_no_exception
+ test/gtest_catch_exceptions_test_.cc)
+ cxx_executable_with_flags(
+ gtest_catch_exceptions_ex_test_
+ "${cxx_exception}"
+ gtest_main
+ test/gtest_catch_exceptions_test_.cc)
+ py_test(gtest_catch_exceptions_test)
+
+ cxx_executable(gtest_color_test_ test gtest)
+ py_test(gtest_color_test)
+
+ cxx_executable(gtest_env_var_test_ test gtest)
+ py_test(gtest_env_var_test)
+
+ cxx_executable(gtest_filter_unittest_ test gtest)
+ py_test(gtest_filter_unittest)
+
+ cxx_executable(gtest_help_test_ test gtest_main)
+ py_test(gtest_help_test)
+
+ cxx_executable(gtest_list_tests_unittest_ test gtest)
+ py_test(gtest_list_tests_unittest)
+
+ cxx_executable(gtest_output_test_ test gtest)
+ py_test(gtest_output_test)
+
+ cxx_executable(gtest_shuffle_test_ test gtest)
+ py_test(gtest_shuffle_test)
+
+ cxx_executable(gtest_throw_on_failure_test_ test gtest_no_exception)
+ set_target_properties(gtest_throw_on_failure_test_
+ PROPERTIES
+ COMPILE_FLAGS "${cxx_no_exception}")
+ py_test(gtest_throw_on_failure_test)
+
+ cxx_executable(gtest_uninitialized_test_ test gtest)
+ py_test(gtest_uninitialized_test)
+
+ cxx_executable(gtest_xml_outfile1_test_ test gtest_main)
+ cxx_executable(gtest_xml_outfile2_test_ test gtest_main)
+ py_test(gtest_xml_outfiles_test)
+
+ cxx_executable(gtest_xml_output_unittest_ test gtest)
+ py_test(gtest_xml_output_unittest)
+endif()
diff --git a/bamtools/src/third_party/gtest-1.6.0/CONTRIBUTORS b/bamtools/src/third_party/gtest-1.6.0/CONTRIBUTORS
new file mode 100644
index 0000000..feae2fc
--- /dev/null
+++ b/bamtools/src/third_party/gtest-1.6.0/CONTRIBUTORS
@@ -0,0 +1,37 @@
+# This file contains a list of people who've made non-trivial
+# contribution to the Google C++ Testing Framework project. People
+# who commit code to the project are encouraged to add their names
+# here. Please keep the list sorted by first names.
+
+Ajay Joshi <***@google.com>
+Balázs Dán <***@gmail.com>
+Bharat Mediratta <***@menalto.com>
+Chandler Carruth <***@google.com>
+Chris Prince <***@google.com>
+Chris Taylor <***@google.com>
+Dan Egnor <***@google.com>
+Eric Roman <***@chromium.org>
+Hady Zalek <***@gmail.com>
+Jeffrey Yasskin <***@google.com>
+Jói Sigurðsson <***@google.com>
+Keir Mierle <***@gmail.com>
+Keith Ray <***@gmail.com>
+Kenton Varda <***@google.com>
+Manuel Klimek <***@google.com>
+Markus Heule <***@gmail.com>
+Mika Raento <***@iki.fi>
+Miklós Fazekas <***@szemafor.com>
+Pasi Valminen <***@gmail.com>
+Patrick Hanna <***@google.com>
+Patrick Riley <***@google.com>
+Peter Kaminski <***@google.com>
+Preston Jackson <***@gmail.com>
+Rainer Klaffenboeck <***@dynatrace.com>
+Russ Cox <***@google.com>
+Russ Rufer <***@pentad.com>
+Sean Mcafee <***@gmail.com>
+Sigurður Ásgeirsson <***@google.com>
+Tracy Bialik <***@pentad.com>
+Vadim Berman <***@google.com>
+Vlad Losev <***@google.com>
+Zhanyong Wan <***@google.com>
diff --git a/bamtools/src/third_party/gtest-1.6.0/COPYING b/bamtools/src/third_party/gtest-1.6.0/COPYING
new file mode 100644
index 0000000..1941a11
--- /dev/null
+++ b/bamtools/src/third_party/gtest-1.6.0/COPYING
@@ -0,0 +1,28 @@
+Copyright 2008, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/bamtools/src/third_party/gtest-1.6.0/README b/bamtools/src/third_party/gtest-1.6.0/README
new file mode 100644
index 0000000..51a9376
--- /dev/null
+++ b/bamtools/src/third_party/gtest-1.6.0/README
@@ -0,0 +1,424 @@
+Google C++ Testing Framework
+============================
+
+http://code.google.com/p/googletest/
+
+Overview
+--------
+
+Google's framework for writing C++ tests on a variety of platforms
+(Linux, Mac OS X, Windows, Windows CE, Symbian, etc). Based on the
+xUnit architecture. Supports automatic test discovery, a rich set of
+assertions, user-defined assertions, death tests, fatal and non-fatal
+failures, various options for running the tests, and XML test report
+generation.
+
+Please see the project page above for more information as well as the
+mailing list for questions, discussions, and development. There is
+also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please
+join us!
+
+Requirements for End Users
+--------------------------
+
+Google Test is designed to have fairly minimal requirements to build
+and use with your projects, but there are some. Currently, we support
+Linux, Windows, Mac OS X, and Cygwin. We will also make our best
+effort to support other platforms (e.g. Solaris, AIX, and z/OS).
+However, since core members of the Google Test project have no access
+to these platforms, Google Test may have outstanding issues there. If
+you notice any problems on your platform, please notify
+***@googlegroups.com. Patches for fixing them are
+even more welcome!
+
+### Linux Requirements ###
+
+These are the base requirements to build and use Google Test from a source
+package (as described below):
+ * GNU-compatible Make or gmake
+ * POSIX-standard shell
+ * POSIX(-2) Regular Expressions (regex.h)
+ * A C++98-standard-compliant compiler
+
+### Windows Requirements ###
+
+ * Microsoft Visual C++ 7.1 or newer
+
+### Cygwin Requirements ###
+
+ * Cygwin 1.5.25-14 or newer
+
+### Mac OS X Requirements ###
+
+ * Mac OS X 10.4 Tiger or newer
+ * Developer Tools Installed
+
+Also, you'll need CMake 2.6.4 or higher if you want to build the
+samples using the provided CMake script, regardless of the platform.
+
+Requirements for Contributors
+-----------------------------
+
+We welcome patches. If you plan to contribute a patch, you need to
+build Google Test and its own tests from an SVN checkout (described
+below), which has further requirements:
+
+ * Python version 2.3 or newer (for running some of the tests and
+ re-generating certain source files from templates)
+ * CMake 2.6.4 or newer
+
+Getting the Source
+------------------
+
+There are two primary ways of getting Google Test's source code: you
+can download a stable source release in your preferred archive format,
+or directly check out the source from our Subversion (SVN) repositary.
+The SVN checkout requires a few extra steps and some extra software
+packages on your system, but lets you track the latest development and
+make patches much more easily, so we highly encourage it.
+
+### Source Package ###
+
+Google Test is released in versioned source packages which can be
+downloaded from the download page [1]. Several different archive
+formats are provided, but the only difference is the tools used to
+manipulate them, and the size of the resulting file. Download
+whichever you are most comfortable with.
+
+ [1] http://code.google.com/p/googletest/downloads/list
+
+Once the package is downloaded, expand it using whichever tools you
+prefer for that type. This will result in a new directory with the
+name "gtest-X.Y.Z" which contains all of the source code. Here are
+some examples on Linux:
+
+ tar -xvzf gtest-X.Y.Z.tar.gz
+ tar -xvjf gtest-X.Y.Z.tar.bz2
+ unzip gtest-X.Y.Z.zip
+
+### SVN Checkout ###
+
+To check out the main branch (also known as the "trunk") of Google
+Test, run the following Subversion command:
+
+ svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn
+
+Setting up the Build
+--------------------
+
+To build Google Test and your tests that use it, you need to tell your
+build system where to find its headers and source files. The exact
+way to do it depends on which build system you use, and is usually
+straightforward.
+
+### Generic Build Instructions ###
+
+Suppose you put Google Test in directory ${GTEST_DIR}. To build it,
+create a library build target (or a project as called by Visual Studio
+and Xcode) to compile
+
+ ${GTEST_DIR}/src/gtest-all.cc
+
+with
+
+ ${GTEST_DIR}/include and ${GTEST_DIR}
+
+in the header search path. Assuming a Linux-like system and gcc,
+something like the following will do:
+
+ g++ -I${GTEST_DIR}/include -I${GTEST_DIR} -c ${GTEST_DIR}/src/gtest-all.cc
+ ar -rv libgtest.a gtest-all.o
+
+Next, you should compile your test source file with
+${GTEST_DIR}/include in the header search path, and link it with gtest
+and any other necessary libraries:
+
+ g++ -I${GTEST_DIR}/include path/to/your_test.cc libgtest.a -o your_test
+
+As an example, the make/ directory contains a Makefile that you can
+use to build Google Test on systems where GNU make is available
+(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google
+Test's own tests. Instead, it just builds the Google Test library and
+a sample test. You can use it as a starting point for your own build
+script.
+
+If the default settings are correct for your environment, the
+following commands should succeed:
+
+ cd ${GTEST_DIR}/make
+ make
+ ./sample1_unittest
+
+If you see errors, try to tweak the contents of make/Makefile to make
+them go away. There are instructions in make/Makefile on how to do
+it.
+
+### Using CMake ###
+
+Google Test comes with a CMake build script (CMakeLists.txt) that can
+be used on a wide range of platforms ("C" stands for cross-platofrm.).
+If you don't have CMake installed already, you can download it for
+free from http://www.cmake.org/.
+
+CMake works by generating native makefiles or build projects that can
+be used in the compiler environment of your choice. The typical
+workflow starts with:
+
+ mkdir mybuild # Create a directory to hold the build output.
+ cd mybuild
+ cmake ${GTEST_DIR} # Generate native build scripts.
+
+If you want to build Google Test's samples, you should replace the
+last command with
+
+ cmake -Dgtest_build_samples=ON ${GTEST_DIR}
+
+If you are on a *nix system, you should now see a Makefile in the
+current directory. Just type 'make' to build gtest.
+
+If you use Windows and have Vistual Studio installed, a gtest.sln file
+and several .vcproj files will be created. You can then build them
+using Visual Studio.
+
+On Mac OS X with Xcode installed, a .xcodeproj file will be generated.
+
+### Legacy Build Scripts ###
+
+Before settling on CMake, we have been providing hand-maintained build
+projects/scripts for Visual Studio, Xcode, and Autotools. While we
+continue to provide them for convenience, they are not actively
+maintained any more. We highly recommend that you follow the
+instructions in the previous two sections to integrate Google Test
+with your existing build system.
+
+If you still need to use the legacy build scripts, here's how:
+
+The msvc\ folder contains two solutions with Visual C++ projects.
+Open the gtest.sln or gtest-md.sln file using Visual Studio, and you
+are ready to build Google Test the same way you build any Visual
+Studio project. Files that have names ending with -md use DLL
+versions of Microsoft runtime libraries (the /MD or the /MDd compiler
+option). Files without that suffix use static versions of the runtime
+libraries (the /MT or the /MTd option). Please note that one must use
+the same option to compile both gtest and the test code. If you use
+Visual Studio 2005 or above, we recommend the -md version as /MD is
+the default for new projects in these versions of Visual Studio.
+
+On Mac OS X, open the gtest.xcodeproj in the xcode/ folder using
+Xcode. Build the "gtest" target. The universal binary framework will
+end up in your selected build directory (selected in the Xcode
+"Preferences..." -> "Building" pane and defaults to xcode/build).
+Alternatively, at the command line, enter:
+
+ xcodebuild
+
+This will build the "Release" configuration of gtest.framework in your
+default build location. See the "xcodebuild" man page for more
+information about building different configurations and building in
+different locations.
+
+Tweaking Google Test
+--------------------
+
+Google Test can be used in diverse environments. The default
+configuration may not work (or may not work well) out of the box in
+some environments. However, you can easily tweak Google Test by
+defining control macros on the compiler command line. Generally,
+these macros are named like GTEST_XYZ and you define them to either 1
+or 0 to enable or disable a certain feature.
+
+We list the most frequently used macros below. For a complete list,
+see file include/gtest/internal/gtest-port.h.
+
+### Choosing a TR1 Tuple Library ###
+
+Some Google Test features require the C++ Technical Report 1 (TR1)
+tuple library, which is not yet available with all compilers. The
+good news is that Google Test implements a subset of TR1 tuple that's
+enough for its own need, and will automatically use this when the
+compiler doesn't provide TR1 tuple.
+
+Usually you don't need to care about which tuple library Google Test
+uses. However, if your project already uses TR1 tuple, you need to
+tell Google Test to use the same TR1 tuple library the rest of your
+project uses, or the two tuple implementations will clash. To do
+that, add
+
+ -DGTEST_USE_OWN_TR1_TUPLE=0
+
+to the compiler flags while compiling Google Test and your tests. If
+you want to force Google Test to use its own tuple library, just add
+
+ -DGTEST_USE_OWN_TR1_TUPLE=1
+
+to the compiler flags instead.
+
+If you don't want Google Test to use tuple at all, add
+
+ -DGTEST_HAS_TR1_TUPLE=0
+
+and all features using tuple will be disabled.
+
+### Multi-threaded Tests ###
+
+Google Test is thread-safe where the pthread library is available.
+After #include "gtest/gtest.h", you can check the GTEST_IS_THREADSAFE
+macro to see whether this is the case (yes if the macro is #defined to
+1, no if it's undefined.).
+
+If Google Test doesn't correctly detect whether pthread is available
+in your environment, you can force it with
+
+ -DGTEST_HAS_PTHREAD=1
+
+or
+
+ -DGTEST_HAS_PTHREAD=0
+
+When Google Test uses pthread, you may need to add flags to your
+compiler and/or linker to select the pthread library, or you'll get
+link errors. If you use the CMake script or the deprecated Autotools
+script, this is taken care of for you. If you use your own build
+script, you'll need to read your compiler and linker's manual to
+figure out what flags to add.
+
+### As a Shared Library (DLL) ###
+
+Google Test is compact, so most users can build and link it as a
+static library for the simplicity. You can choose to use Google Test
+as a shared library (known as a DLL on Windows) if you prefer.
+
+To compile *gtest* as a shared library, add
+
+ -DGTEST_CREATE_SHARED_LIBRARY=1
+
+to the compiler flags. You'll also need to tell the linker to produce
+a shared library instead - consult your linker's manual for how to do
+it.
+
+To compile your *tests* that use the gtest shared library, add
+
+ -DGTEST_LINKED_AS_SHARED_LIBRARY=1
+
+to the compiler flags.
+
+Note: while the above steps aren't technically necessary today when
+using some compilers (e.g. GCC), they may become necessary in the
+future, if we decide to improve the speed of loading the library (see
+http://gcc.gnu.org/wiki/Visibility for details). Therefore you are
+recommended to always add the above flags when using Google Test as a
+shared library. Otherwise a future release of Google Test may break
+your build script.
+
+### Avoiding Macro Name Clashes ###
+
+In C++, macros don't obey namespaces. Therefore two libraries that
+both define a macro of the same name will clash if you #include both
+definitions. In case a Google Test macro clashes with another
+library, you can force Google Test to rename its macro to avoid the
+conflict.
+
+Specifically, if both Google Test and some other code define macro
+FOO, you can add
+
+ -DGTEST_DONT_DEFINE_FOO=1
+
+to the compiler flags to tell Google Test to change the macro's name
+from FOO to GTEST_FOO. Currently FOO can be FAIL, SUCCEED, or TEST.
+For example, with -DGTEST_DONT_DEFINE_TEST=1, you'll need to write
+
+ GTEST_TEST(SomeTest, DoesThis) { ... }
+
+instead of
+
+ TEST(SomeTest, DoesThis) { ... }
+
+in order to define a test.
+
+Upgrating from an Earlier Version
+---------------------------------
+
+We strive to keep Google Test releases backward compatible.
+Sometimes, though, we have to make some breaking changes for the
+users' long-term benefits. This section describes what you'll need to
+do if you are upgrading from an earlier version of Google Test.
+
+### Upgrading from 1.3.0 or Earlier ###
+
+You may need to explicitly enable or disable Google Test's own TR1
+tuple library. See the instructions in section "Choosing a TR1 Tuple
+Library".
+
+### Upgrading from 1.4.0 or Earlier ###
+
+The Autotools build script (configure + make) is no longer officially
+supportted. You are encouraged to migrate to your own build system or
+use CMake. If you still need to use Autotools, you can find
+instructions in the README file from Google Test 1.4.0.
+
+On platforms where the pthread library is available, Google Test uses
+it in order to be thread-safe. See the "Multi-threaded Tests" section
+for what this means to your build script.
+
+If you use Microsoft Visual C++ 7.1 with exceptions disabled, Google
+Test will no longer compile. This should affect very few people, as a
+large portion of STL (including <string>) doesn't compile in this mode
+anyway. We decided to stop supporting it in order to greatly simplify
+Google Test's implementation.
+
+Developing Google Test
+----------------------
+
+This section discusses how to make your own changes to Google Test.
+
+### Testing Google Test Itself ###
+
+To make sure your changes work as intended and don't break existing
+functionality, you'll want to compile and run Google Test's own tests.
+For that you can use CMake:
+
+ mkdir mybuild
+ cd mybuild
+ cmake -Dgtest_build_tests=ON ${GTEST_DIR}
+
+Make sure you have Python installed, as some of Google Test's tests
+are written in Python. If the cmake command complains about not being
+able to find Python ("Could NOT find PythonInterp (missing:
+PYTHON_EXECUTABLE)"), try telling it explicitly where your Python
+executable can be found:
+
+ cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR}
+
+Next, you can build Google Test and all of its own tests. On *nix,
+this is usually done by 'make'. To run the tests, do
+
+ make test
+
+All tests should pass.
+
+### Regenerating Source Files ###
+
+Some of Google Test's source files are generated from templates (not
+in the C++ sense) using a script. A template file is named FOO.pump,
+where FOO is the name of the file it will generate. For example, the
+file include/gtest/internal/gtest-type-util.h.pump is used to generate
+gtest-type-util.h in the same directory.
+
+Normally you don't need to worry about regenerating the source files,
+unless you need to modify them. In that case, you should modify the
+corresponding .pump files instead and run the pump.py Python script to
+regenerate them. You can find pump.py in the scripts/ directory.
+Read the Pump manual [2] for how to use it.
+
+ [2] http://code.google.com/p/googletest/wiki/PumpManual
+
+### Contributing a Patch ###
+
+We welcome patches. Please read the Google Test developer's guide [3]
+for how you can contribute. In particular, make sure you have signed
+the Contributor License Agreement, or we won't be able to accept the
+patch.
+
+ [3] http://code.google.com/p/googletest/wiki/GoogleTestDevGuide
+
+Happy testing!
diff --git a/bamtools/src/third_party/gtest-1.6.0/cmake/internal_utils.cmake b/bamtools/src/third_party/gtest-1.6.0/cmake/internal_utils.cmake
new file mode 100644
index 0000000..7efc2ac
--- /dev/null
+++ b/bamtools/src/third_party/gtest-1.6.0/cmake/internal_utils.cmake
@@ -0,0 +1,216 @@
+# Defines functions and macros useful for building Google Test and
+# Google Mock.
+#
+# Note:
+#
+# - This file will be run twice when building Google Mock (once via
+# Google Test's CMakeLists.txt, and once via Google Mock's).
+# Therefore it shouldn't have any side effects other than defining
+# the functions and macros.
+#
+# - The functions/macros defined in this file may depend on Google
+# Test and Google Mock's option() definitions, and thus must be
+# called *after* the options have been defined.
+
+# Tweaks CMake's default compiler/linker settings to suit Google Test's needs.
+#
+# This must be a macro(), as inside a function string() can only
+# update variables in the function scope.
+macro(fix_default_compiler_settings_)
+ if (MSVC)
+ # For MSVC, CMake sets certain flags to defaults we want to override.
+ # This replacement code is taken from sample in the CMake Wiki at
+ # http://www.cmake.org/Wiki/CMake_FAQ#Dynamic_Replace.
+ foreach (flag_var
+ CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+ if (NOT BUILD_SHARED_LIBS AND NOT gtest_force_shared_crt)
+ # When Google Test is built as a shared library, it should also use
+ # shared runtime libraries. Otherwise, it may end up with multiple
+ # copies of runtime library data in different modules, resulting in
+ # hard-to-find crashes. When it is built as a static library, it is
+ # preferable to use CRT as static libraries, as we don't have to rely
+ # on CRT DLLs being available. CMake always defaults to using shared
+ # CRT libraries, so we override that default here.
+ string(REPLACE "/MD" "-MT" ${flag_var} "${${flag_var}}")
+ endif()
+
+ # We prefer more strict warning checking for building Google Test.
+ # Replaces /W3 with /W4 in defaults.
+ string(REPLACE "/W3" "-W4" ${flag_var} "${${flag_var}}")
+ endforeach()
+ endif()
+endmacro()
+
+# Defines the compiler/linker flags used to build Google Test and
+# Google Mock. You can tweak these definitions to suit your need. A
+# variable's value is empty before it's explicitly assigned to.
+macro(config_compiler_and_linker)
+ if (NOT gtest_disable_pthreads)
+ # Defines CMAKE_USE_PTHREADS_INIT and CMAKE_THREAD_LIBS_INIT.
+ find_package(Threads)
+ endif()
+
+ fix_default_compiler_settings_()
+ if (MSVC)
+ # Newlines inside flags variables break CMake's NMake generator.
+ # TODO(***@google.com): Add -RTCs and -RTCu to debug builds.
+ set(cxx_base_flags "-GS -W4 -WX -wd4127 -wd4251 -wd4275 -nologo -J -Zi")
+ set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32")
+ set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN")
+ set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1")
+ set(cxx_no_exception_flags "-D_HAS_EXCEPTIONS=0")
+ set(cxx_no_rtti_flags "-GR-")
+ elseif (CMAKE_COMPILER_IS_GNUCXX)
+ set(cxx_base_flags "-Wall -Wshadow")
+ set(cxx_exception_flags "-fexceptions")
+ set(cxx_no_exception_flags "-fno-exceptions")
+ # Until version 4.3.2, GCC doesn't define a macro to indicate
+ # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI
+ # explicitly.
+ set(cxx_no_rtti_flags "-fno-rtti -DGTEST_HAS_RTTI=0")
+ set(cxx_strict_flags "-Wextra")
+ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "SunPro")
+ set(cxx_exception_flags "-features=except")
+ # Sun Pro doesn't provide macros to indicate whether exceptions and
+ # RTTI are enabled, so we define GTEST_HAS_* explicitly.
+ set(cxx_no_exception_flags "-features=no%except -DGTEST_HAS_EXCEPTIONS=0")
+ set(cxx_no_rtti_flags "-features=no%rtti -DGTEST_HAS_RTTI=0")
+ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "VisualAge" OR
+ CMAKE_CXX_COMPILER_ID STREQUAL "XL")
+ # CMake 2.8 changes Visual Age's compiler ID to "XL".
+ set(cxx_exception_flags "-qeh")
+ set(cxx_no_exception_flags "-qnoeh")
+ # Until version 9.0, Visual Age doesn't define a macro to indicate
+ # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI
+ # explicitly.
+ set(cxx_no_rtti_flags "-qnortti -DGTEST_HAS_RTTI=0")
+ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "HP")
+ set(cxx_base_flags "-AA -mt")
+ set(cxx_exception_flags "-DGTEST_HAS_EXCEPTIONS=1")
+ set(cxx_no_exception_flags "+noeh -DGTEST_HAS_EXCEPTIONS=0")
+ # RTTI can not be disabled in HP aCC compiler.
+ set(cxx_no_rtti_flags "")
+ endif()
+
+ if (CMAKE_USE_PTHREADS_INIT) # The pthreads library is available and allowed.
+ set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=1")
+ else()
+ set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=0")
+ endif()
+
+ # For building gtest's own tests and samples.
+ set(cxx_exception "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_exception_flags}")
+ set(cxx_no_exception
+ "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_no_exception_flags}")
+ set(cxx_default "${cxx_exception}")
+ set(cxx_no_rtti "${cxx_default} ${cxx_no_rtti_flags}")
+ set(cxx_use_own_tuple "${cxx_default} -DGTEST_USE_OWN_TR1_TUPLE=1")
+
+ # For building the gtest libraries.
+ set(cxx_strict "${cxx_default} ${cxx_strict_flags}")
+endmacro()
+
+# Defines the gtest & gtest_main libraries. User tests should link
+# with one of them.
+function(cxx_library_with_type name type cxx_flags)
+ # type can be either STATIC or SHARED to denote a static or shared library.
+ # ARGN refers to additional arguments after 'cxx_flags'.
+ add_library(${name} ${type} ${ARGN})
+ set_target_properties(${name}
+ PROPERTIES
+ COMPILE_FLAGS "${cxx_flags}")
+ if (BUILD_SHARED_LIBS OR type STREQUAL "SHARED")
+ set_target_properties(${name}
+ PROPERTIES
+ COMPILE_DEFINITIONS "GTEST_CREATE_SHARED_LIBRARY=1")
+ endif()
+ if (CMAKE_USE_PTHREADS_INIT)
+ target_link_libraries(${name} ${CMAKE_THREAD_LIBS_INIT})
+ endif()
+endfunction()
+
+########################################################################
+#
+# Helper functions for creating build targets.
+
+function(cxx_shared_library name cxx_flags)
+ cxx_library_with_type(${name} SHARED "${cxx_flags}" ${ARGN})
+endfunction()
+
+function(cxx_library name cxx_flags)
+ cxx_library_with_type(${name} "" "${cxx_flags}" ${ARGN})
+endfunction()
+
+# cxx_executable_with_flags(name cxx_flags libs srcs...)
+#
+# creates a named C++ executable that depends on the given libraries and
+# is built from the given source files with the given compiler flags.
+function(cxx_executable_with_flags name cxx_flags libs)
+ add_executable(${name} ${ARGN})
+ if (cxx_flags)
+ set_target_properties(${name}
+ PROPERTIES
+ COMPILE_FLAGS "${cxx_flags}")
+ endif()
+ if (BUILD_SHARED_LIBS)
+ set_target_properties(${name}
+ PROPERTIES
+ COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1")
+ endif()
+ # To support mixing linking in static and dynamic libraries, link each
+ # library in with an extra call to target_link_libraries.
+ foreach (lib "${libs}")
+ target_link_libraries(${name} ${lib})
+ endforeach()
+endfunction()
+
+# cxx_executable(name dir lib srcs...)
+#
+# creates a named target that depends on the given libs and is built
+# from the given source files. dir/name.cc is implicitly included in
+# the source file list.
+function(cxx_executable name dir libs)
+ cxx_executable_with_flags(
+ ${name} "${cxx_default}" "${libs}" "${dir}/${name}.cc" ${ARGN})
+endfunction()
+
+# Sets PYTHONINTERP_FOUND and PYTHON_EXECUTABLE.
+find_package(PythonInterp)
+
+# cxx_test_with_flags(name cxx_flags libs srcs...)
+#
+# creates a named C++ test that depends on the given libs and is built
+# from the given source files with the given compiler flags.
+function(cxx_test_with_flags name cxx_flags libs)
+ cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN})
+ add_test(${name} ${name})
+endfunction()
+
+# cxx_test(name libs srcs...)
+#
+# creates a named test target that depends on the given libs and is
+# built from the given source files. Unlike cxx_test_with_flags,
+# test/name.cc is already implicitly included in the source file list.
+function(cxx_test name libs)
+ cxx_test_with_flags("${name}" "${cxx_default}" "${libs}"
+ "test/${name}.cc" ${ARGN})
+endfunction()
+
+# py_test(name)
+#
+# creates a Python test with the given name whose main module is in
+# test/name.py. It does nothing if Python is not installed.
+function(py_test name)
+ # We are not supporting Python tests on Linux yet as they consider
+ # all Linux environments to be google3 and try to use google3 features.
+ if (PYTHONINTERP_FOUND)
+ # ${CMAKE_BINARY_DIR} is known at configuration time, so we can
+ # directly bind it from cmake. ${CTEST_CONFIGURATION_TYPE} is known
+ # only at ctest runtime (by calling ctest -c <Configuration>), so
+ # we have to escape $ to delay variable substitution here.
+ add_test(${name}
+ ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
+ --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE})
+ endif()
+endfunction()
diff --git a/bamtools/src/third_party/gtest-1.6.0/fused-src/gtest/gtest-all.cc b/bamtools/src/third_party/gtest-1.6.0/fused-src/gtest/gtest-all.cc
new file mode 100644
index 0000000..5ced66a
--- /dev/null
+++ b/bamtools/src/third_party/gtest-1.6.0/fused-src/gtest/gtest-all.cc
@@ -0,0 +1,9118 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: ***@google.com (Markus Heule)
+//
+// Google C++ Testing Framework (Google Test)
+//
+// Sometimes it's desirable to build Google Test by compiling a single file.
+// This file serves this purpose.
+
+// This line ensures that gtest.h can be compiled on its own, even
+// when it's fused.
+#include "gtest/gtest.h"
+
+// The following lines pull in the real gtest *.cc files.
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: ***@google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: ***@google.com (Zhanyong Wan)
+//
+// Utilities for testing Google Test itself and code that uses Google Test
+// (e.g. frameworks built on top of Google Test).
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_
+#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_
+
+
+namespace testing {
+
+// This helper class can be used to mock out Google Test failure reporting
+// so that we can test Google Test or code that builds on Google Test.
+//
+// An object of this class appends a TestPartResult object to the
+// TestPartResultArray object given in the constructor whenever a Google Test
+// failure is reported. It can either intercept only failures that are
+// generated in the same thread that created this object or it can intercept
+// all generated failures. The scope of this mock object can be controlled with
+// the second argument to the two arguments constructor.
+class GTEST_API_ ScopedFakeTestPartResultReporter
+ : public TestPartResultReporterInterface {
+ public:
+ // The two possible mocking modes of this object.
+ enum InterceptMode {
+ INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures.
+ INTERCEPT_ALL_THREADS // Intercepts all failures.
+ };
+
+ // The c'tor sets this object as the test part result reporter used
+ // by Google Test. The 'result' parameter specifies where to report the
+ // results. This reporter will only catch failures generated in the current
+ // thread. DEPRECATED
+ explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result);
+
+ // Same as above, but you can choose the interception scope of this object.
+ ScopedFakeTestPartResultReporter(InterceptMode intercept_mode,
+ TestPartResultArray* result);
+
+ // The d'tor restores the previous test part result reporter.
+ virtual ~ScopedFakeTestPartResultReporter();
+
+ // Appends the TestPartResult object to the TestPartResultArray
+ // received in the constructor.
+ //
+ // This method is from the TestPartResultReporterInterface
+ // interface.
+ virtual void ReportTestPartResult(const TestPartResult& result);
+ private:
+ void Init();
+
+ const InterceptMode intercept_mode_;
+ TestPartResultReporterInterface* old_reporter_;
+ TestPartResultArray* const result_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter);
+};
+
+namespace internal {
+
+// A helper class for implementing EXPECT_FATAL_FAILURE() and
+// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given
+// TestPartResultArray contains exactly one failure that has the given
+// type and contains the given substring. If that's not the case, a
+// non-fatal failure will be generated.
+class GTEST_API_ SingleFailureChecker {
+ public:
+ // The constructor remembers the arguments.
+ SingleFailureChecker(const TestPartResultArray* results,
+ TestPartResult::Type type,
+ const string& substr);
+ ~SingleFailureChecker();
+ private:
+ const TestPartResultArray* const results_;
+ const TestPartResult::Type type_;
+ const string substr_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
+};
+
+} // namespace internal
+
+} // namespace testing
+
+// A set of macros for testing Google Test assertions or code that's expected
+// to generate Google Test fatal failures. It verifies that the given
+// statement will cause exactly one fatal Google Test failure with 'substr'
+// being part of the failure message.
+//
+// There are two different versions of this macro. EXPECT_FATAL_FAILURE only
+// affects and considers failures generated in the current thread and
+// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
+//
+// The verification of the assertion is done correctly even when the statement
+// throws an exception or aborts the current function.
+//
+// Known restrictions:
+// - 'statement' cannot reference local non-static variables or
+// non-static members of the current object.
+// - 'statement' cannot return a value.
+// - You cannot stream a failure message to this macro.
+//
+// Note that even though the implementations of the following two
+// macros are much alike, we cannot refactor them to use a common
+// helper macro, due to some peculiarity in how the preprocessor
+// works. The AcceptsMacroThatExpandsToUnprotectedComma test in
+// gtest_unittest.cc will fail to compile if we do that.
+#define EXPECT_FATAL_FAILURE(statement, substr) \
+ do { \
+ class GTestExpectFatalFailureHelper {\
+ public:\
+ static void Execute() { statement; }\
+ };\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ &gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ONLY_CURRENT_THREAD, &gtest_failures);\
+ GTestExpectFatalFailureHelper::Execute();\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
+ do { \
+ class GTestExpectFatalFailureHelper {\
+ public:\
+ static void Execute() { statement; }\
+ };\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ &gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ALL_THREADS, &gtest_failures);\
+ GTestExpectFatalFailureHelper::Execute();\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+// A macro for testing Google Test assertions or code that's expected to
+// generate Google Test non-fatal failures. It asserts that the given
+// statement will cause exactly one non-fatal Google Test failure with 'substr'
+// being part of the failure message.
+//
+// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only
+// affects and considers failures generated in the current thread and
+// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
+//
+// 'statement' is allowed to reference local variables and members of
+// the current object.
+//
+// The verification of the assertion is done correctly even when the statement
+// throws an exception or aborts the current function.
+//
+// Known restrictions:
+// - You cannot stream a failure message to this macro.
+//
+// Note that even though the implementations of the following two
+// macros are much alike, we cannot refactor them to use a common
+// helper macro, due to some peculiarity in how the preprocessor
+// works. If we do that, the code won't compile when the user gives
+// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that
+// expands to code containing an unprotected comma. The
+// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc
+// catches that.
+//
+// For the same reason, we have to write
+// if (::testing::internal::AlwaysTrue()) { statement; }
+// instead of
+// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
+// to avoid an MSVC warning on unreachable code.
+#define EXPECT_NONFATAL_FAILURE(statement, substr) \
+ do {\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ &gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
+ (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ONLY_CURRENT_THREAD, &gtest_failures);\
+ if (::testing::internal::AlwaysTrue()) { statement; }\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
+ do {\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ &gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
+ (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS,\
+ &gtest_failures);\
+ if (::testing::internal::AlwaysTrue()) { statement; }\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_
+
+#include <ctype.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <algorithm>
+#include <ostream> // NOLINT
+#include <sstream>
+#include <vector>
+
+#if GTEST_OS_LINUX
+
+// TODO(***@google.com): Use autoconf to detect availability of
+// gettimeofday().
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+
+# include <fcntl.h> // NOLINT
+# include <limits.h> // NOLINT
+# include <sched.h> // NOLINT
+// Declares vsnprintf(). This header is not available on Windows.
+# include <strings.h> // NOLINT
+# include <sys/mman.h> // NOLINT
+# include <sys/time.h> // NOLINT
+# include <unistd.h> // NOLINT
+# include <string>
+
+#elif GTEST_OS_SYMBIAN
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+# include <sys/time.h> // NOLINT
+
+#elif GTEST_OS_ZOS
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+# include <sys/time.h> // NOLINT
+
+// On z/OS we additionally need strings.h for strcasecmp.
+# include <strings.h> // NOLINT
+
+#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE.
+
+# include <windows.h> // NOLINT
+
+#elif GTEST_OS_WINDOWS // We are on Windows proper.
+
+# include <io.h> // NOLINT
+# include <sys/timeb.h> // NOLINT
+# include <sys/types.h> // NOLINT
+# include <sys/stat.h> // NOLINT
+
+# if GTEST_OS_WINDOWS_MINGW
+// MinGW has gettimeofday() but not _ftime64().
+// TODO(***@google.com): Use autoconf to detect availability of
+// gettimeofday().
+// TODO(***@google.com): There are other ways to get the time on
+// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW
+// supports these. consider using them instead.
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+# include <sys/time.h> // NOLINT
+# endif // GTEST_OS_WINDOWS_MINGW
+
+// cpplint thinks that the header is already included, so we want to
+// silence it.
+# include <windows.h> // NOLINT
+
+#else
+
+// Assume other platforms have gettimeofday().
+// TODO(***@google.com): Use autoconf to detect availability of
+// gettimeofday().
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+
+// cpplint thinks that the header is already included, so we want to
+// silence it.
+# include <sys/time.h> // NOLINT
+# include <unistd.h> // NOLINT
+
+#endif // GTEST_OS_LINUX
+
+#if GTEST_HAS_EXCEPTIONS
+# include <stdexcept>
+#endif
+
+#if GTEST_CAN_STREAM_RESULTS_
+# include <arpa/inet.h> // NOLINT
+# include <netdb.h> // NOLINT
+#endif
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Utility functions and classes used by the Google C++ testing framework.
+//
+// Author: ***@google.com (Zhanyong Wan)
+//
+// This file contains purely Google Test's internal implementation. Please
+// DO NOT #INCLUDE IT IN A USER PROGRAM.
+
+#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_
+#define GTEST_SRC_GTEST_INTERNAL_INL_H_
+
+// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is
+// part of Google Test's implementation; otherwise it's undefined.
+#if !GTEST_IMPLEMENTATION_
+// A user is trying to include this from his code - just say no.
+# error "gtest-internal-inl.h is part of Google Test's internal implementation."
+# error "It must not be included except by Google Test itself."
+#endif // GTEST_IMPLEMENTATION_
+
+#ifndef _WIN32_WCE
+# include <errno.h>
+#endif // !_WIN32_WCE
+#include <stddef.h>
+#include <stdlib.h> // For strtoll/_strtoul64/malloc/free.
+#include <string.h> // For memmove.
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+
+#if GTEST_OS_WINDOWS
+# include <windows.h> // NOLINT
+#endif // GTEST_OS_WINDOWS
+
+
+namespace testing {
+
+// Declares the flags.
+//
+// We don't want the users to modify this flag in the code, but want
+// Google Test's own unit tests to be able to access it. Therefore we
+// declare it here as opposed to in gtest.h.
+GTEST_DECLARE_bool_(death_test_use_fork);
+
+namespace internal {
+
+// The value of GetTestTypeId() as seen from within the Google Test
+// library. This is solely for testing GetTestTypeId().
+GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest;
+
+// Names of the flags (needed for parsing Google Test flags).
+const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests";
+const char kBreakOnFailureFlag[] = "break_on_failure";
+const char kCatchExceptionsFlag[] = "catch_exceptions";
+const char kColorFlag[] = "color";
+const char kFilterFlag[] = "filter";
+const char kListTestsFlag[] = "list_tests";
+const char kOutputFlag[] = "output";
+const char kPrintTimeFlag[] = "print_time";
+const char kRandomSeedFlag[] = "random_seed";
+const char kRepeatFlag[] = "repeat";
+const char kShuffleFlag[] = "shuffle";
+const char kStackTraceDepthFlag[] = "stack_trace_depth";
+const char kStreamResultToFlag[] = "stream_result_to";
+const char kThrowOnFailureFlag[] = "throw_on_failure";
+
+// A valid random seed must be in [1, kMaxRandomSeed].
+const int kMaxRandomSeed = 99999;
+
+// g_help_flag is true iff the --help flag or an equivalent form is
+// specified on the command line.
+GTEST_API_ extern bool g_help_flag;
+
+// Returns the current time in milliseconds.
+GTEST_API_ TimeInMillis GetTimeInMillis();
+
+// Returns true iff Google Test should use colors in the output.
+GTEST_API_ bool ShouldUseColor(bool stdout_is_tty);
+
+// Formats the given time in milliseconds as seconds.
+GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms);
+
+// Parses a string for an Int32 flag, in the form of "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+GTEST_API_ bool ParseInt32Flag(
+ const char* str, const char* flag, Int32* value);
+
+// Returns a random seed in range [1, kMaxRandomSeed] based on the
+// given --gtest_random_seed flag value.
+inline int GetRandomSeedFromFlag(Int32 random_seed_flag) {
+ const unsigned int raw_seed = (random_seed_flag == 0) ?
+ static_cast<unsigned int>(GetTimeInMillis()) :
+ static_cast<unsigned int>(random_seed_flag);
+
+ // Normalizes the actual seed to range [1, kMaxRandomSeed] such that
+ // it's easy to type.
+ const int normalized_seed =
+ static_cast<int>((raw_seed - 1U) %
+ static_cast<unsigned int>(kMaxRandomSeed)) + 1;
+ return normalized_seed;
+}
+
+// Returns the first valid random seed after 'seed'. The behavior is
+// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is
+// considered to be 1.
+inline int GetNextRandomSeed(int seed) {
+ GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed)
+ << "Invalid random seed " << seed << " - must be in [1, "
+ << kMaxRandomSeed << "].";
+ const int next_seed = seed + 1;
+ return (next_seed > kMaxRandomSeed) ? 1 : next_seed;
+}
+
+// This class saves the values of all Google Test flags in its c'tor, and
+// restores them in its d'tor.
+class GTestFlagSaver {
+ public:
+ // The c'tor.
+ GTestFlagSaver() {
+ also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests);
+ break_on_failure_ = GTEST_FLAG(break_on_failure);
+ catch_exceptions_ = GTEST_FLAG(catch_exceptions);
+ color_ = GTEST_FLAG(color);
+ death_test_style_ = GTEST_FLAG(death_test_style);
+ death_test_use_fork_ = GTEST_FLAG(death_test_use_fork);
+ filter_ = GTEST_FLAG(filter);
+ internal_run_death_test_ = GTEST_FLAG(internal_run_death_test);
+ list_tests_ = GTEST_FLAG(list_tests);
+ output_ = GTEST_FLAG(output);
+ print_time_ = GTEST_FLAG(print_time);
+ random_seed_ = GTEST_FLAG(random_seed);
+ repeat_ = GTEST_FLAG(repeat);
+ shuffle_ = GTEST_FLAG(shuffle);
+ stack_trace_depth_ = GTEST_FLAG(stack_trace_depth);
+ stream_result_to_ = GTEST_FLAG(stream_result_to);
+ throw_on_failure_ = GTEST_FLAG(throw_on_failure);
+ }
+
+ // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS.
+ ~GTestFlagSaver() {
+ GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_;
+ GTEST_FLAG(break_on_failure) = break_on_failure_;
+ GTEST_FLAG(catch_exceptions) = catch_exceptions_;
+ GTEST_FLAG(color) = color_;
+ GTEST_FLAG(death_test_style) = death_test_style_;
+ GTEST_FLAG(death_test_use_fork) = death_test_use_fork_;
+ GTEST_FLAG(filter) = filter_;
+ GTEST_FLAG(internal_run_death_test) = internal_run_death_test_;
+ GTEST_FLAG(list_tests) = list_tests_;
+ GTEST_FLAG(output) = output_;
+ GTEST_FLAG(print_time) = print_time_;
+ GTEST_FLAG(random_seed) = random_seed_;
+ GTEST_FLAG(repeat) = repeat_;
+ GTEST_FLAG(shuffle) = shuffle_;
+ GTEST_FLAG(stack_trace_depth) = stack_trace_depth_;
+ GTEST_FLAG(stream_result_to) = stream_result_to_;
+ GTEST_FLAG(throw_on_failure) = throw_on_failure_;
+ }
+ private:
+ // Fields for saving the original values of flags.
+ bool also_run_disabled_tests_;
+ bool break_on_failure_;
+ bool catch_exceptions_;
+ String color_;
+ String death_test_style_;
+ bool death_test_use_fork_;
+ String filter_;
+ String internal_run_death_test_;
+ bool list_tests_;
+ String output_;
+ bool print_time_;
+ bool pretty_;
+ internal::Int32 random_seed_;
+ internal::Int32 repeat_;
+ bool shuffle_;
+ internal::Int32 stack_trace_depth_;
+ String stream_result_to_;
+ bool throw_on_failure_;
+} GTEST_ATTRIBUTE_UNUSED_;
+
+// Converts a Unicode code point to a narrow string in UTF-8 encoding.
+// code_point parameter is of type UInt32 because wchar_t may not be
+// wide enough to contain a code point.
+// The output buffer str must containt at least 32 characters.
+// The function returns the address of the output buffer.
+// If the code_point is not a valid Unicode code point
+// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output
+// as '(Invalid Unicode 0xXXXXXXXX)'.
+GTEST_API_ char* CodePointToUtf8(UInt32 code_point, char* str);
+
+// Converts a wide string to a narrow string in UTF-8 encoding.
+// The wide string is assumed to have the following encoding:
+// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS)
+// UTF-32 if sizeof(wchar_t) == 4 (on Linux)
+// Parameter str points to a null-terminated wide string.
+// Parameter num_chars may additionally limit the number
+// of wchar_t characters processed. -1 is used when the entire string
+// should be processed.
+// If the string contains code points that are not valid Unicode code points
+// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output
+// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding
+// and contains invalid UTF-16 surrogate pairs, values in those pairs
+// will be encoded as individual Unicode characters from Basic Normal Plane.
+GTEST_API_ String WideStringToUtf8(const wchar_t* str, int num_chars);
+
+// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file
+// if the variable is present. If a file already exists at this location, this
+// function will write over it. If the variable is present, but the file cannot
+// be created, prints an error and exits.
+void WriteToShardStatusFileIfNeeded();
+
+// Checks whether sharding is enabled by examining the relevant
+// environment variable values. If the variables are present,
+// but inconsistent (e.g., shard_index >= total_shards), prints
+// an error and exits. If in_subprocess_for_death_test, sharding is
+// disabled because it must only be applied to the original test
+// process. Otherwise, we could filter out death tests we intended to execute.
+GTEST_API_ bool ShouldShard(const char* total_shards_str,
+ const char* shard_index_str,
+ bool in_subprocess_for_death_test);
+
+// Parses the environment variable var as an Int32. If it is unset,
+// returns default_val. If it is not an Int32, prints an error and
+// and aborts.
+GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val);
+
+// Given the total number of shards, the shard index, and the test id,
+// returns true iff the test should be run on this shard. The test id is
+// some arbitrary but unique non-negative integer assigned to each test
+// method. Assumes that 0 <= shard_index < total_shards.
+GTEST_API_ bool ShouldRunTestOnShard(
+ int total_shards, int shard_index, int test_id);
+
+// STL container utilities.
+
+// Returns the number of elements in the given container that satisfy
+// the given predicate.
+template <class Container, typename Predicate>
+inline int CountIf(const Container& c, Predicate predicate) {
+ // Implemented as an explicit loop since std::count_if() in libCstd on
+ // Solaris has a non-standard signature.
+ int count = 0;
+ for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) {
+ if (predicate(*it))
+ ++count;
+ }
+ return count;
+}
+
+// Applies a function/functor to each element in the container.
+template <class Container, typename Functor>
+void ForEach(const Container& c, Functor functor) {
+ std::for_each(c.begin(), c.end(), functor);
+}
+
+// Returns the i-th element of the vector, or default_value if i is not
+// in range [0, v.size()).
+template <typename E>
+inline E GetElementOr(const std::vector<E>& v, int i, E default_value) {
+ return (i < 0 || i >= static_cast<int>(v.size())) ? default_value : v[i];
+}
+
+// Performs an in-place shuffle of a range of the vector's elements.
+// 'begin' and 'end' are element indices as an STL-style range;
+// i.e. [begin, end) are shuffled, where 'end' == size() means to
+// shuffle to the end of the vector.
+template <typename E>
+void ShuffleRange(internal::Random* random, int begin, int end,
+ std::vector<E>* v) {
+ const int size = static_cast<int>(v->size());
+ GTEST_CHECK_(0 <= begin && begin <= size)
+ << "Invalid shuffle range start " << begin << ": must be in range [0, "
+ << size << "].";
+ GTEST_CHECK_(begin <= end && end <= size)
+ << "Invalid shuffle range finish " << end << ": must be in range ["
+ << begin << ", " << size << "].";
+
+ // Fisher-Yates shuffle, from
+ // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
+ for (int range_width = end - begin; range_width >= 2; range_width--) {
+ const int last_in_range = begin + range_width - 1;
+ const int selected = begin + random->Generate(range_width);
+ std::swap((*v)[selected], (*v)[last_in_range]);
+ }
+}
+
+// Performs an in-place shuffle of the vector's elements.
+template <typename E>
+inline void Shuffle(internal::Random* random, std::vector<E>* v) {
+ ShuffleRange(random, 0, static_cast<int>(v->size()), v);
+}
+
+// A function for deleting an object. Handy for being used as a
+// functor.
+template <typename T>
+static void Delete(T* x) {
+ delete x;
+}
+
+// A predicate that checks the key of a TestProperty against a known key.
+//
+// TestPropertyKeyIs is copyable.
+class TestPropertyKeyIs {
+ public:
+ // Constructor.
+ //
+ // TestPropertyKeyIs has NO default constructor.
+ explicit TestPropertyKeyIs(const char* key)
+ : key_(key) {}
+
+ // Returns true iff the test name of test property matches on key_.
+ bool operator()(const TestProperty& test_property) const {
+ return String(test_property.key()).Compare(key_) == 0;
+ }
+
+ private:
+ String key_;
+};
+
+// Class UnitTestOptions.
+//
+// This class contains functions for processing options the user
+// specifies when running the tests. It has only static members.
+//
+// In most cases, the user can specify an option using either an
+// environment variable or a command line flag. E.g. you can set the
+// test filter using either GTEST_FILTER or --gtest_filter. If both
+// the variable and the flag are present, the latter overrides the
+// former.
+class GTEST_API_ UnitTestOptions {
+ public:
+ // Functions for processing the gtest_output flag.
+
+ // Returns the output format, or "" for normal printed output.
+ static String GetOutputFormat();
+
+ // Returns the absolute path of the requested output file, or the
+ // default (test_detail.xml in the original working directory) if
+ // none was explicitly specified.
+ static String GetAbsolutePathToOutputFile();
+
+ // Functions for processing the gtest_filter flag.
+
+ // Returns true iff the wildcard pattern matches the string. The
+ // first ':' or '\0' character in pattern marks the end of it.
+ //
+ // This recursive algorithm isn't very efficient, but is clear and
+ // works well enough for matching test names, which are short.
+ static bool PatternMatchesString(const char *pattern, const char *str);
+
+ // Returns true iff the user-specified filter matches the test case
+ // name and the test name.
+ static bool FilterMatchesTest(const String &test_case_name,
+ const String &test_name);
+
+#if GTEST_OS_WINDOWS
+ // Function for supporting the gtest_catch_exception flag.
+
+ // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the
+ // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise.
+ // This function is useful as an __except condition.
+ static int GTestShouldProcessSEH(DWORD exception_code);
+#endif // GTEST_OS_WINDOWS
+
+ // Returns true if "name" matches the ':' separated list of glob-style
+ // filters in "filter".
+ static bool MatchesFilter(const String& name, const char* filter);
+};
+
+// Returns the current application's name, removing directory path if that
+// is present. Used by UnitTestOptions::GetOutputFile.
+GTEST_API_ FilePath GetCurrentExecutableName();
+
+// The role interface for getting the OS stack trace as a string.
+class OsStackTraceGetterInterface {
+ public:
+ OsStackTraceGetterInterface() {}
+ virtual ~OsStackTraceGetterInterface() {}
+
+ // Returns the current OS stack trace as a String. Parameters:
+ //
+ // max_depth - the maximum number of stack frames to be included
+ // in the trace.
+ // skip_count - the number of top frames to be skipped; doesn't count
+ // against max_depth.
+ virtual String CurrentStackTrace(int max_depth, int skip_count) = 0;
+
+ // UponLeavingGTest() should be called immediately before Google Test calls
+ // user code. It saves some information about the current stack that
+ // CurrentStackTrace() will use to find and hide Google Test stack frames.
+ virtual void UponLeavingGTest() = 0;
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface);
+};
+
+// A working implementation of the OsStackTraceGetterInterface interface.
+class OsStackTraceGetter : public OsStackTraceGetterInterface {
+ public:
+ OsStackTraceGetter() : caller_frame_(NULL) {}
+ virtual String CurrentStackTrace(int max_depth, int skip_count);
+ virtual void UponLeavingGTest();
+
+ // This string is inserted in place of stack frames that are part of
+ // Google Test's implementation.
+ static const char* const kElidedFramesMarker;
+
+ private:
+ Mutex mutex_; // protects all internal state
+
+ // We save the stack frame below the frame that calls user code.
+ // We do this because the address of the frame immediately below
+ // the user code changes between the call to UponLeavingGTest()
+ // and any calls to CurrentStackTrace() from within the user code.
+ void* caller_frame_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter);
+};
+
+// Information about a Google Test trace point.
+struct TraceInfo {
+ const char* file;
+ int line;
+ String message;
+};
+
+// This is the default global test part result reporter used in UnitTestImpl.
+// This class should only be used by UnitTestImpl.
+class DefaultGlobalTestPartResultReporter
+ : public TestPartResultReporterInterface {
+ public:
+ explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test);
+ // Implements the TestPartResultReporterInterface. Reports the test part
+ // result in the current test.
+ virtual void ReportTestPartResult(const TestPartResult& result);
+
+ private:
+ UnitTestImpl* const unit_test_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter);
+};
+
+// This is the default per thread test part result reporter used in
+// UnitTestImpl. This class should only be used by UnitTestImpl.
+class DefaultPerThreadTestPartResultReporter
+ : public TestPartResultReporterInterface {
+ public:
+ explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test);
+ // Implements the TestPartResultReporterInterface. The implementation just
+ // delegates to the current global test part result reporter of *unit_test_.
+ virtual void ReportTestPartResult(const TestPartResult& result);
+
+ private:
+ UnitTestImpl* const unit_test_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter);
+};
+
+// The private implementation of the UnitTest class. We don't protect
+// the methods under a mutex, as this class is not accessible by a
+// user and the UnitTest class that delegates work to this class does
+// proper locking.
+class GTEST_API_ UnitTestImpl {
+ public:
+ explicit UnitTestImpl(UnitTest* parent);
+ virtual ~UnitTestImpl();
+
+ // There are two different ways to register your own TestPartResultReporter.
+ // You can register your own repoter to listen either only for test results
+ // from the current thread or for results from all threads.
+ // By default, each per-thread test result repoter just passes a new
+ // TestPartResult to the global test result reporter, which registers the
+ // test part result for the currently running test.
+
+ // Returns the global test part result reporter.
+ TestPartResultReporterInterface* GetGlobalTestPartResultReporter();
+
+ // Sets the global test part result reporter.
+ void SetGlobalTestPartResultReporter(
+ TestPartResultReporterInterface* reporter);
+
+ // Returns the test part result reporter for the current thread.
+ TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread();
+
+ // Sets the test part result reporter for the current thread.
+ void SetTestPartResultReporterForCurrentThread(
+ TestPartResultReporterInterface* reporter);
+
+ // Gets the number of successful test cases.
+ int successful_test_case_count() const;
+
+ // Gets the number of failed test cases.
+ int failed_test_case_count() const;
+
+ // Gets the number of all test cases.
+ int total_test_case_count() const;
+
+ // Gets the number of all test cases that contain at least one test
+ // that should run.
+ int test_case_to_run_count() const;
+
+ // Gets the number of successful tests.
+ int successful_test_count() const;
+
+ // Gets the number of failed tests.
+ int failed_test_count() const;
+
+ // Gets the number of disabled tests.
+ int disabled_test_count() const;
+
+ // Gets the number of all tests.
+ int total_test_count() const;
+
+ // Gets the number of tests that should run.
+ int test_to_run_count() const;
+
+ // Gets the elapsed time, in milliseconds.
+ TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+ // Returns true iff the unit test passed (i.e. all test cases passed).
+ bool Passed() const { return !Failed(); }
+
+ // Returns true iff the unit test failed (i.e. some test case failed
+ // or something outside of all tests failed).
+ bool Failed() const {
+ return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed();
+ }
+
+ // Gets the i-th test case among all the test cases. i can range from 0 to
+ // total_test_case_count() - 1. If i is not in that range, returns NULL.
+ const TestCase* GetTestCase(int i) const {
+ const int index = GetElementOr(test_case_indices_, i, -1);
+ return index < 0 ? NULL : test_cases_[i];
+ }
+
+ // Gets the i-th test case among all the test cases. i can range from 0 to
+ // total_test_case_count() - 1. If i is not in that range, returns NULL.
+ TestCase* GetMutableTestCase(int i) {
+ const int index = GetElementOr(test_case_indices_, i, -1);
+ return index < 0 ? NULL : test_cases_[index];
+ }
+
+ // Provides access to the event listener list.
+ TestEventListeners* listeners() { return &listeners_; }
+
+ // Returns the TestResult for the test that's currently running, or
+ // the TestResult for the ad hoc test if no test is running.
+ TestResult* current_test_result();
+
+ // Returns the TestResult for the ad hoc test.
+ const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; }
+
+ // Sets the OS stack trace getter.
+ //
+ // Does nothing if the input and the current OS stack trace getter
+ // are the same; otherwise, deletes the old getter and makes the
+ // input the current getter.
+ void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter);
+
+ // Returns the current OS stack trace getter if it is not NULL;
+ // otherwise, creates an OsStackTraceGetter, makes it the current
+ // getter, and returns it.
+ OsStackTraceGetterInterface* os_stack_trace_getter();
+
+ // Returns the current OS stack trace as a String.
+ //
+ // The maximum number of stack frames to be included is specified by
+ // the gtest_stack_trace_depth flag. The skip_count parameter
+ // specifies the number of top frames to be skipped, which doesn't
+ // count against the number of frames to be included.
+ //
+ // For example, if Foo() calls Bar(), which in turn calls
+ // CurrentOsStackTraceExceptTop(1), Foo() will be included in the
+ // trace but Bar() and CurrentOsStackTraceExceptTop() won't.
+ String CurrentOsStackTraceExceptTop(int skip_count);
+
+ // Finds and returns a TestCase with the given name. If one doesn't
+ // exist, creates one and returns it.
+ //
+ // Arguments:
+ //
+ // test_case_name: name of the test case
+ // type_param: the name of the test's type parameter, or NULL if
+ // this is not a typed or a type-parameterized test.
+ // set_up_tc: pointer to the function that sets up the test case
+ // tear_down_tc: pointer to the function that tears down the test case
+ TestCase* GetTestCase(const char* test_case_name,
+ const char* type_param,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc);
+
+ // Adds a TestInfo to the unit test.
+ //
+ // Arguments:
+ //
+ // set_up_tc: pointer to the function that sets up the test case
+ // tear_down_tc: pointer to the function that tears down the test case
+ // test_info: the TestInfo object
+ void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc,
+ TestInfo* test_info) {
+ // In order to support thread-safe death tests, we need to
+ // remember the original working directory when the test program
+ // was first invoked. We cannot do this in RUN_ALL_TESTS(), as
+ // the user may have changed the current directory before calling
+ // RUN_ALL_TESTS(). Therefore we capture the current directory in
+ // AddTestInfo(), which is called to register a TEST or TEST_F
+ // before main() is reached.
+ if (original_working_dir_.IsEmpty()) {
+ original_working_dir_.Set(FilePath::GetCurrentDir());
+ GTEST_CHECK_(!original_working_dir_.IsEmpty())
+ << "Failed to get the current working directory.";
+ }
+
+ GetTestCase(test_info->test_case_name(),
+ test_info->type_param(),
+ set_up_tc,
+ tear_down_tc)->AddTestInfo(test_info);
+ }
+
+#if GTEST_HAS_PARAM_TEST
+ // Returns ParameterizedTestCaseRegistry object used to keep track of
+ // value-parameterized tests and instantiate and register them.
+ internal::ParameterizedTestCaseRegistry& parameterized_test_registry() {
+ return parameterized_test_registry_;
+ }
+#endif // GTEST_HAS_PARAM_TEST
+
+ // Sets the TestCase object for the test that's currently running.
+ void set_current_test_case(TestCase* a_current_test_case) {
+ current_test_case_ = a_current_test_case;
+ }
+
+ // Sets the TestInfo object for the test that's currently running. If
+ // current_test_info is NULL, the assertion results will be stored in
+ // ad_hoc_test_result_.
+ void set_current_test_info(TestInfo* a_current_test_info) {
+ current_test_info_ = a_current_test_info;
+ }
+
+ // Registers all parameterized tests defined using TEST_P and
+ // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter
+ // combination. This method can be called more then once; it has guards
+ // protecting from registering the tests more then once. If
+ // value-parameterized tests are disabled, RegisterParameterizedTests is
+ // present but does nothing.
+ void RegisterParameterizedTests();
+
+ // Runs all tests in this UnitTest object, prints the result, and
+ // returns true if all tests are successful. If any exception is
+ // thrown during a test, this test is considered to be failed, but
+ // the rest of the tests will still be run.
+ bool RunAllTests();
+
+ // Clears the results of all tests, except the ad hoc tests.
+ void ClearNonAdHocTestResult() {
+ ForEach(test_cases_, TestCase::ClearTestCaseResult);
+ }
+
+ // Clears the results of ad-hoc test assertions.
+ void ClearAdHocTestResult() {
+ ad_hoc_test_result_.Clear();
+ }
+
+ enum ReactionToSharding {
+ HONOR_SHARDING_PROTOCOL,
+ IGNORE_SHARDING_PROTOCOL
+ };
+
+ // Matches the full name of each test against the user-specified
+ // filter to decide whether the test should run, then records the
+ // result in each TestCase and TestInfo object.
+ // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests
+ // based on sharding variables in the environment.
+ // Returns the number of tests that should run.
+ int FilterTests(ReactionToSharding shard_tests);
+
+ // Prints the names of the tests matching the user-specified filter flag.
+ void ListTestsMatchingFilter();
+
+ const TestCase* current_test_case() const { return current_test_case_; }
+ TestInfo* current_test_info() { return current_test_info_; }
+ const TestInfo* current_test_info() const { return current_test_info_; }
+
+ // Returns the vector of environments that need to be set-up/torn-down
+ // before/after the tests are run.
+ std::vector<Environment*>& environments() { return environments_; }
+
+ // Getters for the per-thread Google Test trace stack.
+ std::vector<TraceInfo>& gtest_trace_stack() {
+ return *(gtest_trace_stack_.pointer());
+ }
+ const std::vector<TraceInfo>& gtest_trace_stack() const {
+ return gtest_trace_stack_.get();
+ }
+
+#if GTEST_HAS_DEATH_TEST
+ void InitDeathTestSubprocessControlInfo() {
+ internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag());
+ }
+ // Returns a pointer to the parsed --gtest_internal_run_death_test
+ // flag, or NULL if that flag was not specified.
+ // This information is useful only in a death test child process.
+ // Must not be called before a call to InitGoogleTest.
+ const InternalRunDeathTestFlag* internal_run_death_test_flag() const {
+ return internal_run_death_test_flag_.get();
+ }
+
+ // Returns a pointer to the current death test factory.
+ internal::DeathTestFactory* death_test_factory() {
+ return death_test_factory_.get();
+ }
+
+ void SuppressTestEventsIfInSubprocess();
+
+ friend class ReplaceDeathTestFactory;
+#endif // GTEST_HAS_DEATH_TEST
+
+ // Initializes the event listener performing XML output as specified by
+ // UnitTestOptions. Must not be called before InitGoogleTest.
+ void ConfigureXmlOutput();
+
+#if GTEST_CAN_STREAM_RESULTS_
+ // Initializes the event listener for streaming test results to a socket.
+ // Must not be called before InitGoogleTest.
+ void ConfigureStreamingOutput();
+#endif
+
+ // Performs initialization dependent upon flag values obtained in
+ // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to
+ // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest
+ // this function is also called from RunAllTests. Since this function can be
+ // called more than once, it has to be idempotent.
+ void PostFlagParsingInit();
+
+ // Gets the random seed used at the start of the current test iteration.
+ int random_seed() const { return random_seed_; }
+
+ // Gets the random number generator.
+ internal::Random* random() { return &random_; }
+
+ // Shuffles all test cases, and the tests within each test case,
+ // making sure that death tests are still run first.
+ void ShuffleTests();
+
+ // Restores the test cases and tests to their order before the first shuffle.
+ void UnshuffleTests();
+
+ // Returns the value of GTEST_FLAG(catch_exceptions) at the moment
+ // UnitTest::Run() starts.
+ bool catch_exceptions() const { return catch_exceptions_; }
+
+ private:
+ friend class ::testing::UnitTest;
+
+ // Used by UnitTest::Run() to capture the state of
+ // GTEST_FLAG(catch_exceptions) at the moment it starts.
+ void set_catch_exceptions(bool value) { catch_exceptions_ = value; }
+
+ // The UnitTest object that owns this implementation object.
+ UnitTest* const parent_;
+
+ // The working directory when the first TEST() or TEST_F() was
+ // executed.
+ internal::FilePath original_working_dir_;
+
+ // The default test part result reporters.
+ DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_;
+ DefaultPerThreadTestPartResultReporter
+ default_per_thread_test_part_result_reporter_;
+
+ // Points to (but doesn't own) the global test part result reporter.
+ TestPartResultReporterInterface* global_test_part_result_repoter_;
+
+ // Protects read and write access to global_test_part_result_reporter_.
+ internal::Mutex global_test_part_result_reporter_mutex_;
+
+ // Points to (but doesn't own) the per-thread test part result reporter.
+ internal::ThreadLocal<TestPartResultReporterInterface*>
+ per_thread_test_part_result_reporter_;
+
+ // The vector of environments that need to be set-up/torn-down
+ // before/after the tests are run.
+ std::vector<Environment*> environments_;
+
+ // The vector of TestCases in their original order. It owns the
+ // elements in the vector.
+ std::vector<TestCase*> test_cases_;
+
+ // Provides a level of indirection for the test case list to allow
+ // easy shuffling and restoring the test case order. The i-th
+ // element of this vector is the index of the i-th test case in the
+ // shuffled order.
+ std::vector<int> test_case_indices_;
+
+#if GTEST_HAS_PARAM_TEST
+ // ParameterizedTestRegistry object used to register value-parameterized
+ // tests.
+ internal::ParameterizedTestCaseRegistry parameterized_test_registry_;
+
+ // Indicates whether RegisterParameterizedTests() has been called already.
+ bool parameterized_tests_registered_;
+#endif // GTEST_HAS_PARAM_TEST
+
+ // Index of the last death test case registered. Initially -1.
+ int last_death_test_case_;
+
+ // This points to the TestCase for the currently running test. It
+ // changes as Google Test goes through one test case after another.
+ // When no test is running, this is set to NULL and Google Test
+ // stores assertion results in ad_hoc_test_result_. Initially NULL.
+ TestCase* current_test_case_;
+
+ // This points to the TestInfo for the currently running test. It
+ // changes as Google Test goes through one test after another. When
+ // no test is running, this is set to NULL and Google Test stores
+ // assertion results in ad_hoc_test_result_. Initially NULL.
+ TestInfo* current_test_info_;
+
+ // Normally, a user only writes assertions inside a TEST or TEST_F,
+ // or inside a function called by a TEST or TEST_F. Since Google
+ // Test keeps track of which test is current running, it can
+ // associate such an assertion with the test it belongs to.
+ //
+ // If an assertion is encountered when no TEST or TEST_F is running,
+ // Google Test attributes the assertion result to an imaginary "ad hoc"
+ // test, and records the result in ad_hoc_test_result_.
+ TestResult ad_hoc_test_result_;
+
+ // The list of event listeners that can be used to track events inside
+ // Google Test.
+ TestEventListeners listeners_;
+
+ // The OS stack trace getter. Will be deleted when the UnitTest
+ // object is destructed. By default, an OsStackTraceGetter is used,
+ // but the user can set this field to use a custom getter if that is
+ // desired.
+ OsStackTraceGetterInterface* os_stack_trace_getter_;
+
+ // True iff PostFlagParsingInit() has been called.
+ bool post_flag_parse_init_performed_;
+
+ // The random number seed used at the beginning of the test run.
+ int random_seed_;
+
+ // Our random number generator.
+ internal::Random random_;
+
+ // How long the test took to run, in milliseconds.
+ TimeInMillis elapsed_time_;
+
+#if GTEST_HAS_DEATH_TEST
+ // The decomposed components of the gtest_internal_run_death_test flag,
+ // parsed when RUN_ALL_TESTS is called.
+ internal::scoped_ptr<InternalRunDeathTestFlag> internal_run_death_test_flag_;
+ internal::scoped_ptr<internal::DeathTestFactory> death_test_factory_;
+#endif // GTEST_HAS_DEATH_TEST
+
+ // A per-thread stack of traces created by the SCOPED_TRACE() macro.
+ internal::ThreadLocal<std::vector<TraceInfo> > gtest_trace_stack_;
+
+ // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests()
+ // starts.
+ bool catch_exceptions_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl);
+}; // class UnitTestImpl
+
+// Convenience function for accessing the global UnitTest
+// implementation object.
+inline UnitTestImpl* GetUnitTestImpl() {
+ return UnitTest::GetInstance()->impl();
+}
+
+#if GTEST_USES_SIMPLE_RE
+
+// Internal helper functions for implementing the simple regular
+// expression matcher.
+GTEST_API_ bool IsInSet(char ch, const char* str);
+GTEST_API_ bool IsAsciiDigit(char ch);
+GTEST_API_ bool IsAsciiPunct(char ch);
+GTEST_API_ bool IsRepeat(char ch);
+GTEST_API_ bool IsAsciiWhiteSpace(char ch);
+GTEST_API_ bool IsAsciiWordChar(char ch);
+GTEST_API_ bool IsValidEscape(char ch);
+GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch);
+GTEST_API_ bool ValidateRegex(const char* regex);
+GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str);
+GTEST_API_ bool MatchRepetitionAndRegexAtHead(
+ bool escaped, char ch, char repeat, const char* regex, const char* str);
+GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str);
+
+#endif // GTEST_USES_SIMPLE_RE
+
+// Parses the command line for Google Test flags, without initializing
+// other parts of Google Test.
+GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv);
+GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv);
+
+#if GTEST_HAS_DEATH_TEST
+
+// Returns the message describing the last system error, regardless of the
+// platform.
+GTEST_API_ String GetLastErrnoDescription();
+
+# if GTEST_OS_WINDOWS
+// Provides leak-safe Windows kernel handle ownership.
+class AutoHandle {
+ public:
+ AutoHandle() : handle_(INVALID_HANDLE_VALUE) {}
+ explicit AutoHandle(HANDLE handle) : handle_(handle) {}
+
+ ~AutoHandle() { Reset(); }
+
+ HANDLE Get() const { return handle_; }
+ void Reset() { Reset(INVALID_HANDLE_VALUE); }
+ void Reset(HANDLE handle) {
+ if (handle != handle_) {
+ if (handle_ != INVALID_HANDLE_VALUE)
+ ::CloseHandle(handle_);
+ handle_ = handle;
+ }
+ }
+
+ private:
+ HANDLE handle_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle);
+};
+# endif // GTEST_OS_WINDOWS
+
+// Attempts to parse a string into a positive integer pointed to by the
+// number parameter. Returns true if that is possible.
+// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use
+// it here.
+template <typename Integer>
+bool ParseNaturalNumber(const ::std::string& str, Integer* number) {
+ // Fail fast if the given string does not begin with a digit;
+ // this bypasses strtoXXX's "optional leading whitespace and plus
+ // or minus sign" semantics, which are undesirable here.
+ if (str.empty() || !IsDigit(str[0])) {
+ return false;
+ }
+ errno = 0;
+
+ char* end;
+ // BiggestConvertible is the largest integer type that system-provided
+ // string-to-number conversion routines can return.
+
+# if GTEST_OS_WINDOWS && !defined(__GNUC__)
+
+ // MSVC and C++ Builder define __int64 instead of the standard long long.
+ typedef unsigned __int64 BiggestConvertible;
+ const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10);
+
+# else
+
+ typedef unsigned long long BiggestConvertible; // NOLINT
+ const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10);
+
+# endif // GTEST_OS_WINDOWS && !defined(__GNUC__)
+
+ const bool parse_success = *end == '\0' && errno == 0;
+
+ // TODO(***@google.com): Convert this to compile time assertion when it is
+ // available.
+ GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed));
+
+ const Integer result = static_cast<Integer>(parsed);
+ if (parse_success && static_cast<BiggestConvertible>(result) == parsed) {
+ *number = result;
+ return true;
+ }
+ return false;
+}
+#endif // GTEST_HAS_DEATH_TEST
+
+// TestResult contains some private methods that should be hidden from
+// Google Test user but are required for testing. This class allow our tests
+// to access them.
+//
+// This class is supplied only for the purpose of testing Google Test's own
+// constructs. Do not use it in user tests, either directly or indirectly.
+class TestResultAccessor {
+ public:
+ static void RecordProperty(TestResult* test_result,
+ const TestProperty& property) {
+ test_result->RecordProperty(property);
+ }
+
+ static void ClearTestPartResults(TestResult* test_result) {
+ test_result->ClearTestPartResults();
+ }
+
+ static const std::vector<testing::TestPartResult>& test_part_results(
+ const TestResult& test_result) {
+ return test_result.test_part_results();
+ }
+};
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_
+#undef GTEST_IMPLEMENTATION_
+
+#if GTEST_OS_WINDOWS
+# define vsnprintf _vsnprintf
+#endif // GTEST_OS_WINDOWS
+
+namespace testing {
+
+using internal::CountIf;
+using internal::ForEach;
+using internal::GetElementOr;
+using internal::Shuffle;
+
+// Constants.
+
+// A test whose test case name or test name matches this filter is
+// disabled and not run.
+static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*";
+
+// A test case whose name matches this filter is considered a death
+// test case and will be run before test cases whose name doesn't
+// match this filter.
+static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*";
+
+// A test filter that matches everything.
+static const char kUniversalFilter[] = "*";
+
+// The default output file for XML output.
+static const char kDefaultOutputFile[] = "test_detail.xml";
+
+// The environment variable name for the test shard index.
+static const char kTestShardIndex[] = "GTEST_SHARD_INDEX";
+// The environment variable name for the total number of test shards.
+static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS";
+// The environment variable name for the test shard status file.
+static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE";
+
+namespace internal {
+
+// The text used in failure messages to indicate the start of the
+// stack trace.
+const char kStackTraceMarker[] = "\nStack trace:\n";
+
+// g_help_flag is true iff the --help flag or an equivalent form is
+// specified on the command line.
+bool g_help_flag = false;
+
+} // namespace internal
+
+GTEST_DEFINE_bool_(
+ also_run_disabled_tests,
+ internal::BoolFromGTestEnv("also_run_disabled_tests", false),
+ "Run disabled tests too, in addition to the tests normally being run.");
+
+GTEST_DEFINE_bool_(
+ break_on_failure,
+ internal::BoolFromGTestEnv("break_on_failure", false),
+ "True iff a failed assertion should be a debugger break-point.");
+
+GTEST_DEFINE_bool_(
+ catch_exceptions,
+ internal::BoolFromGTestEnv("catch_exceptions", true),
+ "True iff " GTEST_NAME_
+ " should catch exceptions and treat them as test failures.");
+
+GTEST_DEFINE_string_(
+ color,
+ internal::StringFromGTestEnv("color", "auto"),
+ "Whether to use colors in the output. Valid values: yes, no, "
+ "and auto. 'auto' means to use colors if the output is "
+ "being sent to a terminal and the TERM environment variable "
+ "is set to xterm, xterm-color, xterm-256color, linux or cygwin.");
+
+GTEST_DEFINE_string_(
+ filter,
+ internal::StringFromGTestEnv("filter", kUniversalFilter),
+ "A colon-separated list of glob (not regex) patterns "
+ "for filtering the tests to run, optionally followed by a "
+ "'-' and a : separated list of negative patterns (tests to "
+ "exclude). A test is run if it matches one of the positive "
+ "patterns and does not match any of the negative patterns.");
+
+GTEST_DEFINE_bool_(list_tests, false,
+ "List all tests without running them.");
+
+GTEST_DEFINE_string_(
+ output,
+ internal::StringFromGTestEnv("output", ""),
+ "A format (currently must be \"xml\"), optionally followed "
+ "by a colon and an output file name or directory. A directory "
+ "is indicated by a trailing pathname separator. "
+ "Examples: \"xml:filename.xml\", \"xml::directoryname/\". "
+ "If a directory is specified, output files will be created "
+ "within that directory, with file-names based on the test "
+ "executable's name and, if necessary, made unique by adding "
+ "digits.");
+
+GTEST_DEFINE_bool_(
+ print_time,
+ internal::BoolFromGTestEnv("print_time", true),
+ "True iff " GTEST_NAME_
+ " should display elapsed time in text output.");
+
+GTEST_DEFINE_int32_(
+ random_seed,
+ internal::Int32FromGTestEnv("random_seed", 0),
+ "Random number seed to use when shuffling test orders. Must be in range "
+ "[1, 99999], or 0 to use a seed based on the current time.");
+
+GTEST_DEFINE_int32_(
+ repeat,
+ internal::Int32FromGTestEnv("repeat", 1),
+ "How many times to repeat each test. Specify a negative number "
+ "for repeating forever. Useful for shaking out flaky tests.");
+
+GTEST_DEFINE_bool_(
+ show_internal_stack_frames, false,
+ "True iff " GTEST_NAME_ " should include internal stack frames when "
+ "printing test failure stack traces.");
+
+GTEST_DEFINE_bool_(
+ shuffle,
+ internal::BoolFromGTestEnv("shuffle", false),
+ "True iff " GTEST_NAME_
+ " should randomize tests' order on every run.");
+
+GTEST_DEFINE_int32_(
+ stack_trace_depth,
+ internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth),
+ "The maximum number of stack frames to print when an "
+ "assertion fails. The valid range is 0 through 100, inclusive.");
+
+GTEST_DEFINE_string_(
+ stream_result_to,
+ internal::StringFromGTestEnv("stream_result_to", ""),
+ "This flag specifies the host name and the port number on which to stream "
+ "test results. Example: \"localhost:555\". The flag is effective only on "
+ "Linux.");
+
+GTEST_DEFINE_bool_(
+ throw_on_failure,
+ internal::BoolFromGTestEnv("throw_on_failure", false),
+ "When this flag is specified, a failed assertion will throw an exception "
+ "if exceptions are enabled or exit the program with a non-zero code "
+ "otherwise.");
+
+namespace internal {
+
+// Generates a random number from [0, range), using a Linear
+// Congruential Generator (LCG). Crashes if 'range' is 0 or greater
+// than kMaxRange.
+UInt32 Random::Generate(UInt32 range) {
+ // These constants are the same as are used in glibc's rand(3).
+ state_ = (1103515245U*state_ + 12345U) % kMaxRange;
+
+ GTEST_CHECK_(range > 0)
+ << "Cannot generate a number in the range [0, 0).";
+ GTEST_CHECK_(range <= kMaxRange)
+ << "Generation of a number in [0, " << range << ") was requested, "
+ << "but this can only generate numbers in [0, " << kMaxRange << ").";
+
+ // Converting via modulus introduces a bit of downward bias, but
+ // it's simple, and a linear congruential generator isn't too good
+ // to begin with.
+ return state_ % range;
+}
+
+// GTestIsInitialized() returns true iff the user has initialized
+// Google Test. Useful for catching the user mistake of not initializing
+// Google Test before calling RUN_ALL_TESTS().
+//
+// A user must call testing::InitGoogleTest() to initialize Google
+// Test. g_init_gtest_count is set to the number of times
+// InitGoogleTest() has been called. We don't protect this variable
+// under a mutex as it is only accessed in the main thread.
+int g_init_gtest_count = 0;
+static bool GTestIsInitialized() { return g_init_gtest_count != 0; }
+
+// Iterates over a vector of TestCases, keeping a running sum of the
+// results of calling a given int-returning method on each.
+// Returns the sum.
+static int SumOverTestCaseList(const std::vector<TestCase*>& case_list,
+ int (TestCase::*method)() const) {
+ int sum = 0;
+ for (size_t i = 0; i < case_list.size(); i++) {
+ sum += (case_list[i]->*method)();
+ }
+ return sum;
+}
+
+// Returns true iff the test case passed.
+static bool TestCasePassed(const TestCase* test_case) {
+ return test_case->should_run() && test_case->Passed();
+}
+
+// Returns true iff the test case failed.
+static bool TestCaseFailed(const TestCase* test_case) {
+ return test_case->should_run() && test_case->Failed();
+}
+
+// Returns true iff test_case contains at least one test that should
+// run.
+static bool ShouldRunTestCase(const TestCase* test_case) {
+ return test_case->should_run();
+}
+
+// AssertHelper constructor.
+AssertHelper::AssertHelper(TestPartResult::Type type,
+ const char* file,
+ int line,
+ const char* message)
+ : data_(new AssertHelperData(type, file, line, message)) {
+}
+
+AssertHelper::~AssertHelper() {
+ delete data_;
+}
+
+// Message assignment, for assertion streaming support.
+void AssertHelper::operator=(const Message& message) const {
+ UnitTest::GetInstance()->
+ AddTestPartResult(data_->type, data_->file, data_->line,
+ AppendUserMessage(data_->message, message),
+ UnitTest::GetInstance()->impl()
+ ->CurrentOsStackTraceExceptTop(1)
+ // Skips the stack frame for this function itself.
+ ); // NOLINT
+}
+
+// Mutex for linked pointers.
+GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex);
+
+// Application pathname gotten in InitGoogleTest.
+String g_executable_path;
+
+// Returns the current application's name, removing directory path if that
+// is present.
+FilePath GetCurrentExecutableName() {
+ FilePath result;
+
+#if GTEST_OS_WINDOWS
+ result.Set(FilePath(g_executable_path).RemoveExtension("exe"));
+#else
+ result.Set(FilePath(g_executable_path));
+#endif // GTEST_OS_WINDOWS
+
+ return result.RemoveDirectoryName();
+}
+
+// Functions for processing the gtest_output flag.
+
+// Returns the output format, or "" for normal printed output.
+String UnitTestOptions::GetOutputFormat() {
+ const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
+ if (gtest_output_flag == NULL) return String("");
+
+ const char* const colon = strchr(gtest_output_flag, ':');
+ return (colon == NULL) ?
+ String(gtest_output_flag) :
+ String(gtest_output_flag, colon - gtest_output_flag);
+}
+
+// Returns the name of the requested output file, or the default if none
+// was explicitly specified.
+String UnitTestOptions::GetAbsolutePathToOutputFile() {
+ const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
+ if (gtest_output_flag == NULL)
+ return String("");
+
+ const char* const colon = strchr(gtest_output_flag, ':');
+ if (colon == NULL)
+ return String(internal::FilePath::ConcatPaths(
+ internal::FilePath(
+ UnitTest::GetInstance()->original_working_dir()),
+ internal::FilePath(kDefaultOutputFile)).ToString() );
+
+ internal::FilePath output_name(colon + 1);
+ if (!output_name.IsAbsolutePath())
+ // TODO(***@google.com): on Windows \some\path is not an absolute
+ // path (as its meaning depends on the current drive), yet the
+ // following logic for turning it into an absolute path is wrong.
+ // Fix it.
+ output_name = internal::FilePath::ConcatPaths(
+ internal::FilePath(UnitTest::GetInstance()->original_working_dir()),
+ internal::FilePath(colon + 1));
+
+ if (!output_name.IsDirectory())
+ return output_name.ToString();
+
+ internal::FilePath result(internal::FilePath::GenerateUniqueFileName(
+ output_name, internal::GetCurrentExecutableName(),
+ GetOutputFormat().c_str()));
+ return result.ToString();
+}
+
+// Returns true iff the wildcard pattern matches the string. The
+// first ':' or '\0' character in pattern marks the end of it.
+//
+// This recursive algorithm isn't very efficient, but is clear and
+// works well enough for matching test names, which are short.
+bool UnitTestOptions::PatternMatchesString(const char *pattern,
+ const char *str) {
+ switch (*pattern) {
+ case '\0':
+ case ':': // Either ':' or '\0' marks the end of the pattern.
+ return *str == '\0';
+ case '?': // Matches any single character.
+ return *str != '\0' && PatternMatchesString(pattern + 1, str + 1);
+ case '*': // Matches any string (possibly empty) of characters.
+ return (*str != '\0' && PatternMatchesString(pattern, str + 1)) ||
+ PatternMatchesString(pattern + 1, str);
+ default: // Non-special character. Matches itself.
+ return *pattern == *str &&
+ PatternMatchesString(pattern + 1, str + 1);
+ }
+}
+
+bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) {
+ const char *cur_pattern = filter;
+ for (;;) {
+ if (PatternMatchesString(cur_pattern, name.c_str())) {
+ return true;
+ }
+
+ // Finds the next pattern in the filter.
+ cur_pattern = strchr(cur_pattern, ':');
+
+ // Returns if no more pattern can be found.
+ if (cur_pattern == NULL) {
+ return false;
+ }
+
+ // Skips the pattern separater (the ':' character).
+ cur_pattern++;
+ }
+}
+
+// TODO(keithray): move String function implementations to gtest-string.cc.
+
+// Returns true iff the user-specified filter matches the test case
+// name and the test name.
+bool UnitTestOptions::FilterMatchesTest(const String &test_case_name,
+ const String &test_name) {
+ const String& full_name = String::Format("%s.%s",
+ test_case_name.c_str(),
+ test_name.c_str());
+
+ // Split --gtest_filter at '-', if there is one, to separate into
+ // positive filter and negative filter portions
+ const char* const p = GTEST_FLAG(filter).c_str();
+ const char* const dash = strchr(p, '-');
+ String positive;
+ String negative;
+ if (dash == NULL) {
+ positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter
+ negative = String("");
+ } else {
+ positive = String(p, dash - p); // Everything up to the dash
+ negative = String(dash+1); // Everything after the dash
+ if (positive.empty()) {
+ // Treat '-test1' as the same as '*-test1'
+ positive = kUniversalFilter;
+ }
+ }
+
+ // A filter is a colon-separated list of patterns. It matches a
+ // test if any pattern in it matches the test.
+ return (MatchesFilter(full_name, positive.c_str()) &&
+ !MatchesFilter(full_name, negative.c_str()));
+}
+
+#if GTEST_HAS_SEH
+// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the
+// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise.
+// This function is useful as an __except condition.
+int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) {
+ // Google Test should handle a SEH exception if:
+ // 1. the user wants it to, AND
+ // 2. this is not a breakpoint exception, AND
+ // 3. this is not a C++ exception (VC++ implements them via SEH,
+ // apparently).
+ //
+ // SEH exception code for C++ exceptions.
+ // (see http://support.microsoft.com/kb/185294 for more information).
+ const DWORD kCxxExceptionCode = 0xe06d7363;
+
+ bool should_handle = true;
+
+ if (!GTEST_FLAG(catch_exceptions))
+ should_handle = false;
+ else if (exception_code == EXCEPTION_BREAKPOINT)
+ should_handle = false;
+ else if (exception_code == kCxxExceptionCode)
+ should_handle = false;
+
+ return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
+}
+#endif // GTEST_HAS_SEH
+
+} // namespace internal
+
+// The c'tor sets this object as the test part result reporter used by
+// Google Test. The 'result' parameter specifies where to report the
+// results. Intercepts only failures from the current thread.
+ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter(
+ TestPartResultArray* result)
+ : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD),
+ result_(result) {
+ Init();
+}
+
+// The c'tor sets this object as the test part result reporter used by
+// Google Test. The 'result' parameter specifies where to report the
+// results.
+ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter(
+ InterceptMode intercept_mode, TestPartResultArray* result)
+ : intercept_mode_(intercept_mode),
+ result_(result) {
+ Init();
+}
+
+void ScopedFakeTestPartResultReporter::Init() {
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ if (intercept_mode_ == INTERCEPT_ALL_THREADS) {
+ old_reporter_ = impl->GetGlobalTestPartResultReporter();
+ impl->SetGlobalTestPartResultReporter(this);
+ } else {
+ old_reporter_ = impl->GetTestPartResultReporterForCurrentThread();
+ impl->SetTestPartResultReporterForCurrentThread(this);
+ }
+}
+
+// The d'tor restores the test part result reporter used by Google Test
+// before.
+ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() {
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ if (intercept_mode_ == INTERCEPT_ALL_THREADS) {
+ impl->SetGlobalTestPartResultReporter(old_reporter_);
+ } else {
+ impl->SetTestPartResultReporterForCurrentThread(old_reporter_);
+ }
+}
+
+// Increments the test part result count and remembers the result.
+// This method is from the TestPartResultReporterInterface interface.
+void ScopedFakeTestPartResultReporter::ReportTestPartResult(
+ const TestPartResult& result) {
+ result_->Append(result);
+}
+
+namespace internal {
+
+// Returns the type ID of ::testing::Test. We should always call this
+// instead of GetTypeId< ::testing::Test>() to get the type ID of
+// testing::Test. This is to work around a suspected linker bug when
+// using Google Test as a framework on Mac OS X. The bug causes
+// GetTypeId< ::testing::Test>() to return different values depending
+// on whether the call is from the Google Test framework itself or
+// from user test code. GetTestTypeId() is guaranteed to always
+// return the same value, as it always calls GetTypeId<>() from the
+// gtest.cc, which is within the Google Test framework.
+TypeId GetTestTypeId() {
+ return GetTypeId<Test>();
+}
+
+// The value of GetTestTypeId() as seen from within the Google Test
+// library. This is solely for testing GetTestTypeId().
+extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId();
+
+// This predicate-formatter checks that 'results' contains a test part
+// failure of the given type and that the failure message contains the
+// given substring.
+AssertionResult HasOneFailure(const char* /* results_expr */,
+ const char* /* type_expr */,
+ const char* /* substr_expr */,
+ const TestPartResultArray& results,
+ TestPartResult::Type type,
+ const string& substr) {
+ const String expected(type == TestPartResult::kFatalFailure ?
+ "1 fatal failure" :
+ "1 non-fatal failure");
+ Message msg;
+ if (results.size() != 1) {
+ msg << "Expected: " << expected << "\n"
+ << " Actual: " << results.size() << " failures";
+ for (int i = 0; i < results.size(); i++) {
+ msg << "\n" << results.GetTestPartResult(i);
+ }
+ return AssertionFailure() << msg;
+ }
+
+ const TestPartResult& r = results.GetTestPartResult(0);
+ if (r.type() != type) {
+ return AssertionFailure() << "Expected: " << expected << "\n"
+ << " Actual:\n"
+ << r;
+ }
+
+ if (strstr(r.message(), substr.c_str()) == NULL) {
+ return AssertionFailure() << "Expected: " << expected << " containing \""
+ << substr << "\"\n"
+ << " Actual:\n"
+ << r;
+ }
+
+ return AssertionSuccess();
+}
+
+// The constructor of SingleFailureChecker remembers where to look up
+// test part results, what type of failure we expect, and what
+// substring the failure message should contain.
+SingleFailureChecker:: SingleFailureChecker(
+ const TestPartResultArray* results,
+ TestPartResult::Type type,
+ const string& substr)
+ : results_(results),
+ type_(type),
+ substr_(substr) {}
+
+// The destructor of SingleFailureChecker verifies that the given
+// TestPartResultArray contains exactly one failure that has the given
+// type and contains the given substring. If that's not the case, a
+// non-fatal failure will be generated.
+SingleFailureChecker::~SingleFailureChecker() {
+ EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_);
+}
+
+DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter(
+ UnitTestImpl* unit_test) : unit_test_(unit_test) {}
+
+void DefaultGlobalTestPartResultReporter::ReportTestPartResult(
+ const TestPartResult& result) {
+ unit_test_->current_test_result()->AddTestPartResult(result);
+ unit_test_->listeners()->repeater()->OnTestPartResult(result);
+}
+
+DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter(
+ UnitTestImpl* unit_test) : unit_test_(unit_test) {}
+
+void DefaultPerThreadTestPartResultReporter::ReportTestPartResult(
+ const TestPartResult& result) {
+ unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result);
+}
+
+// Returns the global test part result reporter.
+TestPartResultReporterInterface*
+UnitTestImpl::GetGlobalTestPartResultReporter() {
+ internal::MutexLock lock(&global_test_part_result_reporter_mutex_);
+ return global_test_part_result_repoter_;
+}
+
+// Sets the global test part result reporter.
+void UnitTestImpl::SetGlobalTestPartResultReporter(
+ TestPartResultReporterInterface* reporter) {
+ internal::MutexLock lock(&global_test_part_result_reporter_mutex_);
+ global_test_part_result_repoter_ = reporter;
+}
+
+// Returns the test part result reporter for the current thread.
+TestPartResultReporterInterface*
+UnitTestImpl::GetTestPartResultReporterForCurrentThread() {
+ return per_thread_test_part_result_reporter_.get();
+}
+
+// Sets the test part result reporter for the current thread.
+void UnitTestImpl::SetTestPartResultReporterForCurrentThread(
+ TestPartResultReporterInterface* reporter) {
+ per_thread_test_part_result_reporter_.set(reporter);
+}
+
+// Gets the number of successful test cases.
+int UnitTestImpl::successful_test_case_count() const {
+ return CountIf(test_cases_, TestCasePassed);
+}
+
+// Gets the number of failed test cases.
+int UnitTestImpl::failed_test_case_count() const {
+ return CountIf(test_cases_, TestCaseFailed);
+}
+
+// Gets the number of all test cases.
+int UnitTestImpl::total_test_case_count() const {
+ return static_cast<int>(test_cases_.size());
+}
+
+// Gets the number of all test cases that contain at least one test
+// that should run.
+int UnitTestImpl::test_case_to_run_count() const {
+ return CountIf(test_cases_, ShouldRunTestCase);
+}
+
+// Gets the number of successful tests.
+int UnitTestImpl::successful_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count);
+}
+
+// Gets the number of failed tests.
+int UnitTestImpl::failed_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count);
+}
+
+// Gets the number of disabled tests.
+int UnitTestImpl::disabled_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count);
+}
+
+// Gets the number of all tests.
+int UnitTestImpl::total_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::total_test_count);
+}
+
+// Gets the number of tests that should run.
+int UnitTestImpl::test_to_run_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count);
+}
+
+// Returns the current OS stack trace as a String.
+//
+// The maximum number of stack frames to be included is specified by
+// the gtest_stack_trace_depth flag. The skip_count parameter
+// specifies the number of top frames to be skipped, which doesn't
+// count against the number of frames to be included.
+//
+// For example, if Foo() calls Bar(), which in turn calls
+// CurrentOsStackTraceExceptTop(1), Foo() will be included in the
+// trace but Bar() and CurrentOsStackTraceExceptTop() won't.
+String UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) {
+ (void)skip_count;
+ return String("");
+}
+
+// Returns the current time in milliseconds.
+TimeInMillis GetTimeInMillis() {
+#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__)
+ // Difference between 1970-01-01 and 1601-01-01 in milliseconds.
+ // http://analogous.blogspot.com/2005/04/epoch.html
+ const TimeInMillis kJavaEpochToWinFileTimeDelta =
+ static_cast<TimeInMillis>(116444736UL) * 100000UL;
+ const DWORD kTenthMicrosInMilliSecond = 10000;
+
+ SYSTEMTIME now_systime;
+ FILETIME now_filetime;
+ ULARGE_INTEGER now_int64;
+ // TODO(***@google.com): Shouldn't this just use
+ // GetSystemTimeAsFileTime()?
+ GetSystemTime(&now_systime);
+ if (SystemTimeToFileTime(&now_systime, &now_filetime)) {
+ now_int64.LowPart = now_filetime.dwLowDateTime;
+ now_int64.HighPart = now_filetime.dwHighDateTime;
+ now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) -
+ kJavaEpochToWinFileTimeDelta;
+ return now_int64.QuadPart;
+ }
+ return 0;
+#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_
+ __timeb64 now;
+
+# ifdef _MSC_VER
+
+ // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996
+ // (deprecated function) there.
+ // TODO(***@google.com): Use GetTickCount()? Or use
+ // SystemTimeToFileTime()
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4996) // Temporarily disables warning 4996.
+ _ftime64(&now);
+# pragma warning(pop) // Restores the warning state.
+# else
+
+ _ftime64(&now);
+
+# endif // _MSC_VER
+
+ return static_cast<TimeInMillis>(now.time) * 1000 + now.millitm;
+#elif GTEST_HAS_GETTIMEOFDAY_
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return static_cast<TimeInMillis>(now.tv_sec) * 1000 + now.tv_usec / 1000;
+#else
+# error "Don't know how to get the current time on your system."
+#endif
+}
+
+// Utilities
+
+// class String
+
+// Returns the input enclosed in double quotes if it's not NULL;
+// otherwise returns "(null)". For example, "\"Hello\"" is returned
+// for input "Hello".
+//
+// This is useful for printing a C string in the syntax of a literal.
+//
+// Known issue: escape sequences are not handled yet.
+String String::ShowCStringQuoted(const char* c_str) {
+ return c_str ? String::Format("\"%s\"", c_str) : String("(null)");
+}
+
+// Copies at most length characters from str into a newly-allocated
+// piece of memory of size length+1. The memory is allocated with new[].
+// A terminating null byte is written to the memory, and a pointer to it
+// is returned. If str is NULL, NULL is returned.
+static char* CloneString(const char* str, size_t length) {
+ if (str == NULL) {
+ return NULL;
+ } else {
+ char* const clone = new char[length + 1];
+ posix::StrNCpy(clone, str, length);
+ clone[length] = '\0';
+ return clone;
+ }
+}
+
+// Clones a 0-terminated C string, allocating memory using new. The
+// caller is responsible for deleting[] the return value. Returns the
+// cloned string, or NULL if the input is NULL.
+const char * String::CloneCString(const char* c_str) {
+ return (c_str == NULL) ?
+ NULL : CloneString(c_str, strlen(c_str));
+}
+
+#if GTEST_OS_WINDOWS_MOBILE
+// Creates a UTF-16 wide string from the given ANSI string, allocating
+// memory using new. The caller is responsible for deleting the return
+// value using delete[]. Returns the wide string, or NULL if the
+// input is NULL.
+LPCWSTR String::AnsiToUtf16(const char* ansi) {
+ if (!ansi) return NULL;
+ const int length = strlen(ansi);
+ const int unicode_length =
+ MultiByteToWideChar(CP_ACP, 0, ansi, length,
+ NULL, 0);
+ WCHAR* unicode = new WCHAR[unicode_length + 1];
+ MultiByteToWideChar(CP_ACP, 0, ansi, length,
+ unicode, unicode_length);
+ unicode[unicode_length] = 0;
+ return unicode;
+}
+
+// Creates an ANSI string from the given wide string, allocating
+// memory using new. The caller is responsible for deleting the return
+// value using delete[]. Returns the ANSI string, or NULL if the
+// input is NULL.
+const char* String::Utf16ToAnsi(LPCWSTR utf16_str) {
+ if (!utf16_str) return NULL;
+ const int ansi_length =
+ WideCharToMultiByte(CP_ACP, 0, utf16_str, -1,
+ NULL, 0, NULL, NULL);
+ char* ansi = new char[ansi_length + 1];
+ WideCharToMultiByte(CP_ACP, 0, utf16_str, -1,
+ ansi, ansi_length, NULL, NULL);
+ ansi[ansi_length] = 0;
+ return ansi;
+}
+
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+// Compares two C strings. Returns true iff they have the same content.
+//
+// Unlike strcmp(), this function can handle NULL argument(s). A NULL
+// C string is considered different to any non-NULL C string,
+// including the empty string.
+bool String::CStringEquals(const char * lhs, const char * rhs) {
+ if ( lhs == NULL ) return rhs == NULL;
+
+ if ( rhs == NULL ) return false;
+
+ return strcmp(lhs, rhs) == 0;
+}
+
+#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING
+
+// Converts an array of wide chars to a narrow string using the UTF-8
+// encoding, and streams the result to the given Message object.
+static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length,
+ Message* msg) {
+ // TODO(wan): consider allowing a testing::String object to
+ // contain '\0'. This will make it behave more like std::string,
+ // and will allow ToUtf8String() to return the correct encoding
+ // for '\0' s.t. we can get rid of the conditional here (and in
+ // several other places).
+ for (size_t i = 0; i != length; ) { // NOLINT
+ if (wstr[i] != L'\0') {
+ *msg << WideStringToUtf8(wstr + i, static_cast<int>(length - i));
+ while (i != length && wstr[i] != L'\0')
+ i++;
+ } else {
+ *msg << '\0';
+ i++;
+ }
+ }
+}
+
+#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING
+
+} // namespace internal
+
+#if GTEST_HAS_STD_WSTRING
+// Converts the given wide string to a narrow string using the UTF-8
+// encoding, and streams the result to this Message object.
+Message& Message::operator <<(const ::std::wstring& wstr) {
+ internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this);
+ return *this;
+}
+#endif // GTEST_HAS_STD_WSTRING
+
+#if GTEST_HAS_GLOBAL_WSTRING
+// Converts the given wide string to a narrow string using the UTF-8
+// encoding, and streams the result to this Message object.
+Message& Message::operator <<(const ::wstring& wstr) {
+ internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this);
+ return *this;
+}
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+// AssertionResult constructors.
+// Used in EXPECT_TRUE/FALSE(assertion_result).
+AssertionResult::AssertionResult(const AssertionResult& other)
+ : success_(other.success_),
+ message_(other.message_.get() != NULL ?
+ new ::std::string(*other.message_) :
+ static_cast< ::std::string*>(NULL)) {
+}
+
+// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
+AssertionResult AssertionResult::operator!() const {
+ AssertionResult negation(!success_);
+ if (message_.get() != NULL)
+ negation << *message_;
+ return negation;
+}
+
+// Makes a successful assertion result.
+AssertionResult AssertionSuccess() {
+ return AssertionResult(true);
+}
+
+// Makes a failed assertion result.
+AssertionResult AssertionFailure() {
+ return AssertionResult(false);
+}
+
+// Makes a failed assertion result with the given failure message.
+// Deprecated; use AssertionFailure() << message.
+AssertionResult AssertionFailure(const Message& message) {
+ return AssertionFailure() << message;
+}
+
+namespace internal {
+
+// Constructs and returns the message for an equality assertion
+// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
+//
+// The first four parameters are the expressions used in the assertion
+// and their values, as strings. For example, for ASSERT_EQ(foo, bar)
+// where foo is 5 and bar is 6, we have:
+//
+// expected_expression: "foo"
+// actual_expression: "bar"
+// expected_value: "5"
+// actual_value: "6"
+//
+// The ignoring_case parameter is true iff the assertion is a
+// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will
+// be inserted into the message.
+AssertionResult EqFailure(const char* expected_expression,
+ const char* actual_expression,
+ const String& expected_value,
+ const String& actual_value,
+ bool ignoring_case) {
+ Message msg;
+ msg << "Value of: " << actual_expression;
+ if (actual_value != actual_expression) {
+ msg << "\n Actual: " << actual_value;
+ }
+
+ msg << "\nExpected: " << expected_expression;
+ if (ignoring_case) {
+ msg << " (ignoring case)";
+ }
+ if (expected_value != expected_expression) {
+ msg << "\nWhich is: " << expected_value;
+ }
+
+ return AssertionFailure() << msg;
+}
+
+// Constructs a failure message for Boolean assertions such as EXPECT_TRUE.
+String GetBoolAssertionFailureMessage(const AssertionResult& assertion_result,
+ const char* expression_text,
+ const char* actual_predicate_value,
+ const char* expected_predicate_value) {
+ const char* actual_message = assertion_result.message();
+ Message msg;
+ msg << "Value of: " << expression_text
+ << "\n Actual: " << actual_predicate_value;
+ if (actual_message[0] != '\0')
+ msg << " (" << actual_message << ")";
+ msg << "\nExpected: " << expected_predicate_value;
+ return msg.GetString();
+}
+
+// Helper function for implementing ASSERT_NEAR.
+AssertionResult DoubleNearPredFormat(const char* expr1,
+ const char* expr2,
+ const char* abs_error_expr,
+ double val1,
+ double val2,
+ double abs_error) {
+ const double diff = fabs(val1 - val2);
+ if (diff <= abs_error) return AssertionSuccess();
+
+ // TODO(wan): do not print the value of an expression if it's
+ // already a literal.
+ return AssertionFailure()
+ << "The difference between " << expr1 << " and " << expr2
+ << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n"
+ << expr1 << " evaluates to " << val1 << ",\n"
+ << expr2 << " evaluates to " << val2 << ", and\n"
+ << abs_error_expr << " evaluates to " << abs_error << ".";
+}
+
+
+// Helper template for implementing FloatLE() and DoubleLE().
+template <typename RawType>
+AssertionResult FloatingPointLE(const char* expr1,
+ const char* expr2,
+ RawType val1,
+ RawType val2) {
+ // Returns success if val1 is less than val2,
+ if (val1 < val2) {
+ return AssertionSuccess();
+ }
+
+ // or if val1 is almost equal to val2.
+ const FloatingPoint<RawType> lhs(val1), rhs(val2);
+ if (lhs.AlmostEquals(rhs)) {
+ return AssertionSuccess();
+ }
+
+ // Note that the above two checks will both fail if either val1 or
+ // val2 is NaN, as the IEEE floating-point standard requires that
+ // any predicate involving a NaN must return false.
+
+ ::std::stringstream val1_ss;
+ val1_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << val1;
+
+ ::std::stringstream val2_ss;
+ val2_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << val2;
+
+ return AssertionFailure()
+ << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n"
+ << " Actual: " << StringStreamToString(&val1_ss) << " vs "
+ << StringStreamToString(&val2_ss);
+}
+
+} // namespace internal
+
+// Asserts that val1 is less than, or almost equal to, val2. Fails
+// otherwise. In particular, it fails if either val1 or val2 is NaN.
+AssertionResult FloatLE(const char* expr1, const char* expr2,
+ float val1, float val2) {
+ return internal::FloatingPointLE<float>(expr1, expr2, val1, val2);
+}
+
+// Asserts that val1 is less than, or almost equal to, val2. Fails
+// otherwise. In particular, it fails if either val1 or val2 is NaN.
+AssertionResult DoubleLE(const char* expr1, const char* expr2,
+ double val1, double val2) {
+ return internal::FloatingPointLE<double>(expr1, expr2, val1, val2);
+}
+
+namespace internal {
+
+// The helper function for {ASSERT|EXPECT}_EQ with int or enum
+// arguments.
+AssertionResult CmpHelperEQ(const char* expected_expression,
+ const char* actual_expression,
+ BiggestInt expected,
+ BiggestInt actual) {
+ if (expected == actual) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ FormatForComparisonFailureMessage(expected, actual),
+ FormatForComparisonFailureMessage(actual, expected),
+ false);
+}
+
+// A macro for implementing the helper functions needed to implement
+// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here
+// just to avoid copy-and-paste of similar code.
+#define GTEST_IMPL_CMP_HELPER_(op_name, op)\
+AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \
+ BiggestInt val1, BiggestInt val2) {\
+ if (val1 op val2) {\
+ return AssertionSuccess();\
+ } else {\
+ return AssertionFailure() \
+ << "Expected: (" << expr1 << ") " #op " (" << expr2\
+ << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\
+ << " vs " << FormatForComparisonFailureMessage(val2, val1);\
+ }\
+}
+
+// Implements the helper function for {ASSERT|EXPECT}_NE with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(NE, !=)
+// Implements the helper function for {ASSERT|EXPECT}_LE with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(LE, <=)
+// Implements the helper function for {ASSERT|EXPECT}_LT with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(LT, < )
+// Implements the helper function for {ASSERT|EXPECT}_GE with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(GE, >=)
+// Implements the helper function for {ASSERT|EXPECT}_GT with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(GT, > )
+
+#undef GTEST_IMPL_CMP_HELPER_
+
+// The helper function for {ASSERT|EXPECT}_STREQ.
+AssertionResult CmpHelperSTREQ(const char* expected_expression,
+ const char* actual_expression,
+ const char* expected,
+ const char* actual) {
+ if (String::CStringEquals(expected, actual)) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ String::ShowCStringQuoted(expected),
+ String::ShowCStringQuoted(actual),
+ false);
+}
+
+// The helper function for {ASSERT|EXPECT}_STRCASEEQ.
+AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression,
+ const char* actual_expression,
+ const char* expected,
+ const char* actual) {
+ if (String::CaseInsensitiveCStringEquals(expected, actual)) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ String::ShowCStringQuoted(expected),
+ String::ShowCStringQuoted(actual),
+ true);
+}
+
+// The helper function for {ASSERT|EXPECT}_STRNE.
+AssertionResult CmpHelperSTRNE(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2) {
+ if (!String::CStringEquals(s1, s2)) {
+ return AssertionSuccess();
+ } else {
+ return AssertionFailure() << "Expected: (" << s1_expression << ") != ("
+ << s2_expression << "), actual: \""
+ << s1 << "\" vs \"" << s2 << "\"";
+ }
+}
+
+// The helper function for {ASSERT|EXPECT}_STRCASENE.
+AssertionResult CmpHelperSTRCASENE(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2) {
+ if (!String::CaseInsensitiveCStringEquals(s1, s2)) {
+ return AssertionSuccess();
+ } else {
+ return AssertionFailure()
+ << "Expected: (" << s1_expression << ") != ("
+ << s2_expression << ") (ignoring case), actual: \""
+ << s1 << "\" vs \"" << s2 << "\"";
+ }
+}
+
+} // namespace internal
+
+namespace {
+
+// Helper functions for implementing IsSubString() and IsNotSubstring().
+
+// This group of overloaded functions return true iff needle is a
+// substring of haystack. NULL is considered a substring of itself
+// only.
+
+bool IsSubstringPred(const char* needle, const char* haystack) {
+ if (needle == NULL || haystack == NULL)
+ return needle == haystack;
+
+ return strstr(haystack, needle) != NULL;
+}
+
+bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) {
+ if (needle == NULL || haystack == NULL)
+ return needle == haystack;
+
+ return wcsstr(haystack, needle) != NULL;
+}
+
+// StringType here can be either ::std::string or ::std::wstring.
+template <typename StringType>
+bool IsSubstringPred(const StringType& needle,
+ const StringType& haystack) {
+ return haystack.find(needle) != StringType::npos;
+}
+
+// This function implements either IsSubstring() or IsNotSubstring(),
+// depending on the value of the expected_to_be_substring parameter.
+// StringType here can be const char*, const wchar_t*, ::std::string,
+// or ::std::wstring.
+template <typename StringType>
+AssertionResult IsSubstringImpl(
+ bool expected_to_be_substring,
+ const char* needle_expr, const char* haystack_expr,
+ const StringType& needle, const StringType& haystack) {
+ if (IsSubstringPred(needle, haystack) == expected_to_be_substring)
+ return AssertionSuccess();
+
+ const bool is_wide_string = sizeof(needle[0]) > 1;
+ const char* const begin_string_quote = is_wide_string ? "L\"" : "\"";
+ return AssertionFailure()
+ << "Value of: " << needle_expr << "\n"
+ << " Actual: " << begin_string_quote << needle << "\"\n"
+ << "Expected: " << (expected_to_be_substring ? "" : "not ")
+ << "a substring of " << haystack_expr << "\n"
+ << "Which is: " << begin_string_quote << haystack << "\"";
+}
+
+} // namespace
+
+// IsSubstring() and IsNotSubstring() check whether needle is a
+// substring of haystack (NULL is considered a substring of itself
+// only), and return an appropriate error message when they fail.
+
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const char* needle, const char* haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const wchar_t* needle, const wchar_t* haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const char* needle, const char* haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const wchar_t* needle, const wchar_t* haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::string& needle, const ::std::string& haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::string& needle, const ::std::string& haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+
+#if GTEST_HAS_STD_WSTRING
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::wstring& needle, const ::std::wstring& haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::wstring& needle, const ::std::wstring& haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+#endif // GTEST_HAS_STD_WSTRING
+
+namespace internal {
+
+#if GTEST_OS_WINDOWS
+
+namespace {
+
+// Helper function for IsHRESULT{SuccessFailure} predicates
+AssertionResult HRESULTFailureHelper(const char* expr,
+ const char* expected,
+ long hr) { // NOLINT
+# if GTEST_OS_WINDOWS_MOBILE
+
+ // Windows CE doesn't support FormatMessage.
+ const char error_text[] = "";
+
+# else
+
+ // Looks up the human-readable system message for the HRESULT code
+ // and since we're not passing any params to FormatMessage, we don't
+ // want inserts expanded.
+ const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS;
+ const DWORD kBufSize = 4096; // String::Format can't exceed this length.
+ // Gets the system's human readable message string for this HRESULT.
+ char error_text[kBufSize] = { '\0' };
+ DWORD message_length = ::FormatMessageA(kFlags,
+ 0, // no source, we're asking system
+ hr, // the error
+ 0, // no line width restrictions
+ error_text, // output buffer
+ kBufSize, // buf size
+ NULL); // no arguments for inserts
+ // Trims tailing white space (FormatMessage leaves a trailing cr-lf)
+ for (; message_length && IsSpace(error_text[message_length - 1]);
+ --message_length) {
+ error_text[message_length - 1] = '\0';
+ }
+
+# endif // GTEST_OS_WINDOWS_MOBILE
+
+ const String error_hex(String::Format("0x%08X ", hr));
+ return ::testing::AssertionFailure()
+ << "Expected: " << expr << " " << expected << ".\n"
+ << " Actual: " << error_hex << error_text << "\n";
+}
+
+} // namespace
+
+AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT
+ if (SUCCEEDED(hr)) {
+ return AssertionSuccess();
+ }
+ return HRESULTFailureHelper(expr, "succeeds", hr);
+}
+
+AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT
+ if (FAILED(hr)) {
+ return AssertionSuccess();
+ }
+ return HRESULTFailureHelper(expr, "fails", hr);
+}
+
+#endif // GTEST_OS_WINDOWS
+
+// Utility functions for encoding Unicode text (wide strings) in
+// UTF-8.
+
+// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8
+// like this:
+//
+// Code-point length Encoding
+// 0 - 7 bits 0xxxxxxx
+// 8 - 11 bits 110xxxxx 10xxxxxx
+// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx
+// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+// The maximum code-point a one-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint1 = (static_cast<UInt32>(1) << 7) - 1;
+
+// The maximum code-point a two-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint2 = (static_cast<UInt32>(1) << (5 + 6)) - 1;
+
+// The maximum code-point a three-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint3 = (static_cast<UInt32>(1) << (4 + 2*6)) - 1;
+
+// The maximum code-point a four-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint4 = (static_cast<UInt32>(1) << (3 + 3*6)) - 1;
+
+// Chops off the n lowest bits from a bit pattern. Returns the n
+// lowest bits. As a side effect, the original bit pattern will be
+// shifted to the right by n bits.
+inline UInt32 ChopLowBits(UInt32* bits, int n) {
+ const UInt32 low_bits = *bits & ((static_cast<UInt32>(1) << n) - 1);
+ *bits >>= n;
+ return low_bits;
+}
+
+// Converts a Unicode code point to a narrow string in UTF-8 encoding.
+// code_point parameter is of type UInt32 because wchar_t may not be
+// wide enough to contain a code point.
+// The output buffer str must containt at least 32 characters.
+// The function returns the address of the output buffer.
+// If the code_point is not a valid Unicode code point
+// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output
+// as '(Invalid Unicode 0xXXXXXXXX)'.
+char* CodePointToUtf8(UInt32 code_point, char* str) {
+ if (code_point <= kMaxCodePoint1) {
+ str[1] = '\0';
+ str[0] = static_cast<char>(code_point); // 0xxxxxxx
+ } else if (code_point <= kMaxCodePoint2) {
+ str[2] = '\0';
+ str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[0] = static_cast<char>(0xC0 | code_point); // 110xxxxx
+ } else if (code_point <= kMaxCodePoint3) {
+ str[3] = '\0';
+ str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[0] = static_cast<char>(0xE0 | code_point); // 1110xxxx
+ } else if (code_point <= kMaxCodePoint4) {
+ str[4] = '\0';
+ str[3] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[0] = static_cast<char>(0xF0 | code_point); // 11110xxx
+ } else {
+ // The longest string String::Format can produce when invoked
+ // with these parameters is 28 character long (not including
+ // the terminating nul character). We are asking for 32 character
+ // buffer just in case. This is also enough for strncpy to
+ // null-terminate the destination string.
+ posix::StrNCpy(
+ str, String::Format("(Invalid Unicode 0x%X)", code_point).c_str(), 32);
+ str[31] = '\0'; // Makes sure no change in the format to strncpy leaves
+ // the result unterminated.
+ }
+ return str;
+}
+
+// The following two functions only make sense if the the system
+// uses UTF-16 for wide string encoding. All supported systems
+// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16.
+
+// Determines if the arguments constitute UTF-16 surrogate pair
+// and thus should be combined into a single Unicode code point
+// using CreateCodePointFromUtf16SurrogatePair.
+inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) {
+ return sizeof(wchar_t) == 2 &&
+ (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00;
+}
+
+// Creates a Unicode code point from UTF16 surrogate pair.
+inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first,
+ wchar_t second) {
+ const UInt32 mask = (1 << 10) - 1;
+ return (sizeof(wchar_t) == 2) ?
+ (((first & mask) << 10) | (second & mask)) + 0x10000 :
+ // This function should not be called when the condition is
+ // false, but we provide a sensible default in case it is.
+ static_cast<UInt32>(first);
+}
+
+// Converts a wide string to a narrow string in UTF-8 encoding.
+// The wide string is assumed to have the following encoding:
+// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS)
+// UTF-32 if sizeof(wchar_t) == 4 (on Linux)
+// Parameter str points to a null-terminated wide string.
+// Parameter num_chars may additionally limit the number
+// of wchar_t characters processed. -1 is used when the entire string
+// should be processed.
+// If the string contains code points that are not valid Unicode code points
+// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output
+// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding
+// and contains invalid UTF-16 surrogate pairs, values in those pairs
+// will be encoded as individual Unicode characters from Basic Normal Plane.
+String WideStringToUtf8(const wchar_t* str, int num_chars) {
+ if (num_chars == -1)
+ num_chars = static_cast<int>(wcslen(str));
+
+ ::std::stringstream stream;
+ for (int i = 0; i < num_chars; ++i) {
+ UInt32 unicode_code_point;
+
+ if (str[i] == L'\0') {
+ break;
+ } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) {
+ unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i],
+ str[i + 1]);
+ i++;
+ } else {
+ unicode_code_point = static_cast<UInt32>(str[i]);
+ }
+
+ char buffer[32]; // CodePointToUtf8 requires a buffer this big.
+ stream << CodePointToUtf8(unicode_code_point, buffer);
+ }
+ return StringStreamToString(&stream);
+}
+
+// Converts a wide C string to a String using the UTF-8 encoding.
+// NULL will be converted to "(null)".
+String String::ShowWideCString(const wchar_t * wide_c_str) {
+ if (wide_c_str == NULL) return String("(null)");
+
+ return String(internal::WideStringToUtf8(wide_c_str, -1).c_str());
+}
+
+// Similar to ShowWideCString(), except that this function encloses
+// the converted string in double quotes.
+String String::ShowWideCStringQuoted(const wchar_t* wide_c_str) {
+ if (wide_c_str == NULL) return String("(null)");
+
+ return String::Format("L\"%s\"",
+ String::ShowWideCString(wide_c_str).c_str());
+}
+
+// Compares two wide C strings. Returns true iff they have the same
+// content.
+//
+// Unlike wcscmp(), this function can handle NULL argument(s). A NULL
+// C string is considered different to any non-NULL C string,
+// including the empty string.
+bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) {
+ if (lhs == NULL) return rhs == NULL;
+
+ if (rhs == NULL) return false;
+
+ return wcscmp(lhs, rhs) == 0;
+}
+
+// Helper function for *_STREQ on wide strings.
+AssertionResult CmpHelperSTREQ(const char* expected_expression,
+ const char* actual_expression,
+ const wchar_t* expected,
+ const wchar_t* actual) {
+ if (String::WideCStringEquals(expected, actual)) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ String::ShowWideCStringQuoted(expected),
+ String::ShowWideCStringQuoted(actual),
+ false);
+}
+
+// Helper function for *_STRNE on wide strings.
+AssertionResult CmpHelperSTRNE(const char* s1_expression,
+ const char* s2_expression,
+ const wchar_t* s1,
+ const wchar_t* s2) {
+ if (!String::WideCStringEquals(s1, s2)) {
+ return AssertionSuccess();
+ }
+
+ return AssertionFailure() << "Expected: (" << s1_expression << ") != ("
+ << s2_expression << "), actual: "
+ << String::ShowWideCStringQuoted(s1)
+ << " vs " << String::ShowWideCStringQuoted(s2);
+}
+
+// Compares two C strings, ignoring case. Returns true iff they have
+// the same content.
+//
+// Unlike strcasecmp(), this function can handle NULL argument(s). A
+// NULL C string is considered different to any non-NULL C string,
+// including the empty string.
+bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) {
+ if (lhs == NULL)
+ return rhs == NULL;
+ if (rhs == NULL)
+ return false;
+ return posix::StrCaseCmp(lhs, rhs) == 0;
+}
+
+ // Compares two wide C strings, ignoring case. Returns true iff they
+ // have the same content.
+ //
+ // Unlike wcscasecmp(), this function can handle NULL argument(s).
+ // A NULL C string is considered different to any non-NULL wide C string,
+ // including the empty string.
+ // NB: The implementations on different platforms slightly differ.
+ // On windows, this method uses _wcsicmp which compares according to LC_CTYPE
+ // environment variable. On GNU platform this method uses wcscasecmp
+ // which compares according to LC_CTYPE category of the current locale.
+ // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
+ // current locale.
+bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
+ const wchar_t* rhs) {
+ if (lhs == NULL) return rhs == NULL;
+
+ if (rhs == NULL) return false;
+
+#if GTEST_OS_WINDOWS
+ return _wcsicmp(lhs, rhs) == 0;
+#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID
+ return wcscasecmp(lhs, rhs) == 0;
+#else
+ // Android, Mac OS X and Cygwin don't define wcscasecmp.
+ // Other unknown OSes may not define it either.
+ wint_t left, right;
+ do {
+ left = towlower(*lhs++);
+ right = towlower(*rhs++);
+ } while (left && left == right);
+ return left == right;
+#endif // OS selector
+}
+
+// Compares this with another String.
+// Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0
+// if this is greater than rhs.
+int String::Compare(const String & rhs) const {
+ const char* const lhs_c_str = c_str();
+ const char* const rhs_c_str = rhs.c_str();
+
+ if (lhs_c_str == NULL) {
+ return rhs_c_str == NULL ? 0 : -1; // NULL < anything except NULL
+ } else if (rhs_c_str == NULL) {
+ return 1;
+ }
+
+ const size_t shorter_str_len =
+ length() <= rhs.length() ? length() : rhs.length();
+ for (size_t i = 0; i != shorter_str_len; i++) {
+ if (lhs_c_str[i] < rhs_c_str[i]) {
+ return -1;
+ } else if (lhs_c_str[i] > rhs_c_str[i]) {
+ return 1;
+ }
+ }
+ return (length() < rhs.length()) ? -1 :
+ (length() > rhs.length()) ? 1 : 0;
+}
+
+// Returns true iff this String ends with the given suffix. *Any*
+// String is considered to end with a NULL or empty suffix.
+bool String::EndsWith(const char* suffix) const {
+ if (suffix == NULL || CStringEquals(suffix, "")) return true;
+
+ if (c_str() == NULL) return false;
+
+ const size_t this_len = strlen(c_str());
+ const size_t suffix_len = strlen(suffix);
+ return (this_len >= suffix_len) &&
+ CStringEquals(c_str() + this_len - suffix_len, suffix);
+}
+
+// Returns true iff this String ends with the given suffix, ignoring case.
+// Any String is considered to end with a NULL or empty suffix.
+bool String::EndsWithCaseInsensitive(const char* suffix) const {
+ if (suffix == NULL || CStringEquals(suffix, "")) return true;
+
+ if (c_str() == NULL) return false;
+
+ const size_t this_len = strlen(c_str());
+ const size_t suffix_len = strlen(suffix);
+ return (this_len >= suffix_len) &&
+ CaseInsensitiveCStringEquals(c_str() + this_len - suffix_len, suffix);
+}
+
+// Formats a list of arguments to a String, using the same format
+// spec string as for printf.
+//
+// We do not use the StringPrintf class as it is not universally
+// available.
+//
+// The result is limited to 4096 characters (including the tailing 0).
+// If 4096 characters are not enough to format the input, or if
+// there's an error, "<formatting error or buffer exceeded>" is
+// returned.
+String String::Format(const char * format, ...) {
+ va_list args;
+ va_start(args, format);
+
+ char buffer[4096];
+ const int kBufferSize = sizeof(buffer)/sizeof(buffer[0]);
+
+ // MSVC 8 deprecates vsnprintf(), so we want to suppress warning
+ // 4996 (deprecated function) there.
+#ifdef _MSC_VER // We are using MSVC.
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4996) // Temporarily disables warning 4996.
+
+ const int size = vsnprintf(buffer, kBufferSize, format, args);
+
+# pragma warning(pop) // Restores the warning state.
+#else // We are not using MSVC.
+ const int size = vsnprintf(buffer, kBufferSize, format, args);
+#endif // _MSC_VER
+ va_end(args);
+
+ // vsnprintf()'s behavior is not portable. When the buffer is not
+ // big enough, it returns a negative value in MSVC, and returns the
+ // needed buffer size on Linux. When there is an output error, it
+ // always returns a negative value. For simplicity, we lump the two
+ // error cases together.
+ if (size < 0 || size >= kBufferSize) {
+ return String("<formatting error or buffer exceeded>");
+ } else {
+ return String(buffer, size);
+ }
+}
+
+// Converts the buffer in a stringstream to a String, converting NUL
+// bytes to "\\0" along the way.
+String StringStreamToString(::std::stringstream* ss) {
+ const ::std::string& str = ss->str();
+ const char* const start = str.c_str();
+ const char* const end = start + str.length();
+
+ // We need to use a helper stringstream to do this transformation
+ // because String doesn't support push_back().
+ ::std::stringstream helper;
+ for (const char* ch = start; ch != end; ++ch) {
+ if (*ch == '\0') {
+ helper << "\\0"; // Replaces NUL with "\\0";
+ } else {
+ helper.put(*ch);
+ }
+ }
+
+ return String(helper.str().c_str());
+}
+
+// Appends the user-supplied message to the Google-Test-generated message.
+String AppendUserMessage(const String& gtest_msg,
+ const Message& user_msg) {
+ // Appends the user message if it's non-empty.
+ const String user_msg_string = user_msg.GetString();
+ if (user_msg_string.empty()) {
+ return gtest_msg;
+ }
+
+ Message msg;
+ msg << gtest_msg << "\n" << user_msg_string;
+
+ return msg.GetString();
+}
+
+} // namespace internal
+
+// class TestResult
+
+// Creates an empty TestResult.
+TestResult::TestResult()
+ : death_test_count_(0),
+ elapsed_time_(0) {
+}
+
+// D'tor.
+TestResult::~TestResult() {
+}
+
+// Returns the i-th test part result among all the results. i can
+// range from 0 to total_part_count() - 1. If i is not in that range,
+// aborts the program.
+const TestPartResult& TestResult::GetTestPartResult(int i) const {
+ if (i < 0 || i >= total_part_count())
+ internal::posix::Abort();
+ return test_part_results_.at(i);
+}
+
+// Returns the i-th test property. i can range from 0 to
+// test_property_count() - 1. If i is not in that range, aborts the
+// program.
+const TestProperty& TestResult::GetTestProperty(int i) const {
+ if (i < 0 || i >= test_property_count())
+ internal::posix::Abort();
+ return test_properties_.at(i);
+}
+
+// Clears the test part results.
+void TestResult::ClearTestPartResults() {
+ test_part_results_.clear();
+}
+
+// Adds a test part result to the list.
+void TestResult::AddTestPartResult(const TestPartResult& test_part_result) {
+ test_part_results_.push_back(test_part_result);
+}
+
+// Adds a test property to the list. If a property with the same key as the
+// supplied property is already represented, the value of this test_property
+// replaces the old value for that key.
+void TestResult::RecordProperty(const TestProperty& test_property) {
+ if (!ValidateTestProperty(test_property)) {
+ return;
+ }
+ internal::MutexLock lock(&test_properites_mutex_);
+ const std::vector<TestProperty>::iterator property_with_matching_key =
+ std::find_if(test_properties_.begin(), test_properties_.end(),
+ internal::TestPropertyKeyIs(test_property.key()));
+ if (property_with_matching_key == test_properties_.end()) {
+ test_properties_.push_back(test_property);
+ return;
+ }
+ property_with_matching_key->SetValue(test_property.value());
+}
+
+// Adds a failure if the key is a reserved attribute of Google Test
+// testcase tags. Returns true if the property is valid.
+bool TestResult::ValidateTestProperty(const TestProperty& test_property) {
+ internal::String key(test_property.key());
+ if (key == "name" || key == "status" || key == "time" || key == "classname") {
+ ADD_FAILURE()
+ << "Reserved key used in RecordProperty(): "
+ << key
+ << " ('name', 'status', 'time', and 'classname' are reserved by "
+ << GTEST_NAME_ << ")";
+ return false;
+ }
+ return true;
+}
+
+// Clears the object.
+void TestResult::Clear() {
+ test_part_results_.clear();
+ test_properties_.clear();
+ death_test_count_ = 0;
+ elapsed_time_ = 0;
+}
+
+// Returns true iff the test failed.
+bool TestResult::Failed() const {
+ for (int i = 0; i < total_part_count(); ++i) {
+ if (GetTestPartResult(i).failed())
+ return true;
+ }
+ return false;
+}
+
+// Returns true iff the test part fatally failed.
+static bool TestPartFatallyFailed(const TestPartResult& result) {
+ return result.fatally_failed();
+}
+
+// Returns true iff the test fatally failed.
+bool TestResult::HasFatalFailure() const {
+ return CountIf(test_part_results_, TestPartFatallyFailed) > 0;
+}
+
+// Returns true iff the test part non-fatally failed.
+static bool TestPartNonfatallyFailed(const TestPartResult& result) {
+ return result.nonfatally_failed();
+}
+
+// Returns true iff the test has a non-fatal failure.
+bool TestResult::HasNonfatalFailure() const {
+ return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0;
+}
+
+// Gets the number of all test parts. This is the sum of the number
+// of successful test parts and the number of failed test parts.
+int TestResult::total_part_count() const {
+ return static_cast<int>(test_part_results_.size());
+}
+
+// Returns the number of the test properties.
+int TestResult::test_property_count() const {
+ return static_cast<int>(test_properties_.size());
+}
+
+// class Test
+
+// Creates a Test object.
+
+// The c'tor saves the values of all Google Test flags.
+Test::Test()
+ : gtest_flag_saver_(new internal::GTestFlagSaver) {
+}
+
+// The d'tor restores the values of all Google Test flags.
+Test::~Test() {
+ delete gtest_flag_saver_;
+}
+
+// Sets up the test fixture.
+//
+// A sub-class may override this.
+void Test::SetUp() {
+}
+
+// Tears down the test fixture.
+//
+// A sub-class may override this.
+void Test::TearDown() {
+}
+
+// Allows user supplied key value pairs to be recorded for later output.
+void Test::RecordProperty(const char* key, const char* value) {
+ UnitTest::GetInstance()->RecordPropertyForCurrentTest(key, value);
+}
+
+// Allows user supplied key value pairs to be recorded for later output.
+void Test::RecordProperty(const char* key, int value) {
+ Message value_message;
+ value_message << value;
+ RecordProperty(key, value_message.GetString().c_str());
+}
+
+namespace internal {
+
+void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
+ const String& message) {
+ // This function is a friend of UnitTest and as such has access to
+ // AddTestPartResult.
+ UnitTest::GetInstance()->AddTestPartResult(
+ result_type,
+ NULL, // No info about the source file where the exception occurred.
+ -1, // We have no info on which line caused the exception.
+ message,
+ String()); // No stack trace, either.
+}
+
+} // namespace internal
+
+// Google Test requires all tests in the same test case to use the same test
+// fixture class. This function checks if the current test has the
+// same fixture class as the first test in the current test case. If
+// yes, it returns true; otherwise it generates a Google Test failure and
+// returns false.
+bool Test::HasSameFixtureClass() {
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ const TestCase* const test_case = impl->current_test_case();
+
+ // Info about the first test in the current test case.
+ const TestInfo* const first_test_info = test_case->test_info_list()[0];
+ const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_;
+ const char* const first_test_name = first_test_info->name();
+
+ // Info about the current test.
+ const TestInfo* const this_test_info = impl->current_test_info();
+ const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_;
+ const char* const this_test_name = this_test_info->name();
+
+ if (this_fixture_id != first_fixture_id) {
+ // Is the first test defined using TEST?
+ const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId();
+ // Is this test defined using TEST?
+ const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId();
+
+ if (first_is_TEST || this_is_TEST) {
+ // The user mixed TEST and TEST_F in this test case - we'll tell
+ // him/her how to fix it.
+
+ // Gets the name of the TEST and the name of the TEST_F. Note
+ // that first_is_TEST and this_is_TEST cannot both be true, as
+ // the fixture IDs are different for the two tests.
+ const char* const TEST_name =
+ first_is_TEST ? first_test_name : this_test_name;
+ const char* const TEST_F_name =
+ first_is_TEST ? this_test_name : first_test_name;
+
+ ADD_FAILURE()
+ << "All tests in the same test case must use the same test fixture\n"
+ << "class, so mixing TEST_F and TEST in the same test case is\n"
+ << "illegal. In test case " << this_test_info->test_case_name()
+ << ",\n"
+ << "test " << TEST_F_name << " is defined using TEST_F but\n"
+ << "test " << TEST_name << " is defined using TEST. You probably\n"
+ << "want to change the TEST to TEST_F or move it to another test\n"
+ << "case.";
+ } else {
+ // The user defined two fixture classes with the same name in
+ // two namespaces - we'll tell him/her how to fix it.
+ ADD_FAILURE()
+ << "All tests in the same test case must use the same test fixture\n"
+ << "class. However, in test case "
+ << this_test_info->test_case_name() << ",\n"
+ << "you defined test " << first_test_name
+ << " and test " << this_test_name << "\n"
+ << "using two different test fixture classes. This can happen if\n"
+ << "the two classes are from different namespaces or translation\n"
+ << "units and have the same name. You should probably rename one\n"
+ << "of the classes to put the tests into different test cases.";
+ }
+ return false;
+ }
+
+ return true;
+}
+
+#if GTEST_HAS_SEH
+
+// Adds an "exception thrown" fatal failure to the current test. This
+// function returns its result via an output parameter pointer because VC++
+// prohibits creation of objects with destructors on stack in functions
+// using __try (see error C2712).
+static internal::String* FormatSehExceptionMessage(DWORD exception_code,
+ const char* location) {
+ Message message;
+ message << "SEH exception with code 0x" << std::setbase(16) <<
+ exception_code << std::setbase(10) << " thrown in " << location << ".";
+
+ return new internal::String(message.GetString());
+}
+
+#endif // GTEST_HAS_SEH
+
+#if GTEST_HAS_EXCEPTIONS
+
+// Adds an "exception thrown" fatal failure to the current test.
+static internal::String FormatCxxExceptionMessage(const char* description,
+ const char* location) {
+ Message message;
+ if (description != NULL) {
+ message << "C++ exception with description \"" << description << "\"";
+ } else {
+ message << "Unknown C++ exception";
+ }
+ message << " thrown in " << location << ".";
+
+ return message.GetString();
+}
+
+static internal::String PrintTestPartResultToString(
+ const TestPartResult& test_part_result);
+
+// A failed Google Test assertion will throw an exception of this type when
+// GTEST_FLAG(throw_on_failure) is true (if exceptions are enabled). We
+// derive it from std::runtime_error, which is for errors presumably
+// detectable only at run time. Since std::runtime_error inherits from
+// std::exception, many testing frameworks know how to extract and print the
+// message inside it.
+class GoogleTestFailureException : public ::std::runtime_error {
+ public:
+ explicit GoogleTestFailureException(const TestPartResult& failure)
+ : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {}
+};
+#endif // GTEST_HAS_EXCEPTIONS
+
+namespace internal {
+// We put these helper functions in the internal namespace as IBM's xlC
+// compiler rejects the code if they were declared static.
+
+// Runs the given method and handles SEH exceptions it throws, when
+// SEH is supported; returns the 0-value for type Result in case of an
+// SEH exception. (Microsoft compilers cannot handle SEH and C++
+// exceptions in the same function. Therefore, we provide a separate
+// wrapper function for handling SEH exceptions.)
+template <class T, typename Result>
+Result HandleSehExceptionsInMethodIfSupported(
+ T* object, Result (T::*method)(), const char* location) {
+#if GTEST_HAS_SEH
+ __try {
+ return (object->*method)();
+ } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT
+ GetExceptionCode())) {
+ // We create the exception message on the heap because VC++ prohibits
+ // creation of objects with destructors on stack in functions using __try
+ // (see error C2712).
+ internal::String* exception_message = FormatSehExceptionMessage(
+ GetExceptionCode(), location);
+ internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure,
+ *exception_message);
+ delete exception_message;
+ return static_cast<Result>(0);
+ }
+#else
+ (void)location;
+ return (object->*method)();
+#endif // GTEST_HAS_SEH
+}
+
+// Runs the given method and catches and reports C++ and/or SEH-style
+// exceptions, if they are supported; returns the 0-value for type
+// Result in case of an SEH exception.
+template <class T, typename Result>
+Result HandleExceptionsInMethodIfSupported(
+ T* object, Result (T::*method)(), const char* location) {
+ // NOTE: The user code can affect the way in which Google Test handles
+ // exceptions by setting GTEST_FLAG(catch_exceptions), but only before
+ // RUN_ALL_TESTS() starts. It is technically possible to check the flag
+ // after the exception is caught and either report or re-throw the
+ // exception based on the flag's value:
+ //
+ // try {
+ // // Perform the test method.
+ // } catch (...) {
+ // if (GTEST_FLAG(catch_exceptions))
+ // // Report the exception as failure.
+ // else
+ // throw; // Re-throws the original exception.
+ // }
+ //
+ // However, the purpose of this flag is to allow the program to drop into
+ // the debugger when the exception is thrown. On most platforms, once the
+ // control enters the catch block, the exception origin information is
+ // lost and the debugger will stop the program at the point of the
+ // re-throw in this function -- instead of at the point of the original
+ // throw statement in the code under test. For this reason, we perform
+ // the check early, sacrificing the ability to affect Google Test's
+ // exception handling in the method where the exception is thrown.
+ if (internal::GetUnitTestImpl()->catch_exceptions()) {
+#if GTEST_HAS_EXCEPTIONS
+ try {
+ return HandleSehExceptionsInMethodIfSupported(object, method, location);
+ } catch (const GoogleTestFailureException&) { // NOLINT
+ // This exception doesn't originate in code under test. It makes no
+ // sense to report it as a test failure.
+ throw;
+ } catch (const std::exception& e) { // NOLINT
+ internal::ReportFailureInUnknownLocation(
+ TestPartResult::kFatalFailure,
+ FormatCxxExceptionMessage(e.what(), location));
+ } catch (...) { // NOLINT
+ internal::ReportFailureInUnknownLocation(
+ TestPartResult::kFatalFailure,
+ FormatCxxExceptionMessage(NULL, location));
+ }
+ return static_cast<Result>(0);
+#else
+ return HandleSehExceptionsInMethodIfSupported(object, method, location);
+#endif // GTEST_HAS_EXCEPTIONS
+ } else {
+ return (object->*method)();
+ }
+}
+
+} // namespace internal
+
+// Runs the test and updates the test result.
+void Test::Run() {
+ if (!HasSameFixtureClass()) return;
+
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()");
+ // We will run the test only if SetUp() was successful.
+ if (!HasFatalFailure()) {
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &Test::TestBody, "the test body");
+ }
+
+ // However, we want to clean up as much as possible. Hence we will
+ // always call TearDown(), even if SetUp() or the test body has
+ // failed.
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &Test::TearDown, "TearDown()");
+}
+
+// Returns true iff the current test has a fatal failure.
+bool Test::HasFatalFailure() {
+ return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure();
+}
+
+// Returns true iff the current test has a non-fatal failure.
+bool Test::HasNonfatalFailure() {
+ return internal::GetUnitTestImpl()->current_test_result()->
+ HasNonfatalFailure();
+}
+
+// class TestInfo
+
+// Constructs a TestInfo object. It assumes ownership of the test factory
+// object.
+// TODO(***@google.com): Make a_test_case_name and a_name const string&'s
+// to signify they cannot be NULLs.
+TestInfo::TestInfo(const char* a_test_case_name,
+ const char* a_name,
+ const char* a_type_param,
+ const char* a_value_param,
+ internal::TypeId fixture_class_id,
+ internal::TestFactoryBase* factory)
+ : test_case_name_(a_test_case_name),
+ name_(a_name),
+ type_param_(a_type_param ? new std::string(a_type_param) : NULL),
+ value_param_(a_value_param ? new std::string(a_value_param) : NULL),
+ fixture_class_id_(fixture_class_id),
+ should_run_(false),
+ is_disabled_(false),
+ matches_filter_(false),
+ factory_(factory),
+ result_() {}
+
+// Destructs a TestInfo object.
+TestInfo::~TestInfo() { delete factory_; }
+
+namespace internal {
+
+// Creates a new TestInfo object and registers it with Google Test;
+// returns the created object.
+//
+// Arguments:
+//
+// test_case_name: name of the test case
+// name: name of the test
+// type_param: the name of the test's type parameter, or NULL if
+// this is not a typed or a type-parameterized test.
+// value_param: text representation of the test's value parameter,
+// or NULL if this is not a value-parameterized test.
+// fixture_class_id: ID of the test fixture class
+// set_up_tc: pointer to the function that sets up the test case
+// tear_down_tc: pointer to the function that tears down the test case
+// factory: pointer to the factory that creates a test object.
+// The newly created TestInfo instance will assume
+// ownership of the factory object.
+TestInfo* MakeAndRegisterTestInfo(
+ const char* test_case_name, const char* name,
+ const char* type_param,
+ const char* value_param,
+ TypeId fixture_class_id,
+ SetUpTestCaseFunc set_up_tc,
+ TearDownTestCaseFunc tear_down_tc,
+ TestFactoryBase* factory) {
+ TestInfo* const test_info =
+ new TestInfo(test_case_name, name, type_param, value_param,
+ fixture_class_id, factory);
+ GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
+ return test_info;
+}
+
+#if GTEST_HAS_PARAM_TEST
+void ReportInvalidTestCaseType(const char* test_case_name,
+ const char* file, int line) {
+ Message errors;
+ errors
+ << "Attempted redefinition of test case " << test_case_name << ".\n"
+ << "All tests in the same test case must use the same test fixture\n"
+ << "class. However, in test case " << test_case_name << ", you tried\n"
+ << "to define a test using a fixture class different from the one\n"
+ << "used earlier. This can happen if the two fixture classes are\n"
+ << "from different namespaces and have the same name. You should\n"
+ << "probably rename one of the classes to put the tests into different\n"
+ << "test cases.";
+
+ fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(),
+ errors.GetString().c_str());
+}
+#endif // GTEST_HAS_PARAM_TEST
+
+} // namespace internal
+
+namespace {
+
+// A predicate that checks the test name of a TestInfo against a known
+// value.
+//
+// This is used for implementation of the TestCase class only. We put
+// it in the anonymous namespace to prevent polluting the outer
+// namespace.
+//
+// TestNameIs is copyable.
+class TestNameIs {
+ public:
+ // Constructor.
+ //
+ // TestNameIs has NO default constructor.
+ explicit TestNameIs(const char* name)
+ : name_(name) {}
+
+ // Returns true iff the test name of test_info matches name_.
+ bool operator()(const TestInfo * test_info) const {
+ return test_info && internal::String(test_info->name()).Compare(name_) == 0;
+ }
+
+ private:
+ internal::String name_;
+};
+
+} // namespace
+
+namespace internal {
+
+// This method expands all parameterized tests registered with macros TEST_P
+// and INSTANTIATE_TEST_CASE_P into regular tests and registers those.
+// This will be done just once during the program runtime.
+void UnitTestImpl::RegisterParameterizedTests() {
+#if GTEST_HAS_PARAM_TEST
+ if (!parameterized_tests_registered_) {
+ parameterized_test_registry_.RegisterTests();
+ parameterized_tests_registered_ = true;
+ }
+#endif
+}
+
+} // namespace internal
+
+// Creates the test object, runs it, records its result, and then
+// deletes it.
+void TestInfo::Run() {
+ if (!should_run_) return;
+
+ // Tells UnitTest where to store test result.
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ impl->set_current_test_info(this);
+
+ TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();
+
+ // Notifies the unit test event listeners that a test is about to start.
+ repeater->OnTestStart(*this);
+
+ const TimeInMillis start = internal::GetTimeInMillis();
+
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+
+ // Creates the test object.
+ Test* const test = internal::HandleExceptionsInMethodIfSupported(
+ factory_, &internal::TestFactoryBase::CreateTest,
+ "the test fixture's constructor");
+
+ // Runs the test only if the test object was created and its
+ // constructor didn't generate a fatal failure.
+ if ((test != NULL) && !Test::HasFatalFailure()) {
+ // This doesn't throw as all user code that can throw are wrapped into
+ // exception handling code.
+ test->Run();
+ }
+
+ // Deletes the test object.
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ test, &Test::DeleteSelf_, "the test fixture's destructor");
+
+ result_.set_elapsed_time(internal::GetTimeInMillis() - start);
+
+ // Notifies the unit test event listener that a test has just finished.
+ repeater->OnTestEnd(*this);
+
+ // Tells UnitTest to stop associating assertion results to this
+ // test.
+ impl->set_current_test_info(NULL);
+}
+
+// class TestCase
+
+// Gets the number of successful tests in this test case.
+int TestCase::successful_test_count() const {
+ return CountIf(test_info_list_, TestPassed);
+}
+
+// Gets the number of failed tests in this test case.
+int TestCase::failed_test_count() const {
+ return CountIf(test_info_list_, TestFailed);
+}
+
+int TestCase::disabled_test_count() const {
+ return CountIf(test_info_list_, TestDisabled);
+}
+
+// Get the number of tests in this test case that should run.
+int TestCase::test_to_run_count() const {
+ return CountIf(test_info_list_, ShouldRunTest);
+}
+
+// Gets the number of all tests.
+int TestCase::total_test_count() const {
+ return static_cast<int>(test_info_list_.size());
+}
+
+// Creates a TestCase with the given name.
+//
+// Arguments:
+//
+// name: name of the test case
+// a_type_param: the name of the test case's type parameter, or NULL if
+// this is not a typed or a type-parameterized test case.
+// set_up_tc: pointer to the function that sets up the test case
+// tear_down_tc: pointer to the function that tears down the test case
+TestCase::TestCase(const char* a_name, const char* a_type_param,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc)
+ : name_(a_name),
+ type_param_(a_type_param ? new std::string(a_type_param) : NULL),
+ set_up_tc_(set_up_tc),
+ tear_down_tc_(tear_down_tc),
+ should_run_(false),
+ elapsed_time_(0) {
+}
+
+// Destructor of TestCase.
+TestCase::~TestCase() {
+ // Deletes every Test in the collection.
+ ForEach(test_info_list_, internal::Delete<TestInfo>);
+}
+
+// Returns the i-th test among all the tests. i can range from 0 to
+// total_test_count() - 1. If i is not in that range, returns NULL.
+const TestInfo* TestCase::GetTestInfo(int i) const {
+ const int index = GetElementOr(test_indices_, i, -1);
+ return index < 0 ? NULL : test_info_list_[index];
+}
+
+// Returns the i-th test among all the tests. i can range from 0 to
+// total_test_count() - 1. If i is not in that range, returns NULL.
+TestInfo* TestCase::GetMutableTestInfo(int i) {
+ const int index = GetElementOr(test_indices_, i, -1);
+ return index < 0 ? NULL : test_info_list_[index];
+}
+
+// Adds a test to this test case. Will delete the test upon
+// destruction of the TestCase object.
+void TestCase::AddTestInfo(TestInfo * test_info) {
+ test_info_list_.push_back(test_info);
+ test_indices_.push_back(static_cast<int>(test_indices_.size()));
+}
+
+// Runs every test in this TestCase.
+void TestCase::Run() {
+ if (!should_run_) return;
+
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ impl->set_current_test_case(this);
+
+ TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();
+
+ repeater->OnTestCaseStart(*this);
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &TestCase::RunSetUpTestCase, "SetUpTestCase()");
+
+ const internal::TimeInMillis start = internal::GetTimeInMillis();
+ for (int i = 0; i < total_test_count(); i++) {
+ GetMutableTestInfo(i)->Run();
+ }
+ elapsed_time_ = internal::GetTimeInMillis() - start;
+
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &TestCase::RunTearDownTestCase, "TearDownTestCase()");
+
+ repeater->OnTestCaseEnd(*this);
+ impl->set_current_test_case(NULL);
+}
+
+// Clears the results of all tests in this test case.
+void TestCase::ClearResult() {
+ ForEach(test_info_list_, TestInfo::ClearTestResult);
+}
+
+// Shuffles the tests in this test case.
+void TestCase::ShuffleTests(internal::Random* random) {
+ Shuffle(random, &test_indices_);
+}
+
+// Restores the test order to before the first shuffle.
+void TestCase::UnshuffleTests() {
+ for (size_t i = 0; i < test_indices_.size(); i++) {
+ test_indices_[i] = static_cast<int>(i);
+ }
+}
+
+// Formats a countable noun. Depending on its quantity, either the
+// singular form or the plural form is used. e.g.
+//
+// FormatCountableNoun(1, "formula", "formuli") returns "1 formula".
+// FormatCountableNoun(5, "book", "books") returns "5 books".
+static internal::String FormatCountableNoun(int count,
+ const char * singular_form,
+ const char * plural_form) {
+ return internal::String::Format("%d %s", count,
+ count == 1 ? singular_form : plural_form);
+}
+
+// Formats the count of tests.
+static internal::String FormatTestCount(int test_count) {
+ return FormatCountableNoun(test_count, "test", "tests");
+}
+
+// Formats the count of test cases.
+static internal::String FormatTestCaseCount(int test_case_count) {
+ return FormatCountableNoun(test_case_count, "test case", "test cases");
+}
+
+// Converts a TestPartResult::Type enum to human-friendly string
+// representation. Both kNonFatalFailure and kFatalFailure are translated
+// to "Failure", as the user usually doesn't care about the difference
+// between the two when viewing the test result.
+static const char * TestPartResultTypeToString(TestPartResult::Type type) {
+ switch (type) {
+ case TestPartResult::kSuccess:
+ return "Success";
+
+ case TestPartResult::kNonFatalFailure:
+ case TestPartResult::kFatalFailure:
+#ifdef _MSC_VER
+ return "error: ";
+#else
+ return "Failure\n";
+#endif
+ default:
+ return "Unknown result type";
+ }
+}
+
+// Prints a TestPartResult to a String.
+static internal::String PrintTestPartResultToString(
+ const TestPartResult& test_part_result) {
+ return (Message()
+ << internal::FormatFileLocation(test_part_result.file_name(),
+ test_part_result.line_number())
+ << " " << TestPartResultTypeToString(test_part_result.type())
+ << test_part_result.message()).GetString();
+}
+
+// Prints a TestPartResult.
+static void PrintTestPartResult(const TestPartResult& test_part_result) {
+ const internal::String& result =
+ PrintTestPartResultToString(test_part_result);
+ printf("%s\n", result.c_str());
+ fflush(stdout);
+ // If the test program runs in Visual Studio or a debugger, the
+ // following statements add the test part result message to the Output
+ // window such that the user can double-click on it to jump to the
+ // corresponding source code location; otherwise they do nothing.
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+ // We don't call OutputDebugString*() on Windows Mobile, as printing
+ // to stdout is done by OutputDebugString() there already - we don't
+ // want the same message printed twice.
+ ::OutputDebugStringA(result.c_str());
+ ::OutputDebugStringA("\n");
+#endif
+}
+
+// class PrettyUnitTestResultPrinter
+
+namespace internal {
+
+enum GTestColor {
+ COLOR_DEFAULT,
+ COLOR_RED,
+ COLOR_GREEN,
+ COLOR_YELLOW
+};
+
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+
+// Returns the character attribute for the given color.
+WORD GetColorAttribute(GTestColor color) {
+ switch (color) {
+ case COLOR_RED: return FOREGROUND_RED;
+ case COLOR_GREEN: return FOREGROUND_GREEN;
+ case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN;
+ default: return 0;
+ }
+}
+
+#else
+
+// Returns the ANSI color code for the given color. COLOR_DEFAULT is
+// an invalid input.
+const char* GetAnsiColorCode(GTestColor color) {
+ switch (color) {
+ case COLOR_RED: return "1";
+ case COLOR_GREEN: return "2";
+ case COLOR_YELLOW: return "3";
+ default: return NULL;
+ };
+}
+
+#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+
+// Returns true iff Google Test should use colors in the output.
+bool ShouldUseColor(bool stdout_is_tty) {
+ const char* const gtest_color = GTEST_FLAG(color).c_str();
+
+ if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) {
+#if GTEST_OS_WINDOWS
+ // On Windows the TERM variable is usually not set, but the
+ // console there does support colors.
+ return stdout_is_tty;
+#else
+ // On non-Windows platforms, we rely on the TERM variable.
+ const char* const term = posix::GetEnv("TERM");
+ const bool term_supports_color =
+ String::CStringEquals(term, "xterm") ||
+ String::CStringEquals(term, "xterm-color") ||
+ String::CStringEquals(term, "xterm-256color") ||
+ String::CStringEquals(term, "screen") ||
+ String::CStringEquals(term, "linux") ||
+ String::CStringEquals(term, "cygwin");
+ return stdout_is_tty && term_supports_color;
+#endif // GTEST_OS_WINDOWS
+ }
+
+ return String::CaseInsensitiveCStringEquals(gtest_color, "yes") ||
+ String::CaseInsensitiveCStringEquals(gtest_color, "true") ||
+ String::CaseInsensitiveCStringEquals(gtest_color, "t") ||
+ String::CStringEquals(gtest_color, "1");
+ // We take "yes", "true", "t", and "1" as meaning "yes". If the
+ // value is neither one of these nor "auto", we treat it as "no" to
+ // be conservative.
+}
+
+// Helpers for printing colored strings to stdout. Note that on Windows, we
+// cannot simply emit special characters and have the terminal change colors.
+// This routine must actually emit the characters rather than return a string
+// that would be colored when printed, as can be done on Linux.
+void ColoredPrintf(GTestColor color, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS
+ const bool use_color = false;
+#else
+ static const bool in_color_mode =
+ ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0);
+ const bool use_color = in_color_mode && (color != COLOR_DEFAULT);
+#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS
+ // The '!= 0' comparison is necessary to satisfy MSVC 7.1.
+
+ if (!use_color) {
+ vprintf(fmt, args);
+ va_end(args);
+ return;
+ }
+
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+ const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ // Gets the current text color.
+ CONSOLE_SCREEN_BUFFER_INFO buffer_info;
+ GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
+ const WORD old_color_attrs = buffer_info.wAttributes;
+
+ // We need to flush the stream buffers into the console before each
+ // SetConsoleTextAttribute call lest it affect the text that is already
+ // printed but has not yet reached the console.
+ fflush(stdout);
+ SetConsoleTextAttribute(stdout_handle,
+ GetColorAttribute(color) | FOREGROUND_INTENSITY);
+ vprintf(fmt, args);
+
+ fflush(stdout);
+ // Restores the text color.
+ SetConsoleTextAttribute(stdout_handle, old_color_attrs);
+#else
+ printf("\033[0;3%sm", GetAnsiColorCode(color));
+ vprintf(fmt, args);
+ printf("\033[m"); // Resets the terminal to default.
+#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+ va_end(args);
+}
+
+void PrintFullTestCommentIfPresent(const TestInfo& test_info) {
+ const char* const type_param = test_info.type_param();
+ const char* const value_param = test_info.value_param();
+
+ if (type_param != NULL || value_param != NULL) {
+ printf(", where ");
+ if (type_param != NULL) {
+ printf("TypeParam = %s", type_param);
+ if (value_param != NULL)
+ printf(" and ");
+ }
+ if (value_param != NULL) {
+ printf("GetParam() = %s", value_param);
+ }
+ }
+}
+
+// This class implements the TestEventListener interface.
+//
+// Class PrettyUnitTestResultPrinter is copyable.
+class PrettyUnitTestResultPrinter : public TestEventListener {
+ public:
+ PrettyUnitTestResultPrinter() {}
+ static void PrintTestName(const char * test_case, const char * test) {
+ printf("%s.%s", test_case, test);
+ }
+
+ // The following methods override what's in the TestEventListener class.
+ virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration);
+ virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestCaseStart(const TestCase& test_case);
+ virtual void OnTestStart(const TestInfo& test_info);
+ virtual void OnTestPartResult(const TestPartResult& result);
+ virtual void OnTestEnd(const TestInfo& test_info);
+ virtual void OnTestCaseEnd(const TestCase& test_case);
+ virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+ virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {}
+
+ private:
+ static void PrintFailedTests(const UnitTest& unit_test);
+
+ internal::String test_case_name_;
+};
+
+ // Fired before each iteration of tests starts.
+void PrettyUnitTestResultPrinter::OnTestIterationStart(
+ const UnitTest& unit_test, int iteration) {
+ if (GTEST_FLAG(repeat) != 1)
+ printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1);
+
+ const char* const filter = GTEST_FLAG(filter).c_str();
+
+ // Prints the filter if it's not *. This reminds the user that some
+ // tests may be skipped.
+ if (!internal::String::CStringEquals(filter, kUniversalFilter)) {
+ ColoredPrintf(COLOR_YELLOW,
+ "Note: %s filter = %s\n", GTEST_NAME_, filter);
+ }
+
+ if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) {
+ const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1);
+ ColoredPrintf(COLOR_YELLOW,
+ "Note: This is test shard %d of %s.\n",
+ static_cast<int>(shard_index) + 1,
+ internal::posix::GetEnv(kTestTotalShards));
+ }
+
+ if (GTEST_FLAG(shuffle)) {
+ ColoredPrintf(COLOR_YELLOW,
+ "Note: Randomizing tests' orders with a seed of %d .\n",
+ unit_test.random_seed());
+ }
+
+ ColoredPrintf(COLOR_GREEN, "[==========] ");
+ printf("Running %s from %s.\n",
+ FormatTestCount(unit_test.test_to_run_count()).c_str(),
+ FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str());
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart(
+ const UnitTest& /*unit_test*/) {
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("Global test environment set-up.\n");
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) {
+ test_case_name_ = test_case.name();
+ const internal::String counts =
+ FormatCountableNoun(test_case.test_to_run_count(), "test", "tests");
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("%s from %s", counts.c_str(), test_case_name_.c_str());
+ if (test_case.type_param() == NULL) {
+ printf("\n");
+ } else {
+ printf(", where TypeParam = %s\n", test_case.type_param());
+ }
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) {
+ ColoredPrintf(COLOR_GREEN, "[ RUN ] ");
+ PrintTestName(test_case_name_.c_str(), test_info.name());
+ printf("\n");
+ fflush(stdout);
+}
+
+// Called after an assertion failure.
+void PrettyUnitTestResultPrinter::OnTestPartResult(
+ const TestPartResult& result) {
+ // If the test part succeeded, we don't need to do anything.
+ if (result.type() == TestPartResult::kSuccess)
+ return;
+
+ // Print failure message from the assertion (e.g. expected this and got that).
+ PrintTestPartResult(result);
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) {
+ if (test_info.result()->Passed()) {
+ ColoredPrintf(COLOR_GREEN, "[ OK ] ");
+ } else {
+ ColoredPrintf(COLOR_RED, "[ FAILED ] ");
+ }
+ PrintTestName(test_case_name_.c_str(), test_info.name());
+ if (test_info.result()->Failed())
+ PrintFullTestCommentIfPresent(test_info);
+
+ if (GTEST_FLAG(print_time)) {
+ printf(" (%s ms)\n", internal::StreamableToString(
+ test_info.result()->elapsed_time()).c_str());
+ } else {
+ printf("\n");
+ }
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) {
+ if (!GTEST_FLAG(print_time)) return;
+
+ test_case_name_ = test_case.name();
+ const internal::String counts =
+ FormatCountableNoun(test_case.test_to_run_count(), "test", "tests");
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("%s from %s (%s ms total)\n\n",
+ counts.c_str(), test_case_name_.c_str(),
+ internal::StreamableToString(test_case.elapsed_time()).c_str());
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart(
+ const UnitTest& /*unit_test*/) {
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("Global test environment tear-down\n");
+ fflush(stdout);
+}
+
+// Internal helper for printing the list of failed tests.
+void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) {
+ const int failed_test_count = unit_test.failed_test_count();
+ if (failed_test_count == 0) {
+ return;
+ }
+
+ for (int i = 0; i < unit_test.total_test_case_count(); ++i) {
+ const TestCase& test_case = *unit_test.GetTestCase(i);
+ if (!test_case.should_run() || (test_case.failed_test_count() == 0)) {
+ continue;
+ }
+ for (int j = 0; j < test_case.total_test_count(); ++j) {
+ const TestInfo& test_info = *test_case.GetTestInfo(j);
+ if (!test_info.should_run() || test_info.result()->Passed()) {
+ continue;
+ }
+ ColoredPrintf(COLOR_RED, "[ FAILED ] ");
+ printf("%s.%s", test_case.name(), test_info.name());
+ PrintFullTestCommentIfPresent(test_info);
+ printf("\n");
+ }
+ }
+}
+
+void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
+ int /*iteration*/) {
+ ColoredPrintf(COLOR_GREEN, "[==========] ");
+ printf("%s from %s ran.",
+ FormatTestCount(unit_test.test_to_run_count()).c_str(),
+ FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str());
+ if (GTEST_FLAG(print_time)) {
+ printf(" (%s ms total)",
+ internal::StreamableToString(unit_test.elapsed_time()).c_str());
+ }
+ printf("\n");
+ ColoredPrintf(COLOR_GREEN, "[ PASSED ] ");
+ printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str());
+
+ int num_failures = unit_test.failed_test_count();
+ if (!unit_test.Passed()) {
+ const int failed_test_count = unit_test.failed_test_count();
+ ColoredPrintf(COLOR_RED, "[ FAILED ] ");
+ printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str());
+ PrintFailedTests(unit_test);
+ printf("\n%2d FAILED %s\n", num_failures,
+ num_failures == 1 ? "TEST" : "TESTS");
+ }
+
+ int num_disabled = unit_test.disabled_test_count();
+ if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) {
+ if (!num_failures) {
+ printf("\n"); // Add a spacer if no FAILURE banner is displayed.
+ }
+ ColoredPrintf(COLOR_YELLOW,
+ " YOU HAVE %d DISABLED %s\n\n",
+ num_disabled,
+ num_disabled == 1 ? "TEST" : "TESTS");
+ }
+ // Ensure that Google Test output is printed before, e.g., heapchecker output.
+ fflush(stdout);
+}
+
+// End PrettyUnitTestResultPrinter
+
+// class TestEventRepeater
+//
+// This class forwards events to other event listeners.
+class TestEventRepeater : public TestEventListener {
+ public:
+ TestEventRepeater() : forwarding_enabled_(true) {}
+ virtual ~TestEventRepeater();
+ void Append(TestEventListener *listener);
+ TestEventListener* Release(TestEventListener* listener);
+
+ // Controls whether events will be forwarded to listeners_. Set to false
+ // in death test child processes.
+ bool forwarding_enabled() const { return forwarding_enabled_; }
+ void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; }
+
+ virtual void OnTestProgramStart(const UnitTest& unit_test);
+ virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration);
+ virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test);
+ virtual void OnTestCaseStart(const TestCase& test_case);
+ virtual void OnTestStart(const TestInfo& test_info);
+ virtual void OnTestPartResult(const TestPartResult& result);
+ virtual void OnTestEnd(const TestInfo& test_info);
+ virtual void OnTestCaseEnd(const TestCase& test_case);
+ virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test);
+ virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+ virtual void OnTestProgramEnd(const UnitTest& unit_test);
+
+ private:
+ // Controls whether events will be forwarded to listeners_. Set to false
+ // in death test child processes.
+ bool forwarding_enabled_;
+ // The list of listeners that receive events.
+ std::vector<TestEventListener*> listeners_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater);
+};
+
+TestEventRepeater::~TestEventRepeater() {
+ ForEach(listeners_, Delete<TestEventListener>);
+}
+
+void TestEventRepeater::Append(TestEventListener *listener) {
+ listeners_.push_back(listener);
+}
+
+// TODO(***@google.com): Factor the search functionality into Vector::Find.
+TestEventListener* TestEventRepeater::Release(TestEventListener *listener) {
+ for (size_t i = 0; i < listeners_.size(); ++i) {
+ if (listeners_[i] == listener) {
+ listeners_.erase(listeners_.begin() + i);
+ return listener;
+ }
+ }
+
+ return NULL;
+}
+
+// Since most methods are very similar, use macros to reduce boilerplate.
+// This defines a member that forwards the call to all listeners.
+#define GTEST_REPEATER_METHOD_(Name, Type) \
+void TestEventRepeater::Name(const Type& parameter) { \
+ if (forwarding_enabled_) { \
+ for (size_t i = 0; i < listeners_.size(); i++) { \
+ listeners_[i]->Name(parameter); \
+ } \
+ } \
+}
+// This defines a member that forwards the call to all listeners in reverse
+// order.
+#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \
+void TestEventRepeater::Name(const Type& parameter) { \
+ if (forwarding_enabled_) { \
+ for (int i = static_cast<int>(listeners_.size()) - 1; i >= 0; i--) { \
+ listeners_[i]->Name(parameter); \
+ } \
+ } \
+}
+
+GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest)
+GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest)
+GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase)
+GTEST_REPEATER_METHOD_(OnTestStart, TestInfo)
+GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult)
+GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest)
+GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest)
+GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest)
+GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo)
+GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase)
+GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest)
+
+#undef GTEST_REPEATER_METHOD_
+#undef GTEST_REVERSE_REPEATER_METHOD_
+
+void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test,
+ int iteration) {
+ if (forwarding_enabled_) {
+ for (size_t i = 0; i < listeners_.size(); i++) {
+ listeners_[i]->OnTestIterationStart(unit_test, iteration);
+ }
+ }
+}
+
+void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test,
+ int iteration) {
+ if (forwarding_enabled_) {
+ for (int i = static_cast<int>(listeners_.size()) - 1; i >= 0; i--) {
+ listeners_[i]->OnTestIterationEnd(unit_test, iteration);
+ }
+ }
+}
+
+// End TestEventRepeater
+
+// This class generates an XML output file.
+class XmlUnitTestResultPrinter : public EmptyTestEventListener {
+ public:
+ explicit XmlUnitTestResultPrinter(const char* output_file);
+
+ virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+
+ private:
+ // Is c a whitespace character that is normalized to a space character
+ // when it appears in an XML attribute value?
+ static bool IsNormalizableWhitespace(char c) {
+ return c == 0x9 || c == 0xA || c == 0xD;
+ }
+
+ // May c appear in a well-formed XML document?
+ static bool IsValidXmlCharacter(char c) {
+ return IsNormalizableWhitespace(c) || c >= 0x20;
+ }
+
+ // Returns an XML-escaped copy of the input string str. If
+ // is_attribute is true, the text is meant to appear as an attribute
+ // value, and normalizable whitespace is preserved by replacing it
+ // with character references.
+ static String EscapeXml(const char* str, bool is_attribute);
+
+ // Returns the given string with all characters invalid in XML removed.
+ static string RemoveInvalidXmlCharacters(const string& str);
+
+ // Convenience wrapper around EscapeXml when str is an attribute value.
+ static String EscapeXmlAttribute(const char* str) {
+ return EscapeXml(str, true);
+ }
+
+ // Convenience wrapper around EscapeXml when str is not an attribute value.
+ static String EscapeXmlText(const char* str) { return EscapeXml(str, false); }
+
+ // Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
+ static void OutputXmlCDataSection(::std::ostream* stream, const char* data);
+
+ // Streams an XML representation of a TestInfo object.
+ static void OutputXmlTestInfo(::std::ostream* stream,
+ const char* test_case_name,
+ const TestInfo& test_info);
+
+ // Prints an XML representation of a TestCase object
+ static void PrintXmlTestCase(FILE* out, const TestCase& test_case);
+
+ // Prints an XML summary of unit_test to output stream out.
+ static void PrintXmlUnitTest(FILE* out, const UnitTest& unit_test);
+
+ // Produces a string representing the test properties in a result as space
+ // delimited XML attributes based on the property key="value" pairs.
+ // When the String is not empty, it includes a space at the beginning,
+ // to delimit this attribute from prior attributes.
+ static String TestPropertiesAsXmlAttributes(const TestResult& result);
+
+ // The output file.
+ const String output_file_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter);
+};
+
+// Creates a new XmlUnitTestResultPrinter.
+XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file)
+ : output_file_(output_file) {
+ if (output_file_.c_str() == NULL || output_file_.empty()) {
+ fprintf(stderr, "XML output file may not be null\n");
+ fflush(stderr);
+ exit(EXIT_FAILURE);
+ }
+}
+
+// Called after the unit test ends.
+void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
+ int /*iteration*/) {
+ FILE* xmlout = NULL;
+ FilePath output_file(output_file_);
+ FilePath output_dir(output_file.RemoveFileName());
+
+ if (output_dir.CreateDirectoriesRecursively()) {
+ xmlout = posix::FOpen(output_file_.c_str(), "w");
+ }
+ if (xmlout == NULL) {
+ // TODO(wan): report the reason of the failure.
+ //
+ // We don't do it for now as:
+ //
+ // 1. There is no urgent need for it.
+ // 2. It's a bit involved to make the errno variable thread-safe on
+ // all three operating systems (Linux, Windows, and Mac OS).
+ // 3. To interpret the meaning of errno in a thread-safe way,
+ // we need the strerror_r() function, which is not available on
+ // Windows.
+ fprintf(stderr,
+ "Unable to open file \"%s\"\n",
+ output_file_.c_str());
+ fflush(stderr);
+ exit(EXIT_FAILURE);
+ }
+ PrintXmlUnitTest(xmlout, unit_test);
+ fclose(xmlout);
+}
+
+// Returns an XML-escaped copy of the input string str. If is_attribute
+// is true, the text is meant to appear as an attribute value, and
+// normalizable whitespace is preserved by replacing it with character
+// references.
+//
+// Invalid XML characters in str, if any, are stripped from the output.
+// It is expected that most, if not all, of the text processed by this
+// module will consist of ordinary English text.
+// If this module is ever modified to produce version 1.1 XML output,
+// most invalid characters can be retained using character references.
+// TODO(wan): It might be nice to have a minimally invasive, human-readable
+// escaping scheme for invalid characters, rather than dropping them.
+String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) {
+ Message m;
+
+ if (str != NULL) {
+ for (const char* src = str; *src; ++src) {
+ switch (*src) {
+ case '<':
+ m << "&lt;";
+ break;
+ case '>':
+ m << "&gt;";
+ break;
+ case '&':
+ m << "&amp;";
+ break;
+ case '\'':
+ if (is_attribute)
+ m << "&apos;";
+ else
+ m << '\'';
+ break;
+ case '"':
+ if (is_attribute)
+ m << "&quot;";
+ else
+ m << '"';
+ break;
+ default:
+ if (IsValidXmlCharacter(*src)) {
+ if (is_attribute && IsNormalizableWhitespace(*src))
+ m << String::Format("&#x%02X;", unsigned(*src));
+ else
+ m << *src;
+ }
+ break;
+ }
+ }
+ }
+
+ return m.GetString();
+}
+
+// Returns the given string with all characters invalid in XML removed.
+// Currently invalid characters are dropped from the string. An
+// alternative is to replace them with certain characters such as . or ?.
+string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(const string& str) {
+ string output;
+ output.reserve(str.size());
+ for (string::const_iterator it = str.begin(); it != str.end(); ++it)
+ if (IsValidXmlCharacter(*it))
+ output.push_back(*it);
+
+ return output;
+}
+
+// The following routines generate an XML representation of a UnitTest
+// object.
+//
+// This is how Google Test concepts map to the DTD:
+//
+// <testsuites name="AllTests"> <-- corresponds to a UnitTest object
+// <testsuite name="testcase-name"> <-- corresponds to a TestCase object
+// <testcase name="test-name"> <-- corresponds to a TestInfo object
+// <failure message="...">...</failure>
+// <failure message="...">...</failure>
+// <failure message="...">...</failure>
+// <-- individual assertion failures
+// </testcase>
+// </testsuite>
+// </testsuites>
+
+// Formats the given time in milliseconds as seconds.
+std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) {
+ ::std::stringstream ss;
+ ss << ms/1000.0;
+ return ss.str();
+}
+
+// Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
+void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream,
+ const char* data) {
+ const char* segment = data;
+ *stream << "<![CDATA[";
+ for (;;) {
+ const char* const next_segment = strstr(segment, "]]>");
+ if (next_segment != NULL) {
+ stream->write(
+ segment, static_cast<std::streamsize>(next_segment - segment));
+ *stream << "]]>]]&gt;<![CDATA[";
+ segment = next_segment + strlen("]]>");
+ } else {
+ *stream << segment;
+ break;
+ }
+ }
+ *stream << "]]>";
+}
+
+// Prints an XML representation of a TestInfo object.
+// TODO(wan): There is also value in printing properties with the plain printer.
+void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
+ const char* test_case_name,
+ const TestInfo& test_info) {
+ const TestResult& result = *test_info.result();
+ *stream << " <testcase name=\""
+ << EscapeXmlAttribute(test_info.name()).c_str() << "\"";
+
+ if (test_info.value_param() != NULL) {
+ *stream << " value_param=\"" << EscapeXmlAttribute(test_info.value_param())
+ << "\"";
+ }
+ if (test_info.type_param() != NULL) {
+ *stream << " type_param=\"" << EscapeXmlAttribute(test_info.type_param())
+ << "\"";
+ }
+
+ *stream << " status=\""
+ << (test_info.should_run() ? "run" : "notrun")
+ << "\" time=\""
+ << FormatTimeInMillisAsSeconds(result.elapsed_time())
+ << "\" classname=\"" << EscapeXmlAttribute(test_case_name).c_str()
+ << "\"" << TestPropertiesAsXmlAttributes(result).c_str();
+
+ int failures = 0;
+ for (int i = 0; i < result.total_part_count(); ++i) {
+ const TestPartResult& part = result.GetTestPartResult(i);
+ if (part.failed()) {
+ if (++failures == 1)
+ *stream << ">\n";
+ *stream << " <failure message=\""
+ << EscapeXmlAttribute(part.summary()).c_str()
+ << "\" type=\"\">";
+ const string location = internal::FormatCompilerIndependentFileLocation(
+ part.file_name(), part.line_number());
+ const string message = location + "\n" + part.message();
+ OutputXmlCDataSection(stream,
+ RemoveInvalidXmlCharacters(message).c_str());
+ *stream << "</failure>\n";
+ }
+ }
+
+ if (failures == 0)
+ *stream << " />\n";
+ else
+ *stream << " </testcase>\n";
+}
+
+// Prints an XML representation of a TestCase object
+void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out,
+ const TestCase& test_case) {
+ fprintf(out,
+ " <testsuite name=\"%s\" tests=\"%d\" failures=\"%d\" "
+ "disabled=\"%d\" ",
+ EscapeXmlAttribute(test_case.name()).c_str(),
+ test_case.total_test_count(),
+ test_case.failed_test_count(),
+ test_case.disabled_test_count());
+ fprintf(out,
+ "errors=\"0\" time=\"%s\">\n",
+ FormatTimeInMillisAsSeconds(test_case.elapsed_time()).c_str());
+ for (int i = 0; i < test_case.total_test_count(); ++i) {
+ ::std::stringstream stream;
+ OutputXmlTestInfo(&stream, test_case.name(), *test_case.GetTestInfo(i));
+ fprintf(out, "%s", StringStreamToString(&stream).c_str());
+ }
+ fprintf(out, " </testsuite>\n");
+}
+
+// Prints an XML summary of unit_test to output stream out.
+void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out,
+ const UnitTest& unit_test) {
+ fprintf(out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ fprintf(out,
+ "<testsuites tests=\"%d\" failures=\"%d\" disabled=\"%d\" "
+ "errors=\"0\" time=\"%s\" ",
+ unit_test.total_test_count(),
+ unit_test.failed_test_count(),
+ unit_test.disabled_test_count(),
+ FormatTimeInMillisAsSeconds(unit_test.elapsed_time()).c_str());
+ if (GTEST_FLAG(shuffle)) {
+ fprintf(out, "random_seed=\"%d\" ", unit_test.random_seed());
+ }
+ fprintf(out, "name=\"AllTests\">\n");
+ for (int i = 0; i < unit_test.total_test_case_count(); ++i)
+ PrintXmlTestCase(out, *unit_test.GetTestCase(i));
+ fprintf(out, "</testsuites>\n");
+}
+
+// Produces a string representing the test properties in a result as space
+// delimited XML attributes based on the property key="value" pairs.
+String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes(
+ const TestResult& result) {
+ Message attributes;
+ for (int i = 0; i < result.test_property_count(); ++i) {
+ const TestProperty& property = result.GetTestProperty(i);
+ attributes << " " << property.key() << "="
+ << "\"" << EscapeXmlAttribute(property.value()) << "\"";
+ }
+ return attributes.GetString();
+}
+
+// End XmlUnitTestResultPrinter
+
+#if GTEST_CAN_STREAM_RESULTS_
+
+// Streams test results to the given port on the given host machine.
+class StreamingListener : public EmptyTestEventListener {
+ public:
+ // Escapes '=', '&', '%', and '\n' characters in str as "%xx".
+ static string UrlEncode(const char* str);
+
+ StreamingListener(const string& host, const string& port)
+ : sockfd_(-1), host_name_(host), port_num_(port) {
+ MakeConnection();
+ Send("gtest_streaming_protocol_version=1.0\n");
+ }
+
+ virtual ~StreamingListener() {
+ if (sockfd_ != -1)
+ CloseConnection();
+ }
+
+ void OnTestProgramStart(const UnitTest& /* unit_test */) {
+ Send("event=TestProgramStart\n");
+ }
+
+ void OnTestProgramEnd(const UnitTest& unit_test) {
+ // Note that Google Test current only report elapsed time for each
+ // test iteration, not for the entire test program.
+ Send(String::Format("event=TestProgramEnd&passed=%d\n",
+ unit_test.Passed()));
+
+ // Notify the streaming server to stop.
+ CloseConnection();
+ }
+
+ void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) {
+ Send(String::Format("event=TestIterationStart&iteration=%d\n",
+ iteration));
+ }
+
+ void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) {
+ Send(String::Format("event=TestIterationEnd&passed=%d&elapsed_time=%sms\n",
+ unit_test.Passed(),
+ StreamableToString(unit_test.elapsed_time()).c_str()));
+ }
+
+ void OnTestCaseStart(const TestCase& test_case) {
+ Send(String::Format("event=TestCaseStart&name=%s\n", test_case.name()));
+ }
+
+ void OnTestCaseEnd(const TestCase& test_case) {
+ Send(String::Format("event=TestCaseEnd&passed=%d&elapsed_time=%sms\n",
+ test_case.Passed(),
+ StreamableToString(test_case.elapsed_time()).c_str()));
+ }
+
+ void OnTestStart(const TestInfo& test_info) {
+ Send(String::Format("event=TestStart&name=%s\n", test_info.name()));
+ }
+
+ void OnTestEnd(const TestInfo& test_info) {
+ Send(String::Format(
+ "event=TestEnd&passed=%d&elapsed_time=%sms\n",
+ (test_info.result())->Passed(),
+ StreamableToString((test_info.result())->elapsed_time()).c_str()));
+ }
+
+ void OnTestPartResult(const TestPartResult& test_part_result) {
+ const char* file_name = test_part_result.file_name();
+ if (file_name == NULL)
+ file_name = "";
+ Send(String::Format("event=TestPartResult&file=%s&line=%d&message=",
+ UrlEncode(file_name).c_str(),
+ test_part_result.line_number()));
+ Send(UrlEncode(test_part_result.message()) + "\n");
+ }
+
+ private:
+ // Creates a client socket and connects to the server.
+ void MakeConnection();
+
+ // Closes the socket.
+ void CloseConnection() {
+ GTEST_CHECK_(sockfd_ != -1)
+ << "CloseConnection() can be called only when there is a connection.";
+
+ close(sockfd_);
+ sockfd_ = -1;
+ }
+
+ // Sends a string to the socket.
+ void Send(const string& message) {
+ GTEST_CHECK_(sockfd_ != -1)
+ << "Send() can be called only when there is a connection.";
+
+ const int len = static_cast<int>(message.length());
+ if (write(sockfd_, message.c_str(), len) != len) {
+ GTEST_LOG_(WARNING)
+ << "stream_result_to: failed to stream to "
+ << host_name_ << ":" << port_num_;
+ }
+ }
+
+ int sockfd_; // socket file descriptor
+ const string host_name_;
+ const string port_num_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener);
+}; // class StreamingListener
+
+// Checks if str contains '=', '&', '%' or '\n' characters. If yes,
+// replaces them by "%xx" where xx is their hexadecimal value. For
+// example, replaces "=" with "%3D". This algorithm is O(strlen(str))
+// in both time and space -- important as the input str may contain an
+// arbitrarily long test failure message and stack trace.
+string StreamingListener::UrlEncode(const char* str) {
+ string result;
+ result.reserve(strlen(str) + 1);
+ for (char ch = *str; ch != '\0'; ch = *++str) {
+ switch (ch) {
+ case '%':
+ case '=':
+ case '&':
+ case '\n':
+ result.append(String::Format("%%%02x", static_cast<unsigned char>(ch)));
+ break;
+ default:
+ result.push_back(ch);
+ break;
+ }
+ }
+ return result;
+}
+
+void StreamingListener::MakeConnection() {
+ GTEST_CHECK_(sockfd_ == -1)
+ << "MakeConnection() can't be called when there is already a connection.";
+
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses.
+ hints.ai_socktype = SOCK_STREAM;
+ addrinfo* servinfo = NULL;
+
+ // Use the getaddrinfo() to get a linked list of IP addresses for
+ // the given host name.
+ const int error_num = getaddrinfo(
+ host_name_.c_str(), port_num_.c_str(), &hints, &servinfo);
+ if (error_num != 0) {
+ GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: "
+ << gai_strerror(error_num);
+ }
+
+ // Loop through all the results and connect to the first we can.
+ for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL;
+ cur_addr = cur_addr->ai_next) {
+ sockfd_ = socket(
+ cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol);
+ if (sockfd_ != -1) {
+ // Connect the client socket to the server socket.
+ if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) {
+ close(sockfd_);
+ sockfd_ = -1;
+ }
+ }
+ }
+
+ freeaddrinfo(servinfo); // all done with this structure
+
+ if (sockfd_ == -1) {
+ GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to "
+ << host_name_ << ":" << port_num_;
+ }
+}
+
+// End of class Streaming Listener
+#endif // GTEST_CAN_STREAM_RESULTS__
+
+// Class ScopedTrace
+
+// Pushes the given source file location and message onto a per-thread
+// trace stack maintained by Google Test.
+// L < UnitTest::mutex_
+ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) {
+ TraceInfo trace;
+ trace.file = file;
+ trace.line = line;
+ trace.message = message.GetString();
+
+ UnitTest::GetInstance()->PushGTestTrace(trace);
+}
+
+// Pops the info pushed by the c'tor.
+// L < UnitTest::mutex_
+ScopedTrace::~ScopedTrace() {
+ UnitTest::GetInstance()->PopGTestTrace();
+}
+
+
+// class OsStackTraceGetter
+
+// Returns the current OS stack trace as a String. Parameters:
+//
+// max_depth - the maximum number of stack frames to be included
+// in the trace.
+// skip_count - the number of top frames to be skipped; doesn't count
+// against max_depth.
+//
+// L < mutex_
+// We use "L < mutex_" to denote that the function may acquire mutex_.
+String OsStackTraceGetter::CurrentStackTrace(int, int) {
+ return String("");
+}
+
+// L < mutex_
+void OsStackTraceGetter::UponLeavingGTest() {
+}
+
+const char* const
+OsStackTraceGetter::kElidedFramesMarker =
+ "... " GTEST_NAME_ " internal frames ...";
+
+} // namespace internal
+
+// class TestEventListeners
+
+TestEventListeners::TestEventListeners()
+ : repeater_(new internal::TestEventRepeater()),
+ default_result_printer_(NULL),
+ default_xml_generator_(NULL) {
+}
+
+TestEventListeners::~TestEventListeners() { delete repeater_; }
+
+// Returns the standard listener responsible for the default console
+// output. Can be removed from the listeners list to shut down default
+// console output. Note that removing this object from the listener list
+// with Release transfers its ownership to the user.
+void TestEventListeners::Append(TestEventListener* listener) {
+ repeater_->Append(listener);
+}
+
+// Removes the given event listener from the list and returns it. It then
+// becomes the caller's responsibility to delete the listener. Returns
+// NULL if the listener is not found in the list.
+TestEventListener* TestEventListeners::Release(TestEventListener* listener) {
+ if (listener == default_result_printer_)
+ default_result_printer_ = NULL;
+ else if (listener == default_xml_generator_)
+ default_xml_generator_ = NULL;
+ return repeater_->Release(listener);
+}
+
+// Returns repeater that broadcasts the TestEventListener events to all
+// subscribers.
+TestEventListener* TestEventListeners::repeater() { return repeater_; }
+
+// Sets the default_result_printer attribute to the provided listener.
+// The listener is also added to the listener list and previous
+// default_result_printer is removed from it and deleted. The listener can
+// also be NULL in which case it will not be added to the list. Does
+// nothing if the previous and the current listener objects are the same.
+void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) {
+ if (default_result_printer_ != listener) {
+ // It is an error to pass this method a listener that is already in the
+ // list.
+ delete Release(default_result_printer_);
+ default_result_printer_ = listener;
+ if (listener != NULL)
+ Append(listener);
+ }
+}
+
+// Sets the default_xml_generator attribute to the provided listener. The
+// listener is also added to the listener list and previous
+// default_xml_generator is removed from it and deleted. The listener can
+// also be NULL in which case it will not be added to the list. Does
+// nothing if the previous and the current listener objects are the same.
+void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) {
+ if (default_xml_generator_ != listener) {
+ // It is an error to pass this method a listener that is already in the
+ // list.
+ delete Release(default_xml_generator_);
+ default_xml_generator_ = listener;
+ if (listener != NULL)
+ Append(listener);
+ }
+}
+
+// Controls whether events will be forwarded by the repeater to the
+// listeners in the list.
+bool TestEventListeners::EventForwardingEnabled() const {
+ return repeater_->forwarding_enabled();
+}
+
+void TestEventListeners::SuppressEventForwarding() {
+ repeater_->set_forwarding_enabled(false);
+}
+
+// class UnitTest
+
+// Gets the singleton UnitTest object. The first time this method is
+// called, a UnitTest object is constructed and returned. Consecutive
+// calls will return the same object.
+//
+// We don't protect this under mutex_ as a user is not supposed to
+// call this before main() starts, from which point on the return
+// value will never change.
+UnitTest * UnitTest::GetInstance() {
+ // When compiled with MSVC 7.1 in optimized mode, destroying the
+ // UnitTest object upon exiting the program messes up the exit code,
+ // causing successful tests to appear failed. We have to use a
+ // different implementation in this case to bypass the compiler bug.
+ // This implementation makes the compiler happy, at the cost of
+ // leaking the UnitTest object.
+
+ // CodeGear C++Builder insists on a public destructor for the
+ // default implementation. Use this implementation to keep good OO
+ // design with private destructor.
+
+#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__)
+ static UnitTest* const instance = new UnitTest;
+ return instance;
+#else
+ static UnitTest instance;
+ return &instance;
+#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__)
+}
+
+// Gets the number of successful test cases.
+int UnitTest::successful_test_case_count() const {
+ return impl()->successful_test_case_count();
+}
+
+// Gets the number of failed test cases.
+int UnitTest::failed_test_case_count() const {
+ return impl()->failed_test_case_count();
+}
+
+// Gets the number of all test cases.
+int UnitTest::total_test_case_count() const {
+ return impl()->total_test_case_count();
+}
+
+// Gets the number of all test cases that contain at least one test
+// that should run.
+int UnitTest::test_case_to_run_count() const {
+ return impl()->test_case_to_run_count();
+}
+
+// Gets the number of successful tests.
+int UnitTest::successful_test_count() const {
+ return impl()->successful_test_count();
+}
+
+// Gets the number of failed tests.
+int UnitTest::failed_test_count() const { return impl()->failed_test_count(); }
+
+// Gets the number of disabled tests.
+int UnitTest::disabled_test_count() const {
+ return impl()->disabled_test_count();
+}
+
+// Gets the number of all tests.
+int UnitTest::total_test_count() const { return impl()->total_test_count(); }
+
+// Gets the number of tests that should run.
+int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); }
+
+// Gets the elapsed time, in milliseconds.
+internal::TimeInMillis UnitTest::elapsed_time() const {
+ return impl()->elapsed_time();
+}
+
+// Returns true iff the unit test passed (i.e. all test cases passed).
+bool UnitTest::Passed() const { return impl()->Passed(); }
+
+// Returns true iff the unit test failed (i.e. some test case failed
+// or something outside of all tests failed).
+bool UnitTest::Failed() const { return impl()->Failed(); }
+
+// Gets the i-th test case among all the test cases. i can range from 0 to
+// total_test_case_count() - 1. If i is not in that range, returns NULL.
+const TestCase* UnitTest::GetTestCase(int i) const {
+ return impl()->GetTestCase(i);
+}
+
+// Gets the i-th test case among all the test cases. i can range from 0 to
+// total_test_case_count() - 1. If i is not in that range, returns NULL.
+TestCase* UnitTest::GetMutableTestCase(int i) {
+ return impl()->GetMutableTestCase(i);
+}
+
+// Returns the list of event listeners that can be used to track events
+// inside Google Test.
+TestEventListeners& UnitTest::listeners() {
+ return *impl()->listeners();
+}
+
+// Registers and returns a global test environment. When a test
+// program is run, all global test environments will be set-up in the
+// order they were registered. After all tests in the program have
+// finished, all global test environments will be torn-down in the
+// *reverse* order they were registered.
+//
+// The UnitTest object takes ownership of the given environment.
+//
+// We don't protect this under mutex_, as we only support calling it
+// from the main thread.
+Environment* UnitTest::AddEnvironment(Environment* env) {
+ if (env == NULL) {
+ return NULL;
+ }
+
+ impl_->environments().push_back(env);
+ return env;
+}
+
+// Adds a TestPartResult to the current TestResult object. All Google Test
+// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call
+// this to report their results. The user code should use the
+// assertion macros instead of calling this directly.
+// L < mutex_
+void UnitTest::AddTestPartResult(TestPartResult::Type result_type,
+ const char* file_name,
+ int line_number,
+ const internal::String& message,
+ const internal::String& os_stack_trace) {
+ Message msg;
+ msg << message;
+
+ internal::MutexLock lock(&mutex_);
+ if (impl_->gtest_trace_stack().size() > 0) {
+ msg << "\n" << GTEST_NAME_ << " trace:";
+
+ for (int i = static_cast<int>(impl_->gtest_trace_stack().size());
+ i > 0; --i) {
+ const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1];
+ msg << "\n" << internal::FormatFileLocation(trace.file, trace.line)
+ << " " << trace.message;
+ }
+ }
+
+ if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) {
+ msg << internal::kStackTraceMarker << os_stack_trace;
+ }
+
+ const TestPartResult result =
+ TestPartResult(result_type, file_name, line_number,
+ msg.GetString().c_str());
+ impl_->GetTestPartResultReporterForCurrentThread()->
+ ReportTestPartResult(result);
+
+ if (result_type != TestPartResult::kSuccess) {
+ // gtest_break_on_failure takes precedence over
+ // gtest_throw_on_failure. This allows a user to set the latter
+ // in the code (perhaps in order to use Google Test assertions
+ // with another testing framework) and specify the former on the
+ // command line for debugging.
+ if (GTEST_FLAG(break_on_failure)) {
+#if GTEST_OS_WINDOWS
+ // Using DebugBreak on Windows allows gtest to still break into a debugger
+ // when a failure happens and both the --gtest_break_on_failure and
+ // the --gtest_catch_exceptions flags are specified.
+ DebugBreak();
+#else
+ // Dereference NULL through a volatile pointer to prevent the compiler
+ // from removing. We use this rather than abort() or __builtin_trap() for
+ // portability: Symbian doesn't implement abort() well, and some debuggers
+ // don't correctly trap abort().
+ *static_cast<volatile int*>(NULL) = 1;
+#endif // GTEST_OS_WINDOWS
+ } else if (GTEST_FLAG(throw_on_failure)) {
+#if GTEST_HAS_EXCEPTIONS
+ throw GoogleTestFailureException(result);
+#else
+ // We cannot call abort() as it generates a pop-up in debug mode
+ // that cannot be suppressed in VC 7.1 or below.
+ exit(1);
+#endif
+ }
+ }
+}
+
+// Creates and adds a property to the current TestResult. If a property matching
+// the supplied value already exists, updates its value instead.
+void UnitTest::RecordPropertyForCurrentTest(const char* key,
+ const char* value) {
+ const TestProperty test_property(key, value);
+ impl_->current_test_result()->RecordProperty(test_property);
+}
+
+// Runs all tests in this UnitTest object and prints the result.
+// Returns 0 if successful, or 1 otherwise.
+//
+// We don't protect this under mutex_, as we only support calling it
+// from the main thread.
+int UnitTest::Run() {
+ // Captures the value of GTEST_FLAG(catch_exceptions). This value will be
+ // used for the duration of the program.
+ impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));
+
+#if GTEST_HAS_SEH
+ const bool in_death_test_child_process =
+ internal::GTEST_FLAG(internal_run_death_test).length() > 0;
+
+ // Either the user wants Google Test to catch exceptions thrown by the
+ // tests or this is executing in the context of death test child
+ // process. In either case the user does not want to see pop-up dialogs
+ // about crashes - they are expected.
+ if (impl()->catch_exceptions() || in_death_test_child_process) {
+
+# if !GTEST_OS_WINDOWS_MOBILE
+ // SetErrorMode doesn't exist on CE.
+ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT |
+ SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
+# endif // !GTEST_OS_WINDOWS_MOBILE
+
+# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE
+ // Death test children can be terminated with _abort(). On Windows,
+ // _abort() can show a dialog with a warning message. This forces the
+ // abort message to go to stderr instead.
+ _set_error_mode(_OUT_TO_STDERR);
+# endif
+
+# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE
+ // In the debug version, Visual Studio pops up a separate dialog
+ // offering a choice to debug the aborted program. We need to suppress
+ // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement
+ // executed. Google Test will notify the user of any unexpected
+ // failure via stderr.
+ //
+ // VC++ doesn't define _set_abort_behavior() prior to the version 8.0.
+ // Users of prior VC versions shall suffer the agony and pain of
+ // clicking through the countless debug dialogs.
+ // TODO(***@google.com): find a way to suppress the abort dialog() in the
+ // debug mode when compiled with VC 7.1 or lower.
+ if (!GTEST_FLAG(break_on_failure))
+ _set_abort_behavior(
+ 0x0, // Clear the following flags:
+ _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump.
+# endif
+
+ }
+#endif // GTEST_HAS_SEH
+
+ return internal::HandleExceptionsInMethodIfSupported(
+ impl(),
+ &internal::UnitTestImpl::RunAllTests,
+ "auxiliary test code (environments or event listeners)") ? 0 : 1;
+}
+
+// Returns the working directory when the first TEST() or TEST_F() was
+// executed.
+const char* UnitTest::original_working_dir() const {
+ return impl_->original_working_dir_.c_str();
+}
+
+// Returns the TestCase object for the test that's currently running,
+// or NULL if no test is running.
+// L < mutex_
+const TestCase* UnitTest::current_test_case() const {
+ internal::MutexLock lock(&mutex_);
+ return impl_->current_test_case();
+}
+
+// Returns the TestInfo object for the test that's currently running,
+// or NULL if no test is running.
+// L < mutex_
+const TestInfo* UnitTest::current_test_info() const {
+ internal::MutexLock lock(&mutex_);
+ return impl_->current_test_info();
+}
+
+// Returns the random seed used at the start of the current test run.
+int UnitTest::random_seed() const { return impl_->random_seed(); }
+
+#if GTEST_HAS_PARAM_TEST
+// Returns ParameterizedTestCaseRegistry object used to keep track of
+// value-parameterized tests and instantiate and register them.
+// L < mutex_
+internal::ParameterizedTestCaseRegistry&
+ UnitTest::parameterized_test_registry() {
+ return impl_->parameterized_test_registry();
+}
+#endif // GTEST_HAS_PARAM_TEST
+
+// Creates an empty UnitTest.
+UnitTest::UnitTest() {
+ impl_ = new internal::UnitTestImpl(this);
+}
+
+// Destructor of UnitTest.
+UnitTest::~UnitTest() {
+ delete impl_;
+}
+
+// Pushes a trace defined by SCOPED_TRACE() on to the per-thread
+// Google Test trace stack.
+// L < mutex_
+void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) {
+ internal::MutexLock lock(&mutex_);
+ impl_->gtest_trace_stack().push_back(trace);
+}
+
+// Pops a trace from the per-thread Google Test trace stack.
+// L < mutex_
+void UnitTest::PopGTestTrace() {
+ internal::MutexLock lock(&mutex_);
+ impl_->gtest_trace_stack().pop_back();
+}
+
+namespace internal {
+
+UnitTestImpl::UnitTestImpl(UnitTest* parent)
+ : parent_(parent),
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4355) // Temporarily disables warning 4355
+ // (using this in initializer).
+ default_global_test_part_result_reporter_(this),
+ default_per_thread_test_part_result_reporter_(this),
+# pragma warning(pop) // Restores the warning state again.
+#else
+ default_global_test_part_result_reporter_(this),
+ default_per_thread_test_part_result_reporter_(this),
+#endif // _MSC_VER
+ global_test_part_result_repoter_(
+ &default_global_test_part_result_reporter_),
+ per_thread_test_part_result_reporter_(
+ &default_per_thread_test_part_result_reporter_),
+#if GTEST_HAS_PARAM_TEST
+ parameterized_test_registry_(),
+ parameterized_tests_registered_(false),
+#endif // GTEST_HAS_PARAM_TEST
+ last_death_test_case_(-1),
+ current_test_case_(NULL),
+ current_test_info_(NULL),
+ ad_hoc_test_result_(),
+ os_stack_trace_getter_(NULL),
+ post_flag_parse_init_performed_(false),
+ random_seed_(0), // Will be overridden by the flag before first use.
+ random_(0), // Will be reseeded before first use.
+ elapsed_time_(0),
+#if GTEST_HAS_DEATH_TEST
+ internal_run_death_test_flag_(NULL),
+ death_test_factory_(new DefaultDeathTestFactory),
+#endif
+ // Will be overridden by the flag before first use.
+ catch_exceptions_(false) {
+ listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter);
+}
+
+UnitTestImpl::~UnitTestImpl() {
+ // Deletes every TestCase.
+ ForEach(test_cases_, internal::Delete<TestCase>);
+
+ // Deletes every Environment.
+ ForEach(environments_, internal::Delete<Environment>);
+
+ delete os_stack_trace_getter_;
+}
+
+#if GTEST_HAS_DEATH_TEST
+// Disables event forwarding if the control is currently in a death test
+// subprocess. Must not be called before InitGoogleTest.
+void UnitTestImpl::SuppressTestEventsIfInSubprocess() {
+ if (internal_run_death_test_flag_.get() != NULL)
+ listeners()->SuppressEventForwarding();
+}
+#endif // GTEST_HAS_DEATH_TEST
+
+// Initializes event listeners performing XML output as specified by
+// UnitTestOptions. Must not be called before InitGoogleTest.
+void UnitTestImpl::ConfigureXmlOutput() {
+ const String& output_format = UnitTestOptions::GetOutputFormat();
+ if (output_format == "xml") {
+ listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter(
+ UnitTestOptions::GetAbsolutePathToOutputFile().c_str()));
+ } else if (output_format != "") {
+ printf("WARNING: unrecognized output format \"%s\" ignored.\n",
+ output_format.c_str());
+ fflush(stdout);
+ }
+}
+
+#if GTEST_CAN_STREAM_RESULTS_
+// Initializes event listeners for streaming test results in String form.
+// Must not be called before InitGoogleTest.
+void UnitTestImpl::ConfigureStreamingOutput() {
+ const string& target = GTEST_FLAG(stream_result_to);
+ if (!target.empty()) {
+ const size_t pos = target.find(':');
+ if (pos != string::npos) {
+ listeners()->Append(new StreamingListener(target.substr(0, pos),
+ target.substr(pos+1)));
+ } else {
+ printf("WARNING: unrecognized streaming target \"%s\" ignored.\n",
+ target.c_str());
+ fflush(stdout);
+ }
+ }
+}
+#endif // GTEST_CAN_STREAM_RESULTS_
+
+// Performs initialization dependent upon flag values obtained in
+// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to
+// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest
+// this function is also called from RunAllTests. Since this function can be
+// called more than once, it has to be idempotent.
+void UnitTestImpl::PostFlagParsingInit() {
+ // Ensures that this function does not execute more than once.
+ if (!post_flag_parse_init_performed_) {
+ post_flag_parse_init_performed_ = true;
+
+#if GTEST_HAS_DEATH_TEST
+ InitDeathTestSubprocessControlInfo();
+ SuppressTestEventsIfInSubprocess();
+#endif // GTEST_HAS_DEATH_TEST
+
+ // Registers parameterized tests. This makes parameterized tests
+ // available to the UnitTest reflection API without running
+ // RUN_ALL_TESTS.
+ RegisterParameterizedTests();
+
+ // Configures listeners for XML output. This makes it possible for users
+ // to shut down the default XML output before invoking RUN_ALL_TESTS.
+ ConfigureXmlOutput();
+
+#if GTEST_CAN_STREAM_RESULTS_
+ // Configures listeners for streaming test results to the specified server.
+ ConfigureStreamingOutput();
+#endif // GTEST_CAN_STREAM_RESULTS_
+ }
+}
+
+// A predicate that checks the name of a TestCase against a known
+// value.
+//
+// This is used for implementation of the UnitTest class only. We put
+// it in the anonymous namespace to prevent polluting the outer
+// namespace.
+//
+// TestCaseNameIs is copyable.
+class TestCaseNameIs {
+ public:
+ // Constructor.
+ explicit TestCaseNameIs(const String& name)
+ : name_(name) {}
+
+ // Returns true iff the name of test_case matches name_.
+ bool operator()(const TestCase* test_case) const {
+ return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0;
+ }
+
+ private:
+ String name_;
+};
+
+// Finds and returns a TestCase with the given name. If one doesn't
+// exist, creates one and returns it. It's the CALLER'S
+// RESPONSIBILITY to ensure that this function is only called WHEN THE
+// TESTS ARE NOT SHUFFLED.
+//
+// Arguments:
+//
+// test_case_name: name of the test case
+// type_param: the name of the test case's type parameter, or NULL if
+// this is not a typed or a type-parameterized test case.
+// set_up_tc: pointer to the function that sets up the test case
+// tear_down_tc: pointer to the function that tears down the test case
+TestCase* UnitTestImpl::GetTestCase(const char* test_case_name,
+ const char* type_param,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc) {
+ // Can we find a TestCase with the given name?
+ const std::vector<TestCase*>::const_iterator test_case =
+ std::find_if(test_cases_.begin(), test_cases_.end(),
+ TestCaseNameIs(test_case_name));
+
+ if (test_case != test_cases_.end())
+ return *test_case;
+
+ // No. Let's create one.
+ TestCase* const new_test_case =
+ new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc);
+
+ // Is this a death test case?
+ if (internal::UnitTestOptions::MatchesFilter(String(test_case_name),
+ kDeathTestCaseFilter)) {
+ // Yes. Inserts the test case after the last death test case
+ // defined so far. This only works when the test cases haven't
+ // been shuffled. Otherwise we may end up running a death test
+ // after a non-death test.
+ ++last_death_test_case_;
+ test_cases_.insert(test_cases_.begin() + last_death_test_case_,
+ new_test_case);
+ } else {
+ // No. Appends to the end of the list.
+ test_cases_.push_back(new_test_case);
+ }
+
+ test_case_indices_.push_back(static_cast<int>(test_case_indices_.size()));
+ return new_test_case;
+}
+
+// Helpers for setting up / tearing down the given environment. They
+// are for use in the ForEach() function.
+static void SetUpEnvironment(Environment* env) { env->SetUp(); }
+static void TearDownEnvironment(Environment* env) { env->TearDown(); }
+
+// Runs all tests in this UnitTest object, prints the result, and
+// returns true if all tests are successful. If any exception is
+// thrown during a test, the test is considered to be failed, but the
+// rest of the tests will still be run.
+//
+// When parameterized tests are enabled, it expands and registers
+// parameterized tests first in RegisterParameterizedTests().
+// All other functions called from RunAllTests() may safely assume that
+// parameterized tests are ready to be counted and run.
+bool UnitTestImpl::RunAllTests() {
+ // Makes sure InitGoogleTest() was called.
+ if (!GTestIsInitialized()) {
+ printf("%s",
+ "\nThis test program did NOT call ::testing::InitGoogleTest "
+ "before calling RUN_ALL_TESTS(). Please fix it.\n");
+ return false;
+ }
+
+ // Do not run any test if the --help flag was specified.
+ if (g_help_flag)
+ return true;
+
+ // Repeats the call to the post-flag parsing initialization in case the
+ // user didn't call InitGoogleTest.
+ PostFlagParsingInit();
+
+ // Even if sharding is not on, test runners may want to use the
+ // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding
+ // protocol.
+ internal::WriteToShardStatusFileIfNeeded();
+
+ // True iff we are in a subprocess for running a thread-safe-style
+ // death test.
+ bool in_subprocess_for_death_test = false;
+
+#if GTEST_HAS_DEATH_TEST
+ in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL);
+#endif // GTEST_HAS_DEATH_TEST
+
+ const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex,
+ in_subprocess_for_death_test);
+
+ // Compares the full test names with the filter to decide which
+ // tests to run.
+ const bool has_tests_to_run = FilterTests(should_shard
+ ? HONOR_SHARDING_PROTOCOL
+ : IGNORE_SHARDING_PROTOCOL) > 0;
+
+ // Lists the tests and exits if the --gtest_list_tests flag was specified.
+ if (GTEST_FLAG(list_tests)) {
+ // This must be called *after* FilterTests() has been called.
+ ListTestsMatchingFilter();
+ return true;
+ }
+
+ random_seed_ = GTEST_FLAG(shuffle) ?
+ GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0;
+
+ // True iff at least one test has failed.
+ bool failed = false;
+
+ TestEventListener* repeater = listeners()->repeater();
+
+ repeater->OnTestProgramStart(*parent_);
+
+ // How many times to repeat the tests? We don't want to repeat them
+ // when we are inside the subprocess of a death test.
+ const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat);
+ // Repeats forever if the repeat count is negative.
+ const bool forever = repeat < 0;
+ for (int i = 0; forever || i != repeat; i++) {
+ // We want to preserve failures generated by ad-hoc test
+ // assertions executed before RUN_ALL_TESTS().
+ ClearNonAdHocTestResult();
+
+ const TimeInMillis start = GetTimeInMillis();
+
+ // Shuffles test cases and tests if requested.
+ if (has_tests_to_run && GTEST_FLAG(shuffle)) {
+ random()->Reseed(random_seed_);
+ // This should be done before calling OnTestIterationStart(),
+ // such that a test event listener can see the actual test order
+ // in the event.
+ ShuffleTests();
+ }
+
+ // Tells the unit test event listeners that the tests are about to start.
+ repeater->OnTestIterationStart(*parent_, i);
+
+ // Runs each test case if there is at least one test to run.
+ if (has_tests_to_run) {
+ // Sets up all environments beforehand.
+ repeater->OnEnvironmentsSetUpStart(*parent_);
+ ForEach(environments_, SetUpEnvironment);
+ repeater->OnEnvironmentsSetUpEnd(*parent_);
+
+ // Runs the tests only if there was no fatal failure during global
+ // set-up.
+ if (!Test::HasFatalFailure()) {
+ for (int test_index = 0; test_index < total_test_case_count();
+ test_index++) {
+ GetMutableTestCase(test_index)->Run();
+ }
+ }
+
+ // Tears down all environments in reverse order afterwards.
+ repeater->OnEnvironmentsTearDownStart(*parent_);
+ std::for_each(environments_.rbegin(), environments_.rend(),
+ TearDownEnvironment);
+ repeater->OnEnvironmentsTearDownEnd(*parent_);
+ }
+
+ elapsed_time_ = GetTimeInMillis() - start;
+
+ // Tells the unit test event listener that the tests have just finished.
+ repeater->OnTestIterationEnd(*parent_, i);
+
+ // Gets the result and clears it.
+ if (!Passed()) {
+ failed = true;
+ }
+
+ // Restores the original test order after the iteration. This
+ // allows the user to quickly repro a failure that happens in the
+ // N-th iteration without repeating the first (N - 1) iterations.
+ // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in
+ // case the user somehow changes the value of the flag somewhere
+ // (it's always safe to unshuffle the tests).
+ UnshuffleTests();
+
+ if (GTEST_FLAG(shuffle)) {
+ // Picks a new random seed for each iteration.
+ random_seed_ = GetNextRandomSeed(random_seed_);
+ }
+ }
+
+ repeater->OnTestProgramEnd(*parent_);
+
+ return !failed;
+}
+
+// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file
+// if the variable is present. If a file already exists at this location, this
+// function will write over it. If the variable is present, but the file cannot
+// be created, prints an error and exits.
+void WriteToShardStatusFileIfNeeded() {
+ const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile);
+ if (test_shard_file != NULL) {
+ FILE* const file = posix::FOpen(test_shard_file, "w");
+ if (file == NULL) {
+ ColoredPrintf(COLOR_RED,
+ "Could not write to the test shard status file \"%s\" "
+ "specified by the %s environment variable.\n",
+ test_shard_file, kTestShardStatusFile);
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ }
+ fclose(file);
+ }
+}
+
+// Checks whether sharding is enabled by examining the relevant
+// environment variable values. If the variables are present,
+// but inconsistent (i.e., shard_index >= total_shards), prints
+// an error and exits. If in_subprocess_for_death_test, sharding is
+// disabled because it must only be applied to the original test
+// process. Otherwise, we could filter out death tests we intended to execute.
+bool ShouldShard(const char* total_shards_env,
+ const char* shard_index_env,
+ bool in_subprocess_for_death_test) {
+ if (in_subprocess_for_death_test) {
+ return false;
+ }
+
+ const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1);
+ const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1);
+
+ if (total_shards == -1 && shard_index == -1) {
+ return false;
+ } else if (total_shards == -1 && shard_index != -1) {
+ const Message msg = Message()
+ << "Invalid environment variables: you have "
+ << kTestShardIndex << " = " << shard_index
+ << ", but have left " << kTestTotalShards << " unset.\n";
+ ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ } else if (total_shards != -1 && shard_index == -1) {
+ const Message msg = Message()
+ << "Invalid environment variables: you have "
+ << kTestTotalShards << " = " << total_shards
+ << ", but have left " << kTestShardIndex << " unset.\n";
+ ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ } else if (shard_index < 0 || shard_index >= total_shards) {
+ const Message msg = Message()
+ << "Invalid environment variables: we require 0 <= "
+ << kTestShardIndex << " < " << kTestTotalShards
+ << ", but you have " << kTestShardIndex << "=" << shard_index
+ << ", " << kTestTotalShards << "=" << total_shards << ".\n";
+ ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ }
+
+ return total_shards > 1;
+}
+
+// Parses the environment variable var as an Int32. If it is unset,
+// returns default_val. If it is not an Int32, prints an error
+// and aborts.
+Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) {
+ const char* str_val = posix::GetEnv(var);
+ if (str_val == NULL) {
+ return default_val;
+ }
+
+ Int32 result;
+ if (!ParseInt32(Message() << "The value of environment variable " << var,
+ str_val, &result)) {
+ exit(EXIT_FAILURE);
+ }
+ return result;
+}
+
+// Given the total number of shards, the shard index, and the test id,
+// returns true iff the test should be run on this shard. The test id is
+// some arbitrary but unique non-negative integer assigned to each test
+// method. Assumes that 0 <= shard_index < total_shards.
+bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) {
+ return (test_id % total_shards) == shard_index;
+}
+
+// Compares the name of each test with the user-specified filter to
+// decide whether the test should be run, then records the result in
+// each TestCase and TestInfo object.
+// If shard_tests == true, further filters tests based on sharding
+// variables in the environment - see
+// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide.
+// Returns the number of tests that should run.
+int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
+ const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ?
+ Int32FromEnvOrDie(kTestTotalShards, -1) : -1;
+ const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ?
+ Int32FromEnvOrDie(kTestShardIndex, -1) : -1;
+
+ // num_runnable_tests are the number of tests that will
+ // run across all shards (i.e., match filter and are not disabled).
+ // num_selected_tests are the number of tests to be run on
+ // this shard.
+ int num_runnable_tests = 0;
+ int num_selected_tests = 0;
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ TestCase* const test_case = test_cases_[i];
+ const String &test_case_name = test_case->name();
+ test_case->set_should_run(false);
+
+ for (size_t j = 0; j < test_case->test_info_list().size(); j++) {
+ TestInfo* const test_info = test_case->test_info_list()[j];
+ const String test_name(test_info->name());
+ // A test is disabled if test case name or test name matches
+ // kDisableTestFilter.
+ const bool is_disabled =
+ internal::UnitTestOptions::MatchesFilter(test_case_name,
+ kDisableTestFilter) ||
+ internal::UnitTestOptions::MatchesFilter(test_name,
+ kDisableTestFilter);
+ test_info->is_disabled_ = is_disabled;
+
+ const bool matches_filter =
+ internal::UnitTestOptions::FilterMatchesTest(test_case_name,
+ test_name);
+ test_info->matches_filter_ = matches_filter;
+
+ const bool is_runnable =
+ (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) &&
+ matches_filter;
+
+ const bool is_selected = is_runnable &&
+ (shard_tests == IGNORE_SHARDING_PROTOCOL ||
+ ShouldRunTestOnShard(total_shards, shard_index,
+ num_runnable_tests));
+
+ num_runnable_tests += is_runnable;
+ num_selected_tests += is_selected;
+
+ test_info->should_run_ = is_selected;
+ test_case->set_should_run(test_case->should_run() || is_selected);
+ }
+ }
+ return num_selected_tests;
+}
+
+// Prints the names of the tests matching the user-specified filter flag.
+void UnitTestImpl::ListTestsMatchingFilter() {
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ const TestCase* const test_case = test_cases_[i];
+ bool printed_test_case_name = false;
+
+ for (size_t j = 0; j < test_case->test_info_list().size(); j++) {
+ const TestInfo* const test_info =
+ test_case->test_info_list()[j];
+ if (test_info->matches_filter_) {
+ if (!printed_test_case_name) {
+ printed_test_case_name = true;
+ printf("%s.\n", test_case->name());
+ }
+ printf(" %s\n", test_info->name());
+ }
+ }
+ }
+ fflush(stdout);
+}
+
+// Sets the OS stack trace getter.
+//
+// Does nothing if the input and the current OS stack trace getter are
+// the same; otherwise, deletes the old getter and makes the input the
+// current getter.
+void UnitTestImpl::set_os_stack_trace_getter(
+ OsStackTraceGetterInterface* getter) {
+ if (os_stack_trace_getter_ != getter) {
+ delete os_stack_trace_getter_;
+ os_stack_trace_getter_ = getter;
+ }
+}
+
+// Returns the current OS stack trace getter if it is not NULL;
+// otherwise, creates an OsStackTraceGetter, makes it the current
+// getter, and returns it.
+OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() {
+ if (os_stack_trace_getter_ == NULL) {
+ os_stack_trace_getter_ = new OsStackTraceGetter;
+ }
+
+ return os_stack_trace_getter_;
+}
+
+// Returns the TestResult for the test that's currently running, or
+// the TestResult for the ad hoc test if no test is running.
+TestResult* UnitTestImpl::current_test_result() {
+ return current_test_info_ ?
+ &(current_test_info_->result_) : &ad_hoc_test_result_;
+}
+
+// Shuffles all test cases, and the tests within each test case,
+// making sure that death tests are still run first.
+void UnitTestImpl::ShuffleTests() {
+ // Shuffles the death test cases.
+ ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_);
+
+ // Shuffles the non-death test cases.
+ ShuffleRange(random(), last_death_test_case_ + 1,
+ static_cast<int>(test_cases_.size()), &test_case_indices_);
+
+ // Shuffles the tests inside each test case.
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ test_cases_[i]->ShuffleTests(random());
+ }
+}
+
+// Restores the test cases and tests to their order before the first shuffle.
+void UnitTestImpl::UnshuffleTests() {
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ // Unshuffles the tests in each test case.
+ test_cases_[i]->UnshuffleTests();
+ // Resets the index of each test case.
+ test_case_indices_[i] = static_cast<int>(i);
+ }
+}
+
+// Returns the current OS stack trace as a String.
+//
+// The maximum number of stack frames to be included is specified by
+// the gtest_stack_trace_depth flag. The skip_count parameter
+// specifies the number of top frames to be skipped, which doesn't
+// count against the number of frames to be included.
+//
+// For example, if Foo() calls Bar(), which in turn calls
+// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in
+// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't.
+String GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/,
+ int skip_count) {
+ // We pass skip_count + 1 to skip this wrapper function in addition
+ // to what the user really wants to skip.
+ return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1);
+}
+
+// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to
+// suppress unreachable code warnings.
+namespace {
+class ClassUniqueToAlwaysTrue {};
+}
+
+bool IsTrue(bool condition) { return condition; }
+
+bool AlwaysTrue() {
+#if GTEST_HAS_EXCEPTIONS
+ // This condition is always false so AlwaysTrue() never actually throws,
+ // but it makes the compiler think that it may throw.
+ if (IsTrue(false))
+ throw ClassUniqueToAlwaysTrue();
+#endif // GTEST_HAS_EXCEPTIONS
+ return true;
+}
+
+// If *pstr starts with the given prefix, modifies *pstr to be right
+// past the prefix and returns true; otherwise leaves *pstr unchanged
+// and returns false. None of pstr, *pstr, and prefix can be NULL.
+bool SkipPrefix(const char* prefix, const char** pstr) {
+ const size_t prefix_len = strlen(prefix);
+ if (strncmp(*pstr, prefix, prefix_len) == 0) {
+ *pstr += prefix_len;
+ return true;
+ }
+ return false;
+}
+
+// Parses a string as a command line flag. The string should have
+// the format "--flag=value". When def_optional is true, the "=value"
+// part can be omitted.
+//
+// Returns the value of the flag, or NULL if the parsing failed.
+const char* ParseFlagValue(const char* str,
+ const char* flag,
+ bool def_optional) {
+ // str and flag must not be NULL.
+ if (str == NULL || flag == NULL) return NULL;
+
+ // The flag must start with "--" followed by GTEST_FLAG_PREFIX_.
+ const String flag_str = String::Format("--%s%s", GTEST_FLAG_PREFIX_, flag);
+ const size_t flag_len = flag_str.length();
+ if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL;
+
+ // Skips the flag name.
+ const char* flag_end = str + flag_len;
+
+ // When def_optional is true, it's OK to not have a "=value" part.
+ if (def_optional && (flag_end[0] == '\0')) {
+ return flag_end;
+ }
+
+ // If def_optional is true and there are more characters after the
+ // flag name, or if def_optional is false, there must be a '=' after
+ // the flag name.
+ if (flag_end[0] != '=') return NULL;
+
+ // Returns the string after "=".
+ return flag_end + 1;
+}
+
+// Parses a string for a bool flag, in the form of either
+// "--flag=value" or "--flag".
+//
+// In the former case, the value is taken as true as long as it does
+// not start with '0', 'f', or 'F'.
+//
+// In the latter case, the value is taken as true.
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, true);
+
+ // Aborts if the parsing failed.
+ if (value_str == NULL) return false;
+
+ // Converts the string value to a bool.
+ *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F');
+ return true;
+}
+
+// Parses a string for an Int32 flag, in the form of
+// "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseInt32Flag(const char* str, const char* flag, Int32* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, false);
+
+ // Aborts if the parsing failed.
+ if (value_str == NULL) return false;
+
+ // Sets *value to the value of the flag.
+ return ParseInt32(Message() << "The value of flag --" << flag,
+ value_str, value);
+}
+
+// Parses a string for a string flag, in the form of
+// "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseStringFlag(const char* str, const char* flag, String* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, false);
+
+ // Aborts if the parsing failed.
+ if (value_str == NULL) return false;
+
+ // Sets *value to the value of the flag.
+ *value = value_str;
+ return true;
+}
+
+// Determines whether a string has a prefix that Google Test uses for its
+// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_.
+// If Google Test detects that a command line flag has its prefix but is not
+// recognized, it will print its help message. Flags starting with
+// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test
+// internal flags and do not trigger the help message.
+static bool HasGoogleTestFlagPrefix(const char* str) {
+ return (SkipPrefix("--", &str) ||
+ SkipPrefix("-", &str) ||
+ SkipPrefix("/", &str)) &&
+ !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) &&
+ (SkipPrefix(GTEST_FLAG_PREFIX_, &str) ||
+ SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str));
+}
+
+// Prints a string containing code-encoded text. The following escape
+// sequences can be used in the string to control the text color:
+//
+// @@ prints a single '@' character.
+// @R changes the color to red.
+// @G changes the color to green.
+// @Y changes the color to yellow.
+// @D changes to the default terminal text color.
+//
+// TODO(***@google.com): Write tests for this once we add stdout
+// capturing to Google Test.
+static void PrintColorEncoded(const char* str) {
+ GTestColor color = COLOR_DEFAULT; // The current color.
+
+ // Conceptually, we split the string into segments divided by escape
+ // sequences. Then we print one segment at a time. At the end of
+ // each iteration, the str pointer advances to the beginning of the
+ // next segment.
+ for (;;) {
+ const char* p = strchr(str, '@');
+ if (p == NULL) {
+ ColoredPrintf(color, "%s", str);
+ return;
+ }
+
+ ColoredPrintf(color, "%s", String(str, p - str).c_str());
+
+ const char ch = p[1];
+ str = p + 2;
+ if (ch == '@') {
+ ColoredPrintf(color, "@");
+ } else if (ch == 'D') {
+ color = COLOR_DEFAULT;
+ } else if (ch == 'R') {
+ color = COLOR_RED;
+ } else if (ch == 'G') {
+ color = COLOR_GREEN;
+ } else if (ch == 'Y') {
+ color = COLOR_YELLOW;
+ } else {
+ --str;
+ }
+ }
+}
+
+static const char kColorEncodedHelpMessage[] =
+"This program contains tests written using " GTEST_NAME_ ". You can use the\n"
+"following command line flags to control its behavior:\n"
+"\n"
+"Test Selection:\n"
+" @G--" GTEST_FLAG_PREFIX_ "***@D\n"
+" List the names of all tests instead of running them. The name of\n"
+" TEST(Foo, Bar) is \"Foo.Bar\".\n"
+" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS"
+ "[@G-@YNEGATIVE_PATTERNS]@D\n"
+" Run only the tests whose name matches one of the positive patterns but\n"
+" none of the negative patterns. '?' matches any single character; '*'\n"
+" matches any substring; ':' separates two patterns.\n"
+" @G--" GTEST_FLAG_PREFIX_ "***@D\n"
+" Run all disabled tests too.\n"
+"\n"
+"Test Execution:\n"
+" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n"
+" Run the tests repeatedly; use a negative count to repeat forever.\n"
+" @G--" GTEST_FLAG_PREFIX_ "***@D\n"
+" Randomize tests' orders on every iteration.\n"
+" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n"
+" Random number seed to use for shuffling test orders (between 1 and\n"
+" 99999, or 0 to use a seed based on the current time).\n"
+"\n"
+"Test Output:\n"
+" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@***@Y|@***@Y|@***@Y)@D\n"
+" Enable/disable colored output. The default is @***@D.\n"
+" -@G-" GTEST_FLAG_PREFIX_ "print_time=***@D\n"
+" Don't print the elapsed time of each test.\n"
+" @G--" GTEST_FLAG_PREFIX_ "output=***@Y[@G:@***@G"
+ GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n"
+" Generate an XML report in the given directory or with the given file\n"
+" name. @***@D defaults to @***@D.\n"
+#if GTEST_CAN_STREAM_RESULTS_
+" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@***@G:@***@D\n"
+" Stream test results to the given server.\n"
+#endif // GTEST_CAN_STREAM_RESULTS_
+"\n"
+"Assertion Behavior:\n"
+#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
+" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@***@Y|@***@Y)@D\n"
+" Set the default death test style.\n"
+#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
+" @G--" GTEST_FLAG_PREFIX_ "***@D\n"
+" Turn assertion failures into debugger break-points.\n"
+" @G--" GTEST_FLAG_PREFIX_ "***@D\n"
+" Turn assertion failures into C++ exceptions.\n"
+" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=***@D\n"
+" Do not report exceptions as test failures. Instead, allow them\n"
+" to crash the program or throw a pop-up (on Windows).\n"
+"\n"
+"Except for @G--" GTEST_FLAG_PREFIX_ "***@D, you can alternatively set "
+ "the corresponding\n"
+"environment variable of a flag (all letters in upper-case). For example, to\n"
+"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_
+ "color=***@D or set\n"
+"the @G" GTEST_FLAG_PREFIX_UPPER_ "***@D environment variable to @***@D.\n"
+"\n"
+"For more information, please read the " GTEST_NAME_ " documentation at\n"
+"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n"
+"(not one in your own code or tests), please report it to\n"
+"@G<" GTEST_DEV_EMAIL_ ">@D.\n";
+
+// Parses the command line for Google Test flags, without initializing
+// other parts of Google Test. The type parameter CharType can be
+// instantiated to either char or wchar_t.
+template <typename CharType>
+void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) {
+ for (int i = 1; i < *argc; i++) {
+ const String arg_string = StreamableToString(argv[i]);
+ const char* const arg = arg_string.c_str();
+
+ using internal::ParseBoolFlag;
+ using internal::ParseInt32Flag;
+ using internal::ParseStringFlag;
+
+ // Do we see a Google Test flag?
+ if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag,
+ &GTEST_FLAG(also_run_disabled_tests)) ||
+ ParseBoolFlag(arg, kBreakOnFailureFlag,
+ &GTEST_FLAG(break_on_failure)) ||
+ ParseBoolFlag(arg, kCatchExceptionsFlag,
+ &GTEST_FLAG(catch_exceptions)) ||
+ ParseStringFlag(arg, kColorFlag, &GTEST_FLAG(color)) ||
+ ParseStringFlag(arg, kDeathTestStyleFlag,
+ &GTEST_FLAG(death_test_style)) ||
+ ParseBoolFlag(arg, kDeathTestUseFork,
+ &GTEST_FLAG(death_test_use_fork)) ||
+ ParseStringFlag(arg, kFilterFlag, &GTEST_FLAG(filter)) ||
+ ParseStringFlag(arg, kInternalRunDeathTestFlag,
+ &GTEST_FLAG(internal_run_death_test)) ||
+ ParseBoolFlag(arg, kListTestsFlag, &GTEST_FLAG(list_tests)) ||
+ ParseStringFlag(arg, kOutputFlag, &GTEST_FLAG(output)) ||
+ ParseBoolFlag(arg, kPrintTimeFlag, &GTEST_FLAG(print_time)) ||
+ ParseInt32Flag(arg, kRandomSeedFlag, &GTEST_FLAG(random_seed)) ||
+ ParseInt32Flag(arg, kRepeatFlag, &GTEST_FLAG(repeat)) ||
+ ParseBoolFlag(arg, kShuffleFlag, &GTEST_FLAG(shuffle)) ||
+ ParseInt32Flag(arg, kStackTraceDepthFlag,
+ &GTEST_FLAG(stack_trace_depth)) ||
+ ParseStringFlag(arg, kStreamResultToFlag,
+ &GTEST_FLAG(stream_result_to)) ||
+ ParseBoolFlag(arg, kThrowOnFailureFlag,
+ &GTEST_FLAG(throw_on_failure))
+ ) {
+ // Yes. Shift the remainder of the argv list left by one. Note
+ // that argv has (*argc + 1) elements, the last one always being
+ // NULL. The following loop moves the trailing NULL element as
+ // well.
+ for (int j = i; j != *argc; j++) {
+ argv[j] = argv[j + 1];
+ }
+
+ // Decrements the argument count.
+ (*argc)--;
+
+ // We also need to decrement the iterator as we just removed
+ // an element.
+ i--;
+ } else if (arg_string == "--help" || arg_string == "-h" ||
+ arg_string == "-?" || arg_string == "/?" ||
+ HasGoogleTestFlagPrefix(arg)) {
+ // Both help flag and unrecognized Google Test flags (excluding
+ // internal ones) trigger help display.
+ g_help_flag = true;
+ }
+ }
+
+ if (g_help_flag) {
+ // We print the help here instead of in RUN_ALL_TESTS(), as the
+ // latter may not be called at all if the user is using Google
+ // Test with another testing framework.
+ PrintColorEncoded(kColorEncodedHelpMessage);
+ }
+}
+
+// Parses the command line for Google Test flags, without initializing
+// other parts of Google Test.
+void ParseGoogleTestFlagsOnly(int* argc, char** argv) {
+ ParseGoogleTestFlagsOnlyImpl(argc, argv);
+}
+void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) {
+ ParseGoogleTestFlagsOnlyImpl(argc, argv);
+}
+
+// The internal implementation of InitGoogleTest().
+//
+// The type parameter CharType can be instantiated to either char or
+// wchar_t.
+template <typename CharType>
+void InitGoogleTestImpl(int* argc, CharType** argv) {
+ g_init_gtest_count++;
+
+ // We don't want to run the initialization code twice.
+ if (g_init_gtest_count != 1) return;
+
+ if (*argc <= 0) return;
+
+ internal::g_executable_path = internal::StreamableToString(argv[0]);
+
+#if GTEST_HAS_DEATH_TEST
+
+ g_argvs.clear();
+ for (int i = 0; i != *argc; i++) {
+ g_argvs.push_back(StreamableToString(argv[i]));
+ }
+
+#endif // GTEST_HAS_DEATH_TEST
+
+ ParseGoogleTestFlagsOnly(argc, argv);
+ GetUnitTestImpl()->PostFlagParsingInit();
+}
+
+} // namespace internal
+
+// Initializes Google Test. This must be called before calling
+// RUN_ALL_TESTS(). In particular, it parses a command line for the
+// flags that Google Test recognizes. Whenever a Google Test flag is
+// seen, it is removed from argv, and *argc is decremented.
+//
+// No value is returned. Instead, the Google Test flag variables are
+// updated.
+//
+// Calling the function for the second time has no user-visible effect.
+void InitGoogleTest(int* argc, char** argv) {
+ internal::InitGoogleTestImpl(argc, argv);
+}
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+void InitGoogleTest(int* argc, wchar_t** argv) {
+ internal::InitGoogleTestImpl(argc, argv);
+}
+
+} // namespace testing
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: ***@google.com (Zhanyong Wan), ***@google.com (Vlad Losev)
+//
+// This file implements death tests.
+
+
+#if GTEST_HAS_DEATH_TEST
+
+# if GTEST_OS_MAC
+# include <crt_externs.h>
+# endif // GTEST_OS_MAC
+
+# include <errno.h>
+# include <fcntl.h>
+# include <limits.h>
+# include <stdarg.h>
+
+# if GTEST_OS_WINDOWS
+# include <windows.h>
+# else
+# include <sys/mman.h>
+# include <sys/wait.h>
+# endif // GTEST_OS_WINDOWS
+
+#endif // GTEST_HAS_DEATH_TEST
+
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+#undef GTEST_IMPLEMENTATION_
+
+namespace testing {
+
+// Constants.
+
+// The default death test style.
+static const char kDefaultDeathTestStyle[] = "fast";
+
+GTEST_DEFINE_string_(
+ death_test_style,
+ internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle),
+ "Indicates how to run a death test in a forked child process: "
+ "\"threadsafe\" (child process re-executes the test binary "
+ "from the beginning, running only the specific death test) or "
+ "\"fast\" (child process runs the death test immediately "
+ "after forking).");
+
+GTEST_DEFINE_bool_(
+ death_test_use_fork,
+ internal::BoolFromGTestEnv("death_test_use_fork", false),
+ "Instructs to use fork()/_exit() instead of clone() in death tests. "
+ "Ignored and always uses fork() on POSIX systems where clone() is not "
+ "implemented. Useful when running under valgrind or similar tools if "
+ "those do not support clone(). Valgrind 3.3.1 will just fail if "
+ "it sees an unsupported combination of clone() flags. "
+ "It is not recommended to use this flag w/o valgrind though it will "
+ "work in 99% of the cases. Once valgrind is fixed, this flag will "
+ "most likely be removed.");
+
+namespace internal {
+GTEST_DEFINE_string_(
+ internal_run_death_test, "",
+ "Indicates the file, line number, temporal index of "
+ "the single death test to run, and a file descriptor to "
+ "which a success code may be sent, all separated by "
+ "colons. This flag is specified if and only if the current "
+ "process is a sub-process launched for running a thread-safe "
+ "death test. FOR INTERNAL USE ONLY.");
+} // namespace internal
+
+#if GTEST_HAS_DEATH_TEST
+
+// ExitedWithCode constructor.
+ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) {
+}
+
+// ExitedWithCode function-call operator.
+bool ExitedWithCode::operator()(int exit_status) const {
+# if GTEST_OS_WINDOWS
+
+ return exit_status == exit_code_;
+
+# else
+
+ return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_;
+
+# endif // GTEST_OS_WINDOWS
+}
+
+# if !GTEST_OS_WINDOWS
+// KilledBySignal constructor.
+KilledBySignal::KilledBySignal(int signum) : signum_(signum) {
+}
+
+// KilledBySignal function-call operator.
+bool KilledBySignal::operator()(int exit_status) const {
+ return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_;
+}
+# endif // !GTEST_OS_WINDOWS
+
+namespace internal {
+
+// Utilities needed for death tests.
+
+// Generates a textual description of a given exit code, in the format
+// specified by wait(2).
+static String ExitSummary(int exit_code) {
+ Message m;
+
+# if GTEST_OS_WINDOWS
+
+ m << "Exited with exit status " << exit_code;
+
+# else
+
+ if (WIFEXITED(exit_code)) {
+ m << "Exited with exit status " << WEXITSTATUS(exit_code);
+ } else if (WIFSIGNALED(exit_code)) {
+ m << "Terminated by signal " << WTERMSIG(exit_code);
+ }
+# ifdef WCOREDUMP
+ if (WCOREDUMP(exit_code)) {
+ m << " (core dumped)";
+ }
+# endif
+# endif // GTEST_OS_WINDOWS
+
+ return m.GetString();
+}
+
+// Returns true if exit_status describes a process that was terminated
+// by a signal, or exited normally with a nonzero exit code.
+bool ExitedUnsuccessfully(int exit_status) {
+ return !ExitedWithCode(0)(exit_status);
+}
+
+# if !GTEST_OS_WINDOWS
+// Generates a textual failure message when a death test finds more than
+// one thread running, or cannot determine the number of threads, prior
+// to executing the given statement. It is the responsibility of the
+// caller not to pass a thread_count of 1.
+static String DeathTestThreadWarning(size_t thread_count) {
+ Message msg;
+ msg << "Death tests use fork(), which is unsafe particularly"
+ << " in a threaded context. For this test, " << GTEST_NAME_ << " ";
+ if (thread_count == 0)
+ msg << "couldn't detect the number of threads.";
+ else
+ msg << "detected " << thread_count << " threads.";
+ return msg.GetString();
+}
+# endif // !GTEST_OS_WINDOWS
+
+// Flag characters for reporting a death test that did not die.
+static const char kDeathTestLived = 'L';
+static const char kDeathTestReturned = 'R';
+static const char kDeathTestThrew = 'T';
+static const char kDeathTestInternalError = 'I';
+
+// An enumeration describing all of the possible ways that a death test can
+// conclude. DIED means that the process died while executing the test
+// code; LIVED means that process lived beyond the end of the test code;
+// RETURNED means that the test statement attempted to execute a return
+// statement, which is not allowed; THREW means that the test statement
+// returned control by throwing an exception. IN_PROGRESS means the test
+// has not yet concluded.
+// TODO(***@google.com): Unify names and possibly values for
+// AbortReason, DeathTestOutcome, and flag characters above.
+enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW };
+
+// Routine for aborting the program which is safe to call from an
+// exec-style death test child process, in which case the error
+// message is propagated back to the parent process. Otherwise, the
+// message is simply printed to stderr. In either case, the program
+// then exits with status 1.
+void DeathTestAbort(const String& message) {
+ // On a POSIX system, this function may be called from a threadsafe-style
+ // death test child process, which operates on a very small stack. Use
+ // the heap for any additional non-minuscule memory requirements.
+ const InternalRunDeathTestFlag* const flag =
+ GetUnitTestImpl()->internal_run_death_test_flag();
+ if (flag != NULL) {
+ FILE* parent = posix::FDOpen(flag->write_fd(), "w");
+ fputc(kDeathTestInternalError, parent);
+ fprintf(parent, "%s", message.c_str());
+ fflush(parent);
+ _exit(1);
+ } else {
+ fprintf(stderr, "%s", message.c_str());
+ fflush(stderr);
+ posix::Abort();
+ }
+}
+
+// A replacement for CHECK that calls DeathTestAbort if the assertion
+// fails.
+# define GTEST_DEATH_TEST_CHECK_(expression) \
+ do { \
+ if (!::testing::internal::IsTrue(expression)) { \
+ DeathTestAbort(::testing::internal::String::Format( \
+ "CHECK failed: File %s, line %d: %s", \
+ __FILE__, __LINE__, #expression)); \
+ } \
+ } while (::testing::internal::AlwaysFalse())
+
+// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for
+// evaluating any system call that fulfills two conditions: it must return
+// -1 on failure, and set errno to EINTR when it is interrupted and
+// should be tried again. The macro expands to a loop that repeatedly
+// evaluates the expression as long as it evaluates to -1 and sets
+// errno to EINTR. If the expression evaluates to -1 but errno is
+// something other than EINTR, DeathTestAbort is called.
+# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \
+ do { \
+ int gtest_retval; \
+ do { \
+ gtest_retval = (expression); \
+ } while (gtest_retval == -1 && errno == EINTR); \
+ if (gtest_retval == -1) { \
+ DeathTestAbort(::testing::internal::String::Format( \
+ "CHECK failed: File %s, line %d: %s != -1", \
+ __FILE__, __LINE__, #expression)); \
+ } \
+ } while (::testing::internal::AlwaysFalse())
+
+// Returns the message describing the last system error in errno.
+String GetLastErrnoDescription() {
+ return String(errno == 0 ? "" : posix::StrError(errno));
+}
+
+// This is called from a death test parent process to read a failure
+// message from the death test child process and log it with the FATAL
+// severity. On Windows, the message is read from a pipe handle. On other
+// platforms, it is read from a file descriptor.
+static void FailFromInternalError(int fd) {
+ Message error;
+ char buffer[256];
+ int num_read;
+
+ do {
+ while ((num_read = posix::Read(fd, buffer, 255)) > 0) {
+ buffer[num_read] = '\0';
+ error << buffer;
+ }
+ } while (num_read == -1 && errno == EINTR);
+
+ if (num_read == 0) {
+ GTEST_LOG_(FATAL) << error.GetString();
+ } else {
+ const int last_error = errno;
+ GTEST_LOG_(FATAL) << "Error while reading death test internal: "
+ << GetLastErrnoDescription() << " [" << last_error << "]";
+ }
+}
+
+// Death test constructor. Increments the running death test count
+// for the current test.
+DeathTest::DeathTest() {
+ TestInfo* const info = GetUnitTestImpl()->current_test_info();
+ if (info == NULL) {
+ DeathTestAbort("Cannot run a death test outside of a TEST or "
+ "TEST_F construct");
+ }
+}
+
+// Creates and returns a death test by dispatching to the current
+// death test factory.
+bool DeathTest::Create(const char* statement, const RE* regex,
+ const char* file, int line, DeathTest** test) {
+ return GetUnitTestImpl()->death_test_factory()->Create(
+ statement, regex, file, line, test);
+}
+
+const char* DeathTest::LastMessage() {
+ return last_death_test_message_.c_str();
+}
+
+void DeathTest::set_last_death_test_message(const String& message) {
+ last_death_test_message_ = message;
+}
+
+String DeathTest::last_death_test_message_;
+
+// Provides cross platform implementation for some death functionality.
+class DeathTestImpl : public DeathTest {
+ protected:
+ DeathTestImpl(const char* a_statement, const RE* a_regex)
+ : statement_(a_statement),
+ regex_(a_regex),
+ spawned_(false),
+ status_(-1),
+ outcome_(IN_PROGRESS),
+ read_fd_(-1),
+ write_fd_(-1) {}
+
+ // read_fd_ is expected to be closed and cleared by a derived class.
+ ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); }
+
+ void Abort(AbortReason reason);
+ virtual bool Passed(bool status_ok);
+
+ const char* statement() const { return statement_; }
+ const RE* regex() const { return regex_; }
+ bool spawned() const { return spawned_; }
+ void set_spawned(bool is_spawned) { spawned_ = is_spawned; }
+ int status() const { return status_; }
+ void set_status(int a_status) { status_ = a_status; }
+ DeathTestOutcome outcome() const { return outcome_; }
+ void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; }
+ int read_fd() const { return read_fd_; }
+ void set_read_fd(int fd) { read_fd_ = fd; }
+ int write_fd() const { return write_fd_; }
+ void set_write_fd(int fd) { write_fd_ = fd; }
+
+ // Called in the parent process only. Reads the result code of the death
+ // test child process via a pipe, interprets it to set the outcome_
+ // member, and closes read_fd_. Outputs diagnostics and terminates in
+ // case of unexpected codes.
+ void ReadAndInterpretStatusByte();
+
+ private:
+ // The textual content of the code this object is testing. This class
+ // doesn't own this string and should not attempt to delete it.
+ const char* const statement_;
+ // The regular expression which test output must match. DeathTestImpl
+ // doesn't own this object and should not attempt to delete it.
+ const RE* const regex_;
+ // True if the death test child process has been successfully spawned.
+ bool spawned_;
+ // The exit status of the child process.
+ int status_;
+ // How the death test concluded.
+ DeathTestOutcome outcome_;
+ // Descriptor to the read end of the pipe to the child process. It is
+ // always -1 in the child process. The child keeps its write end of the
+ // pipe in write_fd_.
+ int read_fd_;
+ // Descriptor to the child's write end of the pipe to the parent process.
+ // It is always -1 in the parent process. The parent keeps its end of the
+ // pipe in read_fd_.
+ int write_fd_;
+};
+
+// Called in the parent process only. Reads the result code of the death
+// test child process via a pipe, interprets it to set the outcome_
+// member, and closes read_fd_. Outputs diagnostics and terminates in
+// case of unexpected codes.
+void DeathTestImpl::ReadAndInterpretStatusByte() {
+ char flag;
+ int bytes_read;
+
+ // The read() here blocks until data is available (signifying the
+ // failure of the death test) or until the pipe is closed (signifying
+ // its success), so it's okay to call this in the parent before
+ // the child process has exited.
+ do {
+ bytes_read = posix::Read(read_fd(), &flag, 1);
+ } while (bytes_read == -1 && errno == EINTR);
+
+ if (bytes_read == 0) {
+ set_outcome(DIED);
+ } else if (bytes_read == 1) {
+ switch (flag) {
+ case kDeathTestReturned:
+ set_outcome(RETURNED);
+ break;
+ case kDeathTestThrew:
+ set_outcome(THREW);
+ break;
+ case kDeathTestLived:
+ set_outcome(LIVED);
+ break;
+ case kDeathTestInternalError:
+ FailFromInternalError(read_fd()); // Does not return.
+ break;
+ default:
+ GTEST_LOG_(FATAL) << "Death test child process reported "
+ << "unexpected status byte ("
+ << static_cast<unsigned int>(flag) << ")";
+ }
+ } else {
+ GTEST_LOG_(FATAL) << "Read from death test child process failed: "
+ << GetLastErrnoDescription();
+ }
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd()));
+ set_read_fd(-1);
+}
+
+// Signals that the death test code which should have exited, didn't.
+// Should be called only in a death test child process.
+// Writes a status byte to the child's status file descriptor, then
+// calls _exit(1).
+void DeathTestImpl::Abort(AbortReason reason) {
+ // The parent process considers the death test to be a failure if
+ // it finds any data in our pipe. So, here we write a single flag byte
+ // to the pipe, then exit.
+ const char status_ch =
+ reason == TEST_DID_NOT_DIE ? kDeathTestLived :
+ reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned;
+
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1));
+ // We are leaking the descriptor here because on some platforms (i.e.,
+ // when built as Windows DLL), destructors of global objects will still
+ // run after calling _exit(). On such systems, write_fd_ will be
+ // indirectly closed from the destructor of UnitTestImpl, causing double
+ // close if it is also closed here. On debug configurations, double close
+ // may assert. As there are no in-process buffers to flush here, we are
+ // relying on the OS to close the descriptor after the process terminates
+ // when the destructors are not run.
+ _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash)
+}
+
+// Returns an indented copy of stderr output for a death test.
+// This makes distinguishing death test output lines from regular log lines
+// much easier.
+static ::std::string FormatDeathTestOutput(const ::std::string& output) {
+ ::std::string ret;
+ for (size_t at = 0; ; ) {
+ const size_t line_end = output.find('\n', at);
+ ret += "[ DEATH ] ";
+ if (line_end == ::std::string::npos) {
+ ret += output.substr(at);
+ break;
+ }
+ ret += output.substr(at, line_end + 1 - at);
+ at = line_end + 1;
+ }
+ return ret;
+}
+
+// Assesses the success or failure of a death test, using both private
+// members which have previously been set, and one argument:
+//
+// Private data members:
+// outcome: An enumeration describing how the death test
+// concluded: DIED, LIVED, THREW, or RETURNED. The death test
+// fails in the latter three cases.
+// status: The exit status of the child process. On *nix, it is in the
+// in the format specified by wait(2). On Windows, this is the
+// value supplied to the ExitProcess() API or a numeric code
+// of the exception that terminated the program.
+// regex: A regular expression object to be applied to
+// the test's captured standard error output; the death test
+// fails if it does not match.
+//
+// Argument:
+// status_ok: true if exit_status is acceptable in the context of
+// this particular death test, which fails if it is false
+//
+// Returns true iff all of the above conditions are met. Otherwise, the
+// first failing condition, in the order given above, is the one that is
+// reported. Also sets the last death test message string.
+bool DeathTestImpl::Passed(bool status_ok) {
+ if (!spawned())
+ return false;
+
+ const String error_message = GetCapturedStderr();
+
+ bool success = false;
+ Message buffer;
+
+ buffer << "Death test: " << statement() << "\n";
+ switch (outcome()) {
+ case LIVED:
+ buffer << " Result: failed to die.\n"
+ << " Error msg:\n" << FormatDeathTestOutput(error_message);
+ break;
+ case THREW:
+ buffer << " Result: threw an exception.\n"
+ << " Error msg:\n" << FormatDeathTestOutput(error_message);
+ break;
+ case RETURNED:
+ buffer << " Result: illegal return in test statement.\n"
+ << " Error msg:\n" << FormatDeathTestOutput(error_message);
+ break;
+ case DIED:
+ if (status_ok) {
+ const bool matched = RE::PartialMatch(error_message.c_str(), *regex());
+ if (matched) {
+ success = true;
+ } else {
+ buffer << " Result: died but not with expected error.\n"
+ << " Expected: " << regex()->pattern() << "\n"
+ << "Actual msg:\n" << FormatDeathTestOutput(error_message);
+ }
+ } else {
+ buffer << " Result: died but not with expected exit code:\n"
+ << " " << ExitSummary(status()) << "\n"
+ << "Actual msg:\n" << FormatDeathTestOutput(error_message);
+ }
+ break;
+ case IN_PROGRESS:
+ default:
+ GTEST_LOG_(FATAL)
+ << "DeathTest::Passed somehow called before conclusion of test";
+ }
+
+ DeathTest::set_last_death_test_message(buffer.GetString());
+ return success;
+}
+
+# if GTEST_OS_WINDOWS
+// WindowsDeathTest implements death tests on Windows. Due to the
+// specifics of starting new processes on Windows, death tests there are
+// always threadsafe, and Google Test considers the
+// --gtest_death_test_style=fast setting to be equivalent to
+// --gtest_death_test_style=threadsafe there.
+//
+// A few implementation notes: Like the Linux version, the Windows
+// implementation uses pipes for child-to-parent communication. But due to
+// the specifics of pipes on Windows, some extra steps are required:
+//
+// 1. The parent creates a communication pipe and stores handles to both
+// ends of it.
+// 2. The parent starts the child and provides it with the information
+// necessary to acquire the handle to the write end of the pipe.
+// 3. The child acquires the write end of the pipe and signals the parent
+// using a Windows event.
+// 4. Now the parent can release the write end of the pipe on its side. If
+// this is done before step 3, the object's reference count goes down to
+// 0 and it is destroyed, preventing the child from acquiring it. The
+// parent now has to release it, or read operations on the read end of
+// the pipe will not return when the child terminates.
+// 5. The parent reads child's output through the pipe (outcome code and
+// any possible error messages) from the pipe, and its stderr and then
+// determines whether to fail the test.
+//
+// Note: to distinguish Win32 API calls from the local method and function
+// calls, the former are explicitly resolved in the global namespace.
+//
+class WindowsDeathTest : public DeathTestImpl {
+ public:
+ WindowsDeathTest(const char* a_statement,
+ const RE* a_regex,
+ const char* file,
+ int line)
+ : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {}
+
+ // All of these virtual functions are inherited from DeathTest.
+ virtual int Wait();
+ virtual TestRole AssumeRole();
+
+ private:
+ // The name of the file in which the death test is located.
+ const char* const file_;
+ // The line number on which the death test is located.
+ const int line_;
+ // Handle to the write end of the pipe to the child process.
+ AutoHandle write_handle_;
+ // Child process handle.
+ AutoHandle child_handle_;
+ // Event the child process uses to signal the parent that it has
+ // acquired the handle to the write end of the pipe. After seeing this
+ // event the parent can release its own handles to make sure its
+ // ReadFile() calls return when the child terminates.
+ AutoHandle event_handle_;
+};
+
+// Waits for the child in a death test to exit, returning its exit
+// status, or 0 if no child process exists. As a side effect, sets the
+// outcome data member.
+int WindowsDeathTest::Wait() {
+ if (!spawned())
+ return 0;
+
+ // Wait until the child either signals that it has acquired the write end
+ // of the pipe or it dies.
+ const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() };
+ switch (::WaitForMultipleObjects(2,
+ wait_handles,
+ FALSE, // Waits for any of the handles.
+ INFINITE)) {
+ case WAIT_OBJECT_0:
+ case WAIT_OBJECT_0 + 1:
+ break;
+ default:
+ GTEST_DEATH_TEST_CHECK_(false); // Should not get here.
+ }
+
+ // The child has acquired the write end of the pipe or exited.
+ // We release the handle on our side and continue.
+ write_handle_.Reset();
+ event_handle_.Reset();
+
+ ReadAndInterpretStatusByte();
+
+ // Waits for the child process to exit if it haven't already. This
+ // returns immediately if the child has already exited, regardless of
+ // whether previous calls to WaitForMultipleObjects synchronized on this
+ // handle or not.
+ GTEST_DEATH_TEST_CHECK_(
+ WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(),
+ INFINITE));
+ DWORD status_code;
+ GTEST_DEATH_TEST_CHECK_(
+ ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE);
+ child_handle_.Reset();
+ set_status(static_cast<int>(status_code));
+ return status();
+}
+
+// The AssumeRole process for a Windows death test. It creates a child
+// process with the same executable as the current process to run the
+// death test. The child process is given the --gtest_filter and
+// --gtest_internal_run_death_test flags such that it knows to run the
+// current death test only.
+DeathTest::TestRole WindowsDeathTest::AssumeRole() {
+ const UnitTestImpl* const impl = GetUnitTestImpl();
+ const InternalRunDeathTestFlag* const flag =
+ impl->internal_run_death_test_flag();
+ const TestInfo* const info = impl->current_test_info();
+ const int death_test_index = info->result()->death_test_count();
+
+ if (flag != NULL) {
+ // ParseInternalRunDeathTestFlag() has performed all the necessary
+ // processing.
+ set_write_fd(flag->write_fd());
+ return EXECUTE_TEST;
+ }
+
+ // WindowsDeathTest uses an anonymous pipe to communicate results of
+ // a death test.
+ SECURITY_ATTRIBUTES handles_are_inheritable = {
+ sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
+ HANDLE read_handle, write_handle;
+ GTEST_DEATH_TEST_CHECK_(
+ ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable,
+ 0) // Default buffer size.
+ != FALSE);
+ set_read_fd(::_open_osfhandle(reinterpret_cast<intptr_t>(read_handle),
+ O_RDONLY));
+ write_handle_.Reset(write_handle);
+ event_handle_.Reset(::CreateEvent(
+ &handles_are_inheritable,
+ TRUE, // The event will automatically reset to non-signaled state.
+ FALSE, // The initial state is non-signalled.
+ NULL)); // The even is unnamed.
+ GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL);
+ const String filter_flag = String::Format("--%s%s=%s.%s",
+ GTEST_FLAG_PREFIX_, kFilterFlag,
+ info->test_case_name(),
+ info->name());
+ const String internal_flag = String::Format(
+ "--%s%s=%s|%d|%d|%u|%Iu|%Iu",
+ GTEST_FLAG_PREFIX_,
+ kInternalRunDeathTestFlag,
+ file_, line_,
+ death_test_index,
+ static_cast<unsigned int>(::GetCurrentProcessId()),
+ // size_t has the same with as pointers on both 32-bit and 64-bit
+ // Windows platforms.
+ // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx.
+ reinterpret_cast<size_t>(write_handle),
+ reinterpret_cast<size_t>(event_handle_.Get()));
+
+ char executable_path[_MAX_PATH + 1]; // NOLINT
+ GTEST_DEATH_TEST_CHECK_(
+ _MAX_PATH + 1 != ::GetModuleFileNameA(NULL,
+ executable_path,
+ _MAX_PATH));
+
+ String command_line = String::Format("%s %s \"%s\"",
+ ::GetCommandLineA(),
+ filter_flag.c_str(),
+ internal_flag.c_str());
+
+ DeathTest::set_last_death_test_message("");
+
+ CaptureStderr();
+ // Flush the log buffers since the log streams are shared with the child.
+ FlushInfoLog();
+
+ // The child process will share the standard handles with the parent.
+ STARTUPINFOA startup_info;
+ memset(&startup_info, 0, sizeof(STARTUPINFO));
+ startup_info.dwFlags = STARTF_USESTDHANDLES;
+ startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE);
+ startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
+ startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
+
+ PROCESS_INFORMATION process_info;
+ GTEST_DEATH_TEST_CHECK_(::CreateProcessA(
+ executable_path,
+ const_cast<char*>(command_line.c_str()),
+ NULL, // Retuned process handle is not inheritable.
+ NULL, // Retuned thread handle is not inheritable.
+ TRUE, // Child inherits all inheritable handles (for write_handle_).
+ 0x0, // Default creation flags.
+ NULL, // Inherit the parent's environment.
+ UnitTest::GetInstance()->original_working_dir(),
+ &startup_info,
+ &process_info) != FALSE);
+ child_handle_.Reset(process_info.hProcess);
+ ::CloseHandle(process_info.hThread);
+ set_spawned(true);
+ return OVERSEE_TEST;
+}
+# else // We are not on Windows.
+
+// ForkingDeathTest provides implementations for most of the abstract
+// methods of the DeathTest interface. Only the AssumeRole method is
+// left undefined.
+class ForkingDeathTest : public DeathTestImpl {
+ public:
+ ForkingDeathTest(const char* statement, const RE* regex);
+
+ // All of these virtual functions are inherited from DeathTest.
+ virtual int Wait();
+
+ protected:
+ void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; }
+
+ private:
+ // PID of child process during death test; 0 in the child process itself.
+ pid_t child_pid_;
+};
+
+// Constructs a ForkingDeathTest.
+ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex)
+ : DeathTestImpl(a_statement, a_regex),
+ child_pid_(-1) {}
+
+// Waits for the child in a death test to exit, returning its exit
+// status, or 0 if no child process exists. As a side effect, sets the
+// outcome data member.
+int ForkingDeathTest::Wait() {
+ if (!spawned())
+ return 0;
+
+ ReadAndInterpretStatusByte();
+
+ int status_value;
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0));
+ set_status(status_value);
+ return status_value;
+}
+
+// A concrete death test class that forks, then immediately runs the test
+// in the child process.
+class NoExecDeathTest : public ForkingDeathTest {
+ public:
+ NoExecDeathTest(const char* a_statement, const RE* a_regex) :
+ ForkingDeathTest(a_statement, a_regex) { }
+ virtual TestRole AssumeRole();
+};
+
+// The AssumeRole process for a fork-and-run death test. It implements a
+// straightforward fork, with a simple pipe to transmit the status byte.
+DeathTest::TestRole NoExecDeathTest::AssumeRole() {
+ const size_t thread_count = GetThreadCount();
+ if (thread_count != 1) {
+ GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count);
+ }
+
+ int pipe_fd[2];
+ GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1);
+
+ DeathTest::set_last_death_test_message("");
+ CaptureStderr();
+ // When we fork the process below, the log file buffers are copied, but the
+ // file descriptors are shared. We flush all log files here so that closing
+ // the file descriptors in the child process doesn't throw off the
+ // synchronization between descriptors and buffers in the parent process.
+ // This is as close to the fork as possible to avoid a race condition in case
+ // there are multiple threads running before the death test, and another
+ // thread writes to the log file.
+ FlushInfoLog();
+
+ const pid_t child_pid = fork();
+ GTEST_DEATH_TEST_CHECK_(child_pid != -1);
+ set_child_pid(child_pid);
+ if (child_pid == 0) {
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0]));
+ set_write_fd(pipe_fd[1]);
+ // Redirects all logging to stderr in the child process to prevent
+ // concurrent writes to the log files. We capture stderr in the parent
+ // process and append the child process' output to a log.
+ LogToStderr();
+ // Event forwarding to the listeners of event listener API mush be shut
+ // down in death test subprocesses.
+ GetUnitTestImpl()->listeners()->SuppressEventForwarding();
+ return EXECUTE_TEST;
+ } else {
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1]));
+ set_read_fd(pipe_fd[0]);
+ set_spawned(true);
+ return OVERSEE_TEST;
+ }
+}
+
+// A concrete death test class that forks and re-executes the main
+// program from the beginning, with command-line flags set that cause
+// only this specific death test to be run.
+class ExecDeathTest : public ForkingDeathTest {
+ public:
+ ExecDeathTest(const char* a_statement, const RE* a_regex,
+ const char* file, int line) :
+ ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { }
+ virtual TestRole AssumeRole();
+ private:
+ // The name of the file in which the death test is located.
+ const char* const file_;
+ // The line number on which the death test is located.
+ const int line_;
+};
+
+// Utility class for accumulating command-line arguments.
+class Arguments {
+ public:
+ Arguments() {
+ args_.push_back(NULL);
+ }
+
+ ~Arguments() {
+ for (std::vector<char*>::iterator i = args_.begin(); i != args_.end();
+ ++i) {
+ free(*i);
+ }
+ }
+ void AddArgument(const char* argument) {
+ args_.insert(args_.end() - 1, posix::StrDup(argument));
+ }
+
+ template <typename Str>
+ void AddArguments(const ::std::vector<Str>& arguments) {
+ for (typename ::std::vector<Str>::const_iterator i = arguments.begin();
+ i != arguments.end();
+ ++i) {
+ args_.insert(args_.end() - 1, posix::StrDup(i->c_str()));
+ }
+ }
+ char* const* Argv() {
+ return &args_[0];
+ }
+ private:
+ std::vector<char*> args_;
+};
+
+// A struct that encompasses the arguments to the child process of a
+// threadsafe-style death test process.
+struct ExecDeathTestArgs {
+ char* const* argv; // Command-line arguments for the child's call to exec
+ int close_fd; // File descriptor to close; the read end of a pipe
+};
+
+# if GTEST_OS_MAC
+inline char** GetEnviron() {
+ // When Google Test is built as a framework on MacOS X, the environ variable
+ // is unavailable. Apple's documentation (man environ) recommends using
+ // _NSGetEnviron() instead.
+ return *_NSGetEnviron();
+}
+# else
+// Some POSIX platforms expect you to declare environ. extern "C" makes
+// it reside in the global namespace.
+extern "C" char** environ;
+inline char** GetEnviron() { return environ; }
+# endif // GTEST_OS_MAC
+
+// The main function for a threadsafe-style death test child process.
+// This function is called in a clone()-ed process and thus must avoid
+// any potentially unsafe operations like malloc or libc functions.
+static int ExecDeathTestChildMain(void* child_arg) {
+ ExecDeathTestArgs* const args = static_cast<ExecDeathTestArgs*>(child_arg);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd));
+
+ // We need to execute the test program in the same environment where
+ // it was originally invoked. Therefore we change to the original
+ // working directory first.
+ const char* const original_dir =
+ UnitTest::GetInstance()->original_working_dir();
+ // We can safely call chdir() as it's a direct system call.
+ if (chdir(original_dir) != 0) {
+ DeathTestAbort(String::Format("chdir(\"%s\") failed: %s",
+ original_dir,
+ GetLastErrnoDescription().c_str()));
+ return EXIT_FAILURE;
+ }
+
+ // We can safely call execve() as it's a direct system call. We
+ // cannot use execvp() as it's a libc function and thus potentially
+ // unsafe. Since execve() doesn't search the PATH, the user must
+ // invoke the test program via a valid path that contains at least
+ // one path separator.
+ execve(args->argv[0], args->argv, GetEnviron());
+ DeathTestAbort(String::Format("execve(%s, ...) in %s failed: %s",
+ args->argv[0],
+ original_dir,
+ GetLastErrnoDescription().c_str()));
+ return EXIT_FAILURE;
+}
+
+// Two utility routines that together determine the direction the stack
+// grows.
+// This could be accomplished more elegantly by a single recursive
+// function, but we want to guard against the unlikely possibility of
+// a smart compiler optimizing the recursion away.
+//
+// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining
+// StackLowerThanAddress into StackGrowsDown, which then doesn't give
+// correct answer.
+bool StackLowerThanAddress(const void* ptr) GTEST_NO_INLINE_;
+bool StackLowerThanAddress(const void* ptr) {
+ int dummy;
+ return &dummy < ptr;
+}
+
+bool StackGrowsDown() {
+ int dummy;
+ return StackLowerThanAddress(&dummy);
+}
+
+// A threadsafe implementation of fork(2) for threadsafe-style death tests
+// that uses clone(2). It dies with an error message if anything goes
+// wrong.
+static pid_t ExecDeathTestFork(char* const* argv, int close_fd) {
+ ExecDeathTestArgs args = { argv, close_fd };
+ pid_t child_pid = -1;
+
+# if GTEST_HAS_CLONE
+ const bool use_fork = GTEST_FLAG(death_test_use_fork);
+
+ if (!use_fork) {
+ static const bool stack_grows_down = StackGrowsDown();
+ const size_t stack_size = getpagesize();
+ // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead.
+ void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED);
+ void* const stack_top =
+ static_cast<char*>(stack) + (stack_grows_down ? stack_size : 0);
+
+ child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args);
+
+ GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1);
+ }
+# else
+ const bool use_fork = true;
+# endif // GTEST_HAS_CLONE
+
+ if (use_fork && (child_pid = fork()) == 0) {
+ ExecDeathTestChildMain(&args);
+ _exit(0);
+ }
+
+ GTEST_DEATH_TEST_CHECK_(child_pid != -1);
+ return child_pid;
+}
+
+// The AssumeRole process for a fork-and-exec death test. It re-executes the
+// main program from the beginning, setting the --gtest_filter
+// and --gtest_internal_run_death_test flags to cause only the current
+// death test to be re-run.
+DeathTest::TestRole ExecDeathTest::AssumeRole() {
+ const UnitTestImpl* const impl = GetUnitTestImpl();
+ const InternalRunDeathTestFlag* const flag =
+ impl->internal_run_death_test_flag();
+ const TestInfo* const info = impl->current_test_info();
+ const int death_test_index = info->result()->death_test_count();
+
+ if (flag != NULL) {
+ set_write_fd(flag->write_fd());
+ return EXECUTE_TEST;
+ }
+
+ int pipe_fd[2];
+ GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1);
+ // Clear the close-on-exec flag on the write end of the pipe, lest
+ // it be closed when the child process does an exec:
+ GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1);
+
+ const String filter_flag =
+ String::Format("--%s%s=%s.%s",
+ GTEST_FLAG_PREFIX_, kFilterFlag,
+ info->test_case_name(), info->name());
+ const String internal_flag =
+ String::Format("--%s%s=%s|%d|%d|%d",
+ GTEST_FLAG_PREFIX_, kInternalRunDeathTestFlag,
+ file_, line_, death_test_index, pipe_fd[1]);
+ Arguments args;
+ args.AddArguments(GetArgvs());
+ args.AddArgument(filter_flag.c_str());
+ args.AddArgument(internal_flag.c_str());
+
+ DeathTest::set_last_death_test_message("");
+
+ CaptureStderr();
+ // See the comment in NoExecDeathTest::AssumeRole for why the next line
+ // is necessary.
+ FlushInfoLog();
+
+ const pid_t child_pid = ExecDeathTestFork(args.Argv(), pipe_fd[0]);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1]));
+ set_child_pid(child_pid);
+ set_read_fd(pipe_fd[0]);
+ set_spawned(true);
+ return OVERSEE_TEST;
+}
+
+# endif // !GTEST_OS_WINDOWS
+
+// Creates a concrete DeathTest-derived class that depends on the
+// --gtest_death_test_style flag, and sets the pointer pointed to
+// by the "test" argument to its address. If the test should be
+// skipped, sets that pointer to NULL. Returns true, unless the
+// flag is set to an invalid value.
+bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
+ const char* file, int line,
+ DeathTest** test) {
+ UnitTestImpl* const impl = GetUnitTestImpl();
+ const InternalRunDeathTestFlag* const flag =
+ impl->internal_run_death_test_flag();
+ const int death_test_index = impl->current_test_info()
+ ->increment_death_test_count();
+
+ if (flag != NULL) {
+ if (death_test_index > flag->index()) {
+ DeathTest::set_last_death_test_message(String::Format(
+ "Death test count (%d) somehow exceeded expected maximum (%d)",
+ death_test_index, flag->index()));
+ return false;
+ }
+
+ if (!(flag->file() == file && flag->line() == line &&
+ flag->index() == death_test_index)) {
+ *test = NULL;
+ return true;
+ }
+ }
+
+# if GTEST_OS_WINDOWS
+
+ if (GTEST_FLAG(death_test_style) == "threadsafe" ||
+ GTEST_FLAG(death_test_style) == "fast") {
+ *test = new WindowsDeathTest(statement, regex, file, line);
+ }
+
+# else
+
+ if (GTEST_FLAG(death_test_style) == "threadsafe") {
+ *test = new ExecDeathTest(statement, regex, file, line);
+ } else if (GTEST_FLAG(death_test_style) == "fast") {
+ *test = new NoExecDeathTest(statement, regex);
+ }
+
+# endif // GTEST_OS_WINDOWS
+
+ else { // NOLINT - this is more readable than unbalanced brackets inside #if.
+ DeathTest::set_last_death_test_message(String::Format(
+ "Unknown death test style \"%s\" encountered",
+ GTEST_FLAG(death_test_style).c_str()));
+ return false;
+ }
+
+ return true;
+}
+
+// Splits a given string on a given delimiter, populating a given
+// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have
+// ::std::string, so we can use it here.
+static void SplitString(const ::std::string& str, char delimiter,
+ ::std::vector< ::std::string>* dest) {
+ ::std::vector< ::std::string> parsed;
+ ::std::string::size_type pos = 0;
+ while (::testing::internal::AlwaysTrue()) {
+ const ::std::string::size_type colon = str.find(delimiter, pos);
+ if (colon == ::std::string::npos) {
+ parsed.push_back(str.substr(pos));
+ break;
+ } else {
+ parsed.push_back(str.substr(pos, colon - pos));
+ pos = colon + 1;
+ }
+ }
+ dest->swap(parsed);
+}
+
+# if GTEST_OS_WINDOWS
+// Recreates the pipe and event handles from the provided parameters,
+// signals the event, and returns a file descriptor wrapped around the pipe
+// handle. This function is called in the child process only.
+int GetStatusFileDescriptor(unsigned int parent_process_id,
+ size_t write_handle_as_size_t,
+ size_t event_handle_as_size_t) {
+ AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE,
+ FALSE, // Non-inheritable.
+ parent_process_id));
+ if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) {
+ DeathTestAbort(String::Format("Unable to open parent process %u",
+ parent_process_id));
+ }
+
+ // TODO(***@google.com): Replace the following check with a
+ // compile-time assertion when available.
+ GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t));
+
+ const HANDLE write_handle =
+ reinterpret_cast<HANDLE>(write_handle_as_size_t);
+ HANDLE dup_write_handle;
+
+ // The newly initialized handle is accessible only in in the parent
+ // process. To obtain one accessible within the child, we need to use
+ // DuplicateHandle.
+ if (!::DuplicateHandle(parent_process_handle.Get(), write_handle,
+ ::GetCurrentProcess(), &dup_write_handle,
+ 0x0, // Requested privileges ignored since
+ // DUPLICATE_SAME_ACCESS is used.
+ FALSE, // Request non-inheritable handler.
+ DUPLICATE_SAME_ACCESS)) {
+ DeathTestAbort(String::Format(
+ "Unable to duplicate the pipe handle %Iu from the parent process %u",
+ write_handle_as_size_t, parent_process_id));
+ }
+
+ const HANDLE event_handle = reinterpret_cast<HANDLE>(event_handle_as_size_t);
+ HANDLE dup_event_handle;
+
+ if (!::DuplicateHandle(parent_process_handle.Get(), event_handle,
+ ::GetCurrentProcess(), &dup_event_handle,
+ 0x0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ DeathTestAbort(String::Format(
+ "Unable to duplicate the event handle %Iu from the parent process %u",
+ event_handle_as_size_t, parent_process_id));
+ }
+
+ const int write_fd =
+ ::_open_osfhandle(reinterpret_cast<intptr_t>(dup_write_handle), O_APPEND);
+ if (write_fd == -1) {
+ DeathTestAbort(String::Format(
+ "Unable to convert pipe handle %Iu to a file descriptor",
+ write_handle_as_size_t));
+ }
+
+ // Signals the parent that the write end of the pipe has been acquired
+ // so the parent can release its own write end.
+ ::SetEvent(dup_event_handle);
+
+ return write_fd;
+}
+# endif // GTEST_OS_WINDOWS
+
+// Returns a newly created InternalRunDeathTestFlag object with fields
+// initialized from the GTEST_FLAG(internal_run_death_test) flag if
+// the flag is specified; otherwise returns NULL.
+InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() {
+ if (GTEST_FLAG(internal_run_death_test) == "") return NULL;
+
+ // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we
+ // can use it here.
+ int line = -1;
+ int index = -1;
+ ::std::vector< ::std::string> fields;
+ SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields);
+ int write_fd = -1;
+
+# if GTEST_OS_WINDOWS
+
+ unsigned int parent_process_id = 0;
+ size_t write_handle_as_size_t = 0;
+ size_t event_handle_as_size_t = 0;
+
+ if (fields.size() != 6
+ || !ParseNaturalNumber(fields[1], &line)
+ || !ParseNaturalNumber(fields[2], &index)
+ || !ParseNaturalNumber(fields[3], &parent_process_id)
+ || !ParseNaturalNumber(fields[4], &write_handle_as_size_t)
+ || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) {
+ DeathTestAbort(String::Format(
+ "Bad --gtest_internal_run_death_test flag: %s",
+ GTEST_FLAG(internal_run_death_test).c_str()));
+ }
+ write_fd = GetStatusFileDescriptor(parent_process_id,
+ write_handle_as_size_t,
+ event_handle_as_size_t);
+# else
+
+ if (fields.size() != 4
+ || !ParseNaturalNumber(fields[1], &line)
+ || !ParseNaturalNumber(fields[2], &index)
+ || !ParseNaturalNumber(fields[3], &write_fd)) {
+ DeathTestAbort(String::Format(
+ "Bad --gtest_internal_run_death_test flag: %s",
+ GTEST_FLAG(internal_run_death_test).c_str()));
+ }
+
+# endif // GTEST_OS_WINDOWS
+
+ return new InternalRunDeathTestFlag(fields[0], line, index, write_fd);
+}
+
+} // namespace internal
+
+#endif // GTEST_HAS_DEATH_TEST
+
+} // namespace testing
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: ***@gmail.com (Keith Ray)
+
+
+#include <stdlib.h>
+
+#if GTEST_OS_WINDOWS_MOBILE
+# include <windows.h>
+#elif GTEST_OS_WINDOWS
+# include <direct.h>
+# include <io.h>
+#elif GTEST_OS_SYMBIAN || GTEST_OS_NACL
+// Symbian OpenC and NaCl have PATH_MAX in sys/syslimits.h
+# include <sys/syslimits.h>
+#else
+# include <limits.h>
+# include <climits> // Some Linux distributions define PATH_MAX here.
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+#if GTEST_OS_WINDOWS
+# define GTEST_PATH_MAX_ _MAX_PATH
+#elif defined(PATH_MAX)
+# define GTEST_PATH_MAX_ PATH_MAX
+#elif defined(_XOPEN_PATH_MAX)
+# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX
+#else
+# define GTEST_PATH_MAX_ _POSIX_PATH_MAX
+#endif // GTEST_OS_WINDOWS
+
+
+namespace testing {
+namespace internal {
+
+#if GTEST_OS_WINDOWS
+// On Windows, '\\' is the standard path separator, but many tools and the
+// Windows API also accept '/' as an alternate path separator. Unless otherwise
+// noted, a file path can contain either kind of path separators, or a mixture
+// of them.
+const char kPathSeparator = '\\';
+const char kAlternatePathSeparator = '/';
+const char kPathSeparatorString[] = "\\";
+const char kAlternatePathSeparatorString[] = "/";
+# if GTEST_OS_WINDOWS_MOBILE
+// Windows CE doesn't have a current directory. You should not use
+// the current directory in tests on Windows CE, but this at least
+// provides a reasonable fallback.
+const char kCurrentDirectoryString[] = "\\";
+// Windows CE doesn't define INVALID_FILE_ATTRIBUTES
+const DWORD kInvalidFileAttributes = 0xffffffff;
+# else
+const char kCurrentDirectoryString[] = ".\\";
+# endif // GTEST_OS_WINDOWS_MOBILE
+#else
+const char kPathSeparator = '/';
+const char kPathSeparatorString[] = "/";
+const char kCurrentDirectoryString[] = "./";
+#endif // GTEST_OS_WINDOWS
+
+// Returns whether the given character is a valid path separator.
+static bool IsPathSeparator(char c) {
+#if GTEST_HAS_ALT_PATH_SEP_
+ return (c == kPathSeparator) || (c == kAlternatePathSeparator);
+#else
+ return c == kPathSeparator;
+#endif
+}
+
+// Returns the current working directory, or "" if unsuccessful.
+FilePath FilePath::GetCurrentDir() {
+#if GTEST_OS_WINDOWS_MOBILE
+ // Windows CE doesn't have a current directory, so we just return
+ // something reasonable.
+ return FilePath(kCurrentDirectoryString);
+#elif GTEST_OS_WINDOWS
+ char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
+ return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
+#else
+ char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
+ return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
+#endif // GTEST_OS_WINDOWS_MOBILE
+}
+
+// Returns a copy of the FilePath with the case-insensitive extension removed.
+// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
+// FilePath("dir/file"). If a case-insensitive extension is not
+// found, returns a copy of the original FilePath.
+FilePath FilePath::RemoveExtension(const char* extension) const {
+ String dot_extension(String::Format(".%s", extension));
+ if (pathname_.EndsWithCaseInsensitive(dot_extension.c_str())) {
+ return FilePath(String(pathname_.c_str(), pathname_.length() - 4));
+ }
+ return *this;
+}
+
+// Returns a pointer to the last occurence of a valid path separator in
+// the FilePath. On Windows, for example, both '/' and '\' are valid path
+// separators. Returns NULL if no path separator was found.
+const char* FilePath::FindLastPathSeparator() const {
+ const char* const last_sep = strrchr(c_str(), kPathSeparator);
+#if GTEST_HAS_ALT_PATH_SEP_
+ const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
+ // Comparing two pointers of which only one is NULL is undefined.
+ if (last_alt_sep != NULL &&
+ (last_sep == NULL || last_alt_sep > last_sep)) {
+ return last_alt_sep;
+ }
+#endif
+ return last_sep;
+}
+
+// Returns a copy of the FilePath with the directory part removed.
+// Example: FilePath("path/to/file").RemoveDirectoryName() returns
+// FilePath("file"). If there is no directory part ("just_a_file"), it returns
+// the FilePath unmodified. If there is no file part ("just_a_dir/") it
+// returns an empty FilePath ("").
+// On Windows platform, '\' is the path separator, otherwise it is '/'.
+FilePath FilePath::RemoveDirectoryName() const {
+ const char* const last_sep = FindLastPathSeparator();
+ return last_sep ? FilePath(String(last_sep + 1)) : *this;
+}
+
+// RemoveFileName returns the directory path with the filename removed.
+// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
+// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
+// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
+// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
+// On Windows platform, '\' is the path separator, otherwise it is '/'.
+FilePath FilePath::RemoveFileName() const {
+ const char* const last_sep = FindLastPathSeparator();
+ String dir;
+ if (last_sep) {
+ dir = String(c_str(), last_sep + 1 - c_str());
+ } else {
+ dir = kCurrentDirectoryString;
+ }
+ return FilePath(dir);
+}
+
+// Helper functions for naming files in a directory for xml output.
+
+// Given directory = "dir", base_name = "test", number = 0,
+// extension = "xml", returns "dir/test.xml". If number is greater
+// than zero (e.g., 12), returns "dir/test_12.xml".
+// On Windows platform, uses \ as the separator rather than /.
+FilePath FilePath::MakeFileName(const FilePath& directory,
+ const FilePath& base_name,
+ int number,
+ const char* extension) {
+ String file;
+ if (number == 0) {
+ file = String::Format("%s.%s", base_name.c_str(), extension);
+ } else {
+ file = String::Format("%s_%d.%s", base_name.c_str(), number, extension);
+ }
+ return ConcatPaths(directory, FilePath(file));
+}
+
+// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
+// On Windows, uses \ as the separator rather than /.
+FilePath FilePath::ConcatPaths(const FilePath& directory,
+ const FilePath& relative_path) {
+ if (directory.IsEmpty())
+ return relative_path;
+ const FilePath dir(directory.RemoveTrailingPathSeparator());
+ return FilePath(String::Format("%s%c%s", dir.c_str(), kPathSeparator,
+ relative_path.c_str()));
+}
+
+// Returns true if pathname describes something findable in the file-system,
+// either a file, directory, or whatever.
+bool FilePath::FileOrDirectoryExists() const {
+#if GTEST_OS_WINDOWS_MOBILE
+ LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
+ const DWORD attributes = GetFileAttributes(unicode);
+ delete [] unicode;
+ return attributes != kInvalidFileAttributes;
+#else
+ posix::StatStruct file_stat;
+ return posix::Stat(pathname_.c_str(), &file_stat) == 0;
+#endif // GTEST_OS_WINDOWS_MOBILE
+}
+
+// Returns true if pathname describes a directory in the file-system
+// that exists.
+bool FilePath::DirectoryExists() const {
+ bool result = false;
+#if GTEST_OS_WINDOWS
+ // Don't strip off trailing separator if path is a root directory on
+ // Windows (like "C:\\").
+ const FilePath& path(IsRootDirectory() ? *this :
+ RemoveTrailingPathSeparator());
+#else
+ const FilePath& path(*this);
+#endif
+
+#if GTEST_OS_WINDOWS_MOBILE
+ LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
+ const DWORD attributes = GetFileAttributes(unicode);
+ delete [] unicode;
+ if ((attributes != kInvalidFileAttributes) &&
+ (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ result = true;
+ }
+#else
+ posix::StatStruct file_stat;
+ result = posix::Stat(path.c_str(), &file_stat) == 0 &&
+ posix::IsDir(file_stat);
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+ return result;
+}
+
+// Returns true if pathname describes a root directory. (Windows has one
+// root directory per disk drive.)
+bool FilePath::IsRootDirectory() const {
+#if GTEST_OS_WINDOWS
+ // TODO(***@google.com): on Windows a network share like
+ // \\server\share can be a root directory, although it cannot be the
+ // current directory. Handle this properly.
+ return pathname_.length() == 3 && IsAbsolutePath();
+#else
+ return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
+#endif
+}
+
+// Returns true if pathname describes an absolute path.
+bool FilePath::IsAbsolutePath() const {
+ const char* const name = pathname_.c_str();
+#if GTEST_OS_WINDOWS
+ return pathname_.length() >= 3 &&
+ ((name[0] >= 'a' && name[0] <= 'z') ||
+ (name[0] >= 'A' && name[0] <= 'Z')) &&
+ name[1] == ':' &&
+ IsPathSeparator(name[2]);
+#else
+ return IsPathSeparator(name[0]);
+#endif
+}
+
+// Returns a pathname for a file that does not currently exist. The pathname
+// will be directory/base_name.extension or
+// directory/base_name_<number>.extension if directory/base_name.extension
+// already exists. The number will be incremented until a pathname is found
+// that does not already exist.
+// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
+// There could be a race condition if two or more processes are calling this
+// function at the same time -- they could both pick the same filename.
+FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
+ const FilePath& base_name,
+ const char* extension) {
+ FilePath full_pathname;
+ int number = 0;
+ do {
+ full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
+ } while (full_pathname.FileOrDirectoryExists());
+ return full_pathname;
+}
+
+// Returns true if FilePath ends with a path separator, which indicates that
+// it is intended to represent a directory. Returns false otherwise.
+// This does NOT check that a directory (or file) actually exists.
+bool FilePath::IsDirectory() const {
+ return !pathname_.empty() &&
+ IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
+}
+
+// Create directories so that path exists. Returns true if successful or if
+// the directories already exist; returns false if unable to create directories
+// for any reason.
+bool FilePath::CreateDirectoriesRecursively() const {
+ if (!this->IsDirectory()) {
+ return false;
+ }
+
+ if (pathname_.length() == 0 || this->DirectoryExists()) {
+ return true;
+ }
+
+ const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
+ return parent.CreateDirectoriesRecursively() && this->CreateFolder();
+}
+
+// Create the directory so that path exists. Returns true if successful or
+// if the directory already exists; returns false if unable to create the
+// directory for any reason, including if the parent directory does not
+// exist. Not named "CreateDirectory" because that's a macro on Windows.
+bool FilePath::CreateFolder() const {
+#if GTEST_OS_WINDOWS_MOBILE
+ FilePath removed_sep(this->RemoveTrailingPathSeparator());
+ LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
+ int result = CreateDirectory(unicode, NULL) ? 0 : -1;
+ delete [] unicode;
+#elif GTEST_OS_WINDOWS
+ int result = _mkdir(pathname_.c_str());
+#else
+ int result = mkdir(pathname_.c_str(), 0777);
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+ if (result == -1) {
+ return this->DirectoryExists(); // An error is OK if the directory exists.
+ }
+ return true; // No error.
+}
+
+// If input name has a trailing separator character, remove it and return the
+// name, otherwise return the name string unmodified.
+// On Windows platform, uses \ as the separator, other platforms use /.
+FilePath FilePath::RemoveTrailingPathSeparator() const {
+ return IsDirectory()
+ ? FilePath(String(pathname_.c_str(), pathname_.length() - 1))
+ : *this;
+}
+
+// Removes any redundant separators that might be in the pathname.
+// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
+// redundancies that might be in a pathname involving "." or "..".
+// TODO(***@google.com): handle Windows network shares (e.g. \\server\share).
+void FilePath::Normalize() {
+ if (pathname_.c_str() == NULL) {
+ pathname_ = "";
+ return;
+ }
+ const char* src = pathname_.c_str();
+ char* const dest = new char[pathname_.length() + 1];
+ char* dest_ptr = dest;
+ memset(dest_ptr, 0, pathname_.length() + 1);
+
+ while (*src != '\0') {
+ *dest_ptr = *src;
+ if (!IsPathSeparator(*src)) {
+ src++;
+ } else {
+#if GTEST_HAS_ALT_PATH_SEP_
+ if (*dest_ptr == kAlternatePathSeparator) {
+ *dest_ptr = kPathSeparator;
+ }
+#endif
+ while (IsPathSeparator(*src))
+ src++;
+ }
+ dest_ptr++;
+ }
+ *dest_ptr = '\0';
+ pathname_ = dest;
+ delete[] dest;
+}
+
+} // namespace internal
+} // namespace testing
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: ***@google.com (Zhanyong Wan)
+
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if GTEST_OS_WINDOWS_MOBILE
+# include <windows.h> // For TerminateProcess()
+#elif GTEST_OS_WINDOWS
+# include <io.h>
+# include <sys/stat.h>
+#else
+# include <unistd.h>
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+#if GTEST_OS_MAC
+# include <mach/mach_init.h>
+# include <mach/task.h>
+# include <mach/vm_map.h>
+#endif // GTEST_OS_MAC
+
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+#undef GTEST_IMPLEMENTATION_
+
+namespace testing {
+namespace internal {
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+// MSVC and C++Builder do not provide a definition of STDERR_FILENO.
+const int kStdOutFileno = 1;
+const int kStdErrFileno = 2;
+#else
+const int kStdOutFileno = STDOUT_FILENO;
+const int kStdErrFileno = STDERR_FILENO;
+#endif // _MSC_VER
+
+#if GTEST_OS_MAC
+
+// Returns the number of threads running in the process, or 0 to indicate that
+// we cannot detect it.
+size_t GetThreadCount() {
+ const task_t task = mach_task_self();
+ mach_msg_type_number_t thread_count;
+ thread_act_array_t thread_list;
+ const kern_return_t status = task_threads(task, &thread_list, &thread_count);
+ if (status == KERN_SUCCESS) {
+ // task_threads allocates resources in thread_list and we need to free them
+ // to avoid leaks.
+ vm_deallocate(task,
+ reinterpret_cast<vm_address_t>(thread_list),
+ sizeof(thread_t) * thread_count);
+ return static_cast<size_t>(thread_count);
+ } else {
+ return 0;
+ }
+}
+
+#else
+
+size_t GetThreadCount() {
+ // There's no portable way to detect the number of threads, so we just
+ // return 0 to indicate that we cannot detect it.
+ return 0;
+}
+
+#endif // GTEST_OS_MAC
+
+#if GTEST_USES_POSIX_RE
+
+// Implements RE. Currently only needed for death tests.
+
+RE::~RE() {
+ if (is_valid_) {
+ // regfree'ing an invalid regex might crash because the content
+ // of the regex is undefined. Since the regex's are essentially
+ // the same, one cannot be valid (or invalid) without the other
+ // being so too.
+ regfree(&partial_regex_);
+ regfree(&full_regex_);
+ }
+ free(const_cast<char*>(pattern_));
+}
+
+// Returns true iff regular expression re matches the entire str.
+bool RE::FullMatch(const char* str, const RE& re) {
+ if (!re.is_valid_) return false;
+
+ regmatch_t match;
+ return regexec(&re.full_regex_, str, 1, &match, 0) == 0;
+}
+
+// Returns true iff regular expression re matches a substring of str
+// (including str itself).
+bool RE::PartialMatch(const char* str, const RE& re) {
+ if (!re.is_valid_) return false;
+
+ regmatch_t match;
+ return regexec(&re.partial_regex_, str, 1, &match, 0) == 0;
+}
+
+// Initializes an RE from its string representation.
+void RE::Init(const char* regex) {
+ pattern_ = posix::StrDup(regex);
+
+ // Reserves enough bytes to hold the regular expression used for a
+ // full match.
+ const size_t full_regex_len = strlen(regex) + 10;
+ char* const full_pattern = new char[full_regex_len];
+
+ snprintf(full_pattern, full_regex_len, "^(%s)$", regex);
+ is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0;
+ // We want to call regcomp(&partial_regex_, ...) even if the
+ // previous expression returns false. Otherwise partial_regex_ may
+ // not be properly initialized can may cause trouble when it's
+ // freed.
+ //
+ // Some implementation of POSIX regex (e.g. on at least some
+ // versions of Cygwin) doesn't accept the empty string as a valid
+ // regex. We change it to an equivalent form "()" to be safe.
+ if (is_valid_) {
+ const char* const partial_regex = (*regex == '\0') ? "()" : regex;
+ is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0;
+ }
+ EXPECT_TRUE(is_valid_)
+ << "Regular expression \"" << regex
+ << "\" is not a valid POSIX Extended regular expression.";
+
+ delete[] full_pattern;
+}
+
+#elif GTEST_USES_SIMPLE_RE
+
+// Returns true iff ch appears anywhere in str (excluding the
+// terminating '\0' character).
+bool IsInSet(char ch, const char* str) {
+ return ch != '\0' && strchr(str, ch) != NULL;
+}
+
+// Returns true iff ch belongs to the given classification. Unlike
+// similar functions in <ctype.h>, these aren't affected by the
+// current locale.
+bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; }
+bool IsAsciiPunct(char ch) {
+ return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~");
+}
+bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); }
+bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); }
+bool IsAsciiWordChar(char ch) {
+ return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') ||
+ ('0' <= ch && ch <= '9') || ch == '_';
+}
+
+// Returns true iff "\\c" is a supported escape sequence.
+bool IsValidEscape(char c) {
+ return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW"));
+}
+
+// Returns true iff the given atom (specified by escaped and pattern)
+// matches ch. The result is undefined if the atom is invalid.
+bool AtomMatchesChar(bool escaped, char pattern_char, char ch) {
+ if (escaped) { // "\\p" where p is pattern_char.
+ switch (pattern_char) {
+ case 'd': return IsAsciiDigit(ch);
+ case 'D': return !IsAsciiDigit(ch);
+ case 'f': return ch == '\f';
+ case 'n': return ch == '\n';
+ case 'r': return ch == '\r';
+ case 's': return IsAsciiWhiteSpace(ch);
+ case 'S': return !IsAsciiWhiteSpace(ch);
+ case 't': return ch == '\t';
+ case 'v': return ch == '\v';
+ case 'w': return IsAsciiWordChar(ch);
+ case 'W': return !IsAsciiWordChar(ch);
+ }
+ return IsAsciiPunct(pattern_char) && pattern_char == ch;
+ }
+
+ return (pattern_char == '.' && ch != '\n') || pattern_char == ch;
+}
+
+// Helper function used by ValidateRegex() to format error messages.
+String FormatRegexSyntaxError(const char* regex, int index) {
+ return (Message() << "Syntax error at index " << index
+ << " in simple regular expression \"" << regex << "\": ").GetString();
+}
+
+// Generates non-fatal failures and returns false if regex is invalid;
+// otherwise returns true.
+bool ValidateRegex(const char* regex) {
+ if (regex == NULL) {
+ // TODO(***@google.com): fix the source file location in the
+ // assertion failures to match where the regex is used in user
+ // code.
+ ADD_FAILURE() << "NULL is not a valid simple regular expression.";
+ return false;
+ }
+
+ bool is_valid = true;
+
+ // True iff ?, *, or + can follow the previous atom.
+ bool prev_repeatable = false;
+ for (int i = 0; regex[i]; i++) {
+ if (regex[i] == '\\') { // An escape sequence
+ i++;
+ if (regex[i] == '\0') {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1)
+ << "'\\' cannot appear at the end.";
+ return false;
+ }
+
+ if (!IsValidEscape(regex[i])) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1)
+ << "invalid escape sequence \"\\" << regex[i] << "\".";
+ is_valid = false;
+ }
+ prev_repeatable = true;
+ } else { // Not an escape sequence.
+ const char ch = regex[i];
+
+ if (ch == '^' && i > 0) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'^' can only appear at the beginning.";
+ is_valid = false;
+ } else if (ch == '$' && regex[i + 1] != '\0') {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'$' can only appear at the end.";
+ is_valid = false;
+ } else if (IsInSet(ch, "()[]{}|")) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'" << ch << "' is unsupported.";
+ is_valid = false;
+ } else if (IsRepeat(ch) && !prev_repeatable) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'" << ch << "' can only follow a repeatable token.";
+ is_valid = false;
+ }
+
+ prev_repeatable = !IsInSet(ch, "^$?*+");
+ }
+ }
+
+ return is_valid;
+}
+
+// Matches a repeated regex atom followed by a valid simple regular
+// expression. The regex atom is defined as c if escaped is false,
+// or \c otherwise. repeat is the repetition meta character (?, *,
+// or +). The behavior is undefined if str contains too many
+// characters to be indexable by size_t, in which case the test will
+// probably time out anyway. We are fine with this limitation as
+// std::string has it too.
+bool MatchRepetitionAndRegexAtHead(
+ bool escaped, char c, char repeat, const char* regex,
+ const char* str) {
+ const size_t min_count = (repeat == '+') ? 1 : 0;
+ const size_t max_count = (repeat == '?') ? 1 :
+ static_cast<size_t>(-1) - 1;
+ // We cannot call numeric_limits::max() as it conflicts with the
+ // max() macro on Windows.
+
+ for (size_t i = 0; i <= max_count; ++i) {
+ // We know that the atom matches each of the first i characters in str.
+ if (i >= min_count && MatchRegexAtHead(regex, str + i)) {
+ // We have enough matches at the head, and the tail matches too.
+ // Since we only care about *whether* the pattern matches str
+ // (as opposed to *how* it matches), there is no need to find a
+ // greedy match.
+ return true;
+ }
+ if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i]))
+ return false;
+ }
+ return false;
+}
+
+// Returns true iff regex matches a prefix of str. regex must be a
+// valid simple regular expression and not start with "^", or the
+// result is undefined.
+bool MatchRegexAtHead(const char* regex, const char* str) {
+ if (*regex == '\0') // An empty regex matches a prefix of anything.
+ return true;
+
+ // "$" only matches the end of a string. Note that regex being
+ // valid guarantees that there's nothing after "$" in it.
+ if (*regex == '$')
+ return *str == '\0';
+
+ // Is the first thing in regex an escape sequence?
+ const bool escaped = *regex == '\\';
+ if (escaped)
+ ++regex;
+ if (IsRepeat(regex[1])) {
+ // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so
+ // here's an indirect recursion. It terminates as the regex gets
+ // shorter in each recursion.
+ return MatchRepetitionAndRegexAtHead(
+ escaped, regex[0], regex[1], regex + 2, str);
+ } else {
+ // regex isn't empty, isn't "$", and doesn't start with a
+ // repetition. We match the first atom of regex with the first
+ // character of str and recurse.
+ return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) &&
+ MatchRegexAtHead(regex + 1, str + 1);
+ }
+}
+
+// Returns true iff regex matches any substring of str. regex must be
+// a valid simple regular expression, or the result is undefined.
+//
+// The algorithm is recursive, but the recursion depth doesn't exceed
+// the regex length, so we won't need to worry about running out of
+// stack space normally. In rare cases the time complexity can be
+// exponential with respect to the regex length + the string length,
+// but usually it's must faster (often close to linear).
+bool MatchRegexAnywhere(const char* regex, const char* str) {
+ if (regex == NULL || str == NULL)
+ return false;
+
+ if (*regex == '^')
+ return MatchRegexAtHead(regex + 1, str);
+
+ // A successful match can be anywhere in str.
+ do {
+ if (MatchRegexAtHead(regex, str))
+ return true;
+ } while (*str++ != '\0');
+ return false;
+}
+
+// Implements the RE class.
+
+RE::~RE() {
+ free(const_cast<char*>(pattern_));
+ free(const_cast<char*>(full_pattern_));
+}
+
+// Returns true iff regular expression re matches the entire str.
+bool RE::FullMatch(const char* str, const RE& re) {
+ return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str);
+}
+
+// Returns true iff regular expression re matches a substring of str
+// (including str itself).
+bool RE::PartialMatch(const char* str, const RE& re) {
+ return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str);
+}
+
+// Initializes an RE from its string representation.
+void RE::Init(const char* regex) {
+ pattern_ = full_pattern_ = NULL;
+ if (regex != NULL) {
+ pattern_ = posix::StrDup(regex);
+ }
+
+ is_valid_ = ValidateRegex(regex);
+ if (!is_valid_) {
+ // No need to calculate the full pattern when the regex is invalid.
+ return;
+ }
+
+ const size_t len = strlen(regex);
+ // Reserves enough bytes to hold the regular expression used for a
+ // full match: we need space to prepend a '^', append a '$', and
+ // terminate the string with '\0'.
+ char* buffer = static_cast<char*>(malloc(len + 3));
+ full_pattern_ = buffer;
+
+ if (*regex != '^')
+ *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'.
+
+ // We don't use snprintf or strncpy, as they trigger a warning when
+ // compiled with VC++ 8.0.
+ memcpy(buffer, regex, len);
+ buffer += len;
+
+ if (len == 0 || regex[len - 1] != '$')
+ *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'.
+
+ *buffer = '\0';
+}
+
+#endif // GTEST_USES_POSIX_RE
+
+const char kUnknownFile[] = "unknown file";
+
+// Formats a source file path and a line number as they would appear
+// in an error message from the compiler used to compile this code.
+GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) {
+ const char* const file_name = file == NULL ? kUnknownFile : file;
+
+ if (line < 0) {
+ return String::Format("%s:", file_name).c_str();
+ }
+#ifdef _MSC_VER
+ return String::Format("%s(%d):", file_name, line).c_str();
+#else
+ return String::Format("%s:%d:", file_name, line).c_str();
+#endif // _MSC_VER
+}
+
+// Formats a file location for compiler-independent XML output.
+// Although this function is not platform dependent, we put it next to
+// FormatFileLocation in order to contrast the two functions.
+// Note that FormatCompilerIndependentFileLocation() does NOT append colon
+// to the file location it produces, unlike FormatFileLocation().
+GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(
+ const char* file, int line) {
+ const char* const file_name = file == NULL ? kUnknownFile : file;
+
+ if (line < 0)
+ return file_name;
+ else
+ return String::Format("%s:%d", file_name, line).c_str();
+}
+
+
+GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line)
+ : severity_(severity) {
+ const char* const marker =
+ severity == GTEST_INFO ? "[ INFO ]" :
+ severity == GTEST_WARNING ? "[WARNING]" :
+ severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]";
+ GetStream() << ::std::endl << marker << " "
+ << FormatFileLocation(file, line).c_str() << ": ";
+}
+
+// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program.
+GTestLog::~GTestLog() {
+ GetStream() << ::std::endl;
+ if (severity_ == GTEST_FATAL) {
+ fflush(stderr);
+ posix::Abort();
+ }
+}
+// Disable Microsoft deprecation warnings for POSIX functions called from
+// this class (creat, dup, dup2, and close)
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable: 4996)
+#endif // _MSC_VER
+
+#if GTEST_HAS_STREAM_REDIRECTION
+
+// Object that captures an output stream (stdout/stderr).
+class CapturedStream {
+ public:
+ // The ctor redirects the stream to a temporary file.
+ CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) {
+
+# if GTEST_OS_WINDOWS
+ char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT
+ char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT
+
+ ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path);
+ const UINT success = ::GetTempFileNameA(temp_dir_path,
+ "gtest_redir",
+ 0, // Generate unique file name.
+ temp_file_path);
+ GTEST_CHECK_(success != 0)
+ << "Unable to create a temporary file in " << temp_dir_path;
+ const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE);
+ GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file "
+ << temp_file_path;
+ filename_ = temp_file_path;
+# else
+ // There's no guarantee that a test has write access to the
+ // current directory, so we create the temporary file in the /tmp
+ // directory instead.
+ char name_template[] = "/tmp/captured_stream.XXXXXX";
+ const int captured_fd = mkstemp(name_template);
+ filename_ = name_template;
+# endif // GTEST_OS_WINDOWS
+ fflush(NULL);
+ dup2(captured_fd, fd_);
+ close(captured_fd);
+ }
+
+ ~CapturedStream() {
+ remove(filename_.c_str());
+ }
+
+ String GetCapturedString() {
+ if (uncaptured_fd_ != -1) {
+ // Restores the original stream.
+ fflush(NULL);
+ dup2(uncaptured_fd_, fd_);
+ close(uncaptured_fd_);
+ uncaptured_fd_ = -1;
+ }
+
+ FILE* const file = posix::FOpen(filename_.c_str(), "r");
+ const String content = ReadEntireFile(file);
+ posix::FClose(file);
+ return content;
+ }
+
+ private:
+ // Reads the entire content of a file as a String.
+ static String ReadEntireFile(FILE* file);
+
+ // Returns the size (in bytes) of a file.
+ static size_t GetFileSize(FILE* file);
+
+ const int fd_; // A stream to capture.
+ int uncaptured_fd_;
+ // Name of the temporary file holding the stderr output.
+ ::std::string filename_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream);
+};
+
+// Returns the size (in bytes) of a file.
+size_t CapturedStream::GetFileSize(FILE* file) {
+ fseek(file, 0, SEEK_END);
+ return static_cast<size_t>(ftell(file));
+}
+
+// Reads the entire content of a file as a string.
+String CapturedStream::ReadEntireFile(FILE* file) {
+ const size_t file_size = GetFileSize(file);
+ char* const buffer = new char[file_size];
+
+ size_t bytes_last_read = 0; // # of bytes read in the last fread()
+ size_t bytes_read = 0; // # of bytes read so far
+
+ fseek(file, 0, SEEK_SET);
+
+ // Keeps reading the file until we cannot read further or the
+ // pre-determined file size is reached.
+ do {
+ bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file);
+ bytes_read += bytes_last_read;
+ } while (bytes_last_read > 0 && bytes_read < file_size);
+
+ const String content(buffer, bytes_read);
+ delete[] buffer;
+
+ return content;
+}
+
+# ifdef _MSC_VER
+# pragma warning(pop)
+# endif // _MSC_VER
+
+static CapturedStream* g_captured_stderr = NULL;
+static CapturedStream* g_captured_stdout = NULL;
+
+// Starts capturing an output stream (stdout/stderr).
+void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) {
+ if (*stream != NULL) {
+ GTEST_LOG_(FATAL) << "Only one " << stream_name
+ << " capturer can exist at a time.";
+ }
+ *stream = new CapturedStream(fd);
+}
+
+// Stops capturing the output stream and returns the captured string.
+String GetCapturedStream(CapturedStream** captured_stream) {
+ const String content = (*captured_stream)->GetCapturedString();
+
+ delete *captured_stream;
+ *captured_stream = NULL;
+
+ return content;
+}
+
+// Starts capturing stdout.
+void CaptureStdout() {
+ CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout);
+}
+
+// Starts capturing stderr.
+void CaptureStderr() {
+ CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr);
+}
+
+// Stops capturing stdout and returns the captured string.
+String GetCapturedStdout() { return GetCapturedStream(&g_captured_stdout); }
+
+// Stops capturing stderr and returns the captured string.
+String GetCapturedStderr() { return GetCapturedStream(&g_captured_stderr); }
+
+#endif // GTEST_HAS_STREAM_REDIRECTION
+
+#if GTEST_HAS_DEATH_TEST
+
+// A copy of all command line arguments. Set by InitGoogleTest().
+::std::vector<String> g_argvs;
+
+// Returns the command line as a vector of strings.
+const ::std::vector<String>& GetArgvs() { return g_argvs; }
+
+#endif // GTEST_HAS_DEATH_TEST
+
+#if GTEST_OS_WINDOWS_MOBILE
+namespace posix {
+void Abort() {
+ DebugBreak();
+ TerminateProcess(GetCurrentProcess(), 1);
+}
+} // namespace posix
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+// Returns the name of the environment variable corresponding to the
+// given flag. For example, FlagToEnvVar("foo") will return
+// "GTEST_FOO" in the open-source version.
+static String FlagToEnvVar(const char* flag) {
+ const String full_flag =
+ (Message() << GTEST_FLAG_PREFIX_ << flag).GetString();
+
+ Message env_var;
+ for (size_t i = 0; i != full_flag.length(); i++) {
+ env_var << ToUpper(full_flag.c_str()[i]);
+ }
+
+ return env_var.GetString();
+}
+
+// Parses 'str' for a 32-bit signed integer. If successful, writes
+// the result to *value and returns true; otherwise leaves *value
+// unchanged and returns false.
+bool ParseInt32(const Message& src_text, const char* str, Int32* value) {
+ // Parses the environment variable as a decimal integer.
+ char* end = NULL;
+ const long long_value = strtol(str, &end, 10); // NOLINT
+
+ // Has strtol() consumed all characters in the string?
+ if (*end != '\0') {
+ // No - an invalid character was encountered.
+ Message msg;
+ msg << "WARNING: " << src_text
+ << " is expected to be a 32-bit integer, but actually"
+ << " has value \"" << str << "\".\n";
+ printf("%s", msg.GetString().c_str());
+ fflush(stdout);
+ return false;
+ }
+
+ // Is the parsed value in the range of an Int32?
+ const Int32 result = static_cast<Int32>(long_value);
+ if (long_value == LONG_MAX || long_value == LONG_MIN ||
+ // The parsed value overflows as a long. (strtol() returns
+ // LONG_MAX or LONG_MIN when the input overflows.)
+ result != long_value
+ // The parsed value overflows as an Int32.
+ ) {
+ Message msg;
+ msg << "WARNING: " << src_text
+ << " is expected to be a 32-bit integer, but actually"
+ << " has value " << str << ", which overflows.\n";
+ printf("%s", msg.GetString().c_str());
+ fflush(stdout);
+ return false;
+ }
+
+ *value = result;
+ return true;
+}
+
+// Reads and returns the Boolean environment variable corresponding to
+// the given flag; if it's not set, returns default_value.
+//
+// The value is considered true iff it's not "0".
+bool BoolFromGTestEnv(const char* flag, bool default_value) {
+ const String env_var = FlagToEnvVar(flag);
+ const char* const string_value = posix::GetEnv(env_var.c_str());
+ return string_value == NULL ?
+ default_value : strcmp(string_value, "0") != 0;
+}
+
+// Reads and returns a 32-bit integer stored in the environment
+// variable corresponding to the given flag; if it isn't set or
+// doesn't represent a valid 32-bit integer, returns default_value.
+Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) {
+ const String env_var = FlagToEnvVar(flag);
+ const char* const string_value = posix::GetEnv(env_var.c_str());
+ if (string_value == NULL) {
+ // The environment variable is not set.
+ return default_value;
+ }
+
+ Int32 result = default_value;
+ if (!ParseInt32(Message() << "Environment variable " << env_var,
+ string_value, &result)) {
+ printf("The default value %s is used.\n",
+ (Message() << default_value).GetString().c_str());
+ fflush(stdout);
+ return default_value;
+ }
+
+ return result;
+}
+
+// Reads and returns the string environment variable corresponding to
+// the given flag; if it's not set, returns default_value.
+const char* StringFromGTestEnv(const char* flag, const char* default_value) {
+ const String env_var = FlagToEnvVar(flag);
+ const char* const value = posix::GetEnv(env_var.c_str());
+ return value == NULL ? default_value : value;
+}
+
+} // namespace internal
+} // namespace testing
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: ***@google.com (Zhanyong Wan)
+
+// Google Test - The Google C++ Testing Framework
+//
+// This file implements a universal value printer that can print a
+// value of any type T:
+//
+// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
+//
+// It uses the << operator when possible, and prints the bytes in the
+// object otherwise. A user can override its behavior for a class
+// type Foo by defining either operator<<(::std::ostream&, const Foo&)
+// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that
+// defines Foo.
+
+#include <ctype.h>
+#include <stdio.h>
+#include <ostream> // NOLINT
+#include <string>
+
+namespace testing {
+
+namespace {
+
+using ::std::ostream;
+
+#if GTEST_OS_WINDOWS_MOBILE // Windows CE does not define _snprintf_s.
+# define snprintf _snprintf
+#elif _MSC_VER >= 1400 // VC 8.0 and later deprecate snprintf and _snprintf.
+# define snprintf _snprintf_s
+#elif _MSC_VER
+# define snprintf _snprintf
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+// Prints a segment of bytes in the given object.
+void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
+ size_t count, ostream* os) {
+ char text[5] = "";
+ for (size_t i = 0; i != count; i++) {
+ const size_t j = start + i;
+ if (i != 0) {
+ // Organizes the bytes into groups of 2 for easy parsing by
+ // human.
+ if ((j % 2) == 0)
+ *os << ' ';
+ else
+ *os << '-';
+ }
+ snprintf(text, sizeof(text), "%02X", obj_bytes[j]);
+ *os << text;
+ }
+}
+
+// Prints the bytes in the given value to the given ostream.
+void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
+ ostream* os) {
+ // Tells the user how big the object is.
+ *os << count << "-byte object <";
+
+ const size_t kThreshold = 132;
+ const size_t kChunkSize = 64;
+ // If the object size is bigger than kThreshold, we'll have to omit
+ // some details by printing only the first and the last kChunkSize
+ // bytes.
+ // TODO(wan): let the user control the threshold using a flag.
+ if (count < kThreshold) {
+ PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
+ } else {
+ PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os);
+ *os << " ... ";
+ // Rounds up to 2-byte boundary.
+ const size_t resume_pos = (count - kChunkSize + 1)/2*2;
+ PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os);
+ }
+ *os << ">";
+}
+
+} // namespace
+
+namespace internal2 {
+
+// Delegates to PrintBytesInObjectToImpl() to print the bytes in the
+// given object. The delegation simplifies the implementation, which
+// uses the << operator and thus is easier done outside of the
+// ::testing::internal namespace, which contains a << operator that
+// sometimes conflicts with the one in STL.
+void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count,
+ ostream* os) {
+ PrintBytesInObjectToImpl(obj_bytes, count, os);
+}
+
+} // namespace internal2
+
+namespace internal {
+
+// Depending on the value of a char (or wchar_t), we print it in one
+// of three formats:
+// - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
+// - as a hexidecimal escape sequence (e.g. '\x7F'), or
+// - as a special escape sequence (e.g. '\r', '\n').
+enum CharFormat {
+ kAsIs,
+ kHexEscape,
+ kSpecialEscape
+};
+
+// Returns true if c is a printable ASCII character. We test the
+// value of c directly instead of calling isprint(), which is buggy on
+// Windows Mobile.
+inline bool IsPrintableAscii(wchar_t c) {
+ return 0x20 <= c && c <= 0x7E;
+}
+
+// Prints a wide or narrow char c as a character literal without the
+// quotes, escaping it when necessary; returns how c was formatted.
+// The template argument UnsignedChar is the unsigned version of Char,
+// which is the type of c.
+template <typename UnsignedChar, typename Char>
+static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
+ switch (static_cast<wchar_t>(c)) {
+ case L'\0':
+ *os << "\\0";
+ break;
+ case L'\'':
+ *os << "\\'";
+ break;
+ case L'\\':
+ *os << "\\\\";
+ break;
+ case L'\a':
+ *os << "\\a";
+ break;
+ case L'\b':
+ *os << "\\b";
+ break;
+ case L'\f':
+ *os << "\\f";
+ break;
+ case L'\n':
+ *os << "\\n";
+ break;
+ case L'\r':
+ *os << "\\r";
+ break;
+ case L'\t':
+ *os << "\\t";
+ break;
+ case L'\v':
+ *os << "\\v";
+ break;
+ default:
+ if (IsPrintableAscii(c)) {
+ *os << static_cast<char>(c);
+ return kAsIs;
+ } else {
+ *os << String::Format("\\x%X", static_cast<UnsignedChar>(c));
+ return kHexEscape;
+ }
+ }
+ return kSpecialEscape;
+}
+
+// Prints a char c as if it's part of a string literal, escaping it when
+// necessary; returns how c was formatted.
+static CharFormat PrintAsWideStringLiteralTo(wchar_t c, ostream* os) {
+ switch (c) {
+ case L'\'':
+ *os << "'";
+ return kAsIs;
+ case L'"':
+ *os << "\\\"";
+ return kSpecialEscape;
+ default:
+ return PrintAsCharLiteralTo<wchar_t>(c, os);
+ }
+}
+
+// Prints a char c as if it's part of a string literal, escaping it when
+// necessary; returns how c was formatted.
+static CharFormat PrintAsNarrowStringLiteralTo(char c, ostream* os) {
+ return PrintAsWideStringLiteralTo(static_cast<unsigned char>(c), os);
+}
+
+// Prints a wide or narrow character c and its code. '\0' is printed
+// as "'\\0'", other unprintable characters are also properly escaped
+// using the standard C++ escape sequence. The template argument
+// UnsignedChar is the unsigned version of Char, which is the type of c.
+template <typename UnsignedChar, typename Char>
+void PrintCharAndCodeTo(Char c, ostream* os) {
+ // First, print c as a literal in the most readable form we can find.
+ *os << ((sizeof(c) > 1) ? "L'" : "'");
+ const CharFormat format = PrintAsCharLiteralTo<UnsignedChar>(c, os);
+ *os << "'";
+
+ // To aid user debugging, we also print c's code in decimal, unless
+ // it's 0 (in which case c was printed as '\\0', making the code
+ // obvious).
+ if (c == 0)
+ return;
+ *os << " (" << String::Format("%d", c).c_str();
+
+ // For more convenience, we print c's code again in hexidecimal,
+ // unless c was already printed in the form '\x##' or the code is in
+ // [1, 9].
+ if (format == kHexEscape || (1 <= c && c <= 9)) {
+ // Do nothing.
+ } else {
+ *os << String::Format(", 0x%X",
+ static_cast<UnsignedChar>(c)).c_str();
+ }
+ *os << ")";
+}
+
+void PrintTo(unsigned char c, ::std::ostream* os) {
+ PrintCharAndCodeTo<unsigned char>(c, os);
+}
+void PrintTo(signed char c, ::std::ostream* os) {
+ PrintCharAndCodeTo<unsigned char>(c, os);
+}
+
+// Prints a wchar_t as a symbol if it is printable or as its internal
+// code otherwise and also as its code. L'\0' is printed as "L'\\0'".
+void PrintTo(wchar_t wc, ostream* os) {
+ PrintCharAndCodeTo<wchar_t>(wc, os);
+}
+
+// Prints the given array of characters to the ostream.
+// The array starts at *begin, the length is len, it may include '\0' characters
+// and may not be null-terminated.
+static void PrintCharsAsStringTo(const char* begin, size_t len, ostream* os) {
+ *os << "\"";
+ bool is_previous_hex = false;
+ for (size_t index = 0; index < len; ++index) {
+ const char cur = begin[index];
+ if (is_previous_hex && IsXDigit(cur)) {
+ // Previous character is of '\x..' form and this character can be
+ // interpreted as another hexadecimal digit in its number. Break string to
+ // disambiguate.
+ *os << "\" \"";
+ }
+ is_previous_hex = PrintAsNarrowStringLiteralTo(cur, os) == kHexEscape;
+ }
+ *os << "\"";
+}
+
+// Prints a (const) char array of 'len' elements, starting at address 'begin'.
+void UniversalPrintArray(const char* begin, size_t len, ostream* os) {
+ PrintCharsAsStringTo(begin, len, os);
+}
+
+// Prints the given array of wide characters to the ostream.
+// The array starts at *begin, the length is len, it may include L'\0'
+// characters and may not be null-terminated.
+static void PrintWideCharsAsStringTo(const wchar_t* begin, size_t len,
+ ostream* os) {
+ *os << "L\"";
+ bool is_previous_hex = false;
+ for (size_t index = 0; index < len; ++index) {
+ const wchar_t cur = begin[index];
+ if (is_previous_hex && isascii(cur) && IsXDigit(static_cast<char>(cur))) {
+ // Previous character is of '\x..' form and this character can be
+ // interpreted as another hexadecimal digit in its number. Break string to
+ // disambiguate.
+ *os << "\" L\"";
+ }
+ is_previous_hex = PrintAsWideStringLiteralTo(cur, os) == kHexEscape;
+ }
+ *os << "\"";
+}
+
+// Prints the given C string to the ostream.
+void PrintTo(const char* s, ostream* os) {
+ if (s == NULL) {
+ *os << "NULL";
+ } else {
+ *os << ImplicitCast_<const void*>(s) << " pointing to ";
+ PrintCharsAsStringTo(s, strlen(s), os);
+ }
+}
+
+// MSVC compiler can be configured to define whar_t as a typedef
+// of unsigned short. Defining an overload for const wchar_t* in that case
+// would cause pointers to unsigned shorts be printed as wide strings,
+// possibly accessing more memory than intended and causing invalid
+// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when
+// wchar_t is implemented as a native type.
+#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
+// Prints the given wide C string to the ostream.
+void PrintTo(const wchar_t* s, ostream* os) {
+ if (s == NULL) {
+ *os << "NULL";
+ } else {
+ *os << ImplicitCast_<const void*>(s) << " pointing to ";
+ PrintWideCharsAsStringTo(s, wcslen(s), os);
+ }
+}
+#endif // wchar_t is native
+
+// Prints a ::string object.
+#if GTEST_HAS_GLOBAL_STRING
+void PrintStringTo(const ::string& s, ostream* os) {
+ PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif // GTEST_HAS_GLOBAL_STRING
+
+void PrintStringTo(const ::std::string& s, ostream* os) {
+ PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+
+// Prints a ::wstring object.
+#if GTEST_HAS_GLOBAL_WSTRING
+void PrintWideStringTo(const ::wstring& s, ostream* os) {
+ PrintWideCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+#if GTEST_HAS_STD_WSTRING
+void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
+ PrintWideCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif // GTEST_HAS_STD_WSTRING
+
+} // namespace internal
+
+} // namespace testing
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: ***@google.com (Markus Heule)
+//
+// The Google C++ Testing Framework (Google Test)
+
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+#undef GTEST_IMPLEMENTATION_
+
+namespace testing {
+
+using internal::GetUnitTestImpl;
+
+// Gets the summary of the failure message by omitting the stack trace
+// in it.
+internal::String TestPartResult::ExtractSummary(const char* message) {
+ const char* const stack_trace = strstr(message, internal::kStackTraceMarker);
+ return stack_trace == NULL ? internal::String(message) :
+ internal::String(message, stack_trace - message);
+}
+
+// Prints a TestPartResult object.
+std::ostream& operator<<(std::ostream& os, const TestPartResult& result) {
+ return os
+ << result.file_name() << ":" << result.line_number() << ": "
+ << (result.type() == TestPartResult::kSuccess ? "Success" :
+ result.type() == TestPartResult::kFatalFailure ? "Fatal failure" :
+ "Non-fatal failure") << ":\n"
+ << result.message() << std::endl;
+}
+
+// Appends a TestPartResult to the array.
+void TestPartResultArray::Append(const TestPartResult& result) {
+ array_.push_back(result);
+}
+
+// Returns the TestPartResult at the given index (0-based).
+const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const {
+ if (index < 0 || index >= size()) {
+ printf("\nInvalid index (%d) into TestPartResultArray.\n", index);
+ internal::posix::Abort();
+ }
+
+ return array_[index];
+}
+
+// Returns the number of TestPartResult objects in the array.
+int TestPartResultArray::size() const {
+ return static_cast<int>(array_.size());
+}
+
+namespace internal {
+
+HasNewFatalFailureHelper::HasNewFatalFailureHelper()
+ : has_new_fatal_failure_(false),
+ original_reporter_(GetUnitTestImpl()->
+ GetTestPartResultReporterForCurrentThread()) {
+ GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this);
+}
+
+HasNewFatalFailureHelper::~HasNewFatalFailureHelper() {
+ GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(
+ original_reporter_);
+}
+
+void HasNewFatalFailureHelper::ReportTestPartResult(
+ const TestPartResult& result) {
+ if (result.fatally_failed())
+ has_new_fatal_failure_ = true;
+ original_reporter_->ReportTestPartResult(result);
+}
+
+} // namespace internal
+
+} // namespace testing
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: ***@google.com (Zhanyong Wan)
+
+
+namespace testing {
+namespace internal {
+
+#if GTEST_HAS_TYPED_TEST_P
+
+// Skips to the first non-space char in str. Returns an empty string if str
+// contains only whitespace characters.
+static const char* SkipSpaces(const char* str) {
+ while (IsSpace(*str))
+ str++;
+ return str;
+}
+
+// Verifies that registered_tests match the test names in
+// defined_test_names_; returns registered_tests if successful, or
+// aborts the program otherwise.
+const char* TypedTestCasePState::VerifyRegisteredTestNames(
+ const char* file, int line, const char* registered_tests) {
+ typedef ::std::set<const char*>::const_iterator DefinedTestIter;
+ registered_ = true;
+
+ // Skip initial whitespace in registered_tests since some
+ // preprocessors prefix stringizied literals with whitespace.
+ registered_tests = SkipSpaces(registered_tests);
+
+ Message errors;
+ ::std::set<String> tests;
+ for (const char* names = registered_tests; names != NULL;
+ names = SkipComma(names)) {
+ const String name = GetPrefixUntilComma(names);
+ if (tests.count(name) != 0) {
+ errors << "Test " << name << " is listed more than once.\n";
+ continue;
+ }
+
+ bool found = false;
+ for (DefinedTestIter it = defined_test_names_.begin();
+ it != defined_test_names_.end();
+ ++it) {
+ if (name == *it) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ tests.insert(name);
+ } else {
+ errors << "No test named " << name
+ << " can be found in this test case.\n";
+ }
+ }
+
+ for (DefinedTestIter it = defined_test_names_.begin();
+ it != defined_test_names_.end();
+ ++it) {
+ if (tests.count(*it) == 0) {
+ errors << "You forgot to list test " << *it << ".\n";
+ }
+ }
+
+ const String& errors_str = errors.GetString();
+ if (errors_str != "") {
+ fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(),
+ errors_str.c_str());
+ fflush(stderr);
+ posix::Abort();
+ }
+
+ return registered_tests;
+}
+
+#endif // GTEST_HAS_TYPED_TEST_P
+
+} // namespace internal
+} // namespace testing
diff --git a/bamtools/src/third_party/gtest-1.6.0/fused-src/gtest/gtest.h b/bamtools/src/third_party/gtest-1.6.0/fused-src/gtest/gtest.h
new file mode 100644
index 0000000..3143bd6
--- /dev/null
+++ b/bamtools/src/third_party/gtest-1.6.0/fused-src/gtest/gtest.h
@@ -0,0 +1,19537 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: ***@google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines the public API for Google Test. It should be
+// included by any test program that uses Google Test.
+//
+// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
+// leave some internal implementation details in this header file.
+// They are clearly marked by comments like this:
+//
+// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+//
+// Such code is NOT meant to be used by a user directly, and is subject
+// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user
+// program!
+//
+// Acknowledgment: Google Test borrowed the idea of automatic test
+// registration from Barthelemy Dagenais' (***@prologique.com)
+// easyUnit framework.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_H_
+
+#include <limits>
+#include <vector>
+
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: ***@google.com (Zhanyong Wan), ***@gmail.com (Sean Mcafee)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file declares functions and macros used internally by
+// Google Test. They are subject to change without notice.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: ***@google.com (Zhanyong Wan)
+//
+// Low-level types and utilities for porting Google Test to various
+// platforms. They are subject to change without notice. DO NOT USE
+// THEM IN USER CODE.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+
+// The user can define the following macros in the build script to
+// control Google Test's behavior. If the user doesn't define a macro
+// in this list, Google Test will define it.
+//
+// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2)
+// is/isn't available.
+// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions
+// are enabled.
+// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string
+// is/isn't available (some systems define
+// ::string, which is different to std::string).
+// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string
+// is/isn't available (some systems define
+// ::wstring, which is different to std::wstring).
+// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular
+// expressions are/aren't available.
+// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that <pthread.h>
+// is/isn't available.
+// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't
+// enabled.
+// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that
+// std::wstring does/doesn't work (Google Test can
+// be used where std::wstring is unavailable).
+// GTEST_HAS_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple
+// is/isn't available.
+// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the
+// compiler supports Microsoft's "Structured
+// Exception Handling".
+// GTEST_HAS_STREAM_REDIRECTION
+// - Define it to 1/0 to indicate whether the
+// platform supports I/O stream redirection using
+// dup() and dup2().
+// GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google
+// Test's own tr1 tuple implementation should be
+// used. Unused when the user sets
+// GTEST_HAS_TR1_TUPLE to 0.
+// GTEST_LINKED_AS_SHARED_LIBRARY
+// - Define to 1 when compiling tests that use
+// Google Test as a shared library (known as
+// DLL on Windows).
+// GTEST_CREATE_SHARED_LIBRARY
+// - Define to 1 when compiling Google Test itself
+// as a shared library.
+
+// This header defines the following utilities:
+//
+// Macros indicating the current platform (defined to 1 if compiled on
+// the given platform; otherwise undefined):
+// GTEST_OS_AIX - IBM AIX
+// GTEST_OS_CYGWIN - Cygwin
+// GTEST_OS_HPUX - HP-UX
+// GTEST_OS_LINUX - Linux
+// GTEST_OS_LINUX_ANDROID - Google Android
+// GTEST_OS_MAC - Mac OS X
+// GTEST_OS_NACL - Google Native Client (NaCl)
+// GTEST_OS_SOLARIS - Sun Solaris
+// GTEST_OS_SYMBIAN - Symbian
+// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile)
+// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop
+// GTEST_OS_WINDOWS_MINGW - MinGW
+// GTEST_OS_WINDOWS_MOBILE - Windows Mobile
+// GTEST_OS_ZOS - z/OS
+//
+// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the
+// most stable support. Since core members of the Google Test project
+// don't have access to other platforms, support for them may be less
+// stable. If you notice any problems on your platform, please notify
+// ***@googlegroups.com (patches for fixing them are
+// even more welcome!).
+//
+// Note that it is possible that none of the GTEST_OS_* macros are defined.
+//
+// Macros indicating available Google Test features (defined to 1 if
+// the corresponding feature is supported; otherwise undefined):
+// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized
+// tests)
+// GTEST_HAS_DEATH_TEST - death tests
+// GTEST_HAS_PARAM_TEST - value-parameterized tests
+// GTEST_HAS_TYPED_TEST - typed tests
+// GTEST_HAS_TYPED_TEST_P - type-parameterized tests
+// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with
+// GTEST_HAS_POSIX_RE (see above) which users can
+// define themselves.
+// GTEST_USES_SIMPLE_RE - our own simple regex is used;
+// the above two are mutually exclusive.
+// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ().
+//
+// Macros for basic C++ coding:
+// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning.
+// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a
+// variable don't have to be used.
+// GTEST_DISALLOW_ASSIGN_ - disables operator=.
+// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=.
+// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used.
+//
+// Synchronization:
+// Mutex, MutexLock, ThreadLocal, GetThreadCount()
+// - synchronization primitives.
+// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above
+// synchronization primitives have real implementations
+// and Google Test is thread-safe; or 0 otherwise.
+//
+// Template meta programming:
+// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only.
+// IteratorTraits - partial implementation of std::iterator_traits, which
+// is not available in libCstd when compiled with Sun C++.
+//
+// Smart pointers:
+// scoped_ptr - as in TR2.
+//
+// Regular expressions:
+// RE - a simple regular expression class using the POSIX
+// Extended Regular Expression syntax on UNIX-like
+// platforms, or a reduced regular exception syntax on
+// other platforms, including Windows.
+//
+// Logging:
+// GTEST_LOG_() - logs messages at the specified severity level.
+// LogToStderr() - directs all log messages to stderr.
+// FlushInfoLog() - flushes informational log messages.
+//
+// Stdout and stderr capturing:
+// CaptureStdout() - starts capturing stdout.
+// GetCapturedStdout() - stops capturing stdout and returns the captured
+// string.
+// CaptureStderr() - starts capturing stderr.
+// GetCapturedStderr() - stops capturing stderr and returns the captured
+// string.
+//
+// Integer types:
+// TypeWithSize - maps an integer to a int type.
+// Int32, UInt32, Int64, UInt64, TimeInMillis
+// - integers of known sizes.
+// BiggestInt - the biggest signed integer type.
+//
+// Command-line utilities:
+// GTEST_FLAG() - references a flag.
+// GTEST_DECLARE_*() - declares a flag.
+// GTEST_DEFINE_*() - defines a flag.
+// GetArgvs() - returns the command line as a vector of strings.
+//
+// Environment variable utilities:
+// GetEnv() - gets the value of an environment variable.
+// BoolFromGTestEnv() - parses a bool environment variable.
+// Int32FromGTestEnv() - parses an Int32 environment variable.
+// StringFromGTestEnv() - parses a string environment variable.
+
+#include <ctype.h> // for isspace, etc
+#include <stddef.h> // for ptrdiff_t
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifndef _WIN32_WCE
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif // !_WIN32_WCE
+
+#include <iostream> // NOLINT
+#include <sstream> // NOLINT
+#include <string> // NOLINT
+
+#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com"
+#define GTEST_FLAG_PREFIX_ "gtest_"
+#define GTEST_FLAG_PREFIX_DASH_ "gtest-"
+#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_"
+#define GTEST_NAME_ "Google Test"
+#define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/"
+
+// Determines the version of gcc that is used to compile this.
+#ifdef __GNUC__
+// 40302 means version 4.3.2.
+# define GTEST_GCC_VER_ \
+ (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__)
+#endif // __GNUC__
+
+// Determines the platform on which Google Test is compiled.
+#ifdef __CYGWIN__
+# define GTEST_OS_CYGWIN 1
+#elif defined __SYMBIAN32__
+# define GTEST_OS_SYMBIAN 1
+#elif defined _WIN32
+# define GTEST_OS_WINDOWS 1
+# ifdef _WIN32_WCE
+# define GTEST_OS_WINDOWS_MOBILE 1
+# elif defined(__MINGW__) || defined(__MINGW32__)
+# define GTEST_OS_WINDOWS_MINGW 1
+# else
+# define GTEST_OS_WINDOWS_DESKTOP 1
+# endif // _WIN32_WCE
+#elif defined __APPLE__
+# define GTEST_OS_MAC 1
+#elif defined __linux__
+# define GTEST_OS_LINUX 1
+# ifdef ANDROID
+# define GTEST_OS_LINUX_ANDROID 1
+# endif // ANDROID
+#elif defined __MVS__
+# define GTEST_OS_ZOS 1
+#elif defined(__sun) && defined(__SVR4)
+# define GTEST_OS_SOLARIS 1
+#elif defined(_AIX)
+# define GTEST_OS_AIX 1
+#elif defined(__hpux)
+# define GTEST_OS_HPUX 1
+#elif defined __native_client__
+# define GTEST_OS_NACL 1
+#endif // __CYGWIN__
+
+// Brings in definitions for functions used in the testing::internal::posix
+// namespace (read, write, close, chdir, isatty, stat). We do not currently
+// use them on Windows Mobile.
+#if !GTEST_OS_WINDOWS
+// This assumes that non-Windows OSes provide unistd.h. For OSes where this
+// is not the case, we need to include headers that provide the functions
+// mentioned above.
+# include <unistd.h>
+# if !GTEST_OS_NACL
+// TODO(***@google.com): Remove this condition when Native Client SDK adds
+// strings.h (tracked in
+// http://code.google.com/p/nativeclient/issues/detail?id=1175).
+# include <strings.h> // Native Client doesn't provide strings.h.
+# endif
+#elif !GTEST_OS_WINDOWS_MOBILE
+# include <direct.h>
+# include <io.h>
+#endif
+
+// Defines this to true iff Google Test can use POSIX regular expressions.
+#ifndef GTEST_HAS_POSIX_RE
+# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS)
+#endif
+
+#if GTEST_HAS_POSIX_RE
+
+// On some platforms, <regex.h> needs someone to define size_t, and
+// won't compile otherwise. We can #include it here as we already
+// included <stdlib.h>, which is guaranteed to define size_t through
+// <stddef.h>.
+# include <regex.h> // NOLINT
+
+# define GTEST_USES_POSIX_RE 1
+
+#elif GTEST_OS_WINDOWS
+
+// <regex.h> is not available on Windows. Use our own simple regex
+// implementation instead.
+# define GTEST_USES_SIMPLE_RE 1
+
+#else
+
+// <regex.h> may not be available on this platform. Use our own
+// simple regex implementation instead.
+# define GTEST_USES_SIMPLE_RE 1
+
+#endif // GTEST_HAS_POSIX_RE
+
+#ifndef GTEST_HAS_EXCEPTIONS
+// The user didn't tell us whether exceptions are enabled, so we need
+// to figure it out.
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS
+// macro to enable exceptions, so we'll do the same.
+// Assumes that exceptions are enabled by default.
+# ifndef _HAS_EXCEPTIONS
+# define _HAS_EXCEPTIONS 1
+# endif // _HAS_EXCEPTIONS
+# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS
+# elif defined(__GNUC__) && __EXCEPTIONS
+// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled.
+# define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__SUNPRO_CC)
+// Sun Pro CC supports exceptions. However, there is no compile-time way of
+// detecting whether they are enabled or not. Therefore, we assume that
+// they are enabled unless the user tells us otherwise.
+# define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__IBMCPP__) && __EXCEPTIONS
+// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled.
+# define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__HP_aCC)
+// Exception handling is in effect by default in HP aCC compiler. It has to
+// be turned of by +noeh compiler option if desired.
+# define GTEST_HAS_EXCEPTIONS 1
+# else
+// For other compilers, we assume exceptions are disabled to be
+// conservative.
+# define GTEST_HAS_EXCEPTIONS 0
+# endif // defined(_MSC_VER) || defined(__BORLANDC__)
+#endif // GTEST_HAS_EXCEPTIONS
+
+#if !defined(GTEST_HAS_STD_STRING)
+// Even though we don't use this macro any longer, we keep it in case
+// some clients still depend on it.
+# define GTEST_HAS_STD_STRING 1
+#elif !GTEST_HAS_STD_STRING
+// The user told us that ::std::string isn't available.
+# error "Google Test cannot be used where ::std::string isn't available."
+#endif // !defined(GTEST_HAS_STD_STRING)
+
+#ifndef GTEST_HAS_GLOBAL_STRING
+// The user didn't tell us whether ::string is available, so we need
+// to figure it out.
+
+# define GTEST_HAS_GLOBAL_STRING 0
+
+#endif // GTEST_HAS_GLOBAL_STRING
+
+#ifndef GTEST_HAS_STD_WSTRING
+// The user didn't tell us whether ::std::wstring is available, so we need
+// to figure it out.
+// TODO(***@google.com): uses autoconf to detect whether ::std::wstring
+// is available.
+
+// Cygwin 1.7 and below doesn't support ::std::wstring.
+// Solaris' libc++ doesn't support it either. Android has
+// no support for it at least as recent as Froyo (2.2).
+# define GTEST_HAS_STD_WSTRING \
+ (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS))
+
+#endif // GTEST_HAS_STD_WSTRING
+
+#ifndef GTEST_HAS_GLOBAL_WSTRING
+// The user didn't tell us whether ::wstring is available, so we need
+// to figure it out.
+# define GTEST_HAS_GLOBAL_WSTRING \
+ (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING)
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+// Determines whether RTTI is available.
+#ifndef GTEST_HAS_RTTI
+// The user didn't tell us whether RTTI is enabled, so we need to
+// figure it out.
+
+# ifdef _MSC_VER
+
+# ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled.
+# define GTEST_HAS_RTTI 1
+# else
+# define GTEST_HAS_RTTI 0
+# endif
+
+// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled.
+# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302)
+
+# ifdef __GXX_RTTI
+# define GTEST_HAS_RTTI 1
+# else
+# define GTEST_HAS_RTTI 0
+# endif // __GXX_RTTI
+
+// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if
+// both the typeid and dynamic_cast features are present.
+# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900)
+
+# ifdef __RTTI_ALL__
+# define GTEST_HAS_RTTI 1
+# else
+# define GTEST_HAS_RTTI 0
+# endif
+
+# else
+
+// For all other compilers, we assume RTTI is enabled.
+# define GTEST_HAS_RTTI 1
+
+# endif // _MSC_VER
+
+#endif // GTEST_HAS_RTTI
+
+// It's this header's responsibility to #include <typeinfo> when RTTI
+// is enabled.
+#if GTEST_HAS_RTTI
+# include <typeinfo>
+#endif
+
+// Determines whether Google Test can use the pthreads library.
+#ifndef GTEST_HAS_PTHREAD
+// The user didn't tell us explicitly, so we assume pthreads support is
+// available on Linux and Mac.
+//
+// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0
+// to your compiler flags.
+# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX)
+#endif // GTEST_HAS_PTHREAD
+
+#if GTEST_HAS_PTHREAD
+// gtest-port.h guarantees to #include <pthread.h> when GTEST_HAS_PTHREAD is
+// true.
+# include <pthread.h> // NOLINT
+
+// For timespec and nanosleep, used below.
+# include <time.h> // NOLINT
+#endif
+
+// Determines whether Google Test can use tr1/tuple. You can define
+// this macro to 0 to prevent Google Test from using tuple (any
+// feature depending on tuple with be disabled in this mode).
+#ifndef GTEST_HAS_TR1_TUPLE
+// The user didn't tell us not to do it, so we assume it's OK.
+# define GTEST_HAS_TR1_TUPLE 1
+#endif // GTEST_HAS_TR1_TUPLE
+
+// Determines whether Google Test's own tr1 tuple implementation
+// should be used.
+#ifndef GTEST_USE_OWN_TR1_TUPLE
+// The user didn't tell us, so we need to figure it out.
+
+// We use our own TR1 tuple if we aren't sure the user has an
+// implementation of it already. At this time, GCC 4.0.0+ and MSVC
+// 2010 are the only mainstream compilers that come with a TR1 tuple
+// implementation. NVIDIA's CUDA NVCC compiler pretends to be GCC by
+// defining __GNUC__ and friends, but cannot compile GCC's tuple
+// implementation. MSVC 2008 (9.0) provides TR1 tuple in a 323 MB
+// Feature Pack download, which we cannot assume the user has.
+# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000)) \
+ || _MSC_VER >= 1600
+# define GTEST_USE_OWN_TR1_TUPLE 0
+# else
+# define GTEST_USE_OWN_TR1_TUPLE 1
+# endif
+
+#endif // GTEST_USE_OWN_TR1_TUPLE
+
+// To avoid conditional compilation everywhere, we make it
+// gtest-port.h's responsibility to #include the header implementing
+// tr1/tuple.
+#if GTEST_HAS_TR1_TUPLE
+
+# if GTEST_USE_OWN_TR1_TUPLE
+// This file was GENERATED by a script. DO NOT EDIT BY HAND!!!
+
+// Copyright 2009 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: ***@google.com (Zhanyong Wan)
+
+// Implements a subset of TR1 tuple needed by Google Test and Google Mock.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+
+#include <utility> // For ::std::pair.
+
+// The compiler used in Symbian has a bug that prevents us from declaring the
+// tuple template as a friend (it complains that tuple is redefined). This
+// hack bypasses the bug by declaring the members that should otherwise be
+// private as public.
+// Sun Studio versions < 12 also have the above bug.
+#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590)
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public:
+#else
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \
+ template <GTEST_10_TYPENAMES_(U)> friend class tuple; \
+ private:
+#endif
+
+// GTEST_n_TUPLE_(T) is the type of an n-tuple.
+#define GTEST_0_TUPLE_(T) tuple<>
+#define GTEST_1_TUPLE_(T) tuple<T##0, void, void, void, void, void, void, \
+ void, void, void>
+#define GTEST_2_TUPLE_(T) tuple<T##0, T##1, void, void, void, void, void, \
+ void, void, void>
+#define GTEST_3_TUPLE_(T) tuple<T##0, T##1, T##2, void, void, void, void, \
+ void, void, void>
+#define GTEST_4_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, void, void, void, \
+ void, void, void>
+#define GTEST_5_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, void, void, \
+ void, void, void>
+#define GTEST_6_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, void, \
+ void, void, void>
+#define GTEST_7_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ void, void, void>
+#define GTEST_8_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ T##7, void, void>
+#define GTEST_9_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ T##7, T##8, void>
+#define GTEST_10_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ T##7, T##8, T##9>
+
+// GTEST_n_TYPENAMES_(T) declares a list of n typenames.
+#define GTEST_0_TYPENAMES_(T)
+#define GTEST_1_TYPENAMES_(T) typename T##0
+#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1
+#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2
+#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3
+#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4
+#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5
+#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6
+#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6, typename T##7
+#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6, \
+ typename T##7, typename T##8
+#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6, \
+ typename T##7, typename T##8, typename T##9
+
+// In theory, defining stuff in the ::std namespace is undefined
+// behavior. We can do this as we are playing the role of a standard
+// library vendor.
+namespace std {
+namespace tr1 {
+
+template <typename T0 = void, typename T1 = void, typename T2 = void,
+ typename T3 = void, typename T4 = void, typename T5 = void,
+ typename T6 = void, typename T7 = void, typename T8 = void,
+ typename T9 = void>
+class tuple;
+
+// Anything in namespace gtest_internal is Google Test's INTERNAL
+// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code.
+namespace gtest_internal {
+
+// ByRef<T>::type is T if T is a reference; otherwise it's const T&.
+template <typename T>
+struct ByRef { typedef const T& type; }; // NOLINT
+template <typename T>
+struct ByRef<T&> { typedef T& type; }; // NOLINT
+
+// A handy wrapper for ByRef.
+#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef<T>::type
+
+// AddRef<T>::type is T if T is a reference; otherwise it's T&. This
+// is the same as tr1::add_reference<T>::type.
+template <typename T>
+struct AddRef { typedef T& type; }; // NOLINT
+template <typename T>
+struct AddRef<T&> { typedef T& type; }; // NOLINT
+
+// A handy wrapper for AddRef.
+#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef<T>::type
+
+// A helper for implementing get<k>().
+template <int k> class Get;
+
+// A helper for implementing tuple_element<k, T>. kIndexValid is true
+// iff k < the number of fields in tuple type T.
+template <bool kIndexValid, int kIndex, class Tuple>
+struct TupleElement;
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 0, GTEST_10_TUPLE_(T)> { typedef T0 type; };
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 1, GTEST_10_TUPLE_(T)> { typedef T1 type; };
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 2, GTEST_10_TUPLE_(T)> { typedef T2 type; };
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 3, GTEST_10_TUPLE_(T)> { typedef T3 type; };
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 4, GTEST_10_TUPLE_(T)> { typedef T4 type; };
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 5, GTEST_10_TUPLE_(T)> { typedef T5 type; };
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 6, GTEST_10_TUPLE_(T)> { typedef T6 type; };
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 7, GTEST_10_TUPLE_(T)> { typedef T7 type; };
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 8, GTEST_10_TUPLE_(T)> { typedef T8 type; };
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 9, GTEST_10_TUPLE_(T)> { typedef T9 type; };
+
+} // namespace gtest_internal
+
+template <>
+class tuple<> {
+ public:
+ tuple() {}
+ tuple(const tuple& /* t */) {}
+ tuple& operator=(const tuple& /* t */) { return *this; }
+};
+
+template <GTEST_1_TYPENAMES_(T)>
+class GTEST_1_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {}
+
+ tuple(const tuple& t) : f0_(t.f0_) {}
+
+ template <GTEST_1_TYPENAMES_(U)>
+ tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_1_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_1_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_1_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ return *this;
+ }
+
+ T0 f0_;
+};
+
+template <GTEST_2_TYPENAMES_(T)>
+class GTEST_2_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0),
+ f1_(f1) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {}
+
+ template <GTEST_2_TYPENAMES_(U)>
+ tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {}
+ template <typename U0, typename U1>
+ tuple(const ::std::pair<U0, U1>& p) : f0_(p.first), f1_(p.second) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_2_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_2_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+ template <typename U0, typename U1>
+ tuple& operator=(const ::std::pair<U0, U1>& p) {
+ f0_ = p.first;
+ f1_ = p.second;
+ return *this;
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_2_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+};
+
+template <GTEST_3_TYPENAMES_(T)>
+class GTEST_3_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {}
+
+ template <GTEST_3_TYPENAMES_(U)>
+ tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_3_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_3_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_3_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+};
+
+template <GTEST_4_TYPENAMES_(T)>
+class GTEST_4_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2),
+ f3_(f3) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {}
+
+ template <GTEST_4_TYPENAMES_(U)>
+ tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_4_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_4_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_4_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+};
+
+template <GTEST_5_TYPENAMES_(T)>
+class GTEST_5_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3,
+ GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_) {}
+
+ template <GTEST_5_TYPENAMES_(U)>
+ tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_5_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_5_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_5_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+};
+
+template <GTEST_6_TYPENAMES_(T)>
+class GTEST_6_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+ f5_(f5) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_) {}
+
+ template <GTEST_6_TYPENAMES_(U)>
+ tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_6_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_6_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_6_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+};
+
+template <GTEST_7_TYPENAMES_(T)>
+class GTEST_7_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2),
+ f3_(f3), f4_(f4), f5_(f5), f6_(f6) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {}
+
+ template <GTEST_7_TYPENAMES_(U)>
+ tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_7_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_7_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_7_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+};
+
+template <GTEST_8_TYPENAMES_(T)>
+class GTEST_8_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6,
+ GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+ f5_(f5), f6_(f6), f7_(f7) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {}
+
+ template <GTEST_8_TYPENAMES_(U)>
+ tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_8_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_8_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_8_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ f7_ = t.f7_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+ T7 f7_;
+};
+
+template <GTEST_9_TYPENAMES_(T)>
+class GTEST_9_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7,
+ GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+ f5_(f5), f6_(f6), f7_(f7), f8_(f8) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {}
+
+ template <GTEST_9_TYPENAMES_(U)>
+ tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_9_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_9_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_9_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ f7_ = t.f7_;
+ f8_ = t.f8_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+ T7 f7_;
+ T8 f8_;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+class tuple {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(),
+ f9_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7,
+ GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2),
+ f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {}
+
+ template <GTEST_10_TYPENAMES_(U)>
+ tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_),
+ f9_(t.f9_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_10_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_10_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_10_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ f7_ = t.f7_;
+ f8_ = t.f8_;
+ f9_ = t.f9_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+ T7 f7_;
+ T8 f8_;
+ T9 f9_;
+};
+
+// 6.1.3.2 Tuple creation functions.
+
+// Known limitations: we don't support passing an
+// std::tr1::reference_wrapper<T> to make_tuple(). And we don't
+// implement tie().
+
+inline tuple<> make_tuple() { return tuple<>(); }
+
+template <GTEST_1_TYPENAMES_(T)>
+inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) {
+ return GTEST_1_TUPLE_(T)(f0);
+}
+
+template <GTEST_2_TYPENAMES_(T)>
+inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) {
+ return GTEST_2_TUPLE_(T)(f0, f1);
+}
+
+template <GTEST_3_TYPENAMES_(T)>
+inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) {
+ return GTEST_3_TUPLE_(T)(f0, f1, f2);
+}
+
+template <GTEST_4_TYPENAMES_(T)>
+inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3) {
+ return GTEST_4_TUPLE_(T)(f0, f1, f2, f3);
+}
+
+template <GTEST_5_TYPENAMES_(T)>
+inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4) {
+ return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4);
+}
+
+template <GTEST_6_TYPENAMES_(T)>
+inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5) {
+ return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5);
+}
+
+template <GTEST_7_TYPENAMES_(T)>
+inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6) {
+ return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6);
+}
+
+template <GTEST_8_TYPENAMES_(T)>
+inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) {
+ return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7);
+}
+
+template <GTEST_9_TYPENAMES_(T)>
+inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7,
+ const T8& f8) {
+ return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8);
+}
+
+template <GTEST_10_TYPENAMES_(T)>
+inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7,
+ const T8& f8, const T9& f9) {
+ return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9);
+}
+
+// 6.1.3.3 Tuple helper classes.
+
+template <typename Tuple> struct tuple_size;
+
+template <GTEST_0_TYPENAMES_(T)>
+struct tuple_size<GTEST_0_TUPLE_(T)> { static const int value = 0; };
+
+template <GTEST_1_TYPENAMES_(T)>
+struct tuple_size<GTEST_1_TUPLE_(T)> { static const int value = 1; };
+
+template <GTEST_2_TYPENAMES_(T)>
+struct tuple_size<GTEST_2_TUPLE_(T)> { static const int value = 2; };
+
+template <GTEST_3_TYPENAMES_(T)>
+struct tuple_size<GTEST_3_TUPLE_(T)> { static const int value = 3; };
+
+template <GTEST_4_TYPENAMES_(T)>
+struct tuple_size<GTEST_4_TUPLE_(T)> { static const int value = 4; };
+
+template <GTEST_5_TYPENAMES_(T)>
+struct tuple_size<GTEST_5_TUPLE_(T)> { static const int value = 5; };
+
+template <GTEST_6_TYPENAMES_(T)>
+struct tuple_size<GTEST_6_TUPLE_(T)> { static const int value = 6; };
+
+template <GTEST_7_TYPENAMES_(T)>
+struct tuple_size<GTEST_7_TUPLE_(T)> { static const int value = 7; };
+
+template <GTEST_8_TYPENAMES_(T)>
+struct tuple_size<GTEST_8_TUPLE_(T)> { static const int value = 8; };
+
+template <GTEST_9_TYPENAMES_(T)>
+struct tuple_size<GTEST_9_TUPLE_(T)> { static const int value = 9; };
+
+template <GTEST_10_TYPENAMES_(T)>
+struct tuple_size<GTEST_10_TUPLE_(T)> { static const int value = 10; };
+
+template <int k, class Tuple>
+struct tuple_element {
+ typedef typename gtest_internal::TupleElement<
+ k < (tuple_size<Tuple>::value), k, Tuple>::type type;
+};
+
+#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element<k, Tuple >::type
+
+// 6.1.3.4 Element access.
+
+namespace gtest_internal {
+
+template <>
+class Get<0> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple))
+ Field(Tuple& t) { return t.f0_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple))
+ ConstField(const Tuple& t) { return t.f0_; }
+};
+
+template <>
+class Get<1> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple))
+ Field(Tuple& t) { return t.f1_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple))
+ ConstField(const Tuple& t) { return t.f1_; }
+};
+
+template <>
+class Get<2> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple))
+ Field(Tuple& t) { return t.f2_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple))
+ ConstField(const Tuple& t) { return t.f2_; }
+};
+
+template <>
+class Get<3> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple))
+ Field(Tuple& t) { return t.f3_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple))
+ ConstField(const Tuple& t) { return t.f3_; }
+};
+
+template <>
+class Get<4> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple))
+ Field(Tuple& t) { return t.f4_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple))
+ ConstField(const Tuple& t) { return t.f4_; }
+};
+
+template <>
+class Get<5> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple))
+ Field(Tuple& t) { return t.f5_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple))
+ ConstField(const Tuple& t) { return t.f5_; }
+};
+
+template <>
+class Get<6> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple))
+ Field(Tuple& t) { return t.f6_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple))
+ ConstField(const Tuple& t) { return t.f6_; }
+};
+
+template <>
+class Get<7> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple))
+ Field(Tuple& t) { return t.f7_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple))
+ ConstField(const Tuple& t) { return t.f7_; }
+};
+
+template <>
+class Get<8> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple))
+ Field(Tuple& t) { return t.f8_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple))
+ ConstField(const Tuple& t) { return t.f8_; }
+};
+
+template <>
+class Get<9> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple))
+ Field(Tuple& t) { return t.f9_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple))
+ ConstField(const Tuple& t) { return t.f9_; }
+};
+
+} // namespace gtest_internal
+
+template <int k, GTEST_10_TYPENAMES_(T)>
+GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T)))
+get(GTEST_10_TUPLE_(T)& t) {
+ return gtest_internal::Get<k>::Field(t);
+}
+
+template <int k, GTEST_10_TYPENAMES_(T)>
+GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T)))
+get(const GTEST_10_TUPLE_(T)& t) {
+ return gtest_internal::Get<k>::ConstField(t);
+}
+
+// 6.1.3.5 Relational operators
+
+// We only implement == and !=, as we don't have a need for the rest yet.
+
+namespace gtest_internal {
+
+// SameSizeTuplePrefixComparator<k, k>::Eq(t1, t2) returns true if the
+// first k fields of t1 equals the first k fields of t2.
+// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if
+// k1 != k2.
+template <int kSize1, int kSize2>
+struct SameSizeTuplePrefixComparator;
+
+template <>
+struct SameSizeTuplePrefixComparator<0, 0> {
+ template <class Tuple1, class Tuple2>
+ static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) {
+ return true;
+ }
+};
+
+template <int k>
+struct SameSizeTuplePrefixComparator<k, k> {
+ template <class Tuple1, class Tuple2>
+ static bool Eq(const Tuple1& t1, const Tuple2& t2) {
+ return SameSizeTuplePrefixComparator<k - 1, k - 1>::Eq(t1, t2) &&
+ ::std::tr1::get<k - 1>(t1) == ::std::tr1::get<k - 1>(t2);
+ }
+};
+
+} // namespace gtest_internal
+
+template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)>
+inline bool operator==(const GTEST_10_TUPLE_(T)& t,
+ const GTEST_10_TUPLE_(U)& u) {
+ return gtest_internal::SameSizeTuplePrefixComparator<
+ tuple_size<GTEST_10_TUPLE_(T)>::value,
+ tuple_size<GTEST_10_TUPLE_(U)>::value>::Eq(t, u);
+}
+
+template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)>
+inline bool operator!=(const GTEST_10_TUPLE_(T)& t,
+ const GTEST_10_TUPLE_(U)& u) { return !(t == u); }
+
+// 6.1.4 Pairs.
+// Unimplemented.
+
+} // namespace tr1
+} // namespace std
+
+#undef GTEST_0_TUPLE_
+#undef GTEST_1_TUPLE_
+#undef GTEST_2_TUPLE_
+#undef GTEST_3_TUPLE_
+#undef GTEST_4_TUPLE_
+#undef GTEST_5_TUPLE_
+#undef GTEST_6_TUPLE_
+#undef GTEST_7_TUPLE_
+#undef GTEST_8_TUPLE_
+#undef GTEST_9_TUPLE_
+#undef GTEST_10_TUPLE_
+
+#undef GTEST_0_TYPENAMES_
+#undef GTEST_1_TYPENAMES_
+#undef GTEST_2_TYPENAMES_
+#undef GTEST_3_TYPENAMES_
+#undef GTEST_4_TYPENAMES_
+#undef GTEST_5_TYPENAMES_
+#undef GTEST_6_TYPENAMES_
+#undef GTEST_7_TYPENAMES_
+#undef GTEST_8_TYPENAMES_
+#undef GTEST_9_TYPENAMES_
+#undef GTEST_10_TYPENAMES_
+
+#undef GTEST_DECLARE_TUPLE_AS_FRIEND_
+#undef GTEST_BY_REF_
+#undef GTEST_ADD_REF_
+#undef GTEST_TUPLE_ELEMENT_
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+# elif GTEST_OS_SYMBIAN
+
+// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to
+// use STLport's tuple implementation, which unfortunately doesn't
+// work as the copy of STLport distributed with Symbian is incomplete.
+// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to
+// use its own tuple implementation.
+# ifdef BOOST_HAS_TR1_TUPLE
+# undef BOOST_HAS_TR1_TUPLE
+# endif // BOOST_HAS_TR1_TUPLE
+
+// This prevents <boost/tr1/detail/config.hpp>, which defines
+// BOOST_HAS_TR1_TUPLE, from being #included by Boost's <tuple>.
+# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED
+# include <tuple>
+
+# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000)
+// GCC 4.0+ implements tr1/tuple in the <tr1/tuple> header. This does
+// not conform to the TR1 spec, which requires the header to be <tuple>.
+
+# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
+// Until version 4.3.2, gcc has a bug that causes <tr1/functional>,
+// which is #included by <tr1/tuple>, to not compile when RTTI is
+// disabled. _TR1_FUNCTIONAL is the header guard for
+// <tr1/functional>. Hence the following #define is a hack to prevent
+// <tr1/functional> from being included.
+# define _TR1_FUNCTIONAL 1
+# include <tr1/tuple>
+# undef _TR1_FUNCTIONAL // Allows the user to #include
+ // <tr1/functional> if he chooses to.
+# else
+# include <tr1/tuple> // NOLINT
+# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
+
+# else
+// If the compiler is not GCC 4.0+, we assume the user is using a
+// spec-conforming TR1 implementation.
+# include <tuple> // NOLINT
+# endif // GTEST_USE_OWN_TR1_TUPLE
+
+#endif // GTEST_HAS_TR1_TUPLE
+
+// Determines whether clone(2) is supported.
+// Usually it will only be available on Linux, excluding
+// Linux on the Itanium architecture.
+// Also see http://linux.die.net/man/2/clone.
+#ifndef GTEST_HAS_CLONE
+// The user didn't tell us, so we need to figure it out.
+
+# if GTEST_OS_LINUX && !defined(__ia64__)
+# define GTEST_HAS_CLONE 1
+# else
+# define GTEST_HAS_CLONE 0
+# endif // GTEST_OS_LINUX && !defined(__ia64__)
+
+#endif // GTEST_HAS_CLONE
+
+// Determines whether to support stream redirection. This is used to test
+// output correctness and to implement death tests.
+#ifndef GTEST_HAS_STREAM_REDIRECTION
+// By default, we assume that stream redirection is supported on all
+// platforms except known mobile ones.
+# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN
+# define GTEST_HAS_STREAM_REDIRECTION 0
+# else
+# define GTEST_HAS_STREAM_REDIRECTION 1
+# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN
+#endif // GTEST_HAS_STREAM_REDIRECTION
+
+// Determines whether to support death tests.
+// Google Test does not support death tests for VC 7.1 and earlier as
+// abort() in a VC 7.1 application compiled as GUI in debug config
+// pops up a dialog window that cannot be suppressed programmatically.
+#if (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
+ (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \
+ GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX)
+# define GTEST_HAS_DEATH_TEST 1
+# include <vector> // NOLINT
+#endif
+
+// We don't support MSVC 7.1 with exceptions disabled now. Therefore
+// all the compilers we care about are adequate for supporting
+// value-parameterized tests.
+#define GTEST_HAS_PARAM_TEST 1
+
+// Determines whether to support type-driven tests.
+
+// Typed tests need <typeinfo> and variadic macros, which GCC, VC++ 8.0,
+// Sun Pro CC, IBM Visual Age, and HP aCC support.
+#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \
+ defined(__IBMCPP__) || defined(__HP_aCC)
+# define GTEST_HAS_TYPED_TEST 1
+# define GTEST_HAS_TYPED_TEST_P 1
+#endif
+
+// Determines whether to support Combine(). This only makes sense when
+// value-parameterized tests are enabled. The implementation doesn't
+// work on Sun Studio since it doesn't understand templated conversion
+// operators.
+#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC)
+# define GTEST_HAS_COMBINE 1
+#endif
+
+// Determines whether the system compiler uses UTF-16 for encoding wide strings.
+#define GTEST_WIDE_STRING_USES_UTF16_ \
+ (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX)
+
+// Determines whether test results can be streamed to a socket.
+#if GTEST_OS_LINUX
+# define GTEST_CAN_STREAM_RESULTS_ 1
+#endif
+
+// Defines some utility macros.
+
+// The GNU compiler emits a warning if nested "if" statements are followed by
+// an "else" statement and braces are not used to explicitly disambiguate the
+// "else" binding. This leads to problems with code like:
+//
+// if (gate)
+// ASSERT_*(condition) << "Some message";
+//
+// The "switch (0) case 0:" idiom is used to suppress this.
+#ifdef __INTEL_COMPILER
+# define GTEST_AMBIGUOUS_ELSE_BLOCKER_
+#else
+# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT
+#endif
+
+// Use this annotation at the end of a struct/class definition to
+// prevent the compiler from optimizing away instances that are never
+// used. This is useful when all interesting logic happens inside the
+// c'tor and / or d'tor. Example:
+//
+// struct Foo {
+// Foo() { ... }
+// } GTEST_ATTRIBUTE_UNUSED_;
+//
+// Also use it after a variable or parameter declaration to tell the
+// compiler the variable/parameter does not have to be used.
+#if defined(__GNUC__) && !defined(COMPILER_ICC)
+# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused))
+#else
+# define GTEST_ATTRIBUTE_UNUSED_
+#endif
+
+// A macro to disallow operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_ASSIGN_(type)\
+ void operator=(type const &)
+
+// A macro to disallow copy constructor and operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\
+ type(type const &);\
+ GTEST_DISALLOW_ASSIGN_(type)
+
+// Tell the compiler to warn about unused return values for functions declared
+// with this macro. The macro should be used on function declarations
+// following the argument list:
+//
+// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_;
+#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC)
+# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result))
+#else
+# define GTEST_MUST_USE_RESULT_
+#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC
+
+// Determine whether the compiler supports Microsoft's Structured Exception
+// Handling. This is supported by several Windows compilers but generally
+// does not exist on any other system.
+#ifndef GTEST_HAS_SEH
+// The user didn't tell us, so we need to figure it out.
+
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+// These two compilers are known to support SEH.
+# define GTEST_HAS_SEH 1
+# else
+// Assume no SEH.
+# define GTEST_HAS_SEH 0
+# endif
+
+#endif // GTEST_HAS_SEH
+
+#ifdef _MSC_VER
+
+# if GTEST_LINKED_AS_SHARED_LIBRARY
+# define GTEST_API_ __declspec(dllimport)
+# elif GTEST_CREATE_SHARED_LIBRARY
+# define GTEST_API_ __declspec(dllexport)
+# endif
+
+#endif // _MSC_VER
+
+#ifndef GTEST_API_
+# define GTEST_API_
+#endif
+
+#ifdef __GNUC__
+// Ask the compiler to never inline a given function.
+# define GTEST_NO_INLINE_ __attribute__((noinline))
+#else
+# define GTEST_NO_INLINE_
+#endif
+
+namespace testing {
+
+class Message;
+
+namespace internal {
+
+class String;
+
+// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
+// content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+
+template <bool>
+struct CompileAssert {
+};
+
+#define GTEST_COMPILE_ASSERT_(expr, msg) \
+ typedef ::testing::internal::CompileAssert<(bool(expr))> \
+ msg[bool(expr) ? 1 : -1]
+
+// Implementation details of GTEST_COMPILE_ASSERT_:
+//
+// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1
+// elements (and thus is invalid) when the expression is false.
+//
+// - The simpler definition
+//
+// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1]
+//
+// does not work, as gcc supports variable-length arrays whose sizes
+// are determined at run-time (this is gcc's extension and not part
+// of the C++ standard). As a result, gcc fails to reject the
+// following code with the simple definition:
+//
+// int foo;
+// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is
+// // not a compile-time constant.
+//
+// - By using the type CompileAssert<(bool(expr))>, we ensures that
+// expr is a compile-time constant. (Template arguments must be
+// determined at compile-time.)
+//
+// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
+// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
+//
+// CompileAssert<bool(expr)>
+//
+// instead, these compilers will refuse to compile
+//
+// GTEST_COMPILE_ASSERT_(5 > 0, some_message);
+//
+// (They seem to think the ">" in "5 > 0" marks the end of the
+// template argument list.)
+//
+// - The array size is (bool(expr) ? 1 : -1), instead of simply
+//
+// ((expr) ? 1 : -1).
+//
+// This is to avoid running into a bug in MS VC 7.1, which
+// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
+
+// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h.
+//
+// This template is declared, but intentionally undefined.
+template <typename T1, typename T2>
+struct StaticAssertTypeEqHelper;
+
+template <typename T>
+struct StaticAssertTypeEqHelper<T, T> {};
+
+#if GTEST_HAS_GLOBAL_STRING
+typedef ::string string;
+#else
+typedef ::std::string string;
+#endif // GTEST_HAS_GLOBAL_STRING
+
+#if GTEST_HAS_GLOBAL_WSTRING
+typedef ::wstring wstring;
+#elif GTEST_HAS_STD_WSTRING
+typedef ::std::wstring wstring;
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+// A helper for suppressing warnings on constant condition. It just
+// returns 'condition'.
+GTEST_API_ bool IsTrue(bool condition);
+
+// Defines scoped_ptr.
+
+// This implementation of scoped_ptr is PARTIAL - it only contains
+// enough stuff to satisfy Google Test's need.
+template <typename T>
+class scoped_ptr {
+ public:
+ typedef T element_type;
+
+ explicit scoped_ptr(T* p = NULL) : ptr_(p) {}
+ ~scoped_ptr() { reset(); }
+
+ T& operator*() const { return *ptr_; }
+ T* operator->() const { return ptr_; }
+ T* get() const { return ptr_; }
+
+ T* release() {
+ T* const ptr = ptr_;
+ ptr_ = NULL;
+ return ptr;
+ }
+
+ void reset(T* p = NULL) {
+ if (p != ptr_) {
+ if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type.
+ delete ptr_;
+ }
+ ptr_ = p;
+ }
+ }
+ private:
+ T* ptr_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr);
+};
+
+// Defines RE.
+
+// A simple C++ wrapper for <regex.h>. It uses the POSIX Extended
+// Regular Expression syntax.
+class GTEST_API_ RE {
+ public:
+ // A copy constructor is required by the Standard to initialize object
+ // references from r-values.
+ RE(const RE& other) { Init(other.pattern()); }
+
+ // Constructs an RE from a string.
+ RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT
+
+#if GTEST_HAS_GLOBAL_STRING
+
+ RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT
+
+#endif // GTEST_HAS_GLOBAL_STRING
+
+ RE(const char* regex) { Init(regex); } // NOLINT
+ ~RE();
+
+ // Returns the string representation of the regex.
+ const char* pattern() const { return pattern_; }
+
+ // FullMatch(str, re) returns true iff regular expression re matches
+ // the entire str.
+ // PartialMatch(str, re) returns true iff regular expression re
+ // matches a substring of str (including str itself).
+ //
+ // TODO(***@google.com): make FullMatch() and PartialMatch() work
+ // when str contains NUL characters.
+ static bool FullMatch(const ::std::string& str, const RE& re) {
+ return FullMatch(str.c_str(), re);
+ }
+ static bool PartialMatch(const ::std::string& str, const RE& re) {
+ return PartialMatch(str.c_str(), re);
+ }
+
+#if GTEST_HAS_GLOBAL_STRING
+
+ static bool FullMatch(const ::string& str, const RE& re) {
+ return FullMatch(str.c_str(), re);
+ }
+ static bool PartialMatch(const ::string& str, const RE& re) {
+ return PartialMatch(str.c_str(), re);
+ }
+
+#endif // GTEST_HAS_GLOBAL_STRING
+
+ static bool FullMatch(const char* str, const RE& re);
+ static bool PartialMatch(const char* str, const RE& re);
+
+ private:
+ void Init(const char* regex);
+
+ // We use a const char* instead of a string, as Google Test may be used
+ // where string is not available. We also do not use Google Test's own
+ // String type here, in order to simplify dependencies between the
+ // files.
+ const char* pattern_;
+ bool is_valid_;
+
+#if GTEST_USES_POSIX_RE
+
+ regex_t full_regex_; // For FullMatch().
+ regex_t partial_regex_; // For PartialMatch().
+
+#else // GTEST_USES_SIMPLE_RE
+
+ const char* full_pattern_; // For FullMatch();
+
+#endif
+
+ GTEST_DISALLOW_ASSIGN_(RE);
+};
+
+// Formats a source file path and a line number as they would appear
+// in an error message from the compiler used to compile this code.
+GTEST_API_ ::std::string FormatFileLocation(const char* file, int line);
+
+// Formats a file location for compiler-independent XML output.
+// Although this function is not platform dependent, we put it next to
+// FormatFileLocation in order to contrast the two functions.
+GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file,
+ int line);
+
+// Defines logging utilities:
+// GTEST_LOG_(severity) - logs messages at the specified severity level. The
+// message itself is streamed into the macro.
+// LogToStderr() - directs all log messages to stderr.
+// FlushInfoLog() - flushes informational log messages.
+
+enum GTestLogSeverity {
+ GTEST_INFO,
+ GTEST_WARNING,
+ GTEST_ERROR,
+ GTEST_FATAL
+};
+
+// Formats log entry severity, provides a stream object for streaming the
+// log message, and terminates the message with a newline when going out of
+// scope.
+class GTEST_API_ GTestLog {
+ public:
+ GTestLog(GTestLogSeverity severity, const char* file, int line);
+
+ // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program.
+ ~GTestLog();
+
+ ::std::ostream& GetStream() { return ::std::cerr; }
+
+ private:
+ const GTestLogSeverity severity_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog);
+};
+
+#define GTEST_LOG_(severity) \
+ ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \
+ __FILE__, __LINE__).GetStream()
+
+inline void LogToStderr() {}
+inline void FlushInfoLog() { fflush(NULL); }
+
+// INTERNAL IMPLEMENTATION - DO NOT USE.
+//
+// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition
+// is not satisfied.
+// Synopsys:
+// GTEST_CHECK_(boolean_condition);
+// or
+// GTEST_CHECK_(boolean_condition) << "Additional message";
+//
+// This checks the condition and if the condition is not satisfied
+// it prints message about the condition violation, including the
+// condition itself, plus additional message streamed into it, if any,
+// and then it aborts the program. It aborts the program irrespective of
+// whether it is built in the debug mode or not.
+#define GTEST_CHECK_(condition) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::IsTrue(condition)) \
+ ; \
+ else \
+ GTEST_LOG_(FATAL) << "Condition " #condition " failed. "
+
+// An all-mode assert to verify that the given POSIX-style function
+// call returns 0 (indicating success). Known limitation: this
+// doesn't expand to a balanced 'if' statement, so enclose the macro
+// in {} if you need to use it as the only statement in an 'if'
+// branch.
+#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \
+ if (const int gtest_error = (posix_call)) \
+ GTEST_LOG_(FATAL) << #posix_call << "failed with error " \
+ << gtest_error
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Use ImplicitCast_ as a safe version of static_cast for upcasting in
+// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a
+// const Foo*). When you use ImplicitCast_, the compiler checks that
+// the cast is safe. Such explicit ImplicitCast_s are necessary in
+// surprisingly many situations where C++ demands an exact type match
+// instead of an argument type convertable to a target type.
+//
+// The syntax for using ImplicitCast_ is the same as for static_cast:
+//
+// ImplicitCast_<ToType>(expr)
+//
+// ImplicitCast_ would have been part of the C++ standard library,
+// but the proposal was submitted too late. It will probably make
+// its way into the language in the future.
+//
+// This relatively ugly name is intentional. It prevents clashes with
+// similar functions users may have (e.g., implicit_cast). The internal
+// namespace alone is not enough because the function can be found by ADL.
+template<typename To>
+inline To ImplicitCast_(To x) { return x; }
+
+// When you upcast (that is, cast a pointer from type Foo to type
+// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts
+// always succeed. When you downcast (that is, cast a pointer from
+// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
+// how do you know the pointer is really of type SubclassOfFoo? It
+// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus,
+// when you downcast, you should use this macro. In debug mode, we
+// use dynamic_cast<> to double-check the downcast is legal (we die
+// if it's not). In normal mode, we do the efficient static_cast<>
+// instead. Thus, it's important to test in debug mode to make sure
+// the cast is legal!
+// This is the only place in the code we should use dynamic_cast<>.
+// In particular, you SHOULDN'T be using dynamic_cast<> in order to
+// do RTTI (eg code like this:
+// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
+// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
+// You should design the code some other way not to need this.
+//
+// This relatively ugly name is intentional. It prevents clashes with
+// similar functions users may have (e.g., down_cast). The internal
+// namespace alone is not enough because the function can be found by ADL.
+template<typename To, typename From> // use like this: DownCast_<T*>(foo);
+inline To DownCast_(From* f) { // so we only accept pointers
+ // Ensures that To is a sub-type of
Brad Chapman
2014-02-02 15:53:40 UTC
Permalink
This is an automated email from the git hooks/post-receive script.

chapmanb-guest pushed a commit to branch master
in repository freebayes.

commit 70a2413b64920ab37a5f6c7fea90aa6374a7cc0c
Author: chapmanb <***@50mail.com>
Date: Sun Feb 2 06:48:54 2014 -0500

Add debian build files and patches
---
.gitignore | 1 -
debian/changelog | 5 +++++
debian/compat | 1 +
debian/control | 20 ++++++++++++++++++++
debian/copyright | 35 +++++++++++++++++++++++++++++++++++
debian/install | 2 ++
debian/patches/clean.patch | 12 ++++++++++++
debian/patches/series | 1 +
debian/rules | 7 +++++++
debian/source/format | 1 +
debian/upstream | 8 ++++++++
debian/watch | 7 +++++++
12 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index a68208e..7e817d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
test/
bugs/
*.bak
-*.patch
*~
.Rhistory
simulation
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..9c70b47
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+freebayes (0.9.10.11-1) UNRELEASED; urgency=low
+
+ * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
+
+ -- Brad Chapman <***@50mail.com> Sun, 02 Feb 2014 05:14:33 -0500
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..f11c82a
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
\ No newline at end of file
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..f528923
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,20 @@
+Source: freebayes
+Section: science
+Priority: optional
+Maintainer: Brad Chapman <***@50mail.com>
+Build-Depends: debhelper (>= 9)
+Standards-Version: 3.9.5
+Homepage: https://github.com/ekg/freebayes
+Vcs-Browser: http://anonscm.debian.org/gitweb/?p=debian-med/freebayes.git
+Vcs-Git: git://anonscm.debian.org/debian-med/freebayes.git
+
+Package: freebayes
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: Bayesian haplotype-based polymorphism discovery and genotyping
+ FreeBayes is a Bayesian genetic variant detector designed to find
+ small polymorphisms, specifically SNPs (single-nucleotide
+ polymorphisms), indels (insertions and deletions), MNPs
+ (multi-nucleotide polymorphisms), and complex events (composite
+ insertion and substitution events) smaller than the length of a
+ short-read sequencing alignment.
\ No newline at end of file
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..0b32fa0
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,35 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: freebayes
+Source: https://github.com/ekg/freebayes
+
+Files: *
+Copyright: 2010-2014 Erik Garrison, Gabor Marth
+License: MIT
+
+Files: debian/*
+Copyright: 2014 Brad Chapman <***@50mail.com>
+License: MIT
+
+License: MIT
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# Please also look if there are files or directories which have a
+# different copyright/license attached and list them here.
+# Please avoid to pick license terms that are more restrictive than the
+# packaged work, as it may make Debian's contributions unacceptable upstream.
diff --git a/debian/install b/debian/install
new file mode 100644
index 0000000..f82faed
--- /dev/null
+++ b/debian/install
@@ -0,0 +1,2 @@
+bin/freebayes usr/bin
+bin/bamleftalign usr/bin
\ No newline at end of file
diff --git a/debian/patches/clean.patch b/debian/patches/clean.patch
new file mode 100644
index 0000000..e5d43c1
--- /dev/null
+++ b/debian/patches/clean.patch
@@ -0,0 +1,12 @@
+Index: freebayes/src/Makefile
+===================================================================
+--- freebayes.orig/src/Makefile 2014-02-02 06:40:47.242351987 -0500
++++ freebayes/src/Makefile 2014-02-02 06:42:43.390356892 -0500
+@@ -259,6 +259,6 @@
+
+ clean:
+ rm -rf *.o *.cgh *~ freebayes alleles ../bin/freebayes ../bin/alleles ../vcflib/*.o ../vcflib/tabixpp/*.{o,a}
+- cd $(BAMTOOLS_ROOT)/build && make clean
++ test -d $(BAMTOOLS_ROOT)/build && cd $(BAMTOOLS_ROOT)/build && make clean
+ cd ../vcflib/smithwaterman && make clean
+
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..5df678c
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+clean.patch
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..c30b66e
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,7 @@
+#!/usr/bin/make -f
+
+# This has to be exported to make some magic below work.
+export DH_OPTIONS
+
+%:
+ dh $@
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/upstream b/debian/upstream
new file mode 100644
index 0000000..eb3d836
--- /dev/null
+++ b/debian/upstream
@@ -0,0 +1,8 @@
+Reference:
+ Author: Erik Garrison and Gabor Marth
+ Title: Haplotype-based variant detection from short-read sequencing
+ Journal: arXiv
+ Year: 2012
+ DOI: arXiv:1207.3907
+ URL: http://arxiv.org/abs/1207.3907
+ eprint: http://arxiv.org/pdf/1207.3907v2.pdf
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..34b97ca
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,7 @@
+version=3
+
+# Need to manually clone and tar/gzip from GitHub
+# git clone --recursive git://github.com/ekg/freebayes.git
+# For eventual releases:
+opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/<project>-$1\.tar\.gz/ \
+ https://github.com/<user>/<project>/tags .*/v?(\d\S*)\.tar\.gz
\ No newline at end of file
--
Alioth's /git/debian-med/git-commit-notice on /srv/git.debian.org/git/debian-med/freebayes.git
Brad Chapman
2014-02-02 15:53:40 UTC
Permalink
This is an automated email from the git hooks/post-receive script.

chapmanb-guest pushed a commit to branch master
in repository freebayes.

commit 0bfee0237c1ee96869c3443253b296b30bbd86ba
Author: chapmanb <***@50mail.com>
Date: Sun Feb 2 10:41:29 2014 -0500

Add patches for intial clean and cmake dependency
---
.gitignore | 9 ---------
debian/changelog | 2 +-
debian/control | 15 ++++++++-------
debian/patches/clean.patch | 8 +++-----
debian/rules | 3 ---
src/Makefile | 2 +-
6 files changed, 13 insertions(+), 26 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7e817d2..e43b0f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1 @@
-test/
-bugs/
-*.bak
-*~
-.Rhistory
-simulation
-src/libbamtools.a
-performance
-math
.DS_Store
diff --git a/debian/changelog b/debian/changelog
index 9c70b47..7471511 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,5 @@
freebayes (0.9.10.11-1) UNRELEASED; urgency=low

- * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
+ * Initial FreeBayes packaging (Closes: #nnnn)

-- Brad Chapman <***@50mail.com> Sun, 02 Feb 2014 05:14:33 -0500
diff --git a/debian/control b/debian/control
index f528923..734b026 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,8 @@ Source: freebayes
Section: science
Priority: optional
Maintainer: Brad Chapman <***@50mail.com>
-Build-Depends: debhelper (>= 9)
+Build-Depends: debhelper (>= 9),
+ cmake (>= 2)
Standards-Version: 3.9.5
Homepage: https://github.com/ekg/freebayes
Vcs-Browser: http://anonscm.debian.org/gitweb/?p=debian-med/freebayes.git
@@ -12,9 +13,9 @@ Package: freebayes
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Bayesian haplotype-based polymorphism discovery and genotyping
- FreeBayes is a Bayesian genetic variant detector designed to find
- small polymorphisms, specifically SNPs (single-nucleotide
- polymorphisms), indels (insertions and deletions), MNPs
- (multi-nucleotide polymorphisms), and complex events (composite
- insertion and substitution events) smaller than the length of a
- short-read sequencing alignment.
\ No newline at end of file
+ FreeBayes is a Bayesian genetic variant detector designed to find
+ small polymorphisms, specifically SNPs (single-nucleotide
+ polymorphisms), indels (insertions and deletions), MNPs
+ (multi-nucleotide polymorphisms), and complex events (composite
+ insertion and substitution events) smaller than the length of a
+ short-read sequencing alignment.
\ No newline at end of file
diff --git a/debian/patches/clean.patch b/debian/patches/clean.patch
index e5d43c1..34aefeb 100644
--- a/debian/patches/clean.patch
+++ b/debian/patches/clean.patch
@@ -1,12 +1,10 @@
-Index: freebayes/src/Makefile
-===================================================================
---- freebayes.orig/src/Makefile 2014-02-02 06:40:47.242351987 -0500
-+++ freebayes/src/Makefile 2014-02-02 06:42:43.390356892 -0500
+--- freebayes.orig/src/Makefile
++++ freebayes/src/Makefile
@@ -259,6 +259,6 @@

clean:
rm -rf *.o *.cgh *~ freebayes alleles ../bin/freebayes ../bin/alleles ../vcflib/*.o ../vcflib/tabixpp/*.{o,a}
- cd $(BAMTOOLS_ROOT)/build && make clean
-+ test -d $(BAMTOOLS_ROOT)/build && cd $(BAMTOOLS_ROOT)/build && make clean
++ test ! -f $(BAMTOOLS_ROOT)/build/Makefile || (cd $(BAMTOOLS_ROOT)/build && make clean)
cd ../vcflib/smithwaterman && make clean

diff --git a/debian/rules b/debian/rules
index c30b66e..4067ba0 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,7 +1,4 @@
#!/usr/bin/make -f

-# This has to be exported to make some magic below work.
-export DH_OPTIONS
-
%:
dh $@
diff --git a/src/Makefile b/src/Makefile
index fc72a07..3fd822c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -259,6 +259,6 @@ autoversion:

clean:
rm -rf *.o *.cgh *~ freebayes alleles ../bin/freebayes ../bin/alleles ../vcflib/*.o ../vcflib/tabixpp/*.{o,a}
- cd $(BAMTOOLS_ROOT)/build && make clean
+ test ! -f $(BAMTOOLS_ROOT)/build/Makefile || (cd $(BAMTOOLS_ROOT)/build && make clean)
cd ../vcflib/smithwaterman && make clean
--
Alioth's /git/debian-med/git-commit-notice on /srv/git.debian.org/git/debian-med/freebayes.git
Loading...