[med-svn] [pbdagcon] 01/02: Imported Upstream version 0~20151114+git1d12e13+ds
Afif Elghraoui
afif-guest at moszumanska.debian.org
Sat Nov 28 20:44:12 UTC 2015
This is an automated email from the git hooks/post-receive script.
afif-guest pushed a commit to branch master
in repository pbdagcon.
commit d20cc2250d8fc5cfe75b33d8d3221d21648c2aea
Author: Afif Elghraoui <afif at ghraoui.name>
Date: Fri Nov 27 15:40:18 2015 -0800
Imported Upstream version 0~20151114+git1d12e13+ds
---
.gitignore | 10 +
.gitmodules | 3 +
.travis.yml | 24 +
Makefile | 48 +
README.md | 127 +
src/cpp/Alignment.cpp | 263 ++
src/cpp/Alignment.hpp | 112 +
src/cpp/AlnGraphBoost.cpp | 521 +++
src/cpp/AlnGraphBoost.hpp | 182 +
src/cpp/AlnProvider.hpp | 85 +
src/cpp/BlasrM5AlnProvider.cpp | 133 +
src/cpp/BlasrM5AlnProvider.hpp | 107 +
src/cpp/BoundedBuffer.hpp | 89 +
src/cpp/DB.c | 1106 ++++++
src/cpp/DB.h | 357 ++
src/cpp/DazAlnProvider.cpp | 452 +++
src/cpp/DazAlnProvider.hpp | 222 ++
src/cpp/Makefile | 38 +
src/cpp/ProgramOpts.hpp | 39 +
src/cpp/SimpleAligner.cpp | 103 +
src/cpp/SimpleAligner.hpp | 73 +
src/cpp/align.c | 3805 ++++++++++++++++++++
src/cpp/align.h | 331 ++
src/cpp/boost.mk | 18 +
src/cpp/dazcon.cpp | 257 ++
src/cpp/main.cpp | 314 ++
src/cpp/pbdagcon_wf.sh | 74 +
src/cpp/pbi.mk | 22 +
src/cpp/third-party/easylogging++.h | 6663 +++++++++++++++++++++++++++++++++++
src/filterm4.py | 64 +
src/m4topre.py | 208 ++
src/q-sense.py | 170 +
src/tests/test_aligngraph.py | 204 ++
test/cpp/AlignmentTest.cpp | 78 +
test/cpp/AlnGraphBoostTest.cpp | 56 +
test/cpp/Makefile | 77 +
test/cpp/SimpleAlignerTest.cpp | 21 +
test/cpp/TargetHitTest.cpp | 77 +
test/cpp/TargetTest.cpp | 22 +
test/cpp/basic.m5 | 2 +
test/cpp/gtest.mk | 10 +
test/cpp/parsequery.m5 | 1 +
42 files changed, 16568 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b69e8a4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*.d
+*.o
+*.log
+*.swp
+*.swo
+pbdagcon
+dazcon
+test_*
+boost_*
+gtest-*
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..ce6830a
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "blasr_libcpp"]
+ path = blasr_libcpp
+ url = https://github.com/PacificBiosciences/blasr_libcpp.git
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..8ce6492
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,24 @@
+language: cpp
+script:
+ - make init-submodule
+ - make
+ - make check
+compiler:
+ - gcc
+ - clang
+install:
+- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
+addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - libyajl-dev
+ - libxml2-dev
+ - gcc-4.8
+ - g++-4.8
+ - clang
+# - libxqilla-dev # missing, but not needed?
+notifications:
+ email: false
+sudo: false
diff --git a/Makefile b/Makefile
new file mode 100755
index 0000000..6d11406
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,48 @@
+.PHONY: all clean test init-submodule cpp-github cpp cpp-check cpp-clean
+SHELL = /bin/bash -e
+
+all: cpp-github
+
+clean: cpp-clean
+
+check: cpp-github-check
+
+project: init-submodule cpp-github
+
+init-submodule:
+ $(MAKE) update-submodule
+ $(MAKE) build-submodule
+
+update-submodule:
+ git submodule update --init
+
+build-submodule:
+ $(MAKE) -C blasr_libcpp/pbdata nopbbam=1 mklibconfig
+ #$(MAKE) -C blasr_libcpp/pbdata mklibconfig
+ $(MAKE) -C blasr_libcpp/alignment -f simple.mk nohdf=1
+ $(MAKE) -C blasr_libcpp/pbdata nopbbam=1
+
+submodule-clean:
+ $(RM) -r blasr_libcpp
+
+# C++ project build directives
+cpp-github:
+ $(MAKE) -C src/cpp BLASR=$(PWD)/blasr_libcpp/alignment PBDATA=$(PWD)/blasr_libcpp/pbdata
+
+cpp-github-check:
+ $(MAKE) -C test/cpp BLASR=$(PWD)/blasr_libcpp/alignment PBDATA=$(PWD)/blasr_libcpp/pbdata
+
+cpp:
+ $(MAKE) -C src/cpp
+
+cpp-check: cpp
+ $(MAKE) -C test/cpp
+
+cpp-clean:
+ $(MAKE) -C src/cpp clean
+ $(MAKE) -C test/cpp clean
+
+clean-all: cpp-clean submodule-clean
+ $(RM)r src/cpp/third-party/boost_1_58_0-headersonly
+ $(RM)r test/cpp/gtest-1.7.0
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5b858e6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,127 @@
+[![Build Status](https://travis-ci.org/pbjd/pbdagcon.svg?branch=master)](https://travis-ci.org/pbjd/pbdagcon)
+What is pbdagcon?
+=================
+
+pbdagcon is a tool that implements DAGCon (Directed Acyclic Graph Consensus)
+which is a sequence consensus algorithm based on using directed acyclic graphs
+to encode multiple sequence alignment.
+
+It uses the alignment information from blasr to align sequence reads to a
+"backbone" sequence. Based on the underlying alignment directed acyclic graph
+(DAG), it will be able to use the new information from the reads to find the
+discrepancies between the reads and the "backbone" sequences. A dynamic
+programming process is then applied to the DAG to find the optimum sequence of
+bases as the consensus. The new consensus can be used as a new backbone
+sequence to iteratively improve the consensus quality.
+
+While the code is developed for processing PacBio(TM) raw sequence data, the
+algorithm can be used for general consensus purpose. Currently, it only takes
+FASTA input. For shorter read sequences, one might need to adjust the blasr
+alignment parameters to get the alignment string properly.
+
+The code and the underlying graphical data structure have been used for some
+algorithm development prototyping including phasing reads, pre-assembly and a
+work around to generate consensus from intermediate Celera Assembler outputs.
+
+The initial graphical algorithm was a pure python implementation. Cython was
+then use to speed it up.
+
+Check out the example/ directory to see how to use it.
+
+This code is released under the assumption it will help the community to adopt
+the PacBio data and make interesting science project possible and more
+feasible. It is not an official software release from the PacBio(TM) software
+developing organization.
+
+Building
+========
+The following are instructions on how to build the C++ pbdagcon executable.
+The code now depends on C++11 features, in particular std::thread, std::move.
+GCC 4.8.1 or higher is known to work.
+
+This project requires that you have boost headers available. You can either
+supply them yourself or the Makefile will obtain them for you from the
+internet.
+
+### Compile/Check (pbdagcon)
+```sh
+ # First fetch and build the relevant portions of the blasr_libcpp
+ # submodule
+ make init-submodule
+
+ # build pbdagcon executable (Makefile fetches boost headers)
+ make
+ # or, if you already have boost headers
+ make boost=<path to headers>
+
+ # build and run unit tests
+ # THIS IS CURRENTLY BROKEN. -cdunn
+ ###make check
+
+ # usage
+ cd src/cpp
+ ./pbdagcon --help
+```
+
+Running
+=======
+
+### Use Case: Generating consensus from BLASR alignments
+The most basic use case where one can generate a consensus from a set of
+alignments using the pbdagcon executable directly.
+
+At the most basic level, pbdagcon takes information from BLASR alignments
+sorted by target and generates fasta-formatted corrected target sequences.
+The alignments from BLASR can be formatted with either *-m 4* or *-m 5*.
+For *-m 4* format, the alignments must be run through a format adapter,
+*[m4topre.py][]*, in order to generate suitable input to *pbdagcon*.
+
+The following example shows the simplest way to generate a consensus for one
+target using BLASR *-m 5* alignments as input.
+
+ blasr queries.fasta target.fasta -bestn 1 -m 5 -out mapped.m5
+ pbdagcon mapped.m5 > consensus.fasta
+
+### Use Case: Generating consensus from daligner alignments
+Can parse LAS/DB files generated from the following commits:
+* [DALIGNER](https://github.com/thegenemyers/DALIGNER/commit/8edd180ba7b5302c6f1fc859eef5c646db99fd87)
+* [DAZ_DB](https://github.com/thegenemyers/DAZZ_DB/commit/84fa98fde94ba0ab56dd715aa7f8fe7e150290f8)
+
+
+### Use Case: HGAP correction of PacBio reads
+Walks through how one could use pbdagcon to correct PacBio reads. This
+example demonstrates how correction is performed in PacBio's "Hierarchichal
+Genome Assembly Process" (HGAP) workflow. HGAP uses BLASR *-m 4* output.
+
+This example makes use of the *[filterm4.py][]* and *[m4topre.py][]* scripts:
+```sh
+ # First filter the m4 file to help remove chimeras
+ filterm4.py mapped.m4 > mapped.m4.filt
+
+ # Next run the m4 adapter script, generating 'pre-alignments'
+ m4topre.py mapped.m4.filt mapped.m4.filt reads.fasta 24 > mapped.pre
+
+ # Finally, correct using pbdagcon, typically using multiple consensus
+ # threads.
+ pbdagcon -j 4 -a mapped.pre > corrected.fasta
+```
+
+The *[pbdagcon_wf.sh][]* script automates this workflow.
+
+-----------------------------------------------------------------------------
+
+<script>
+(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+ga('create', 'UA-13166584-17', 'github.com');
+ga('send', 'pageview');
+</script>
+
+[m4topre.py]:
+ src/m4topre.py 'code'
+[filterm4.py]:
+ src/filterm4.py 'code'
+[pbdagcon_wf.sh]:
+ src/cpp/pbdagcon_wf.sh 'code'
diff --git a/src/cpp/Alignment.cpp b/src/cpp/Alignment.cpp
new file mode 100644
index 0000000..c039254
--- /dev/null
+++ b/src/cpp/Alignment.cpp
@@ -0,0 +1,263 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <list>
+#include <vector>
+#include <cassert>
+#include "Alignment.hpp"
+
+using namespace dagcon;
+
+///
+/// Simple method to reverse complement a sequence.
+///
+std::string revComp(std::string& seq) {
+ const std::string bases = "ACTG";
+ std::string::iterator curr = seq.begin();
+ for (; curr != seq.end(); ++curr) {
+ char& c = *curr;
+ c = c == 'T' ? bases[0] :
+ c == 'G' ? bases[1] :
+ c == 'A' ? bases[2] :
+ c == 'C' ? bases[3] : c;
+ }
+ return std::string(seq.rbegin(), seq.rend());
+}
+
+// Set this to false if the alignments are grouped by query. The parse
+// routine will be adjusted to build the alignment graph based on the
+// queries.
+bool Alignment::groupByTarget = true;
+
+Alignment::Alignment() :
+ tlen(0),
+ start(0),
+ end(0),
+ id(""),
+ sid(""),
+ strand('+'),
+ qstr(""),
+ tstr("") { }
+
+// Parses blasr m5 output grouped either by target or query.
+void parseM5(std::istream& stream, Alignment* aln) {
+ std::string line;
+ std::getline(stream, line);
+ std::stringstream row(line);
+ std::string col;
+ std::vector<std::string> fields;
+ while(std::getline(row, col, ' ')) {
+ if (col == "") continue;
+ fields.push_back(col);
+ }
+
+ // avoids *some* empty lines
+ if (fields.size() == 0) return;
+
+ // base query id (without the last '/<coordinates>'), allows us to
+ // group properly by query when asked.
+ std::string baseQid = fields[0].substr(0,fields[0].find_last_of("/"));
+ aln->sid = fields[0];
+ aln->id = Alignment::groupByTarget ? fields[5] : baseQid;
+
+ std::istringstream ssLen(Alignment::groupByTarget ? fields[6] : fields[1]);
+ ssLen >> aln->tlen;
+ std::istringstream ssStart(Alignment::groupByTarget ? fields[7] : fields[2]);
+ ssStart >> aln->start;
+ aln->start++;
+
+ // the target is always reversed.
+ aln->strand = fields[9][0];
+ if (aln->strand == '-' && Alignment::groupByTarget) {
+ // only need to reverse complement when correcting targets
+ aln->qstr = revComp(fields[16]);
+ aln->tstr = revComp(fields[18]);
+ } else {
+ aln->qstr = Alignment::groupByTarget ? fields[16] : fields[18];
+ aln->tstr = Alignment::groupByTarget ? fields[18] : fields[16];
+ }
+}
+
+void parsePre(std::istream& stream, Alignment* aln) {
+ std::string line;
+ std::getline(stream, line);
+ std::stringstream row(line);
+ std::string col;
+ std::vector<std::string> fields;
+ while(std::getline(row, col, ' ')) {
+ if (col == "") continue;
+ fields.push_back(col);
+ }
+
+ // avoids *some* empty lines
+ if (fields.size() == 0) return;
+
+ // qid, tid, strand, tlen, tstart, tend, qstr, tstr
+ aln->sid = fields[0];
+ aln->id = fields[1];
+ aln->strand = fields[2][0];
+
+ std::istringstream ssLen(fields[3]);
+ ssLen >> aln->tlen;
+
+ std::istringstream ssStart(fields[4]);
+ ssStart >> aln->start;
+
+ std::istringstream ssEnd(fields[5]);
+ ssEnd >> aln->end;
+
+ aln->qstr = fields[6];
+ aln->tstr = fields[7];
+}
+
+// default to parsing m5
+Alignment::ParseFunc Alignment::parse = parseM5;
+
+std::istream& operator>>(std::istream& instrm, Alignment& data) {
+ Alignment::parse(instrm, &data);
+ return instrm;
+}
+
+std::ostream& operator<<(std::ostream& ostrm, Alignment& data) {
+ ostrm << "target: " << data.id << ", query: " << data.sid;
+ ostrm << ", start: " << data.start << ", end: " << data.end;
+ ostrm << ", length: " << data.tlen << std::endl;
+ ostrm << "tstr(50): " << data.tstr.substr(0,50) << std::endl;
+ ostrm << "qstr(50): " << data.qstr.substr(0,50) << std::endl;
+ return ostrm;
+}
+
+Alignment normalizeGaps(Alignment& aln, bool push) {
+ // XXX: optimize this
+ size_t qlen = aln.qstr.length(), tlen = aln.tstr.length();
+ assert(qlen == tlen);
+ std::string qNorm, tNorm;
+ qNorm.reserve(qlen+100);
+ tNorm.reserve(tlen+100);
+
+ // convert mismatches to indels
+ for (size_t i=0; i < qlen; i++) {
+ char qb = aln.qstr[i], tb = aln.tstr[i];
+ if (qb != tb && qb != '-' && tb != '-') {
+ qNorm += '-';
+ qNorm += qb;
+ tNorm += tb;
+ tNorm += '-';
+ } else {
+ qNorm += qb;
+ tNorm += tb;
+ }
+ }
+
+ // update lengths
+ qlen = qNorm.length();
+ tlen = tNorm.length();
+
+ if (push) {
+ // push gaps to the right, but not past the end
+ for (size_t i=0; i < qlen-1; i++) {
+ // pushing target gaps
+ if (tNorm[i] == '-') {
+ size_t j = i;
+ while (true) {
+ char c = tNorm[++j];
+ if (c != '-' || j > qlen - 1) {
+ if (c == qNorm[i]) {
+ tNorm[i] = c;
+ tNorm[j] = '-';
+ }
+ break;
+ }
+ }
+ }
+
+ // pushing query gaps
+ if (qNorm[i] == '-') {
+ size_t j = i;
+ while (true) {
+ char c = qNorm[++j];
+ if (c != '-' || j > tlen - 1) {
+ if (c == tNorm[i]) {
+ qNorm[i] = c;
+ qNorm[j] = '-';
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // generate the final, normalized alignment strings
+ Alignment finalNorm;
+ finalNorm.id = aln.id;
+ finalNorm.sid = aln.sid;
+ finalNorm.start = aln.start;
+ finalNorm.tlen = aln.tlen;
+ finalNorm.strand = aln.strand;
+ for (size_t i=0; i < qlen; i++) {
+ if (qNorm[i] != '-' || tNorm[i] != '-') {
+ finalNorm.qstr += qNorm[i];
+ finalNorm.tstr += tNorm[i];
+ }
+ }
+
+ return finalNorm;
+}
+
+void trimAln(Alignment& aln, int trimLen) {
+ int lbases = 0, loffs = 0;
+ while(lbases < trimLen) {
+ if (aln.tstr[loffs++] != '-') {
+ lbases++;
+ }
+ }
+
+ int rbases = 0, roffs = aln.tstr.length();
+ while (rbases < trimLen) {
+ if (aln.tstr[roffs--] != '-') {
+ rbases++;
+ }
+ }
+
+ aln.start += lbases;
+ aln.qstr = aln.qstr.substr(loffs, roffs - loffs);
+ aln.tstr = aln.tstr.substr(loffs, roffs - loffs);
+}
diff --git a/src/cpp/Alignment.hpp b/src/cpp/Alignment.hpp
new file mode 100644
index 0000000..ff1ae71
--- /dev/null
+++ b/src/cpp/Alignment.hpp
@@ -0,0 +1,112 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+
+#ifndef __GCON_ALIGNMENT_HPP__
+#define __GCON_ALIGNMENT_HPP__
+
+#include <stdint.h>
+
+///
+/// Super-simple alignment representation. Represents an alignment between two
+/// PacBio reads, one of which we're trying to correct. The read to correct
+/// may be either the target or the query, depending on how the alignment was
+/// done.
+///
+namespace dagcon {
+class Alignment {
+public:
+ typedef void (*ParseFunc)(std::istream&, Alignment* aln);
+
+ // May correct the target or the query, default is target
+ static bool groupByTarget;
+
+ // length of the sequence we are trying to correct
+ uint32_t tlen;
+
+ // conforming offsets are 1-based
+ uint32_t start;
+
+ uint32_t end;
+
+ // ID of the read we're trying to correct (target)
+ std::string id;
+
+ // ID of the supporting read (query)
+ std::string sid;
+
+ char strand;
+
+ // query and target strings must be equal length
+ std::string qstr;
+ std::string tstr;
+
+ Alignment();
+
+ static ParseFunc parse;
+};
+}
+
+std::istream& operator>>(std::istream& instrm, dagcon::Alignment& data);
+std::ostream& operator<<(std::ostream& ostrm, dagcon::Alignment& data);
+
+void parseM5(std::istream& stream, dagcon::Alignment* aln);
+
+void parsePre(std::istream& stream, dagcon::Alignment* aln);
+
+/// Simplifies the alignment by normalizing gaps. Converts mismatches into
+/// indels ...
+/// query: CAC query: C-AC
+/// | | ---> | |
+/// target: CGC target: CG-C
+///
+/// Shifts equivalent gaps to the right in the reference ...
+/// query: CAACAT query: CAACAT
+/// | | || ---> ||| |
+/// target: C-A-AT target: CAA--T
+///
+/// Shifts equivalent gaps to the right in the read ...
+/// query: -C--CGT query: CCG--T
+/// | | | ---> ||| |
+/// target: CCGAC-T target: CCGACT
+/// Allow optional gap pushing, some aligners may not need it and I'd like
+/// to get rid of it anyway.
+dagcon::Alignment normalizeGaps(dagcon::Alignment& aln, bool push=true);
+
+void trimAln(dagcon::Alignment& aln, int trimLen=50);
+
+std::string revComp(std::string& seq);
+
+#endif // __GCON_ALIGNMENT_HPP__
diff --git a/src/cpp/AlnGraphBoost.cpp b/src/cpp/AlnGraphBoost.cpp
new file mode 100644
index 0000000..79ded57
--- /dev/null
+++ b/src/cpp/AlnGraphBoost.cpp
@@ -0,0 +1,521 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+
+#include <cstdint>
+#include <cfloat>
+#include <cassert>
+#include <string>
+#include <queue>
+#include <map>
+#include <vector>
+#include <boost/format.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/graphviz.hpp>
+#include <boost/utility/value_init.hpp>
+#include "Alignment.hpp"
+#include "AlnGraphBoost.hpp"
+
+AlnGraphBoost::AlnGraphBoost(const std::string& backbone) {
+ // initialize the graph structure with the backbone length + enter/exit
+ // vertex
+ size_t blen = backbone.length();
+ _g = G(blen+1);
+ for (size_t i = 0; i < blen+1; i++)
+ boost::add_edge(i, i+1, _g);
+
+ VtxIter curr, last;
+ boost::tie(curr, last) = boost::vertices(_g);
+ _enterVtx = *curr++;
+ _g[_enterVtx].base = '^';
+ _g[_enterVtx].backbone = true;
+ for (size_t i = 0; i < blen; i++, ++curr) {
+ VtxDesc v = *curr;
+ _g[v].backbone = true;
+ _g[v].weight = 1;
+ _g[v].base = backbone[i];
+ _bbMap[v] = v;
+ }
+ _exitVtx = *curr;
+ _g[_exitVtx].base = '$';
+ _g[_exitVtx].backbone = true;
+}
+
+AlnGraphBoost::AlnGraphBoost(const size_t blen) {
+ _g = G(blen+1);
+ for (size_t i = 0; i < blen+1; i++)
+ boost::add_edge(i, i+1, _g);
+
+ VtxIter curr, last;
+ boost::tie(curr, last) = boost::vertices(_g);
+ _enterVtx = *curr++;
+ _g[_enterVtx].base = '^';
+ _g[_enterVtx].backbone = true;
+ for (size_t i = 0; i < blen; i++, ++curr) {
+ VtxDesc v = *curr;
+ _g[v].backbone = true;
+ _g[v].weight = 1;
+ _g[v].deleted = false;
+ _g[v].base = 'N';
+ _bbMap[v] = v;
+ }
+ _exitVtx = *curr;
+ _g[_exitVtx].base = '$';
+ _g[_exitVtx].backbone = true;
+}
+
+void AlnGraphBoost::addAln(dagcon::Alignment& aln) {
+ IndexMap index = boost::get(boost::vertex_index, _g);
+ // tracks the position on the backbone
+ uint32_t bbPos = aln.start;
+ VtxDesc prevVtx = _enterVtx;
+ for (size_t i = 0; i < aln.qstr.length(); i++) {
+ char queryBase = aln.qstr[i], targetBase = aln.tstr[i];
+ VtxDesc currVtx = index[bbPos];
+ // match
+ if (queryBase == targetBase) {
+ _g[_bbMap[currVtx]].coverage++;
+
+ // NOTE: for empty backbones
+ _g[_bbMap[currVtx]].base = targetBase;
+
+ _g[currVtx].weight++;
+ addEdge(prevVtx, currVtx);
+ bbPos++;
+ prevVtx = currVtx;
+ // query deletion
+ } else if (queryBase == '-' && targetBase != '-') {
+ _g[_bbMap[currVtx]].coverage++;
+
+ // NOTE: for empty backbones
+ _g[_bbMap[currVtx]].base = targetBase;
+
+ bbPos++;
+ // query insertion
+ } else if (queryBase != '-' && targetBase == '-') {
+ // create new node and edge
+ VtxDesc newVtx = boost::add_vertex(_g);
+ _g[newVtx].base = queryBase;
+ _g[newVtx].weight++;
+ _g[newVtx].backbone = false;
+ _g[newVtx].deleted = false;
+ _bbMap[newVtx] = bbPos;
+ addEdge(prevVtx, newVtx);
+ prevVtx = newVtx;
+ }
+ }
+ addEdge(prevVtx, _exitVtx);
+}
+
+void AlnGraphBoost::addEdge(VtxDesc u, VtxDesc v) {
+ // Check if edge exists with prev node. If it does, increment edge counter,
+ // otherwise add a new edge.
+ InEdgeIter ii, ie;
+ bool edgeExists = false;
+ for (boost::tie(ii, ie) = boost::in_edges(v, _g); ii != ie; ++ii) {
+ EdgeDesc e = *ii;
+ if (boost::source(e , _g) == u) {
+ // increment edge count
+ _g[e].count++;
+ edgeExists = true;
+ }
+ }
+ if (! edgeExists) {
+ // add new edge
+ std::pair<EdgeDesc, bool> p = boost::add_edge(u, v, _g);
+ _g[p.first].count++;
+ }
+}
+
+void AlnGraphBoost::mergeNodes() {
+ std::queue<VtxDesc> seedNodes;
+ seedNodes.push(_enterVtx);
+
+ while(true) {
+ if (seedNodes.size() == 0)
+ break;
+
+ VtxDesc u = seedNodes.front();
+ seedNodes.pop();
+ mergeInNodes(u);
+ mergeOutNodes(u);
+
+ OutEdgeIter oi, oe;
+ for (boost::tie(oi, oe) = boost::out_edges(u, _g); oi != oe; ++oi) {
+ EdgeDesc e = *oi;
+ _g[e].visited = true;
+ VtxDesc v = boost::target(e, _g);
+ InEdgeIter ii, ie;
+ int notVisited = 0;
+ for (boost::tie(ii, ie) = boost::in_edges(v, _g); ii != ie; ++ii) {
+ if (_g[*ii].visited == false)
+ notVisited++;
+ }
+
+ // move onto the boost::target node after we visit all incoming edges for
+ // the boost::target node
+ if (notVisited == 0)
+ seedNodes.push(v);
+ }
+ }
+}
+
+void AlnGraphBoost::mergeInNodes(VtxDesc n) {
+ std::map<char, std::vector<VtxDesc>> nodeGroups;
+ InEdgeIter ii, ie;
+ // Group neighboring nodes by base
+ for(boost::tie(ii, ie) = boost::in_edges(n, _g); ii != ie; ++ii) {
+ VtxDesc inNode = boost::source(*ii, _g);
+ if (out_degree(inNode, _g) == 1) {
+ nodeGroups[_g[inNode].base].push_back(inNode);
+ }
+ }
+
+ // iterate over node groups, merge an accumulate information
+ for(auto kvp = nodeGroups.cbegin(); kvp != nodeGroups.end(); ++kvp) {
+ std::vector<VtxDesc> nodes = (*kvp).second;
+ if (nodes.size() <= 1)
+ continue;
+
+ std::vector<VtxDesc>::const_iterator ni = nodes.cbegin();
+ VtxDesc an = *ni++;
+ OutEdgeIter anoi, anoe;
+ boost::tie(anoi, anoe) = boost::out_edges(an, _g);
+
+ // Accumulate out edge information
+ for (; ni != nodes.cend(); ++ni) {
+ OutEdgeIter oi, oe;
+ boost::tie(oi, oe) = boost::out_edges(*ni, _g);
+ _g[*anoi].count += _g[*oi].count;
+ _g[an].weight += _g[*ni].weight;
+ }
+
+ // Accumulate in edge information, merges nodes
+ ni = nodes.cbegin();
+ ++ni;
+ for (; ni != nodes.cend(); ++ni) {
+ InEdgeIter ii, ie;
+ VtxDesc n = *ni;
+ for (boost::tie(ii, ie) = boost::in_edges(n, _g); ii != ie; ++ii) {
+ VtxDesc n1 = boost::source(*ii, _g);
+ EdgeDesc e;
+ bool exists;
+ boost::tie(e, exists) = edge(n1, an, _g);
+ if (exists) {
+ _g[e].count += _g[*ii].count;
+ } else {
+ std::pair<EdgeDesc, bool> p = boost::add_edge(n1, an, _g);
+ _g[p.first].count = _g[*ii].count;
+ _g[p.first].visited = _g[*ii].visited;
+ }
+ }
+ markForReaper(n);
+ }
+ mergeInNodes(an);
+ }
+}
+
+void AlnGraphBoost::mergeOutNodes(VtxDesc n) {
+ std::map<char, std::vector<VtxDesc>> nodeGroups;
+ OutEdgeIter oi, oe;
+ for(boost::tie(oi, oe) = boost::out_edges(n, _g); oi != oe; ++oi) {
+ VtxDesc outNode = boost::target(*oi, _g);
+ if (in_degree(outNode, _g) == 1) {
+ nodeGroups[_g[outNode].base].push_back(outNode);
+ }
+ }
+
+ for(auto kvp = nodeGroups.cbegin(); kvp != nodeGroups.end(); ++kvp) {
+ std::vector<VtxDesc> nodes = (*kvp).second;
+ if (nodes.size() <= 1)
+ continue;
+
+ std::vector<VtxDesc>::const_iterator ni = nodes.cbegin();
+ VtxDesc an = *ni++;
+ InEdgeIter anii, anie;
+ boost::tie(anii, anie) = boost::in_edges(an, _g);
+
+ // Accumulate inner edge information
+ for (; ni != nodes.cend(); ++ni) {
+ InEdgeIter ii, ie;
+ boost::tie(ii, ie) = boost::in_edges(*ni, _g);
+ _g[*anii].count += _g[*ii].count;
+ _g[an].weight += _g[*ni].weight;
+ }
+
+ // Accumulate and merge outer edge information
+ ni = nodes.cbegin();
+ ++ni;
+ for (; ni != nodes.cend(); ++ni) {
+ OutEdgeIter oi, oe;
+ VtxDesc n = *ni;
+ for (boost::tie(oi, oe) = boost::out_edges(n, _g); oi != oe; ++oi) {
+ VtxDesc n2 = boost::target(*oi, _g);
+ EdgeDesc e;
+ bool exists;
+ boost::tie(e, exists) = edge(an, n2, _g);
+ if (exists) {
+ _g[e].count += _g[*oi].count;
+ } else {
+ std::pair<EdgeDesc, bool> p = boost::add_edge(an, n2, _g);
+ _g[p.first].count = _g[*oi].count;
+ _g[p.first].visited = _g[*oi].visited;
+ }
+ }
+ markForReaper(n);
+ }
+ }
+}
+
+void AlnGraphBoost::markForReaper(VtxDesc n) {
+ _g[n].deleted = true;
+ clear_vertex(n, _g);
+ _reaperBag.push_back(n);
+}
+
+void AlnGraphBoost::reapNodes() {
+ int reapCount = 0;
+ std::sort(_reaperBag.begin(), _reaperBag.end());
+ std::vector<VtxDesc>::iterator curr = _reaperBag.begin();
+ for (; curr != _reaperBag.end(); ++curr) {
+ assert(_g[*curr].backbone==false);
+ remove_vertex(*curr-reapCount++, _g);
+ }
+}
+
+const std::string AlnGraphBoost::consensus(int minWeight) {
+ // get the best scoring path
+ std::vector<AlnNode> path = bestPath();
+
+ // consensus sequence
+ std::string cns;
+
+ // track the longest consensus path meeting minimum weight
+ int offs = 0, bestOffs = 0, length = 0, idx = 0;
+ bool metWeight = false;
+ std::vector<AlnNode>::iterator curr = path.begin();
+ for (; curr != path.end(); ++curr) {
+ AlnNode n = *curr;
+ if (n.base == _g[_enterVtx].base || n.base == _g[_exitVtx].base)
+ continue;
+
+ cns += n.base;
+
+ // initial beginning of minimum weight section
+ if (!metWeight && n.weight >= minWeight) {
+ offs = idx;
+ metWeight = true;
+ } else if (metWeight && n.weight < minWeight) {
+ // concluded minimum weight section, update if longest seen so far
+ if ((idx - offs) > length) {
+ bestOffs = offs;
+ length = idx - offs;
+ }
+ metWeight = false;
+ }
+ idx++;
+ }
+
+ // include end of sequence
+ if (metWeight && (idx - offs) > length) {
+ bestOffs = offs;
+ length = idx - offs;
+ }
+
+ return cns.substr(bestOffs, length);
+}
+
+void AlnGraphBoost::consensus(std::vector<CnsResult>& seqs, int minWeight, size_t minLen) {
+ seqs.clear();
+
+ // get the best scoring path
+ std::vector<AlnNode> path = bestPath();
+
+ // consensus sequence
+ std::string cns;
+
+ // track the longest consensus path meeting minimum weight
+ int offs = 0, idx = 0;
+ bool metWeight = false;
+ std::vector<AlnNode>::iterator curr = path.begin();
+ for (; curr != path.end(); ++curr) {
+ AlnNode n = *curr;
+ if (n.base == _g[_enterVtx].base || n.base == _g[_exitVtx].base)
+ continue;
+
+ cns += n.base;
+
+ // initial beginning of minimum weight section
+ if (!metWeight && n.weight >= minWeight) {
+ offs = idx;
+ metWeight = true;
+ } else if (metWeight && n.weight < minWeight) {
+ // concluded minimum weight section, add sequence to supplied vector
+ metWeight = false;
+ CnsResult result;
+ result.range[0] = offs;
+ result.range[1] = idx;
+ size_t length = idx - offs;
+ result.seq = cns.substr(offs, length);
+ if (length >= minLen) seqs.push_back(result);
+ }
+ idx++;
+ }
+
+ // include end of sequence
+ if (metWeight) {
+ size_t length = idx - offs;
+ CnsResult result;
+ result.range[0] = offs;
+ result.range[1] = idx;
+ result.seq = cns.substr(offs, length);
+ if (length >= minLen) seqs.push_back(result);
+ }
+}
+
+const std::vector<AlnNode> AlnGraphBoost::bestPath() {
+ EdgeIter ei, ee;
+ for (boost::tie(ei, ee) = edges(_g); ei != ee; ++ei)
+ _g[*ei].visited = false;
+
+ std::map<VtxDesc, EdgeDesc> bestNodeScoreEdge;
+ std::map<VtxDesc, float> nodeScore;
+ std::queue<VtxDesc> seedNodes;
+
+ // start at the end and make our way backwards
+ seedNodes.push(_exitVtx);
+ nodeScore[_exitVtx] = 0.0f;
+
+ while (true) {
+ if (seedNodes.size() == 0)
+ break;
+
+ VtxDesc n = seedNodes.front();
+ seedNodes.pop();
+
+ bool bestEdgeFound = false;
+ float bestScore = -FLT_MAX;
+ EdgeDesc bestEdgeD = boost::initialized_value;
+ OutEdgeIter oi, oe;
+ for(boost::tie(oi, oe) = boost::out_edges(n, _g); oi != oe; ++oi) {
+ EdgeDesc outEdgeD = *oi;
+ VtxDesc outNodeD = boost::target(outEdgeD, _g);
+ AlnNode outNode = _g[outNodeD];
+ float newScore, score = nodeScore[outNodeD];
+ if (outNode.backbone && outNode.weight == 1) {
+ newScore = score - 10.0f;
+ } else {
+ AlnNode bbNode = _g[_bbMap[outNodeD]];
+ newScore = _g[outEdgeD].count - bbNode.coverage*0.5f + score;
+ }
+
+ if (newScore > bestScore) {
+ bestScore = newScore;
+ bestEdgeD = outEdgeD;
+ bestEdgeFound = true;
+ }
+ }
+
+ if (bestEdgeFound) {
+ nodeScore[n]= bestScore;
+ bestNodeScoreEdge[n] = bestEdgeD;
+ }
+
+ InEdgeIter ii, ie;
+ for (boost::tie(ii, ie) = boost::in_edges(n, _g); ii != ie; ++ii) {
+ EdgeDesc inEdge = *ii;
+ _g[inEdge].visited = true;
+ VtxDesc inNode = boost::source(inEdge, _g);
+ int notVisited = 0;
+ OutEdgeIter oi, oe;
+ for (boost::tie(oi, oe) = boost::out_edges(inNode, _g); oi != oe; ++oi) {
+ if (_g[*oi].visited == false)
+ notVisited++;
+ }
+
+ // move onto the target node after we visit all incoming edges for
+ // the target node
+ if (notVisited == 0)
+ seedNodes.push(inNode);
+ }
+ }
+
+ // construct the final best path
+ VtxDesc prev = _enterVtx, next;
+ std::vector<AlnNode> bpath;
+ while (true) {
+ bpath.push_back(_g[prev]);
+ if (bestNodeScoreEdge.count(prev) == 0) {
+ break;
+ } else {
+ EdgeDesc bestOutEdge = bestNodeScoreEdge[prev];
+ _g[prev].bestOutEdge = bestOutEdge;
+ next = boost::target(bestOutEdge, _g);
+ _g[next].bestInEdge = bestOutEdge;
+ prev = next;
+ }
+ }
+
+ return bpath;
+}
+
+void AlnGraphBoost::printGraph() {
+ reapNodes();
+ boost::write_graphviz(std::cout, _g,
+ make_label_writer(get(&AlnNode::base, _g)),
+ make_label_writer(get(&AlnEdge::count, _g)));
+}
+
+bool AlnGraphBoost::danglingNodes() {
+ VtxIter curr, last;
+ boost::tie(curr, last) = boost::vertices(_g);
+ bool found = false;
+ for (;curr != last; ++curr) {
+ if (_g[*curr].deleted)
+ continue;
+ if (_g[*curr].base == _g[_enterVtx].base || _g[*curr].base == _g[_exitVtx].base)
+ continue;
+
+ int indeg = out_degree(*curr, _g);
+ int outdeg = in_degree(*curr, _g);
+ if (outdeg > 0 && indeg > 0) continue;
+
+ found = true;
+ }
+ return found;
+}
+
+AlnGraphBoost::~AlnGraphBoost(){}
diff --git a/src/cpp/AlnGraphBoost.hpp b/src/cpp/AlnGraphBoost.hpp
new file mode 100644
index 0000000..faf759c
--- /dev/null
+++ b/src/cpp/AlnGraphBoost.hpp
@@ -0,0 +1,182 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+
+#ifndef __GCON_ALNGRAPHBOOST_HPP__
+#define __GCON_ALNGRAPHBOOST_HPP__
+
+/// Alignment graph representation and consensus caller. Based on the original
+/// Python implementation, pbdagcon. This class is modelled after its
+/// aligngraph.py component, which accumulates alignment information into a
+/// partial-order graph and then calls consensus. Used to error-correct pacbio
+/// on pacbio reads.
+///
+/// Implemented using the boost graph library.
+
+// forward declaration
+//struct Alignment;
+
+// this allows me to forward-declare properties with graph descriptors as
+// members types
+typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS> graphTraits;
+
+/// Graph vertex property. An alignment node, which represents one base position
+/// in the alignment graph.
+struct AlnNode {
+ char base; ///< DNA base: [ACTG]
+ int coverage; ///< Number of reads align to this position, but not
+ ///< necessarily match
+ int weight; ///< Number of reads that align to this node *with the same base*, but not
+ ///< necessarily represented in the target.
+ bool backbone; ///< Is this node based on the reference
+ bool deleted; ///< mark for removed as part of the merging process
+ graphTraits::edge_descriptor bestInEdge; ///< Best scoring in edge
+ graphTraits::edge_descriptor bestOutEdge; ///< Best scoring out edge
+ AlnNode() {
+ base = 'N';
+ coverage = 0;
+ weight = 0;
+ backbone = false;
+ deleted = false;
+ }
+};
+
+/// Graph edge property. Represents an edge between alignment nodes.
+struct AlnEdge {
+ int count; ///< Number of times this edge was confirmed by an alignment
+ bool visited; ///< Tracks a visit during algorithm processing
+ AlnEdge() {
+ count = 0;
+ visited = false;
+ }
+};
+
+// Boost-related typedefs
+// XXX: listS, listS?
+typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, AlnNode, AlnEdge> G;
+typedef boost::graph_traits<G>::vertex_descriptor VtxDesc;
+typedef boost::graph_traits<G>::vertex_iterator VtxIter;
+typedef boost::graph_traits<G>::edge_descriptor EdgeDesc;
+typedef boost::graph_traits<G>::edge_iterator EdgeIter;
+typedef boost::graph_traits<G>::in_edge_iterator InEdgeIter;
+typedef boost::graph_traits<G>::out_edge_iterator OutEdgeIter;
+typedef boost::property_map<G, boost::vertex_index_t>::type IndexMap;
+
+///
+/// Simple consensus interface datastructure
+///
+struct CnsResult {
+ int range[2]; ///< Range on the target
+ std::string seq; ///< Consensus fragment
+};
+
+///
+/// Core alignments into consensus algorithm, implemented using the boost graph
+/// library. Takes a set of alignments to a reference and builds a higher
+/// accuracy (~ 99.9) consensus sequence from it. Designed for use in the HGAP
+/// pipeline as a long read error correction step.
+///
+class AlnGraphBoost {
+public:
+ /// Constructor. Initialize graph based on the given sequence. Graph is
+ /// annotated with the bases from the backbone.
+ /// \param backbone the reference sequence.
+ AlnGraphBoost(const std::string& backbone);
+
+ /// Constructor. Initialize graph to a given backbone length. Base
+ /// information is filled in as alignments are added.
+ /// \param blen length of the reference sequence.
+ AlnGraphBoost(const size_t blen);
+
+ /// Add alignment to the graph.
+ /// \param Alignment an alignment record (see Alignment.hpp)
+ void addAln(dagcon::Alignment& aln);
+
+ /// Adds a new or increments an existing edge between two aligned bases.
+ /// \param u the 'from' vertex descriptor
+ /// \param v the 'to' vertex descriptor
+ void addEdge(VtxDesc u, VtxDesc v);
+
+ /// Collapses degenerate nodes (vertices). Must be called before
+ /// consensus(). Calls mergeInNodes() followed by mergeOutNodes().
+ void mergeNodes();
+
+ /// Recursive merge of 'in' nodes.
+ /// \param n the base node to merge around.
+ void mergeInNodes(VtxDesc n);
+
+ /// Non-recursive merge of 'out' nodes.
+ /// \param n the base node to merge around.
+ void mergeOutNodes(VtxDesc n);
+
+ /// Mark a given node for removal from graph. Doesn't not modify graph.
+ /// \param n the node to remove.
+ void markForReaper(VtxDesc n);
+
+ /// Removes the set of nodes that have been marked. Modifies graph.
+ /// Prohibitively expensive when using vecS as the vertex container.
+ void reapNodes();
+
+ /// Generates the consensus from the graph. Must be called after
+ /// mergeNodes(). Returns the longest contiguous consensus sequence where
+ /// each base meets the minimum weight requirement.
+ /// \param minWeight sets the minimum weight for each base in the consensus.
+ /// default = 0
+ const std::string consensus(int minWeight=0);
+
+ /// Generates all consensus sequences from a target that meet the minimum
+ /// weight requirement.
+ void consensus(std::vector<CnsResult>& seqs, int minWeight=0, size_t minLength=500);
+
+ /// Locates the optimal path through the graph. Called by consensus()
+ const std::vector<AlnNode> bestPath();
+
+ /// Emits the current graph, in dot format, to stdout.
+ void printGraph();
+
+ /// Locate nodes that are missing either in or out edges.
+ bool danglingNodes();
+
+ /// Destructor.
+ virtual ~AlnGraphBoost();
+private:
+ G _g;
+ VtxDesc _enterVtx;
+ VtxDesc _exitVtx;
+ std::map<VtxDesc, VtxDesc> _bbMap;
+ std::vector<VtxDesc> _reaperBag;
+};
+
+#endif // __GCON_ALNGRAPHBOOST_HPP__
diff --git a/src/cpp/AlnProvider.hpp b/src/cpp/AlnProvider.hpp
new file mode 100644
index 0000000..98373d4
--- /dev/null
+++ b/src/cpp/AlnProvider.hpp
@@ -0,0 +1,85 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+
+#ifndef __GCON_ALN_PROVIDER__
+#define __GCON_ALN_PROVIDER__
+
+#include <exception>
+
+///
+/// Exceptions thrown by derived classes
+///
+namespace PacBio {
+namespace DagCon {
+ class IOException : public std::exception {
+ public:
+ IOException(const char* desc);
+ virtual const char* what() const throw() {
+ return desc_;
+ }
+ private:
+ const char* desc_;
+ };
+
+ class MemoryException : public std::exception {
+ public:
+ virtual const char* what() const throw() {
+ return "Failed to allocate memory";
+ }
+ };
+}}
+
+
+///
+/// Generic alignment provider interface.
+///
+class AlnProvider {
+public:
+ /// Gets the set of alignments for the next target and puts them into the
+ /// given vector. Note this function will clear the contents of the vector
+ /// prior to adding the next set of alignments.
+ /// \param dest reference to a vector to hold the alignments.
+ /// \return True if there are more targets, otherwise false.
+ virtual bool nextTarget(std::vector<dagcon::Alignment>& dest) = 0;
+
+ /// Same as nextTarget(dest), except it also returns the target sequence we are
+ /// going to correct.
+ virtual bool nextTarget(std::string& targSeq, std::vector<dagcon::Alignment>& dest) = 0;
+
+ virtual ~AlnProvider() {};
+};
+
+#endif //__GCON_ALN_PROVIDER__
diff --git a/src/cpp/BlasrM5AlnProvider.cpp b/src/cpp/BlasrM5AlnProvider.cpp
new file mode 100644
index 0000000..6b71b7c
--- /dev/null
+++ b/src/cpp/BlasrM5AlnProvider.cpp
@@ -0,0 +1,133 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+
+#include <vector>
+#include <iostream>
+#include <istream>
+#include <fstream>
+#include <sstream>
+#include <boost/format.hpp>
+#include "Alignment.hpp"
+#include "BlasrM5AlnProvider.hpp"
+
+
+BlasrM5AlnProvider::BlasrM5AlnProvider(const std::string& fpath) :
+ fpath_(fpath),
+ currId_(""),
+ firstAln_(true),
+ fs_() {
+
+ //checkFormat();
+ fs_.open(fpath_);
+ is_ = &fs_;
+}
+
+BlasrM5AlnProvider::BlasrM5AlnProvider(std::istream* stream) :
+ fpath_(""),
+ currId_(""),
+ firstAln_(true),
+ fs_(),
+ is_(stream) {
+}
+
+BlasrM5AlnProvider::~BlasrM5AlnProvider() {
+ delete is_;
+}
+
+bool BlasrM5AlnProvider::nextTarget(std::vector<dagcon::Alignment>& dest) {
+ // first clear any previous alignments
+ dest.clear();
+
+ // process up to EOF or next target
+ // need to maintain state in between calls
+ if (! firstAln_)
+ dest.push_back(prevAln_);
+
+ dagcon::Alignment aln;
+ while (*is_ >> aln) {
+ if (aln.id != currId_) {
+ firstAln_ = false;
+ prevAln_ = aln;
+ currId_ = aln.id;
+ break;
+ }
+ dest.push_back(aln);
+ }
+
+ return bool(*is_);
+}
+
+bool BlasrM5AlnProvider::nextTarget(std::string& targetSeq, std::vector<dagcon::Alignment>& dest) {
+ // NOOP
+ return false;
+}
+
+void BlasrM5AlnProvider::checkFormat() {
+ std::ifstream ifs(fpath_);
+ if (! ifs.is_open() || ifs.fail()) {
+ throw M5Exception::FileOpenError();
+ }
+ // parse the first line and run some field checks
+ std::string line;
+ std::getline(ifs, line);
+ std::stringstream row(line);
+ std::string col;
+ std::vector<std::string> fields;
+
+ while(std::getline(row, col, ' ')) {
+ if (col == "") continue;
+ fields.push_back(col);
+ }
+
+ if (fields.size() < 19) {
+ boost::format msg("Expected 19 fields, found %d");
+ msg % fields.size();
+ throw M5Exception::FormatError(msg.str());
+ }
+
+ // check how the alignments are grouped
+ dagcon::Alignment aln;
+ std::vector<std::string> raw, sorted;
+ int max = 50, count = 0;
+ while(ifs >> aln && count++ < max)
+ raw.push_back(aln.id);
+
+ sorted = raw;
+ std::sort(sorted.begin(), sorted.end());
+
+ std::string logl = "dagcon::Alignments appear to be grouped by %s";
+ ifs.close();
+}
diff --git a/src/cpp/BlasrM5AlnProvider.hpp b/src/cpp/BlasrM5AlnProvider.hpp
new file mode 100644
index 0000000..087555a
--- /dev/null
+++ b/src/cpp/BlasrM5AlnProvider.hpp
@@ -0,0 +1,107 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+
+#ifndef __GCON_BLASRM5_ALN_PROVIDER__
+#define __GCON_BLASRM5_ALN_PROVIDER__
+
+#include "AlnProvider.hpp"
+
+///
+/// Exceptions thrown by this class
+///
+namespace M5Exception {
+ struct FormatError {
+ std::string msg;
+ FormatError(std::string m) { msg = m; }
+ };
+
+ struct SortError {};
+
+ struct FileOpenError {};
+}
+
+///
+/// Provides sets of alignments for a given target sequence from a blasr M5
+/// file. File may be grouped by target or query. The grouping determines
+/// which set gets corrected. Earlier, pre-assembly reads were corrected as
+/// targets. However, we can avoid the sort step if we can correct the reads
+/// as queries, since blasr groups alignments by query.
+///
+class BlasrM5AlnProvider : public AlnProvider {
+public:
+ /// Constructs a new alignment provider. Checks the format of the file and
+ /// throws an exception if it's malformed.
+ /// \param fpath Path to the file containing alignments.
+ BlasrM5AlnProvider(const std::string& fpath);
+
+ /// Constructs a provider based on the given stream. Note that no checks
+ /// are actually made on the validity of the format, caveat emptor. This
+ /// can be used to take a piped stream of alignments straight from blasr.
+ BlasrM5AlnProvider(std::istream* stream);
+
+ /// Cleans up some stuff.
+ ~BlasrM5AlnProvider();
+
+ /// Gets the set of alignments for the next target and puts them into the
+ /// given vector. Note this function will clear the contents of the vector
+ /// prior to adding the next set of alignments.
+ /// \param dest reference to a vector to hold the alignments.
+ /// \return True if there are more targets, otherwise false.
+ bool nextTarget(std::vector<dagcon::Alignment>& dest);
+
+ /// Same as \fn bool nextTarget(std::vector<dagcon::Alignment>& dest) except it
+ /// also returns the target sequence we are going to correct.
+ bool nextTarget(std::string& targetSeq, std::vector<dagcon::Alignment>& dest);
+
+ /// Called during constructor, checks that the file is formatted correctly.
+ /// Also determines if the input is grouped by query or target.
+ void checkFormat();
+
+private:
+ /// Path to the input file
+ const std::string fpath_;
+
+ /// State variables
+ std::string currId_;
+ dagcon::Alignment prevAln_;
+ bool firstAln_;
+
+ /// Represents an input stream to the alignments.
+ std::ifstream fs_;
+ std::istream* is_;
+};
+
+#endif //__GCON_BLASRM5_ALN_PROVIDER__
diff --git a/src/cpp/BoundedBuffer.hpp b/src/cpp/BoundedBuffer.hpp
new file mode 100644
index 0000000..394b50a
--- /dev/null
+++ b/src/cpp/BoundedBuffer.hpp
@@ -0,0 +1,89 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+#ifndef __GCON_BOUNDEDBUFFER__
+#define __GCON_BOUNDEDBUFFER__
+
+#include <deque>
+#include <condition_variable>
+#include <mutex>
+#include <functional>
+#include <vector>
+#include <string>
+#include "Alignment.hpp"
+
+///
+/// Templated, thread-safe buffer container, uses uses boost::circular buffer
+/// bounded by a given capacity specified by the caller. When the buffer is
+/// full, the push waits for an open spot. When the buffer is empty, the pop
+/// waits for an item to be present. Condition variables are used to signal
+/// the state of the buffer.
+///
+template <class T>
+class BoundedBuffer {
+public:
+ typedef std::deque<T> buffer_type;
+
+ BoundedBuffer(int max) : max_(max) { }
+
+ void push(T item) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ not_full_.wait(lock, [this](){return buffer_.size() != max_;});
+ buffer_.push_front(item);
+ not_empty_.notify_one();
+ }
+
+ void pop(T* pItem) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ not_empty_.wait(lock, [this](){return buffer_.size() != 0;});
+ *pItem = buffer_.back();
+ buffer_.pop_back();
+ not_full_.notify_one();
+ }
+
+ unsigned int size() {
+ return buffer_.size();
+ }
+
+private:
+ int max_;
+ buffer_type buffer_;
+ std::mutex mutex_;
+ std::condition_variable not_empty_;
+ std::condition_variable not_full_;
+};
+
+#endif // __GCON_BOUNDEDBUFFER__
+
diff --git a/src/cpp/DB.c b/src/cpp/DB.c
new file mode 100644
index 0000000..95f073d
--- /dev/null
+++ b/src/cpp/DB.c
@@ -0,0 +1,1106 @@
+/************************************************************************************\
+* *
+* Copyright (c) 2014, Dr. Eugene W. Myers (EWM). 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. *
+* *
+* · The name of EWM may not be used to endorse or promote products derived from *
+* this software without specific prior written permission. *
+* *
+* THIS SOFTWARE IS PROVIDED BY EWM ”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 EWM 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. *
+* *
+* For any issues regarding this software and its use, contact EWM at: *
+* *
+* Eugene W. Myers Jr. *
+* Bautzner Str. 122e *
+* 01099 Dresden *
+* GERMANY *
+* Email: gene.myers at gmail.com *
+* *
+\************************************************************************************/
+
+/*******************************************************************************************
+ *
+ * Compressed data base module. Auxiliary routines to open and manipulate a data base for
+ * which the sequence and read information are separated into two separate files, and the
+ * sequence is compressed into 2-bits for each base. Support for tracks of additional
+ * information, and trimming according to the current partition. Eventually will also
+ * support compressed quality information.
+ *
+ * Author : Gene Myers
+ * Date : July 2013
+ * Revised: April 2014
+ *
+ ********************************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "DB.h"
+
+#ifdef HIDE_FILES
+#define PATHSEP "/."
+#else
+#define PATHSEP "/"
+#endif
+
+
+/*******************************************************************************************
+ *
+ * GENERAL UTILITIES
+ *
+ ********************************************************************************************/
+
+char *Prog_Name;
+
+void *Malloc(int64 size, char *mesg)
+{ void *p;
+
+ if ((p = malloc(size)) == NULL)
+ { if (mesg == NULL)
+ fprintf(stderr,"%s: Out of memory\n",Prog_Name);
+ else
+ fprintf(stderr,"%s: Out of memory (%s)\n",Prog_Name,mesg);
+ }
+ return (p);
+}
+
+void *Realloc(void *p, int64 size, char *mesg)
+{ if ((p = realloc(p,size)) == NULL)
+ { if (mesg == NULL)
+ fprintf(stderr,"%s: Out of memory\n",Prog_Name);
+ else
+ fprintf(stderr,"%s: Out of memory (%s)\n",Prog_Name,mesg);
+ }
+ return (p);
+}
+
+char *Strdup(char *name, char *mesg)
+{ char *s;
+
+ if (name == NULL)
+ return (NULL);
+ if ((s = strdup(name)) == NULL)
+ { if (mesg == NULL)
+ fprintf(stderr,"%s: Out of memory\n",Prog_Name);
+ else
+ fprintf(stderr,"%s: Out of memory (%s)\n",Prog_Name,mesg);
+ }
+ return (s);
+}
+
+FILE *Fopen(char *name, char *mode)
+{ FILE *f;
+
+ if (name == NULL || mode == NULL)
+ return (NULL);
+ if ((f = fopen(name,mode)) == NULL)
+ fprintf(stderr,"%s: Cannot open %s for '%s'\n",Prog_Name,name,mode);
+ return (f);
+}
+
+char *PathTo(char *name)
+{ char *path, *find;
+
+ if (name == NULL)
+ return (NULL);
+ if ((find = rindex(name,'/')) != NULL)
+ { *find = '\0';
+ path = Strdup(name,"Extracting path from");
+ *find = '/';
+ }
+ else
+ path = Strdup(".","Allocating default path");
+ return (path);
+}
+
+char *Root(char *name, char *suffix)
+{ char *path, *find, *dot;
+ int epos;
+
+ if (name == NULL)
+ return (NULL);
+ find = rindex(name,'/');
+ if (find == NULL)
+ find = name;
+ else
+ find += 1;
+ if (suffix == NULL)
+ { dot = strchr(find,'.');
+ if (dot != NULL)
+ *dot = '\0';
+ path = Strdup(find,"Extracting root from");
+ if (dot != NULL)
+ *dot = '.';
+ }
+ else
+ { epos = strlen(find);
+ epos -= strlen(suffix);
+ if (epos > 0 && strcasecmp(find+epos,suffix) == 0)
+ { find[epos] = '\0';
+ path = Strdup(find,"Extracting root from");
+ find[epos] = suffix[0];
+ }
+ else
+ path = Strdup(find,"Allocating root");
+ }
+ return (path);
+}
+
+char *Catenate(char *path, char *sep, char *root, char *suffix)
+{ static char *cat = NULL;
+ static int max = -1;
+ int len;
+
+ if (path == NULL || root == NULL || sep == NULL || suffix == NULL)
+ return (NULL);
+ len = strlen(path);
+ len += strlen(sep);
+ len += strlen(root);
+ len += strlen(suffix);
+ if (len > max)
+ { max = ((int) (1.2*len)) + 100;
+ if ((cat = (char *) realloc(cat,max+1)) == NULL)
+ { fprintf(stderr,"%s: Out of memory (Making path name for %s)\n",Prog_Name,root);
+ return (NULL);
+ }
+ }
+ sprintf(cat,"%s%s%s%s",path,sep,root,suffix);
+ return (cat);
+}
+
+char *Numbered_Suffix(char *left, int num, char *right)
+{ static char *suffix = NULL;
+ static int max = -1;
+ int len;
+
+ if (left == NULL || right == NULL)
+ return (NULL);
+ len = strlen(left);
+ len += strlen(right) + 40;
+ if (len > max)
+ { max = ((int) (1.2*len)) + 100;
+ if ((suffix = (char *) realloc(suffix,max+1)) == NULL)
+ { fprintf(stderr,"%s: Out of memory (Making number suffix for %d)\n",Prog_Name,num);
+ return (NULL);
+ }
+ }
+ sprintf(suffix,"%s%d%s",left,num,right);
+ return (suffix);
+}
+
+
+#define COMMA ','
+
+// Print big integers with commas/periods for better readability
+
+void Print_Number(int64 num, int width, FILE *out)
+{ if (width == 0)
+ { if (num < 1000ll)
+ fprintf(out,"%lld",num);
+ else if (num < 1000000ll)
+ fprintf(out,"%lld%c%03lld",num/1000ll,COMMA,num%1000ll);
+ else if (num < 1000000000ll)
+ fprintf(out,"%lld%c%03lld%c%03lld",num/1000000ll,
+ COMMA,(num%1000000ll)/1000ll,COMMA,num%1000ll);
+ else
+ fprintf(out,"%lld%c%03lld%c%03lld%c%03lld",num/1000000000ll,
+ COMMA,(num%1000000000ll)/1000000ll,
+ COMMA,(num%1000000ll)/1000ll,COMMA,num%1000ll);
+ }
+ else
+ { if (num < 1000ll)
+ fprintf(out,"%*lld",width,num);
+ else if (num < 1000000ll)
+ { if (width <= 4)
+ fprintf(out,"%lld%c%03lld",num/1000ll,COMMA,num%1000ll);
+ else
+ fprintf(out,"%*lld%c%03lld",width-4,num/1000ll,COMMA,num%1000ll);
+ }
+ else if (num < 1000000000ll)
+ { if (width <= 8)
+ fprintf(out,"%lld%c%03lld%c%03lld",num/1000000ll,COMMA,(num%1000000ll)/1000ll,
+ COMMA,num%1000ll);
+ else
+ fprintf(out,"%*lld%c%03lld%c%03lld",width-8,num/1000000ll,COMMA,(num%1000000ll)/1000ll,
+ COMMA,num%1000ll);
+ }
+ else
+ { if (width <= 12)
+ fprintf(out,"%lld%c%03lld%c%03lld%c%03lld",num/1000000000ll,COMMA,
+ (num%1000000000ll)/1000000ll,COMMA,
+ (num%1000000ll)/1000ll,COMMA,num%1000ll);
+ else
+ fprintf(out,"%*lld%c%03lld%c%03lld%c%03lld",width-12,num/1000000000ll,COMMA,
+ (num%1000000000ll)/1000000ll,COMMA,
+ (num%1000000ll)/1000ll,COMMA,num%1000ll);
+ }
+ }
+}
+
+// Return the number of digits, base 10, of num
+
+int Number_Digits(int64 num)
+{ int digit;
+
+ digit = 0;
+ while (num >= 1)
+ { num /= 10;
+ digit += 1;
+ }
+ return (digit);
+}
+
+
+/*******************************************************************************************
+ *
+ * READ COMPRESSION/DECOMPRESSION UTILITIES
+ *
+ ********************************************************************************************/
+
+// Compress read into 2-bits per base (from [0-3] per byte representation
+
+void Compress_Read(int len, char *s)
+{ int i;
+ char c, d;
+ char *s0, *s1, *s2, *s3;
+
+ s0 = s;
+ s1 = s0+1;
+ s2 = s1+1;
+ s3 = s2+1;
+
+ c = s1[len];
+ d = s2[len];
+ s0[len] = s1[len] = s2[len] = 0;
+
+ for (i = 0; i < len; i += 4)
+ *s++ = (char ) ((s0[i] << 6) | (s1[i] << 4) | (s2[i] << 2) | s3[i]);
+
+ s1[len] = c;
+ s2[len] = d;
+}
+
+// Uncompress read form 2-bits per base into [0-3] per byte representation
+
+void Uncompress_Read(int len, char *s)
+{ int i, tlen, byte;
+ char *s0, *s1, *s2, *s3;
+ char *t;
+
+ s0 = s;
+ s1 = s0+1;
+ s2 = s1+1;
+ s3 = s2+1;
+
+ tlen = (len-1)/4;
+
+ t = s+tlen;
+ for (i = tlen*4; i >= 0; i -= 4)
+ { byte = *t--;
+ s0[i] = (char) ((byte >> 6) & 0x3);
+ s1[i] = (char) ((byte >> 4) & 0x3);
+ s2[i] = (char) ((byte >> 2) & 0x3);
+ s3[i] = (char) (byte & 0x3);
+ }
+ s[len] = 4;
+}
+
+// Convert read in [0-3] representation to ascii representation (end with '\n')
+
+void Lower_Read(char *s)
+{ static char letter[4] = { 'a', 'c', 'g', 't' };
+
+ for ( ; *s != 4; s++)
+ *s = letter[(int) *s];
+ *s = '\0';
+}
+
+void Upper_Read(char *s)
+{ static char letter[4] = { 'A', 'C', 'G', 'T' };
+
+ for ( ; *s != 4; s++)
+ *s = letter[(int) *s];
+ *s = '\0';
+}
+
+// Convert read in ascii representation to [0-3] representation (end with 4)
+
+void Number_Read(char *s)
+{ static char number[128] =
+ { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+
+ for ( ; *s != '\0'; s++)
+ *s = number[(int) *s];
+ *s = 4;
+}
+
+
+/*******************************************************************************************
+ *
+ * DB OPEN, TRIM & CLOSE ROUTINES
+ *
+ ********************************************************************************************/
+
+
+// Open the given database or dam, "path" into the supplied HITS_DB record "db". If the name has
+// a part # in it then just the part is opened. The index array is allocated (for all or
+// just the part) and read in.
+// Return status of routine:
+// -1: The DB could not be opened for a reason reported by the routine to stderr
+// 0: Open of DB proceeded without mishap
+// 1: Open of DAM proceeded without mishap
+
+int Open_DB(char* path, HITS_DB *db)
+{ char *root, *pwd, *bptr, *fptr, *cat;
+ int nreads;
+ FILE *index, *dbvis;
+ int status, plen, isdam;
+ int part, cutoff, all;
+ int ofirst, bfirst, olast;
+
+
+ plen = strlen(path);
+ if (strcmp(path+(plen-4),".dam") == 0)
+ root = Root(path,".dam");
+ else
+ root = Root(path,".db");
+ pwd = PathTo(path);
+
+ bptr = rindex(root,'.');
+ if (bptr != NULL && bptr[1] != '\0' && bptr[1] != '-')
+ { part = strtol(bptr+1,&fptr,10);
+ if (*fptr != '\0' || part == 0)
+ part = 0;
+ else
+ *bptr = '\0';
+ }
+ else
+ part = 0;
+
+ isdam = 0;
+ cat = Catenate(pwd,"/",root,".db");
+ if (cat == NULL)
+ exit (1);
+ if ((dbvis = fopen(cat,"r")) == NULL)
+ { cat = Catenate(pwd,"/",root,".dam");
+ if (cat == NULL)
+ exit (1);
+ if ((dbvis = fopen(cat,"r")) == NULL)
+ { status = -1;
+ fprintf(stderr,"%s: Could not open database %s\n",Prog_Name,path);
+ goto exit;
+ }
+ isdam = 1;
+ }
+
+ if ((index = Fopen(Catenate(pwd,PATHSEP,root,".idx"),"rm")) == NULL)
+ { status = -1;
+ goto exit1;
+ }
+ if (fread(db,sizeof(HITS_DB),1,index) != 1)
+ SYSTEM_ERROR
+ nreads = db->oreads;
+
+ { int p, nblocks, nfiles, blast;
+ int64 size;
+ char fname[MAX_NAME], prolog[MAX_NAME];
+
+ nblocks = 0;
+ if (fscanf(dbvis,DB_NFILE,&nfiles) != 1)
+ SYSTEM_ERROR
+ for (p = 0; p < nfiles; p++)
+ if (fscanf(dbvis,DB_FDATA,&blast,fname,prolog) != 3)
+ SYSTEM_ERROR
+ if (fscanf(dbvis,DB_NBLOCK,&nblocks) != 1)
+ if (part == 0)
+ { cutoff = 0;
+ all = 1;
+ }
+ else
+ { fprintf(stderr,"%s: DB %s has not yet been partitioned, cannot request a block !\n",
+ Prog_Name,root);
+ status = -1;
+ goto exit2;
+ }
+ else
+ { if (fscanf(dbvis,DB_PARAMS,&size,&cutoff,&all) != 3)
+ SYSTEM_ERROR
+ if (part > nblocks)
+ { fprintf(stderr,"%s: DB %s has only %d blocks\n",Prog_Name,root,nblocks);
+ status = -1;
+ goto exit2;
+ }
+ }
+
+ if (part > 0)
+ { for (p = 1; p <= part; p++)
+ if (fscanf(dbvis,DB_BDATA,&ofirst,&bfirst) != 2)
+ SYSTEM_ERROR
+ if (fscanf(dbvis,DB_BDATA,&olast,&blast) != 2)
+ SYSTEM_ERROR
+ }
+ else
+ { ofirst = bfirst = 0;
+ olast = nreads;
+ }
+ }
+
+ db->trimmed = 0;
+ db->tracks = NULL;
+ db->part = part;
+ db->cutoff = cutoff;
+ db->all = all;
+ db->ofirst = ofirst;
+ db->bfirst = bfirst;
+
+ if (part <= 0)
+ { db->reads = (HITS_READ *) Malloc(sizeof(HITS_READ)*(nreads+1),"Allocating Open_DB index");
+ if (fread(db->reads,sizeof(HITS_READ),nreads,index) != (size_t) nreads)
+ SYSTEM_ERROR
+ }
+ else
+ { HITS_READ *reads;
+ int i, r, maxlen;
+ int64 totlen;
+
+ nreads = olast-ofirst;
+ reads = (HITS_READ *) Malloc(sizeof(HITS_READ)*(nreads+1),"Allocating Open_DB index");
+
+ fseeko(index,sizeof(HITS_READ)*ofirst,SEEK_CUR);
+ if (fread(reads,sizeof(HITS_READ),nreads,index) != (size_t) nreads)
+ SYSTEM_ERROR
+
+ totlen = 0;
+ maxlen = 0;
+ for (i = 0; i < nreads; i++)
+ { r = reads[i].rlen;
+ totlen += r;
+ if (r > maxlen)
+ maxlen = r;
+ }
+
+ db->maxlen = maxlen;
+ db->totlen = totlen;
+ db->reads = reads;
+ }
+
+ db->nreads = nreads;
+ db->path = Strdup(Catenate(pwd,PATHSEP,root,""),"Allocating Open_DB path");
+ db->bases = NULL;
+ db->loaded = 0;
+
+ status = isdam;
+
+exit2:
+ fclose(index);
+exit1:
+ fclose(dbvis);
+exit:
+ if (bptr != NULL)
+ *bptr = '.';
+
+ free(pwd);
+ free(root);
+
+ return (status);
+}
+
+
+// Trim the DB or part thereof and all loaded tracks according to the cuttof and all settings
+// of the current DB partition. Reallocate smaller memory blocks for the information kept
+// for the retained reads.
+
+void Trim_DB(HITS_DB *db)
+{ int i, j, r;
+ int allflag, cutoff;
+ int64 totlen;
+ int maxlen, nreads;
+ HITS_TRACK *record;
+ HITS_READ *reads;
+
+ if (db->trimmed) return;
+
+ if (db->cutoff <= 0 && db->all) return;
+
+ cutoff = db->cutoff;
+ if (db->all)
+ allflag = 0;
+ else
+ allflag = DB_BEST;
+
+ reads = db->reads;
+ nreads = db->nreads;
+
+ for (record = db->tracks; record != NULL; record = record->next)
+ { int *anno4, size;
+ int64 *anno8;
+ char *anno, *data;
+
+ size = record->size;
+ data = (char *) record->data;
+ if (data == NULL)
+ { anno = (char *) record->anno;
+ j = 0;
+ for (i = r = 0; i < db->nreads; i++, r += size)
+ if ((reads[i].flags & DB_BEST) >= allflag && reads[i].rlen >= cutoff)
+ { memmove(anno+j,anno+r,size);
+ j += size;
+ }
+ memmove(anno+j,anno+r,size);
+ }
+ else if (size == 4)
+ { int ai;
+
+ anno4 = (int *) (record->anno);
+ j = anno4[0] = 0;
+ for (i = 0; i < db->nreads; i++)
+ if ((reads[i].flags & DB_BEST) >= allflag && reads[i].rlen >= cutoff)
+ { ai = anno4[i];
+ anno4[j+1] = anno4[j] + (anno4[i+1]-ai);
+ memmove(data+anno4[j],data+ai,anno4[i+1]-ai);
+ j += 1;
+ }
+ record->data = Realloc(record->data,anno4[j],NULL);
+ }
+ else // size == 8
+ { int64 ai;
+
+ anno8 = (int64 *) (record->anno);
+ j = anno8[0] = 0;
+ for (i = 0; i < db->nreads; i++)
+ if ((reads[i].flags & DB_BEST) >= allflag && reads[i].rlen >= cutoff)
+ { ai = anno8[i];
+ anno8[j+1] = anno8[j] + (anno8[i+1]-ai);
+ memmove(data+anno8[j],data+ai,anno8[i+1]-ai);
+ j += 1;
+ }
+ record->data = Realloc(record->data,anno8[j],NULL);
+ }
+ record->anno = Realloc(record->anno,record->size*(j+1),NULL);
+ }
+
+ totlen = maxlen = 0;
+ for (j = i = 0; i < nreads; i++)
+ { r = reads[i].rlen;
+ if ((reads[i].flags & DB_BEST) >= allflag && r >= cutoff)
+ { totlen += r;
+ if (r > maxlen)
+ maxlen = r;
+ reads[j++] = reads[i];
+ }
+ }
+
+ db->totlen = totlen;
+ db->maxlen = maxlen;
+ db->nreads = j;
+ db->trimmed = 1;
+
+ if (j < nreads)
+ db->reads = Realloc(reads,sizeof(HITS_READ)*(j+1),NULL);
+}
+
+// Shut down an open 'db' by freeing all associated space, including tracks and QV structures,
+// and any open file pointers. The record pointed at by db however remains (the user
+// supplied it and so should free it).
+
+void Close_DB(HITS_DB *db)
+{ HITS_TRACK *t, *p;
+
+ if (db->loaded)
+ free(((char *) (db->bases)) - 1);
+ else if (db->bases != NULL)
+ fclose((FILE *) db->bases);
+ free(db->reads);
+ free(db->path);
+
+ for (t = db->tracks; t != NULL; t = p)
+ { p = t->next;
+ free(t->anno);
+ free(t->data);
+ free(t);
+ }
+}
+
+/*******************************************************************************************
+ *
+ * TRACK LOAD & CLOSE ROUTINES
+ *
+ ********************************************************************************************/
+
+// Return status of track:
+// 1: Track is for trimmed DB
+// 0: Track is for untrimmed DB
+// -1: Track is not the right size of DB either trimmed or untrimmed
+// -2: Could not find the track
+
+int Check_Track(HITS_DB *db, char *track)
+{ FILE *afile;
+ int tracklen;
+
+ afile = fopen(Catenate(db->path,".",track,".anno"),"r");
+ if (afile == NULL)
+ return (-2);
+
+ if (fread(&tracklen,sizeof(int),1,afile) != 1)
+ SYSTEM_ERROR
+
+ fclose(afile);
+
+ if (tracklen == db->breads)
+ return (1);
+ else if (tracklen == db->oreads)
+ return (0);
+ else
+ return (-1);
+}
+
+// If track is not already in the db's track list, then allocate all the storage for it,
+// read it in from the appropriate file, add it to the track list, and return a pointer
+// to the newly created HITS_TRACK record. If the track does not exist or cannot be
+// opened for some reason, then NULL is returned.
+
+HITS_TRACK *Load_Track(HITS_DB *db, char *track)
+{ FILE *afile, *dfile;
+ int tracklen, size;
+ int nreads;
+ void *anno;
+ void *data;
+ HITS_TRACK *record;
+
+ if (track[0] == '.')
+ { fprintf(stderr,"Track names cannot begin with a .\n");
+ exit (1);
+ }
+
+ for (record = db->tracks; record != NULL; record = record->next)
+ if (strcmp(record->name,track) == 0)
+ return (record);
+
+ afile = fopen(Catenate(db->path,".",track,".anno"),"r");
+ if (afile == NULL)
+ return (NULL);
+ dfile = fopen(Catenate(db->path,".",track,".data"),"r");
+
+ if (fread(&tracklen,sizeof(int),1,afile) != 1)
+ SYSTEM_ERROR
+ if (fread(&size,sizeof(int),1,afile) != 1)
+ SYSTEM_ERROR
+
+ if (db->trimmed)
+ { if (tracklen != db->breads)
+ { fprintf(stderr,"%s: Track %s not same size as database !\n",Prog_Name,track);
+ exit (1);
+ }
+ if (db->part > 0)
+ fseeko(afile,size*db->bfirst,SEEK_CUR);
+ }
+ else
+ { if (tracklen != db->oreads)
+ { fprintf(stderr,"%s: Track %s not same size as database !\n",Prog_Name,track);
+ exit (1);
+ }
+ if (db->part > 0)
+ fseeko(afile,size*db->ofirst,SEEK_CUR);
+ }
+ nreads = db->nreads;
+
+ anno = (void *) Malloc(size*(nreads+1),"Allocating Track Anno Vector");
+
+ if (size > 0)
+ { if (dfile == NULL)
+ { if (fread(anno,size,nreads,afile) != (size_t) nreads)
+ SYSTEM_ERROR
+ }
+ else
+ { if (fread(anno,size,nreads+1,afile) != (size_t) (nreads+1))
+ SYSTEM_ERROR
+ }
+ }
+ else
+ SYSTEM_ERROR
+
+ if (dfile != NULL)
+ { int64 *anno8, off8, dlen;
+ int *anno4, off4;
+ int i;
+
+ if (size == 4)
+ { anno4 = (int *) anno;
+ off4 = anno4[0];
+ if (off4 != 0)
+ { for (i = 0; i <= nreads; i++)
+ anno4[i] -= off4;
+ fseeko(dfile,off4,SEEK_SET);
+ }
+ dlen = anno4[nreads];
+ data = (void *) Malloc(dlen,"Allocating Track Data Vector");
+ }
+ else
+ { anno8 = (int64 *) anno;
+ off8 = anno8[0];
+ if (off8 != 0)
+ { for (i = 0; i <= nreads; i++)
+ anno8[i] -= off8;
+ fseeko(dfile,off8,SEEK_SET);
+ }
+ dlen = anno8[nreads];
+ data = (void *) Malloc(dlen,"Allocating Track Data Vector");
+ }
+ if (dlen > 0)
+ { if (fread(data,dlen,1,dfile) != 1)
+ SYSTEM_ERROR
+ }
+ fclose(dfile);
+ }
+ else
+ data = NULL;
+
+ fclose(afile);
+
+ record = (HITS_TRACK *) Malloc(sizeof(HITS_TRACK),"Allocating Track Record");
+ record->name = Strdup(track,"Allocating Track Name");
+ record->data = data;
+ record->anno = anno;
+ record->size = size;
+
+ if (db->tracks != NULL && strcmp(db->tracks->name,". at qvs") == 0)
+ { record->next = db->tracks->next;
+ db->tracks->next = record;
+ }
+ else
+ { record->next = db->tracks;
+ db->tracks = record;
+ }
+
+ return (record);
+}
+
+void Close_Track(HITS_DB *db, char *track)
+{ HITS_TRACK *record, *prev;
+
+ prev = NULL;
+ for (record = db->tracks; record != NULL; record = record->next)
+ { if (strcmp(record->name,track) == 0)
+ { free(record->anno);
+ free(record->data);
+ free(record->name);
+ if (prev == NULL)
+ db->tracks = record->next;
+ else
+ prev->next = record->next;
+ free(record);
+ return;
+ }
+ prev = record;
+ }
+ return;
+}
+
+
+/*******************************************************************************************
+ *
+ * READ BUFFER ALLOCATION AND READ ACCESS
+ *
+ ********************************************************************************************/
+
+// Allocate and return a buffer big enough for the largest read in 'db', leaving room
+// for an initial delimiter character
+
+char *New_Read_Buffer(HITS_DB *db)
+{ char *read;
+
+ read = (char *) Malloc(db->maxlen+4,"Allocating New Read Buffer");
+ if (read == NULL)
+ exit (1);
+ return (read+1);
+}
+
+// Load into 'read' the i'th read in 'db'. As an upper case ASCII string if ascii is 2, as a
+// lower-case ASCII string is ascii is 1, and as a numeric string over 0(A), 1(C), 2(G), and
+// 3(T) otherwise.
+//
+// **NB**, the byte before read will be set to a delimiter character!
+
+void Load_Read(HITS_DB *db, int i, char *read, int ascii)
+{ FILE *bases = (FILE *) db->bases;
+ int64 off;
+ int len, clen;
+ HITS_READ *r = db->reads;
+
+ if (bases == NULL)
+ { db->bases = (void *) (bases = Fopen(Catenate(db->path,"","",".bps"),"r"));
+ if (bases == NULL)
+ exit (1);
+ }
+ if (i >= db->nreads)
+ { fprintf(stderr,"%s: Index out of bounds (Load_Read)\n",Prog_Name);
+ exit (1);
+ }
+
+ off = r[i].boff;
+ len = r[i].rlen;
+
+ if (ftello(bases) != off)
+ fseeko(bases,off,SEEK_SET);
+ clen = COMPRESSED_LEN(len);
+ if (clen > 0)
+ { if (fread(read,clen,1,bases) != 1)
+ SYSTEM_ERROR
+ }
+ Uncompress_Read(len,read);
+ if (ascii == 1)
+ { Lower_Read(read);
+ read[-1] = '\0';
+ }
+ else if (ascii == 2)
+ { Upper_Read(read);
+ read[-1] = '\0';
+ }
+ else
+ read[-1] = 4;
+}
+
+char *Load_Subread(HITS_DB *db, int i, int beg, int end, char *read, int ascii)
+{ FILE *bases = (FILE *) db->bases;
+ int64 off;
+ int len, clen;
+ int bbeg, bend;
+ HITS_READ *r = db->reads;
+
+ if (bases == NULL)
+ { db->bases = (void *) (bases = Fopen(Catenate(db->path,"","",".bps"),"rm"));
+ if (bases == NULL)
+ exit (1);
+ }
+ if (i >= db->nreads)
+ { fprintf(stderr,"%s: Index out of bounds (Load_Read)\n",Prog_Name);
+ exit (1);
+ }
+
+ bbeg = beg/4;
+ bend = (end-1)/4+1;
+
+ off = r[i].boff + bbeg;
+ len = end - beg;
+
+ if (ftello(bases) != off)
+ fseeko(bases,off,SEEK_SET);
+ clen = bend-bbeg;
+ if (clen > 0)
+ { if (fread(read,clen,1,bases) != 1)
+ SYSTEM_ERROR
+ }
+ Uncompress_Read(4*clen,read);
+ read += beg%4;
+ read[len] = 4;
+ if (ascii == 1)
+ { Lower_Read(read);
+ read[-1] = '\0';
+ }
+ else if (ascii == 2)
+ { Upper_Read(read);
+ read[-1] = '\0';
+ }
+ else
+ read[-1] = 4;
+
+ return (read);
+}
+
+
+/*******************************************************************************************
+ *
+ * BLOCK LOAD OF ALL READS (PRIMARILY FOR DALIGNER)
+ *
+ ********************************************************************************************/
+
+// Allocate a block big enough for all the uncompressed sequences, read them into it,
+// reset the 'off' in each read record to be its in-memory offset, and set the
+// bases pointer to point at the block after closing the bases file. If ascii is
+// non-zero then the reads are converted to ACGT ascii, otherwise the reads are left
+// as numeric strings over 0(A), 1(C), 2(G), and 3(T).
+
+void Read_All_Sequences(HITS_DB *db, int ascii)
+{ FILE *bases = (FILE *) db->bases;
+ int nreads = db->nreads;
+ HITS_READ *reads = db->reads;
+ void (*translate)(char *s);
+
+ char *seq;
+ int64 o, off;
+ int i, len, clen;
+
+ if (bases == NULL)
+ db->bases = (void *) (bases = Fopen(Catenate(db->path,"","",".bps"),"r"));
+ else
+ rewind(bases);
+
+ seq = (char *) Malloc(db->totlen+nreads+4,"Allocating All Sequence Reads");
+
+ *seq++ = 4;
+
+ if (ascii == 1)
+ translate = Lower_Read;
+ else
+ translate = Upper_Read;
+
+ o = 0;
+ for (i = 0; i < nreads; i++)
+ { len = reads[i].rlen;
+ off = reads[i].boff;
+ if (ftello(bases) != off)
+ fseeko(bases,off,SEEK_SET);
+ clen = COMPRESSED_LEN(len);
+ if (clen > 0)
+ { if (fread(seq+o,clen,1,bases) != 1)
+ SYSTEM_ERROR
+ }
+ Uncompress_Read(len,seq+o);
+ if (ascii)
+ translate(seq+o);
+ reads[i].boff = o;
+ o += (len+1);
+ }
+ reads[nreads].boff = o;
+
+ fclose(bases);
+
+ db->bases = (void *) seq;
+ db->loaded = 1;
+}
+
+int List_DB_Files(char *path, void foreach(char *path, char *extension))
+{ int status, rlen, dlen;
+ char *root, *pwd, *name;
+ int isdam;
+ DIR *dirp;
+ struct dirent *dp;
+
+ status = 0;
+ pwd = PathTo(path);
+ root = Root(path,".db");
+ rlen = strlen(root);
+
+ if (root == NULL || pwd == NULL)
+ { status = 1;
+ goto exit;
+ }
+
+ if ((dirp = opendir(pwd)) == NULL)
+ { status = 1;
+ goto exit;
+ }
+
+ isdam = 0;
+ while ((dp = readdir(dirp)) != NULL) // Get case dependent root name (if necessary)
+ { name = dp->d_name;
+ if (strcmp(name,Catenate("","",root,".db")) == 0)
+ break;
+ if (strcmp(name,Catenate("","",root,".dam")) == 0)
+ { isdam = 1;
+ break;
+ }
+ if (strcasecmp(name,Catenate("","",root,".db")) == 0)
+ { strncpy(root,name,rlen);
+ break;
+ }
+ if (strcasecmp(name,Catenate("","",root,".dam")) == 0)
+ { strncpy(root,name,rlen);
+ isdam = 1;
+ break;
+ }
+ }
+ if (dp == NULL)
+ { status = 1;
+ closedir(dirp);
+ goto exit;
+ }
+
+ if (isdam)
+ foreach(Catenate(pwd,"/",root,".dam"),"dam");
+ else
+ foreach(Catenate(pwd,"/",root,".db"),"db");
+
+ rewinddir(dirp); // Report each auxiliary file
+ while ((dp = readdir(dirp)) != NULL)
+ { name = dp->d_name;
+ dlen = strlen(name);
+#ifdef HIDE_FILES
+ if (name[0] != '.')
+ continue;
+ dlen -= 1;
+ name += 1;
+#endif
+ if (dlen < rlen+1)
+ continue;
+ if (name[rlen] != '.')
+ continue;
+ if (strncmp(name,root,rlen) != 0)
+ continue;
+ foreach(Catenate(pwd,PATHSEP,name,""),name+(rlen+1));
+ }
+ closedir(dirp);
+
+exit:
+ free(pwd);
+ free(root);
+ return (status);
+}
+
+void Print_Read(char *s, int width)
+{ int i;
+
+ if (s[0] < 4)
+ { for (i = 0; s[i] != 4; i++)
+ { if (i%width == 0 && i != 0)
+ printf("\n");
+ printf("%d",s[i]);
+ }
+ printf("\n");
+ }
+ else
+ { for (i = 0; s[i] != '\0'; i++)
+ { if (i%width == 0 && i != 0)
+ printf("\n");
+ printf("%c",s[i]);
+ }
+ printf("\n");
+ }
+}
diff --git a/src/cpp/DB.h b/src/cpp/DB.h
new file mode 100644
index 0000000..16f57ba
--- /dev/null
+++ b/src/cpp/DB.h
@@ -0,0 +1,357 @@
+/************************************************************************************\
+* *
+* Copyright (c) 2014, Dr. Eugene W. Myers (EWM). 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. *
+* *
+* · The name of EWM may not be used to endorse or promote products derived from *
+* this software without specific prior written permission. *
+* *
+* THIS SOFTWARE IS PROVIDED BY EWM ”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 EWM 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. *
+* *
+* For any issues regarding this software and its use, contact EWM at: *
+* *
+* Eugene W. Myers Jr. *
+* Bautzner Str. 122e *
+* 01099 Dresden *
+* GERMANY *
+* Email: gene.myers at gmail.com *
+* *
+\************************************************************************************/
+
+/*******************************************************************************************
+ *
+ * Compressed data base module. Auxiliary routines to open and manipulate a data base for
+ * which the sequence and read information are separated into two separate files, and the
+ * sequence is compressed into 2-bits for each base. Support for tracks of additional
+ * information, and trimming according to the current partition. Eventually will also
+ * support compressed quality information.
+ *
+ * Author : Gene Myers
+ * Date : July 2013
+ * Revised: April 2014
+ *
+ ********************************************************************************************/
+
+#ifndef _HITS_DB
+
+#define _HITS_DB
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+typedef unsigned long long uint64;
+typedef signed char int8;
+typedef signed short int16;
+typedef signed int int32;
+typedef signed long long int64;
+typedef float float32;
+typedef double float64;
+
+#define HIDE_FILES // Auxiliary DB files start with a . so they are "hidden"
+ // Undefine if you don't want this
+
+
+/*******************************************************************************************
+ *
+ * COMMAND LINE INTERPRETATION MACROS
+ *
+ ********************************************************************************************/
+
+extern char *Prog_Name; // Name of program
+
+#define SYSTEM_ERROR \
+ { fprintf(stderr,"%s: System error, read failed!\n",Prog_Name); \
+ exit (2); \
+ }
+
+#define ARG_INIT(name) \
+ Prog_Name = Strdup(name,""); \
+ for (i = 0; i < 128; i++) \
+ flags[i] = 0;
+
+#define ARG_FLAGS(set) \
+ for (k = 1; argv[i][k] != '\0'; k++) \
+ { if (index(set,argv[i][k]) == NULL) \
+ { fprintf(stderr,"%s: -%c is an illegal option\n",Prog_Name,argv[i][k]); \
+ exit (1); \
+ } \
+ flags[(int) argv[i][k]] = 1; \
+ }
+
+#define ARG_POSITIVE(var,name) \
+ var = strtol(argv[i]+2,&eptr,10); \
+ if (*eptr != '\0' || argv[i][2] == '\0') \
+ { fprintf(stderr,"%s: -%c argument is not an integer\n",Prog_Name,argv[i][1]); \
+ exit (1); \
+ } \
+ if (var <= 0) \
+ { fprintf(stderr,"%s: %s must be positive (%d)\n",Prog_Name,name,var); \
+ exit (1); \
+ }
+
+#define ARG_NON_NEGATIVE(var,name) \
+ var = strtol(argv[i]+2,&eptr,10); \
+ if (*eptr != '\0' || argv[i][2] == '\0') \
+ { fprintf(stderr,"%s: -%c argument is not an integer\n",Prog_Name,argv[i][1]); \
+ exit (1); \
+ } \
+ if (var < 0) \
+ { fprintf(stderr,"%s: %s must be non-negative (%d)\n",Prog_Name,name,var); \
+ exit (1); \
+ }
+
+#define ARG_REAL(var) \
+ var = strtod(argv[i]+2,&eptr); \
+ if (*eptr != '\0' || argv[i][2] == '\0') \
+ { fprintf(stderr,"%s: -%c argument is not a real number\n",Prog_Name,argv[i][1]); \
+ exit (1); \
+ }
+
+/*******************************************************************************************
+ *
+ * UTILITIES
+ *
+ ********************************************************************************************/
+
+// The following general utilities return NULL if any of their input pointers are NULL, or if they
+// could not perform their function (in which case they also print an error to stderr).
+
+void *Malloc(int64 size, char *mesg); // Guarded versions of malloc, realloc
+void *Realloc(void *object, int64 size, char *mesg); // and strdup, that output "mesg" to
+char *Strdup(char *string, char *mesg); // stderr if out of memory
+
+FILE *Fopen(char *path, char *mode); // Open file path for "mode"
+char *PathTo(char *path); // Return path portion of file name "path"
+char *Root(char *path, char *suffix); // Return the root name, excluding suffix, of "path"
+
+// Catenate returns concatenation of path.sep.root.suffix in a *temporary* buffer
+// Numbered_Suffix returns concatenation of left.<num>.right in a *temporary* buffer
+
+char *Catenate(char *path, char *sep, char *root, char *suffix);
+char *Numbered_Suffix(char *left, int num, char *right);
+
+
+// DB-related utilities
+
+void Print_Number(int64 num, int width, FILE *out); // Print readable big integer
+int Number_Digits(int64 num); // Return # of digits in printed number
+
+#define COMPRESSED_LEN(len) (((len)+3) >> 2)
+
+void Compress_Read(int len, char *s); // Compress read in-place into 2-bit form
+void Uncompress_Read(int len, char *s); // Uncompress read in-place into numeric form
+void Print_Read(char *s, int width);
+
+void Lower_Read(char *s); // Convert read from numbers to lowercase letters (0-3 to acgt)
+void Upper_Read(char *s); // Convert read from numbers to uppercase letters (0-3 to ACGT)
+void Number_Read(char *s); // Convert read from letters to numbers
+
+
+/*******************************************************************************************
+ *
+ * DB IN-CORE DATA STRUCTURES
+ *
+ ********************************************************************************************/
+
+#define DB_CSS 0x0400 // This is the second or later of a group of reads from a given insert
+#define DB_BEST 0x0800 // This is the longest read of a given insert (may be the only 1)
+
+typedef struct
+ { int origin; // Well #
+ int rlen; // Length of the sequence (Last pulse = fpulse + rlen)
+ int fpulse; // First pulse
+ int64 boff; // Offset (in bytes) of compressed read in 'bases' file, or offset of
+ // uncompressed bases in memory block
+ int64 coff; // Offset (in bytes) of compressed quiva streams in 'quiva' file
+ int flags; // QV of read + flags above
+ } HITS_READ;
+
+// A track can be of 3 types:
+// data == NULL: there are nreads+1 'anno' records of size 'size'.
+// data != NULL && size == 4: anno is an array of nreads+1 int's and data[anno[i]..anno[i+1])
+// contains the variable length data
+// data != NULL && size == 8: anno is an array of nreads+1 int64's and data[anno[i]..anno[i+1])
+// contains the variable length data
+
+typedef struct _track
+ { struct _track *next; // Link to next track
+ char *name; // Symbolic name of track
+ int size; // Size in bytes of anno records
+ void *anno; // over [0,nreads]: read i annotation: int, int64, or 'size' records
+ void *data; // data[anno[i] .. anno[i+1]-1] is data if data != NULL
+ } HITS_TRACK;
+
+
+// The DB record holds all information about the current state of an active DB including an
+// array of HITS_READS, one per read, and a linked list of HITS_TRACKs the first of which
+// is always a HITS_QV pseudo-track (if the QVs have been loaded).
+
+typedef struct
+ { int oreads; // Total number of reads in DB
+ int breads; // Total number of reads in trimmed DB (if trimmed set)
+ int cutoff; // Minimum read length in block (-1 if not yet set)
+ int all; // Consider multiple reads from a given well
+ float freq[4]; // frequency of A, C, G, T, respectively
+
+ // Set with respect to "active" part of DB (all vs block, untrimmed vs trimmed)
+
+ int maxlen; // length of maximum read (initially over all DB)
+ int64 totlen; // total # of bases (initially over all DB)
+
+ int nreads; // # of reads in actively loaded portion of DB
+ int trimmed; // DB has been trimmed by cutoff/all
+ int part; // DB block (if > 0), total DB (if == 0)
+ int ofirst; // Index of first read in block (without trimming)
+ int bfirst; // Index of first read in block (with trimming)
+
+ char *path; // Root name of DB for .bps and tracks
+ int loaded; // Are reads loaded in memory?
+ void *bases; // file pointer for bases file (to fetch reads from),
+ // or memory pointer to uncompressed block of all sequences.
+ HITS_READ *reads; // Array [0..nreads] of HITS_READ
+ HITS_TRACK *tracks; // Linked list of loaded tracks
+ } HITS_DB;
+
+
+/*******************************************************************************************
+ *
+ * DB STUB FILE FORMAT = NFILE FDATA^nfile NBLOCK PARAMS BDATA^nblock
+ *
+ ********************************************************************************************/
+
+#define MAX_NAME 10000 // Longest file name or fasta header line
+
+#define DB_NFILE "files = %9d\n" // number of files
+#define DB_FDATA " %9d %s %s\n" // last read index + 1, fasta prolog, file name
+#define DB_NBLOCK "blocks = %9d\n" // number of blocks
+#define DB_PARAMS "size = %9lld cutoff = %9d all = %1d\n" // block size, len cutoff, all in well
+#define DB_BDATA " %9d %9d\n" // First read index (untrimmed), first read index (trimmed)
+
+
+/*******************************************************************************************
+ *
+ * DB ROUTINES
+ *
+ ********************************************************************************************/
+
+ // Suppose DB is the name of an original database. Then there will be files .DB.idx, .DB.bps,
+ // .DB.qvs, and files .DB.<track>.anno and DB.<track>.data where <track> is a track name
+ // (not containing a . !).
+
+ // A DAM is basically a DB except that:
+ // 1. there are no QV's, instead .coff points the '\0' terminated fasta header of the read
+ // in the file .<dam>.hdr file
+ // 2. .origin contains the contig # of the read within a fasta entry (assembly sequences
+ // contain N-separated contigs), and .fpulse the first base ofn the contig in the
+ // fasta entry
+
+ // Open the given database or dam, "path" into the supplied HITS_DB record "db". If the name has
+ // a part # in it then just the part is opened. The index array is allocated (for all or
+ // just the part) and read in.
+ // Return status of routine:
+ // -1: The DB could not be opened for a reason reported by the routine to stderr
+ // 0: Open of DB proceeded without mishap
+ // 1: Open of DAM proceeded without mishap
+
+int Open_DB(char *path, HITS_DB *db);
+
+ // Trim the DB or part thereof and all loaded tracks according to the cuttof and all settings
+ // of the current DB partition. Reallocate smaller memory blocks for the information kept
+ // for the retained reads.
+
+void Trim_DB(HITS_DB *db);
+
+ // Shut down an open 'db' by freeing all associated space, including tracks and QV structures,
+ // and any open file pointers. The record pointed at by db however remains (the user
+ // supplied it and so should free it).
+
+void Close_DB(HITS_DB *db);
+
+ // Look up the file and header in the file of the indicated track. Return:
+ // 1: Track is for trimmed DB
+ // 0: Track is for untrimmed DB
+ // -1: Track is not the right size of DB either trimmed or untrimmed
+ // -2: Could not find the track
+
+int Check_Track(HITS_DB *db, char *track);
+
+ // If track is not already in the db's track list, then allocate all the storage for it,
+ // read it in from the appropriate file, add it to the track list, and return a pointer
+ // to the newly created HITS_TRACK record. If the track does not exist or cannot be
+ // opened for some reason, then NULL is returned.
+
+HITS_TRACK *Load_Track(HITS_DB *db, char *track);
+
+ // If track is on the db's track list, then it is removed and all storage associated with it
+ // is freed.
+
+void Close_Track(HITS_DB *db, char *track);
+
+ // Allocate and return a buffer big enough for the largest read in 'db'.
+ // **NB** free(x-1) if x is the value returned as *prefix* and suffix '\0'(4)-byte
+ // are needed by the alignment algorithms.
+
+char *New_Read_Buffer(HITS_DB *db);
+
+ // Load into 'read' the i'th read in 'db'. As a lower case ascii string if ascii is 1, an
+ // upper case ascii string if ascii is 2, and a numeric string over 0(A), 1(C), 2(G), and 3(T)
+ // otherwise. A '\0' (or 4) is prepended and appended to the string so it has a delimeter
+ // for traversals in either direction.
+
+void Load_Read(HITS_DB *db, int i, char *read, int ascii);
+
+ // Load into 'read' the subread [beg,end] of the i'th read in 'db' and return a pointer to the
+ // the start of the subinterval (not necessarily = to read !!! ). As a lower case ascii
+ // string if ascii is 1, an upper case ascii string if ascii is 2, and a numeric string
+ // over 0(A), 1(C), 2(G), and 3(T) otherwise. A '\0' (or 4) is prepended and appended to
+ // the string holding the substring so it has a delimeter for traversals in either direction.
+
+char *Load_Subread(HITS_DB *db, int i, int beg, int end, char *read, int ascii);
+
+ // Allocate a block big enough for all the uncompressed sequences, read them into it,
+ // reset the 'off' in each read record to be its in-memory offset, and set the
+ // bases pointer to point at the block after closing the bases file. If ascii is
+ // 1 then the reads are converted to lowercase ascii, if 2 then uppercase ascii, and
+ // otherwise the reads are left as numeric strings over 0(A), 1(C), 2(G), and 3(T).
+
+void Read_All_Sequences(HITS_DB *db, int ascii);
+
+ // For the DB or DAM "path" = "prefix/root[.db|.dam]", find all the files for that DB, i.e. all
+ // those of the form "prefix/[.]root.part" and call foreach with the complete path to each file
+ // pointed at by path, and the suffix of the path by extension. The . proceeds the root
+ // name if the defined constant HIDE_FILES is set. Always the first call is with the
+ // path "prefix/root.db" and extension "db". There will always be calls for
+ // "prefix/[.]root.idx" and "prefix/[.]root.bps". All other calls are for *tracks* and
+ // so this routine gives one a way to know all the tracks associated with a given DB.
+ // Return non-zero iff path could not be opened for any reason.
+
+int List_DB_Files(char *path, void foreach(char *path, char *extension));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _HITS_DB
diff --git a/src/cpp/DazAlnProvider.cpp b/src/cpp/DazAlnProvider.cpp
new file mode 100644
index 0000000..0300734
--- /dev/null
+++ b/src/cpp/DazAlnProvider.cpp
@@ -0,0 +1,452 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+#include <stdlib.h>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <cstdlib>
+#include <cstdio>
+#include <algorithm>
+#include <numeric>
+#include <set>
+#include "DazAlnProvider.hpp"
+
+#undef DEBUG
+
+using namespace PacBio::DagCon; // for IOException
+
+IOException::IOException(const char* desc) : desc_(desc) {}
+
+static char ToU[8] = { 'A', 'C', 'G', 'T', '.', '[', ']', '-' };
+static int BORDER = 10;
+
+// Should write my own, but for reasons of expediency, this borrows heavily
+// from LAshow.c
+DazAlnProvider::DazAlnProvider(const ProgramOpts& popts) :
+ popts_(popts),
+ novl_(0),
+ covl_(0) {
+
+ // Initialize the sequence DB accessor
+
+ // Open_DB takes a mutable char pointer, booo
+ char* path = new char[popts_.seqFile.length()+1];
+ std::strcpy(path,popts_.seqFile.c_str());
+ path[popts_.seqFile.length()] = '\0';
+ int status = Open_DB(path, &db_);
+ if (status < 0)
+ throw IOException("Failed to open DB");
+
+ delete[] path;
+
+ Trim_DB(&db_);
+
+ // Initialize the alignment file reader
+ input_ = fopen(popts_.alnFile.c_str(),"r");
+ if (input_ == NULL) {
+ std::string msg = "Open failed: " + popts_.alnFile;
+ throw IOException(msg.c_str());
+ }
+
+ if (fread(&novl_,sizeof(int64),1,input_) != 1)
+ throw IOException("Failed to read novl");
+
+ int tspace;
+ if (fread(&tspace,sizeof(int),1,input_) != 1)
+ throw IOException("Failed to read tspace");
+
+ int small;
+ if (tspace <= TRACE_XOVR) {
+ small = 1;
+ tbytes_ = sizeof(uint8);
+ } else {
+ small = 0;
+ tbytes_ = sizeof(uint16);
+ }
+
+ trg_ = new Target(db_, tspace, small);
+ targSeqBuf_ = New_Read_Buffer(&db_);
+
+ // Read in the first record
+ nextRecord(prevRec_);
+}
+
+DazAlnProvider::~DazAlnProvider() {
+ fclose(input_);
+ Close_DB(&db_);
+ free(targSeqBuf_-1);
+ delete trg_;
+}
+
+bool DazAlnProvider::nextTarget(std::vector<dagcon::Alignment> &dest) {
+ dest.clear();
+
+ // constructor initializes the first prevRec_ struct
+ trg_->firstRecord(prevRec_, popts_.properOvls);
+
+ std::set<int> tfilt = popts_.targets;
+ unsigned int filter = tfilt.size();
+ bool skipTarget = false;
+ while(covl_++ < novl_) {
+ Record rec;
+ nextRecord(rec);
+
+ if (rec.ovl.aread != trg_->id || covl_ == novl_) {
+ int tid = trg_->id + 1;
+ if (filter == 0 || tfilt.find(tid) != tfilt.end()) {
+ trg_->getAlignments(dest, popts_.maxHits, popts_.sortCov);
+ if (dest.size() < popts_.minCov) {
+ dest.clear();
+ skipTarget = true;
+ }
+ } else {
+ skipTarget = true;
+ }
+
+ if (skipTarget && covl_ != novl_) {
+ trg_->firstRecord(rec, popts_.properOvls);
+ skipTarget = false;
+ continue;
+ }
+ // This will initialize the target object on the next call
+ prevRec_ = rec;
+ break;
+ }
+ trg_->addRecord(rec, popts_.properOvls);
+ }
+
+ return covl_ != novl_;
+}
+
+bool DazAlnProvider::nextTarget(std::string& targSeq, std::vector<dagcon::Alignment>& dest) {
+
+ bool hasNext = nextTarget(dest);
+
+ targSeq.resize(trg_->length);
+ char* seq;
+ seq = Load_Subread(&db_, trg_->id, 0, trg_->length, targSeqBuf_, 0);
+
+ int i;
+ for (i = 0; i < trg_->length; i++)
+ targSeq[i] = ToU[(int)seq[i]];
+
+ return hasNext;
+}
+
+void DazAlnProvider::nextRecord(Record& rec) {
+ Read_Overlap(input_,&rec.ovl);
+ int tmax = ((int)1.2*rec.ovl.path.tlen) + 100;
+ rec.trace.resize(tmax,0);
+ rec.ovl.path.trace = (void *) &rec.trace.front();
+ Read_Trace(input_, &rec.ovl, tbytes_);
+}
+
+TargetHit::TargetHit() :
+ ovlScore(0.0f),
+ covScore(0.0f),
+ aread(-1),
+ bread(0),
+ flags(0) {}
+
+TargetHit::TargetHit(Record& rec) : TargetHit() {
+ aread = rec.ovl.aread;
+ bread = rec.ovl.bread;
+ flags = rec.ovl.flags;
+ add(rec);
+}
+
+std::ostream& operator<<(std::ostream& ostrm, TargetHit& hit) {
+ ostrm << "target: " << (hit.aread+1) << " query: " << (hit.bread+1);
+ ostrm << " flags: " << hit.flags << " tstart: " << hit.abeg();
+ ostrm << " tend: " << hit.aend() << " gaps: ";
+ ostrm << hit.records.size() << " ovlscore: " << hit.ovlScore;
+ ostrm << " covScore: " << hit.covScore << std::endl;
+ return ostrm;
+}
+
+bool TargetHit::belongs(Overlap& ovl) {
+ return aread == ovl.aread &&
+ bread == ovl.bread &&
+ flags == ovl.flags;
+}
+
+void TargetHit::add(Record& rec) {
+ if (records.size() == 0) {
+ records.push_back(std::move(rec));
+ } else {
+ Path prev = records.back().ovl.path;
+ int prevLen = prev.aepos - prev.abpos;
+
+ Path curr = rec.ovl.path;
+ int currLen = curr.aepos - curr.abpos;
+
+ if (curr.abpos > prev.aepos) {
+ records.push_back(std::move(rec));
+ } else if (currLen > prevLen) {
+ records.pop_back();
+ records.push_back(std::move(rec));
+ }
+ }
+}
+
+void TargetHit::computeOvlScore(bool proper) {
+ int ahlen = 0, bhlen = 0, diff = 0;
+
+ // XXX: penalize for gaps between records?
+ for (auto& rec: records) {
+ Path p = rec.ovl.path;
+ ahlen += p.aepos - p.abpos;
+ bhlen += p.bepos - p.bbpos;
+ diff += std::abs(ahlen - bhlen) + p.diffs;
+ }
+
+ ovlScore = (1 - diff/(float)ahlen) * ahlen;
+
+ if (proper) {
+ const Path& f = records.front().ovl.path;
+ const Path& b = records.back().ovl.path;
+ if (f.abpos != 0 && b.bbpos != 0)
+ ovlScore = 0.0f;
+ if (f.aepos != alen && b.bepos != blen)
+ ovlScore = 0.0f;
+ }
+}
+
+int TargetHit::abeg() {
+ return records.front().ovl.path.abpos;
+}
+
+int TargetHit::aend() {
+ return records.back().ovl.path.aepos;
+}
+
+// Simplify unit testing, don't burden with malloc'd
+// daligner structures.
+Target::Target(): needsFree_(false) { }
+
+Target::Target(HITS_DB& db, int tspace, int small) :
+ db_(db),
+ tspace_(tspace),
+ small_(small),
+ needsFree_(true) {
+
+ work_ = New_Work_Data();
+ abuffer_ = New_Read_Buffer(&db_);
+ bbuffer_ = New_Read_Buffer(&db_);
+}
+
+Target::~Target() {
+ if (needsFree_) {
+ free(abuffer_-1);
+ free(bbuffer_-1);
+ Free_Work_Data(work_);
+ }
+}
+
+void Target::firstRecord(Record& rec, bool proper) {
+ id = rec.ovl.aread;
+ length = db_.reads[id].rlen;
+
+ hits.clear();
+ coverage_.clear();
+
+ if (coverage_.size() < (unsigned int) length)
+ coverage_.resize(length);
+
+ auto beg = coverage_.begin();
+ std::for_each(beg, beg+length, [](unsigned int& x){x=0;});
+
+ TargetHit hit(rec);
+ hit.alen = length;
+ hit.blen = db_.reads[rec.ovl.bread].rlen;
+ hit.computeOvlScore(proper);
+ hits.push_back(std::move(hit));
+}
+
+void Target::addRecord(Record& rec, bool proper) {
+ // collapse into target hits, maintaining only the best hit from either
+ // forward or reverse.
+ if (hits.size() > 0) {
+ TargetHit& prev = hits.back();
+
+ // Add overlap to previous hit, instead of storing a new one
+ if (prev.belongs(rec.ovl)) {
+ prev.add(rec);
+ prev.computeOvlScore(proper);
+ return;
+ }
+ }
+
+ TargetHit hit(rec);
+ hit.alen = length;
+ hit.blen = db_.reads[rec.ovl.bread].rlen;
+ hit.computeOvlScore(proper);
+ hits.push_back(std::move(hit));
+}
+
+void Target::sortHits(bool sortCov) {
+ // sort descending based on ovl length x percent id
+ std::sort(hits.begin(), hits.end(), cmpHitOvlScore);
+
+ if (! sortCov) return;
+
+ // Coverage based scoring
+ for (auto& hit : hits) {
+ for (auto const& rec : hit.records) {
+ auto beg = coverage_.begin() + rec.ovl.path.abpos;
+ auto end = coverage_.begin() + rec.ovl.path.aepos;
+ std::for_each(beg, end, [](unsigned int& x){++x;});
+ hit.covScore = std::accumulate(beg, end, 0.0, invertedSum);
+ }
+ }
+
+ std::sort(hits.begin(), hits.end(), cmpHitCovScore);
+}
+
+void Target::getAlignments(std::vector<dagcon::Alignment> &alns, unsigned int max, bool sortCov) {
+ sortHits(sortCov);
+ auto hbeg = hits.begin();
+ auto hend = hits.size() > max ? hbeg + max : hits.end();
+ for (auto& hit = hbeg; hit != hend; ++hit) {
+ //std::cerr << *hit;
+ for (auto const& rec : hit->records) {
+ Overlap ovl = rec.ovl;
+ char* aseq, *bseq;
+ int amin, amax;
+ int bmin, bmax;
+ Alignment aln;
+ aln.path = &(ovl.path);
+ aln.alen = length;
+ aln.blen = hit->blen;
+ aln.flags = ovl.flags;
+
+ if (small_)
+ Decompress_TraceTo16(&ovl);
+
+ amin = ovl.path.abpos - BORDER;
+ if (amin < 0) amin = 0;
+ amax = ovl.path.aepos + BORDER;
+ if (amax > aln.alen) amax = aln.alen;
+ if (COMP(aln.flags)) {
+ bmin = (aln.blen-ovl.path.bepos) - BORDER;
+ if (bmin < 0) bmin = 0;
+ bmax = (aln.blen-ovl.path.bbpos) + BORDER;
+ if (bmax > aln.blen) bmax = aln.blen;
+ } else {
+ bmin = ovl.path.bbpos - BORDER;
+ if (bmin < 0) bmin = 0;
+ bmax = ovl.path.bepos + BORDER;
+ if (bmax > aln.blen) bmax = aln.blen;
+ }
+
+ // XXX: potential optimization, cache reads
+ aseq = Load_Subread(&db_, ovl.aread, amin, amax, abuffer_, 0);
+ bseq = Load_Subread(&db_, ovl.bread, bmin, bmax, bbuffer_, 0);
+
+ aln.aseq = aseq - amin;
+ if (COMP(aln.flags)) {
+ Complement_Seq(bseq,bmax-bmin);
+ aln.bseq = bseq - (aln.blen - bmax);
+ } else
+ aln.bseq = bseq - bmin;
+
+ Compute_Trace_PTS(&aln, work_, tspace_);
+
+ // initialize the dagcon alignment class
+ dagcon::Alignment dest;
+ std::stringstream tid, qid;
+ tid << (ovl.aread+1);
+ qid << (ovl.bread+1);
+ dest.id = tid.str();
+ dest.sid = qid.str();
+ dest.tlen = aln.alen;
+
+ dest.start = ovl.path.abpos+1;
+ dest.end = ovl.path.aepos+1;
+
+ decodeAlignment(&aln, dest);
+ alns.push_back(dest);
+ }
+ }
+}
+
+bool cmpHitOvlScore(const TargetHit& l, const TargetHit& r) {
+ return l.ovlScore > r.ovlScore;
+}
+
+bool cmpHitCovScore(const TargetHit& l, const TargetHit& r) {
+ return l.covScore > r.covScore;
+}
+
+float invertedSum(float x, unsigned int y) {
+ return x + 1/(float)y;
+}
+
+void decodeAlignment(Alignment* src, dagcon::Alignment& dest) {
+ int i, j, tlen, c, p;
+ char* a, *b; // pointers to the sequence
+ int* trace = (int *) src->path->trace;
+ a = src->aseq;
+ b = src->bseq;
+ tlen = src->path->tlen;
+ i = src->path->abpos;
+ j = src->path->bbpos;
+ // XXX: slow. pre-allocate string length or work directly with
+ // encoded strings.
+ for (c = 0; c < tlen; c++) {
+ if ((p = trace[c]) < 0) {
+ p = -p;
+ while (i != p) {
+ dest.tstr += ToU[(int)a[i++]];
+ dest.qstr += ToU[(int)b[j++]];
+ }
+ dest.tstr += ToU[7];
+ dest.qstr += ToU[(int)b[j++]];
+ } else {
+ while (j != p) {
+ dest.tstr += ToU[(int)a[i++]];
+ dest.qstr += ToU[(int)b[j++]];
+ }
+ dest.tstr += ToU[(int)a[i++]];
+ dest.qstr += ToU[7];
+ }
+ }
+ p = src->path->aepos;
+ while(i <= p) {
+ dest.tstr += ToU[(int)a[i++]];
+ dest.qstr += ToU[(int)b[j++]];
+ }
+}
diff --git a/src/cpp/DazAlnProvider.hpp b/src/cpp/DazAlnProvider.hpp
new file mode 100644
index 0000000..a059c51
--- /dev/null
+++ b/src/cpp/DazAlnProvider.hpp
@@ -0,0 +1,222 @@
+
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+
+#ifndef __GCON_DAZ_ALN_PROVIDER__
+#define __GCON_DAZ_ALN_PROVIDER__
+
+#include <stdio.h>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <atomic>
+#include "ProgramOpts.hpp"
+#include "Alignment.hpp"
+#include "AlnProvider.hpp"
+
+// Dazzler headers
+#include "DB.h"
+#include "align.h"
+
+// Represents one record from the LAS file, essentially a thin container for
+// a dazzler overlap so we can manage things on the stack.
+struct Record {
+ Overlap ovl;
+ std::vector<uint16> trace;
+
+ Record() {}
+ ~Record() {}
+
+ Record(Record &&o) noexcept :
+ ovl(std::move(o.ovl)),
+ trace(std::move(o.trace)) {
+ o.ovl.path.trace = NULL;
+ }
+
+ Record& operator=(Record &&o) noexcept {
+ ovl = std::move(o.ovl);
+ trace = std::move(o.trace);
+ o.ovl.path.trace = NULL;
+ return *this;
+ }
+
+ Record(const Record& o) : ovl(o.ovl), trace(o.trace) {
+ ovl.path.trace = (void *) &trace.front();
+ }
+
+ Record& operator=(const Record& o) {
+ ovl = o.ovl;
+ trace = o.trace;
+ ovl.path.trace = (void *) &trace.front();
+ return *this;
+ }
+
+};
+
+// Holds information for all the a,b overlaps in a particular direction,
+// either forward or reverse. Overlaps for a particular a,b,strand
+// combination may come in as multiple overlaps. This class allows us to
+// handle them as a unit.
+class TargetHit {
+public:
+ TargetHit();
+ TargetHit(Record& rec);
+
+ // Returns true if given overlap belongs to this hit, otherwise false
+ bool belongs(Overlap& ovl);
+
+ // Adds the next overlap to this hit set (a,b,strand combination)
+ void add(Record& rec);
+
+ void computeOvlScore(bool proper=false);
+
+ int abeg();
+ int aend();
+
+ int alen;
+ int blen;
+
+ // Tracks the overlap score, (pct id) x (a-read aln len)
+ float ovlScore;
+
+ // Tracks the coverage score sum(1/depth for aln len)
+ float covScore;
+
+ // information promoted from the overlap struct
+ int aread;
+ int bread;
+ uint32 flags;
+
+ // container holding records related to this hit
+ std::vector<Record> records;
+};
+
+std::ostream& operator<<(std::ostream& ostrm, TargetHit& hit);
+
+// Re-usable class container for a target and its overlaps
+class Target {
+public:
+ Target();
+ Target(HITS_DB& db, int tspace, int small);
+ ~Target();
+
+ // Initializes this target based on the given record, possibly scoring as
+ // a 'proper' overlap (more stringent).
+ void firstRecord(Record& rec, bool proper=false);
+
+ // Adds the next overlap record to this target, possibly scoring as
+ // a 'proper' overlap (more stringent).
+ void addRecord(Record& rec, bool proper=false);
+
+ // Sorts overlaps based on a two-phase scoring system
+ void sortHits(bool sortCov);
+
+ void getAlignments(std::vector<dagcon::Alignment> &alns, unsigned int max, bool sortCov);
+
+ // ID of the target
+ int id;
+
+ // Length of the target
+ int length;
+
+ std::vector<TargetHit> hits;
+
+private:
+ HITS_DB db_;
+ char* abuffer_, *bbuffer_;
+ Work_Data* work_;
+ int tspace_, small_;
+ std::vector<unsigned int> coverage_;
+ bool needsFree_; // track if we need to free memory on destruct
+};
+
+///
+/// Provides sets of alignments for a given target sequence from a daligner
+/// output file.
+///
+class DazAlnProvider : public AlnProvider {
+public:
+ /// Constructs a new alignment provider. Checks the format of the file and
+ /// throws an exception if it's malformed.
+ /// \param popts options passed to the program on the command line
+ DazAlnProvider(const ProgramOpts& popts);
+
+ /// Cleans up some stuff.
+ ~DazAlnProvider();
+
+ /// Gets the set of alignments for the next target and puts them into the
+ /// given vector. Note this function will clear the contents of the vector
+ /// prior to adding the next set of alignments.
+ /// In dazzler parlance, this will correct the A reads as targets.
+ /// \param dest reference to a vector to hold the alignments.
+ /// \return True if there are more targets, otherwise false.
+ bool nextTarget(std::vector<dagcon::Alignment>& dest);
+
+ bool nextTarget(std::string& targSeq, std::vector<dagcon::Alignment>& dest);
+
+private:
+ // maintains the previous record
+ Record prevRec_;
+ Target* trg_;
+ const ProgramOpts popts_;
+
+ // Dazzler-related data
+ HITS_DB db_;
+ int64 novl_, covl_;
+ int tbytes_;
+ FILE* input_;
+ char* targSeqBuf_;
+
+ /// Obtain data from the las file for the next overlap record
+ void nextRecord(Record& rec);
+
+};
+
+
+/// Compares the hits based on (percent id) x (query alignment length)
+bool cmpHitOvlScore(const TargetHit& l, const TargetHit& r);
+
+/// Compares based on coverage score
+bool cmpHitCovScore(const TargetHit& l, const TargetHit& r);
+
+float invertedSum(float x, unsigned int y);
+
+/// Convert dazzler alignment into a dagcon alignment. Eventually, we
+/// should update the alignment graph to process the dazzler alignment
+/// directly, but this will be useful for debugging purposes.
+void decodeAlignment(Alignment* src, dagcon::Alignment& dest);
+
+#endif //__GCON_DAZ_ALN_PROVIDER__
diff --git a/src/cpp/Makefile b/src/cpp/Makefile
new file mode 100644
index 0000000..f1e7099
--- /dev/null
+++ b/src/cpp/Makefile
@@ -0,0 +1,38 @@
+include pbi.mk
+include boost.mk
+
+COMMON_OBJECTS := Alignment.o AlnGraphBoost.o
+PBDAGCON_OBJECTS := BlasrM5AlnProvider.o main.o SimpleAligner.o
+DAZCON_OBJECTS := DB.o align.o DazAlnProvider.o dazcon.o
+
+CXXFLAGS = -O3 -std=c++11 -Wall -Wuninitialized -pedantic -I third-party \
+ -I $(BOOST_HEADERS)
+
+CFLAGS = -O3 -Wall -Wextra -fno-strict-aliasing
+
+INCDIRS := -I$(PBDATA) -I$(BLASR) $(EXTRA_INCDIRS)
+LDFLAGS := -L$(PBDATA) -L$(BLASR) $(EXTRA_LDFLAGS)
+
+all: pbdagcon
+
+dazcon: LDLIBS = -lpthread
+dazcon: $(COMMON_OBJECTS) $(DAZCON_OBJECTS)
+ $(CXX) -Wl,--no-as-needed -o $@ $^ $(LDLIBS)
+
+pbdagcon: LDLIBS = -lpbdata -lblasr -lpthread $(EXTRA_LDLIBS)
+pbdagcon: CXXFLAGS += $(INCDIRS)
+pbdagcon: $(COMMON_OBJECTS) $(PBDAGCON_OBJECTS)
+ $(CXX) -Wl,--no-as-needed $(LIBDIRS) -o $@ $^ $(LDFLAGS) $(LDLIBS)
+
+$(COMMON_OBJECTS): $(BOOST_HEADERS)
+
+$(BOOST_HEADERS):
+ cd third-party && $(GET_BOOST)
+
+clean:
+ $(RM) *.d
+ $(RM) *.o
+ $(RM) pbdagcon
+ $(RM) dazcon
+
+.PHONY: all clean
diff --git a/src/cpp/ProgramOpts.hpp b/src/cpp/ProgramOpts.hpp
new file mode 100644
index 0000000..45242ad
--- /dev/null
+++ b/src/cpp/ProgramOpts.hpp
@@ -0,0 +1,39 @@
+#ifndef __GCON_PROGRAMOPTS__
+#define __GCON_PROGRAMOPTS__
+
+#include <string>
+#include <set>
+
+// Normalizing *slightly* difference cmd line interfaces between pbdagcon and
+// dazcon. May unify someday ...
+struct ProgramOpts {
+ // Common to both pbdagcon and dazcon
+ /// Minimum alignment coverage for consensus
+ size_t minCov;
+ /// Minimum consensus length to output
+ size_t minLen;
+ /// Amount to trim alignments by on either side.
+ unsigned int trim;
+ /// Number of threads to use
+ int threads;
+
+ // Specific to pbdagcon
+ bool align;
+ std::string input;
+
+ // Specific to dazcon
+ /// Path to the alignment file
+ std::string alnFile;
+ /// Path to the sequence file
+ std::string seqFile;
+ /// Maximimum number of hits to include in correction
+ unsigned int maxHits;
+ /// Sort hits by coverage score
+ bool sortCov;
+ /// Use only proper overlaps for correction
+ bool properOvls;
+ /// Limit correction to these targets
+ std::set<int> targets;
+};
+
+#endif // __GCON_PROGRAMOPTS__
diff --git a/src/cpp/SimpleAligner.cpp b/src/cpp/SimpleAligner.cpp
new file mode 100644
index 0000000..6086187
--- /dev/null
+++ b/src/cpp/SimpleAligner.cpp
@@ -0,0 +1,103 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+
+#include <vector>
+#include <stdint.h>
+#include <cstring>
+#include <string>
+#include <cassert>
+#include <algorithm>
+#include "Alignment.hpp"
+#include "SimpleAligner.hpp"
+
+SimpleAligner::SimpleAligner() {
+ config_.indelRate = 0.3;
+ config_.indel = 5;
+ config_.match = 0;
+ config_.sdpIndel = 5;
+ config_.sdpIns = 5;
+ config_.sdpDel = 10;
+ config_.kmer = 11;
+ config_.bandSize = 10;
+ tupleMetrics_.Initialize(config_.kmer);
+ distScoreFn_.del = config_.indel;
+ distScoreFn_.ins = 4;
+ distScoreFn_.InitializeScoreMatrix(SMRTDistanceMatrix);
+}
+
+void SimpleAligner::align(dagcon::Alignment& aln) {
+ // This alignment type defined in blasr code base
+ blasr::Alignment initialAln, refinedAln;
+ FASTQSequence query;
+ query.seq = (Nucleotide*)aln.qstr.c_str();
+ query.length = aln.qstr.length();
+
+ DNASequence target;
+ target.seq = (Nucleotide*)aln.tstr.c_str();
+ target.length = aln.tstr.length();
+ SDPAlign(query, target, distScoreFn_, tupleMetrics_.tupleSize,
+ config_.sdpIndel, config_.sdpIndel, config_.indelRate*2,
+ initialAln, Local);
+
+ GuidedAlign(query, target, initialAln, distScoreFn_,
+ config_.bandSize, refinedAln);
+
+ std::string queryStr, alignStr, targetStr;
+
+ //StickPrintAlignment(initialAln, query, target, std::cout);
+ //StickPrintAlignment(refinedAln, query, target, std::cout);
+
+ CreateAlignmentStrings(refinedAln, query.seq, target.seq,
+ targetStr, alignStr, queryStr, query.length, target.length);
+
+ // alignment coordinates may change, update alignment object
+ aln.start += refinedAln.GenomicTBegin();
+ aln.end = aln.start + refinedAln.GenomicTEnd();
+
+ if (aln.strand == '-') {
+ aln.start = aln.tlen - aln.end;
+ aln.qstr = revComp(queryStr);
+ aln.tstr = revComp(targetStr);
+ } else {
+ aln.qstr = queryStr;
+ aln.tstr = targetStr;
+ }
+ aln.start++;
+}
+
+void SimpleAligner::operator() (dagcon::Alignment& aln) {
+ align(aln);
+}
diff --git a/src/cpp/SimpleAligner.hpp b/src/cpp/SimpleAligner.hpp
new file mode 100644
index 0000000..209b0dc
--- /dev/null
+++ b/src/cpp/SimpleAligner.hpp
@@ -0,0 +1,73 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+
+#ifndef __GCON_SIMPLE_ALIGNER__
+#define __GCON_SIMPLE_ALIGNER__
+#include "Types.h"
+#include "Enumerations.h"
+#include "DNASequence.hpp"
+#include "datastructures/alignment/Alignment.hpp"
+#include "algorithms/alignment/AlignmentUtils.hpp"
+#include "algorithms/alignment/SDPAlign.hpp"
+#include "algorithms/alignment/GuidedAlign.hpp"
+#include "format/StickAlignmentPrinter.hpp"
+#include "FASTQSequence.hpp"
+
+namespace Aligner {
+struct Config {
+ float indelRate;
+ int indel;
+ int match;
+ int sdpIndel;
+ int sdpIns;
+ int sdpDel;
+ int kmer;
+ int bandSize;
+};
+}
+
+class SimpleAligner {
+public:
+ SimpleAligner();
+ void align(dagcon::Alignment& aln);
+ void operator() (dagcon::Alignment& aln);
+private:
+ Aligner::Config config_;
+ TupleMetrics tupleMetrics_;
+ DistanceMatrixScoreFunction<DNASequence, FASTQSequence> distScoreFn_;
+};
+
+#endif // __GCON_SIMPLE_ALIGNER__
diff --git a/src/cpp/align.c b/src/cpp/align.c
new file mode 100644
index 0000000..b60c6c0
--- /dev/null
+++ b/src/cpp/align.c
@@ -0,0 +1,3805 @@
+/************************************************************************************\
+* *
+* Copyright (c) 2014, Dr. Eugene W. Myers (EWM). 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. *
+* *
+* · The name of EWM may not be used to endorse or promote products derived from *
+* this software without specific prior written permission. *
+* *
+* THIS SOFTWARE IS PROVIDED BY EWM ”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 EWM 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. *
+* *
+* For any issues regarding this software and its use, contact EWM at: *
+* *
+* Eugene W. Myers Jr. *
+* Bautzner Str. 122e *
+* 01099 Dresden *
+* GERMANY *
+* Email: gene.myers at gmail.com *
+* *
+\************************************************************************************/
+
+/*******************************************************************************************
+ *
+ * Fast alignment discovery and trace generation along with utilites for displaying alignments
+ * Based on previously unpublished ideas from 2005, subsequently refined in 2013-14. Basic
+ * idea is to keep a dynamically selected interval of the f.r. waves from my 1986 O(nd) paper.
+ * A recent cool idea is to not record all the details of an alignment while discovering it
+ * but simply record trace points through which the optimal alignment passes every 100bp,
+ * allowing rapid recomputation of the alignment details between trace points.
+ *
+ * Author : Gene Myers
+ * First : June 2013
+ * Current: June 1, 2014
+ *
+ ********************************************************************************************/
+
+// align1: Derived from the original BOA aligner
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+#include <limits.h>
+
+#include "DB.h"
+#include "align.h"
+
+#define DELTAS
+
+#undef DEBUG_PASSES // Show forward / backward extension termini for Local_Alignment
+#undef DEBUG_POINTS // Show trace points
+#undef DEBUG_WAVE // Show waves of Local_Alignment
+#undef SHOW_MATCH_WAVE // For waves of Local_Alignment also show # of matches
+#undef SHOW_TRAIL // Show trace at the end of forward and reverse passes
+#undef SHOW_TPS // Show trace points as they are encountered in a wave
+
+#undef DEBUG_EXTEND // Show waves of Extend_Until_Overlap
+
+#undef DEBUG_ALIGN // Show division points of Compute_Trace
+#undef DEBUG_SCRIPT // Show trace additions for Compute_Trace
+#undef DEBUG_AWAVE // Show F/R waves of Compute_Trace
+#define SMALL_BIT 100
+
+#undef SHOW_TRACE // Show full trace for Print_Alignment
+
+#undef WAVE_STATS
+
+
+/****************************************************************************************\
+* *
+* Working Storage Abstraction *
+* *
+\****************************************************************************************/
+
+typedef struct // Hidden from the user, working space for each thread
+ { int vecmax;
+ void *vector;
+ int celmax;
+ void *cells;
+ int pntmax;
+ void *points;
+ int tramax;
+ void *trace;
+ } _Work_Data;
+
+Work_Data *New_Work_Data()
+{ _Work_Data *work;
+
+ work = (_Work_Data *) Malloc(sizeof(_Work_Data),"Allocating work data block");
+ if (work == NULL)
+ exit (1);
+ work->vecmax = 0;
+ work->vector = NULL;
+ work->pntmax = 0;
+ work->points = NULL;
+ work->tramax = 0;
+ work->trace = NULL;
+ work->celmax = 0;
+ work->cells = NULL;
+ return ((Work_Data *) work);
+}
+
+static void enlarge_vector(_Work_Data *work, int newmax)
+{ work->vecmax = ((int) (newmax*1.2)) + 10000;
+ work->vector = Realloc(work->vector,work->vecmax,"Enlarging DP vector");
+ if (work->vector == NULL)
+ exit (1);
+}
+
+static void enlarge_points(_Work_Data *work, int newmax)
+{ work->pntmax = ((int) (newmax*1.2)) + 10000;
+ work->points = Realloc(work->points,work->pntmax,"Enlarging point vector");
+ if (work->points == NULL)
+ exit (1);
+}
+
+static void enlarge_trace(_Work_Data *work, int newmax)
+{ work->tramax = ((int) (newmax*1.2)) + 10000;
+ work->trace = Realloc(work->trace,work->tramax,"Enlarging trace vector");
+ if (work->trace == NULL)
+ exit (1);
+}
+
+void Free_Work_Data(Work_Data *ework)
+{ _Work_Data *work = (_Work_Data *) ework;
+ if (work->vector != NULL)
+ free(work->vector);
+ if (work->cells != NULL)
+ free(work->cells);
+ if (work->trace != NULL)
+ free(work->trace);
+ if (work->points != NULL)
+ free(work->points);
+ free(work);
+}
+
+
+/****************************************************************************************\
+* *
+* ADAPTIVE PATH FINDING *
+* *
+\****************************************************************************************/
+
+ // Absolute/Fixed Parameters
+
+#define BVEC uint64 // Can be uint32 if PATH_LEN <= 32
+
+#define TRIM_LEN 15 // Report as the tip, the last wave maximum for which the last
+ // 2*TRIM_LEN edits are prefix-positive at rate ave_corr*f(bias)
+ // (max value is 20)
+
+#define PATH_LEN 60 // Follow the last PATH_LEN columns/edges (max value is 63)
+
+ // Derivative fixed parameters
+
+#define PATH_TOP 0x1000000000000000ll // Must be 1 << PATH_LEN
+#define PATH_INT 0x0fffffffffffffffll // Must be PATH_TOP-1
+#define TRIM_MASK 0x7fff // Must be (1 << TRIM_LEN) - 1
+#define TRIM_MLAG 200 // How far can last trim point be behind best point
+#define WAVE_LAG 30 // How far can worst point be behind the best point
+
+static double Bias_Factor[10] = { .690, .690, .690, .690, .780,
+ .850, .900, .933, .966, 1.000 };
+
+ // Micro-Sat Band Parameters
+
+#define MICRO_SAT 20
+
+static int Sat_Width[MICRO_SAT+1] =
+ { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 };
+
+#define SAT_LOW .75
+#define SAT_HGH 1.25
+
+ // Adjustable paramters
+
+typedef struct
+ { double ave_corr;
+ int trace_space;
+ float freq[4];
+ int ave_path;
+ int16 *score;
+ int16 *table;
+ } _Align_Spec;
+
+/* Fill in bit table: TABLE[x] = 1 iff the alignment modeled by x (1 = match, 0 = mismatch)
+ has a non-negative score for every suffix of the alignment under the scoring scheme
+ where match = MATCH and mismatch = -1. MATCH is set so that an alignment with TRIM_PCT
+ matches has zero score ( (1-TRIM_PCT) / TRIM_PCT ). */
+
+#define FRACTION 1000 // Implicit fractional part of scores, i.e. score = x/FRACTION
+
+typedef struct
+ { int mscore;
+ int dscore;
+ int16 *table;
+ int16 *score;
+ } Table_Bits;
+
+static void set_table(int bit, int prefix, int score, int max, Table_Bits *parms)
+{ if (bit >= TRIM_LEN)
+ { parms->table[prefix] = (int16) (score-max);
+ parms->score[prefix] = (int16) score;
+ }
+ else
+ { if (score > max)
+ max = score;
+ set_table(bit+1,(prefix<<1),score - parms->dscore,max,parms);
+ set_table(bit+1,(prefix<<1) | 1,score + parms->mscore,max,parms);
+ }
+}
+
+/* Create an alignment specification record including path tip tables & values */
+
+Align_Spec *New_Align_Spec(double ave_corr, int trace_space, float *freq)
+{ _Align_Spec *spec;
+ Table_Bits parms;
+ double match;
+ int bias;
+
+ spec = (_Align_Spec *) Malloc(sizeof(_Align_Spec),"Allocating alignment specification");
+ if (spec == NULL)
+ exit (1);
+
+ spec->ave_corr = ave_corr;
+ spec->trace_space = trace_space;
+ spec->freq[0] = freq[0];
+ spec->freq[1] = freq[1];
+ spec->freq[2] = freq[2];
+ spec->freq[3] = freq[3];
+
+ match = freq[0] + freq[3];
+ if (match > .5)
+ match = 1.-match;
+ bias = (int) ((match+.025)*20.-1.);
+ if (match < .2)
+ { fprintf(stderr,"Warning: Base bias worse than 80/20%% !\n");
+ bias = 3;
+ }
+
+ spec->ave_path = (int) (PATH_LEN * (1. - Bias_Factor[bias] * (1. - ave_corr)));
+ parms.mscore = (int) (FRACTION * Bias_Factor[bias] * (1. - ave_corr));
+ parms.dscore = FRACTION - parms.mscore;
+
+ parms.score = (int16 *) Malloc(sizeof(int16)*(TRIM_MASK+1)*2,"Allocating trim table");
+ if (parms.score == NULL)
+ exit (1);
+ parms.table = parms.score + (TRIM_MASK+1);
+
+ set_table(0,0,0,0,&parms);
+
+ spec->table = parms.table;
+ spec->score = parms.score;
+
+ return ((Align_Spec *) spec);
+}
+
+void Free_Align_Spec(Align_Spec *espec)
+{ _Align_Spec *spec = (_Align_Spec *) espec;
+ free(spec->score);
+ free(spec);
+}
+
+double Average_Correlation(Align_Spec *espec)
+{ return (((_Align_Spec *) espec)->ave_corr); }
+
+int Trace_Spacing(Align_Spec *espec)
+{ return (((_Align_Spec *) espec)->trace_space); }
+
+float *Base_Frequencies(Align_Spec *espec)
+{ return (((_Align_Spec *) espec)->freq); }
+
+
+/****************************************************************************************\
+* *
+* LOCAL ALIGNMENT FINDER: forward_/reverse_wave and Local_Alignment *
+* *
+\****************************************************************************************/
+
+
+#ifdef WAVE_STATS
+
+static int64 MAX, TOT, NWV;
+static int64 RESTARTS;
+
+void Init_Stats()
+{ MAX = TOT = NWV = 0;
+ RESTARTS = 0;
+}
+
+void Print_Stats()
+{ printf("\nMax = %lld Ave = %.1f # = %lld\n",MAX,(1.*TOT)/NWV,NWV);
+ printf("\nRestarts = %lld\n",RESTARTS);
+}
+
+#endif
+
+
+#ifdef DEBUG_WAVE
+
+static void print_wave(int *V, int *M, int low, int hgh, int besta)
+{ int k, bestk;
+
+ (void) M;
+ printf(" [%6d,%6d]: ",low,hgh);
+ for (k = low; k <= hgh; k++)
+ { if (besta == V[k])
+ bestk = k;
+ // printf(" %3d",(V[k]+k)/2);
+ printf(" %3d",besta-V[k]);
+ }
+ printf(" : %d (%d,%d)\n",besta,(besta+bestk)/2,(besta-bestk)/2);
+#ifdef SHOW_MATCH_WAVE
+ printf(" ");
+ for (k = low; k <= hgh; k++)
+ printf(" %3d",M[k]);
+ printf("\n");
+#endif
+ fflush(stdout);
+}
+
+#endif
+
+/* At each furthest reaching point, keep a-coordinate of point (V), bitvector
+ recording the last TRIM_LEN columns of the implied alignment (T), and the
+ # of matches (1-bits) in the bitvector (M). */
+
+typedef struct
+ { int ptr;
+ int diag;
+#ifdef DELTAS
+ int diff;
+#endif
+ int mark;
+ } Pebble;
+
+// This (and reverse) take the bulk of the processing time in daligner
+// Isn't easily vectorizable given all the 'while/break' looping
+static int forward_wave(_Work_Data *work, _Align_Spec *spec,
+ Alignment *align, Path *bpath,
+ int mind, int maxd, int mida)
+{ char *aseq = align->aseq;
+ char *bseq = align->bseq;
+ Path *apath = align->path;
+
+ int hgh, low, dif;
+ int minp, maxp;
+ int *V, *M;
+ BVEC *T;
+
+ int *HA, *HB;
+ int *NA, *NB;
+ Pebble *cells;
+ int avail, cmax, boff;
+
+ int TRACE_SPACE = spec->trace_space;
+ int PATH_AVE = spec->ave_path;
+ int16 *SCORE = spec->score;
+ int16 *TABLE = spec->table;
+
+ int besta, besty;
+ int trima, trimy, trimd;
+ int trimha, trimhb;
+ int morea, morey, mored;
+ int moreha, morehb;
+ int more, morem, lasta;
+ int aclip, bclip;
+
+ { int alen = align->alen + 1;
+ int blen = align->blen + 1;
+ int tlen = alen + blen + 1;
+
+ V = ((int *) work->vector) + blen;
+ M = V + tlen;
+ HA = M + tlen;
+ HB = HA + tlen;
+ NA = HB + tlen;
+ NB = NA + tlen;
+ T = ((BVEC *) (NB + alen)) + blen;
+
+ cells = (Pebble *) (work->cells);
+ cmax = work->celmax;
+ avail = 0;
+
+ if (COMP(align->flags))
+ boff = align->blen % TRACE_SPACE;
+ else
+ boff = 0;
+ }
+
+ /* Compute 0-wave starting from mid-line */
+
+ hgh = maxd;
+ low = mind;
+ if (aseq == bseq)
+ { if (low < 0)
+ { int big = -low;
+ int sml = -hgh;
+
+ if (big <= MICRO_SAT)
+ minp = low - Sat_Width[big];
+ else
+ minp = -SAT_HGH*big;
+ if (sml <= MICRO_SAT)
+ maxp = hgh + Sat_Width[sml];
+ else
+ maxp = -SAT_LOW*sml;
+ }
+ else
+ { if (low <= MICRO_SAT)
+ minp = low - Sat_Width[low];
+ else
+ minp = SAT_LOW*low;
+ if (hgh <= MICRO_SAT)
+ maxp = hgh + Sat_Width[hgh];
+ else
+ maxp = SAT_HGH*hgh;
+ }
+ }
+ else
+ { minp = -INT32_MAX;
+ maxp = INT32_MAX;
+ }
+ dif = 0;
+
+ more = 1;
+ aclip = INT32_MAX;
+ bclip = -INT32_MAX;
+
+ besta = trima = morea = lasta = mida;
+ besty = trimy = morey = (mida-hgh) >> 1;
+ trimd = mored = 0;
+ trimha = moreha = 0;
+ trimhb = morehb = 1;
+ morem = -1;
+
+ { int k;
+ char *a;
+
+ a = aseq + hgh;
+ for (k = hgh; k >= low; k--)
+ { int y, c, d;
+ int ha, hb;
+ int na, nb;
+ Pebble *pb;
+
+ y = (mida-k) >> 1;
+
+ if (avail >= cmax-1)
+ { cmax = ((int) (avail*1.2)) + 10000;
+ cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+ if (cells == NULL)
+ exit (1);
+ }
+
+ na = ((y+k)/TRACE_SPACE)*TRACE_SPACE;
+#ifdef SHOW_TPS
+ printf(" A %d: %d,%d,0,%d\n",avail,-1,k,na); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = -1;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = 0;
+#endif
+ pb->mark = na;
+ ha = avail++;
+ na += TRACE_SPACE;
+
+ nb = ((y+(TRACE_SPACE-boff))/TRACE_SPACE-1)*TRACE_SPACE+boff;
+#ifdef SHOW_TPS
+ printf(" B %d: %d,%d,0,%d\n",avail,-1,k,nb); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = -1;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = 0;
+#endif
+ pb->mark = nb;
+ hb = avail++;
+ nb += TRACE_SPACE;
+
+ while (1)
+ { c = bseq[y];
+ if (c == 4)
+ { more = 0;
+ if (bclip < k)
+ bclip = k;
+ break;
+ }
+ d = a[y];
+ if (c != d)
+ { if (d == 4)
+ { more = 0;
+ aclip = k;
+ }
+ break;
+ }
+ y += 1;
+ }
+ c = (y << 1) + k;
+
+ while (y+k >= na)
+ { if (avail >= cmax)
+ { cmax = ((int) (avail*1.2)) + 10000;
+ cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+ if (cells == NULL)
+ exit (1);
+ }
+#ifdef SHOW_TPS
+ printf(" A %d: %d,%d,0,%d\n",avail,ha,k,na); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = ha;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = 0;
+#endif
+ pb->mark = na;
+ ha = avail++;
+ na += TRACE_SPACE;
+ }
+ while (y >= nb)
+ { if (avail >= cmax)
+ { cmax = ((int) (avail*1.2)) + 10000;
+ cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+ if (cells == NULL)
+ exit (1);
+ }
+#ifdef SHOW_TPS
+ printf(" B %d: %d,%d,0,%d\n",avail,hb,k,nb); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = hb;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = 0;
+#endif
+ pb->mark = nb;
+ hb = avail++;
+ nb += TRACE_SPACE;
+ }
+
+ if (c > besta)
+ { besta = trima = lasta = c;
+ besty = trimy = y;
+ trimha = ha;
+ trimhb = hb;
+ }
+
+ V[k] = c;
+ T[k] = PATH_INT;
+ M[k] = PATH_LEN;
+ HA[k] = ha;
+ HB[k] = hb;
+ NA[k] = na;
+ NB[k] = nb;
+
+ a -= 1;
+ }
+ }
+
+ if (more == 0)
+ { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+ more = 1;
+ if (hgh >= aclip)
+ { hgh = aclip-1;
+ if (morem <= M[aclip])
+ { morem = M[aclip];
+ morea = V[aclip];
+ morey = (morea - aclip)/2;
+ moreha = HA[aclip];
+ morehb = HB[aclip];
+ }
+ }
+ if (low <= bclip)
+ { low = bclip+1;
+ if (morem <= M[bclip])
+ { morem = M[bclip];
+ morea = V[bclip];
+ morey = (morea - bclip)/2;
+ moreha = HA[bclip];
+ morehb = HB[bclip];
+ }
+ }
+ aclip = INT32_MAX;
+ bclip = -INT32_MAX;
+ }
+
+#ifdef DEBUG_WAVE
+ printf("\nFORWARD WAVE:\n");
+ print_wave(V,M,low,hgh,besta);
+#endif
+
+ /* Compute successive waves until no furthest reaching points remain */
+
+ while (more && lasta >= besta - TRIM_MLAG)
+ { int k, n;
+ int ua, ub;
+ BVEC t;
+ int am, ac, ap;
+ char *a;
+
+ if (low > minp)
+ { low -= 1;
+ NA[low] = NA[low+1];
+ NB[low] = NB[low+1];
+ V[low] = -1;
+ }
+ if (hgh < maxp)
+ { hgh += 1;
+ NA[hgh] = NA[hgh-1];
+ NB[hgh] = NB[hgh-1];
+ V[hgh] = am = -1;
+ }
+ else
+ am = V[hgh];
+ dif += 1;
+
+ ac = V[hgh+1] = V[low-1] = -1;
+ a = aseq + hgh;
+ t = PATH_INT;
+ n = PATH_LEN;
+ ua = ub = -1;
+ for (k = hgh; k >= low; k--)
+ { int y, m;
+ int ha, hb;
+ int c, d;
+ BVEC b;
+ Pebble *pb;
+
+ ap = ac;
+ ac = am;
+ am = V[d = k-1];
+
+ if (ac < am)
+ if (am < ap)
+ { c = ap+1;
+ m = n;
+ b = t;
+ ha = ua;
+ hb = ub;
+ }
+ else
+ { c = am+1;
+ m = M[d];
+ b = T[d];
+ ha = HA[d];
+ hb = HB[d];
+ }
+ else
+ if (ac < ap)
+ { c = ap+1;
+ m = n;
+ b = t;
+ ha = ua;
+ hb = ub;
+ }
+ else
+ { c = ac+2;
+ m = M[k];
+ b = T[k];
+ ha = HA[k];
+ hb = HB[k];
+ }
+
+ if ((b & PATH_TOP) != 0)
+ m -= 1;
+ b <<= 1;
+
+ y = (c-k) >> 1;
+ while (1)
+ { c = bseq[y];
+ if (c == 4)
+ { more = 0;
+ if (bclip < k)
+ bclip = k;
+ break;
+ }
+ d = a[y];
+ if (c != d)
+ { if (d == 4)
+ { more = 0;
+ aclip = k;
+ }
+ break;
+ }
+ y += 1;
+ if ((b & PATH_TOP) == 0)
+ m += 1;
+ b = (b << 1) | 1;
+ }
+ c = (y << 1) + k;
+
+ while (y+k >= NA[k])
+ { if (cells[ha].mark < NA[k])
+ { if (avail >= cmax)
+ { cmax = ((int) (avail*1.2)) + 10000;
+ cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+ "Reallocating trace cells");
+ if (cells == NULL)
+ exit (1);
+ }
+#ifdef SHOW_TPS
+ printf(" A %d: %d,%d,%d,%d\n",avail,ha,k,dif,NA[k]); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = ha;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = dif;
+#endif
+ pb->mark = NA[k];
+ ha = avail++;
+ }
+ NA[k] += TRACE_SPACE;
+ }
+
+ while (y >= NB[k])
+ { if (cells[hb].mark < NB[k])
+ { if (avail >= cmax)
+ { cmax = ((int) (avail*1.2)) + 10000;
+ cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+ "Reallocating trace cells");
+ if (cells == NULL)
+ exit (1);
+ }
+#ifdef SHOW_TPS
+ printf(" B %d: %d,%d,%d,%d\n",avail,hb,k,dif,NB[k]); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = hb;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = dif;
+#endif
+ pb->mark = NB[k];
+ hb = avail++;
+ }
+ NB[k] += TRACE_SPACE;
+ }
+
+ if (c > besta)
+ { besta = c;
+ besty = y;
+ if (m >= PATH_AVE)
+ { lasta = c;
+ if (TABLE[b & TRIM_MASK] >= 0)
+ if (TABLE[(b >> TRIM_LEN) & TRIM_MASK] + SCORE[b & TRIM_MASK] >= 0)
+ { trima = c;
+ trimy = y;
+ trimd = dif;
+ trimha = ha;
+ trimhb = hb;
+ }
+ }
+ }
+
+ t = T[k];
+ n = M[k];
+ ua = HA[k];
+ ub = HB[k];
+ V[k] = c;
+ T[k] = b;
+ M[k] = m;
+ HA[k] = ha;
+ HB[k] = hb;
+
+ a -= 1;
+ }
+
+ if (more == 0)
+ { if (bseq[besty] != 4 && aseq[besta-besty] != 4)
+ more = 1;
+ if (hgh >= aclip)
+ { hgh = aclip-1;
+ if (morem <= M[aclip])
+ { morem = M[aclip];
+ morea = V[aclip];
+ morey = (morea - aclip)/2;
+ mored = dif;
+ moreha = HA[aclip];
+ morehb = HB[aclip];
+ }
+ }
+ if (low <= bclip)
+ { low = bclip+1;
+ if (morem <= M[bclip])
+ { morem = M[bclip];
+ morea = V[bclip];
+ morey = (morea - bclip)/2;
+ mored = dif;
+ moreha = HA[bclip];
+ morehb = HB[bclip];
+ }
+ }
+ aclip = INT32_MAX;
+ bclip = -INT32_MAX;
+ }
+
+ n = besta - WAVE_LAG;
+ while (hgh >= low)
+ if (V[hgh] < n)
+ hgh -= 1;
+ else
+ { while (V[low] < n)
+ low += 1;
+ break;
+ }
+
+#ifdef WAVE_STATS
+ k = (hgh-low)+1;
+ if (k > MAX)
+ MAX = k;
+ TOT += k;
+ NWV += 1;
+#endif
+
+#ifdef DEBUG_WAVE
+ print_wave(V,M,low,hgh,besta);
+#endif
+ }
+
+ { uint16 *atrace = (uint16 *) apath->trace;
+ uint16 *btrace = (uint16 *) bpath->trace;
+ int atlen, btlen;
+ int trimx;
+ int a, b, k, h;
+#ifdef DELTAS
+ int d, e;
+#endif
+
+ if (morem >= 0)
+ { trimx = morea-morey;
+ trimy = morey;
+ trimd = mored;
+ trimha = moreha;
+ trimhb = morehb;
+ }
+ else
+ trimx = trima-trimy;
+
+ atlen = btlen = 0;
+
+ a = -1;
+ for (h = trimha; h >= 0; h = b)
+ { b = cells[h].ptr;
+ cells[h].ptr = a;
+ a = h;
+ }
+ h = a;
+
+#ifdef DELTAS
+
+ k = cells[h].diag;
+ b = (mida-k)/2;
+ e = 0;
+#ifdef SHOW_TRAIL
+ printf(" A path = (%5d,%5d)\n",(mida+k)/2,b); fflush(stdout);
+#endif
+ for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+ { k = cells[h].diag;
+ a = cells[h].mark - k;
+ d = cells[h].diff;
+ atrace[atlen++] = (uint16) (d-e);
+ atrace[atlen++] = (uint16) (a-b);
+#ifdef SHOW_TRAIL
+ printf(" %4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,a-b); fflush(stdout);
+#endif
+ b = a;
+ e = d;
+ }
+ if (b+k != trimx)
+ { atrace[atlen++] = (uint16) (trimd-e);
+ atrace[atlen++] = (uint16) (trimy-b);
+#ifdef SHOW_TRAIL
+ printf(" (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimy-b); fflush(stdout);
+#endif
+ }
+ else if (b != trimy)
+ { atrace[atlen-1] = (uint16) (atrace[atlen-1] + (trimy-b));
+ atrace[atlen-2] = (uint16) (atrace[atlen-2] + (trimd-e));
+#ifdef SHOW_TRAIL
+ printf(" @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimy-b); fflush(stdout);
+#endif
+ }
+
+#else // DELTAS
+
+ k = cells[h].diag;
+ b = (mida-k)/2;
+#ifdef SHOW_TRAIL
+ printf(" A path = (%5d,%5d)\n",(mida+k)/2,b); fflush(stdout);
+#endif
+ for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+ { k = cells[h].diag;
+ a = cells[h].mark - k;
+ atrace[atlen++] = (uint16) (a-b);
+#ifdef SHOW_TRAIL
+ printf(" %4d: (%5d,%5d): %3d\n",h,a+k,a,a-b); fflush(stdout);
+#endif
+ b = a;
+ }
+ if (b+k != trimx)
+ { atrace[atlen++] = (uint16) (trimy-b);
+#ifdef SHOW_TRAIL
+ printf(" (%5d,%5d): %3d\n",trimx,trimy,trimy-b); fflush(stdout);
+#endif
+ }
+ else if (b != trimy)
+ { atrace[atlen-1] = (uint16) (atrace[atlen-1] + (trimy-b));
+#ifdef SHOW_TRAIL
+ printf(" @ (%5d,%5d): %3d\n",trimx,trimy,trimy-b); fflush(stdout);
+#endif
+ }
+
+#endif // DELTAS
+
+ a = -1;
+ for (h = trimhb; h >= 0; h = b)
+ { b = cells[h].ptr;
+ cells[h].ptr = a;
+ a = h;
+ }
+ h = a;
+
+#ifdef DELTAS
+
+ k = cells[h].diag;
+ b = (mida+k)/2;
+ e = 0;
+ low = k;
+#ifdef SHOW_TRAIL
+ printf(" B path = (%5d,%5d)\n",b,(mida-k)/2); fflush(stdout);
+#endif
+ for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+ { k = cells[h].diag;
+ a = cells[h].mark + k;
+ d = cells[h].diff;
+ btrace[btlen++] = (uint16) (d-e);
+ btrace[btlen++] = (uint16) (a-b);
+#ifdef SHOW_TRAIL
+ printf(" %4d: (%5d,%5d): %3d / %3d\n",h,a,a-k,d-e,a-b); fflush(stdout);
+#endif
+ b = a;
+ e = d;
+ }
+ if (b-k != trimy)
+ { btrace[btlen++] = (uint16) (trimd-e);
+ btrace[btlen++] = (uint16) (trimx-b);
+#ifdef SHOW_TRAIL
+ printf(" (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimx-b); fflush(stdout);
+#endif
+ }
+ else if (b != trimx)
+ { btrace[btlen-1] = (uint16) (btrace[btlen-1] + (trimx-b));
+ btrace[btlen-2] = (uint16) (btrace[btlen-2] + (trimd-e));
+#ifdef SHOW_TRAIL
+ printf(" @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimx-b); fflush(stdout);
+#endif
+ }
+
+#else // DELTAS
+
+ k = cells[h].diag;
+ b = (mida+k)/2;
+ low = k;
+#ifdef SHOW_TRAIL
+ printf(" B path = (%5d,%5d)\n",b,(mida-k)/2); fflush(stdout);
+#endif
+ for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+ { k = cells[h].diag;
+ a = cells[h].mark + k;
+ btrace[btlen++] = (uint16) (a-b);
+#ifdef SHOW_TRAIL
+ printf(" %4d: (%5d,%5d): %3d\n",h,a,a-k,a-b); fflush(stdout);
+#endif
+ b = a;
+ }
+ if (b-k != trimy)
+ { btrace[btlen++] = (uint16) (trimx-b);
+#ifdef SHOW_TRAIL
+ printf(" (%5d,%5d): %3d\n",trimx,trimy,trimx-b); fflush(stdout);
+#endif
+ }
+ else if (b != trimx)
+ { btrace[btlen-1] = (uint16) (btrace[btlen-1] + (trimx-b));
+#ifdef SHOW_TRAIL
+ printf(" @ (%5d,%5d): %3d\n",trimx,trimy,trimx-b); fflush(stdout);
+#endif
+ }
+
+#endif // DELTAS
+
+ apath->aepos = trimx;
+ apath->bepos = trimy;
+ apath->diffs = trimd;
+ apath->tlen = atlen;
+ if (COMP(align->flags))
+ { bpath->abpos = align->blen - apath->bepos;
+ bpath->bbpos = align->alen - apath->aepos;
+ }
+ else
+ { bpath->aepos = apath->bepos;
+ bpath->bepos = apath->aepos;
+ }
+ bpath->diffs = trimd;
+ bpath->tlen = btlen;
+ }
+
+ work->cells = (void *) cells;
+ work->celmax = cmax;
+
+ return (low);
+}
+
+/*** Reverse Wave ***/
+
+static void reverse_wave(_Work_Data *work, _Align_Spec *spec,
+ Alignment *align, Path *bpath, int mind, int maxd, int mida)
+{ char *aseq = align->aseq - 1;
+ char *bseq = align->bseq - 1;
+ Path *apath = align->path;
+
+ int hgh, low, dif;
+ int minp, maxp;
+ int *V, *M;
+ BVEC *T;
+
+ int *HA, *HB;
+ int *NA, *NB;
+ Pebble *cells;
+ int avail, cmax, boff;
+
+ int TRACE_SPACE = spec->trace_space;
+ int PATH_AVE = spec->ave_path;
+ int16 *SCORE = spec->score;
+ int16 *TABLE = spec->table;
+
+ int besta, besty;
+ int trima, trimy, trimd;
+ int trimha, trimhb;
+ int morea, morey, mored;
+ int moreha, morehb;
+ int more, morem, lasta;
+ int aclip, bclip;
+
+ { int alen = align->alen + 1;
+ int blen = align->blen + 1;
+ int tlen = alen + blen + 1;
+
+ V = ((int *) work->vector) + blen;
+ M = V + tlen;
+ HA = M + tlen;
+ HB = HA + tlen;
+ NA = HB + tlen;
+ NB = NA + tlen;
+ T = ((BVEC *) (NB + alen)) + blen;
+
+ cells = (Pebble *) (work->cells);
+ cmax = work->celmax;
+ avail = 0;
+
+ if (COMP(align->flags))
+ boff = align->blen % TRACE_SPACE;
+ else
+ boff = 0;
+ }
+
+ hgh = maxd;
+ low = mind;
+ if (aseq == bseq)
+ { if (low < 0)
+ { int big = -low;
+ int sml = -hgh;
+
+ if (big <= MICRO_SAT)
+ minp = low - Sat_Width[big];
+ else
+ minp = -SAT_HGH*big;
+ if (sml <= MICRO_SAT)
+ maxp = hgh + Sat_Width[sml];
+ else
+ maxp = -SAT_LOW*sml;
+ }
+ else
+ { if (low <= MICRO_SAT)
+ minp = low - Sat_Width[low];
+ else
+ minp = SAT_LOW*low;
+ if (hgh <= MICRO_SAT)
+ maxp = hgh + Sat_Width[hgh];
+ else
+ maxp = SAT_HGH*hgh;
+ }
+ }
+ else
+ { minp = -INT32_MAX;
+ maxp = INT32_MAX;
+ }
+ dif = 0;
+
+ more = 1;
+ aclip = -INT32_MAX;
+ bclip = INT32_MAX;
+
+ besta = trima = morea = lasta = mida;
+ besty = trimy = morey = (mida-hgh) >> 1;
+ trimd = mored = 0;
+ trimha = moreha = 0;
+ trimhb = morehb = 1;
+ morem = -1;
+
+ { int k;
+ char *a;
+
+ a = aseq + low;
+ for (k = low; k <= hgh; k++)
+ { int y, c, d;
+ int ha, hb;
+ int na, nb;
+ Pebble *pb;
+
+ y = (mida-k) >> 1;
+
+ if (avail >= cmax-1)
+ { cmax = ((int) (avail*1.2)) + 10000;
+ cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+ if (cells == NULL)
+ exit (1);
+ }
+
+ na = ((y+k+TRACE_SPACE-1)/TRACE_SPACE-1)*TRACE_SPACE;
+#ifdef SHOW_TPS
+ printf(" A %d: -1,%d,0,%d\n",avail,k,na+TRACE_SPACE); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = -1;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = 0;
+#endif
+ pb->mark = y+k;
+ ha = avail++;
+
+ nb = ((y+(TRACE_SPACE-boff)-1)/TRACE_SPACE-1)*TRACE_SPACE+boff;
+#ifdef SHOW_TPS
+ printf(" B %d: -1,%d,0,%d\n",avail,k,nb+TRACE_SPACE); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = -1;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = 0;
+#endif
+ pb->mark = y;
+ hb = avail++;
+
+ while (1)
+ { c = bseq[y];
+ if (c == 4)
+ { more = 0;
+ if (bclip > k)
+ bclip = k;
+ break;
+ }
+ d = a[y];
+ if (c != d)
+ { if (d == 4)
+ { more = 0;
+ aclip = k;
+ }
+ break;
+ }
+ y -= 1;
+ }
+ c = (y << 1) + k;
+
+ while (y+k <= na)
+ { if (avail >= cmax)
+ { cmax = ((int) (avail*1.2)) + 10000;
+ cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+ if (cells == NULL)
+ exit (1);
+ }
+#ifdef SHOW_TPS
+ printf(" A %d: %d,%d,0,%d\n",avail,ha,k,na); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = ha;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = 0;
+#endif
+ pb->mark = na;
+ ha = avail++;
+ na -= TRACE_SPACE;
+ }
+ while (y <= nb)
+ { if (avail >= cmax)
+ { cmax = ((int) (avail*1.2)) + 10000;
+ cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+ if (cells == NULL)
+ exit (1);
+ }
+#ifdef SHOW_TPS
+ printf(" B %d: %d,%d,0,%d\n",avail,hb,k,nb); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = hb;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = 0;
+#endif
+ pb->mark = nb;
+ hb = avail++;
+ nb -= TRACE_SPACE;
+ }
+
+ if (c < besta)
+ { besta = trima = lasta = c;
+ besty = trimy = y;
+ trimha = ha;
+ trimhb = hb;
+ }
+
+ V[k] = c;
+ T[k] = PATH_INT;
+ M[k] = PATH_LEN;
+ HA[k] = ha;
+ HB[k] = hb;
+ NA[k] = na;
+ NB[k] = nb;
+
+ a += 1;
+ }
+ }
+
+ if (more == 0)
+ { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+ more = 1;
+ if (low <= aclip)
+ { low = aclip+1;
+ if (morem <= M[aclip])
+ { morem = M[aclip];
+ morea = V[aclip];
+ morey = (morea - aclip)/2;
+ moreha = HA[aclip];
+ morehb = HB[aclip];
+ }
+ }
+ if (hgh >= bclip)
+ { hgh = bclip-1;
+ if (morem <= M[bclip])
+ { morem = M[bclip];
+ morea = V[bclip];
+ morey = (morea - bclip)/2;
+ moreha = HA[bclip];
+ morehb = HB[bclip];
+ }
+ }
+ aclip = -INT32_MAX;
+ bclip = INT32_MAX;
+ }
+
+#ifdef DEBUG_WAVE
+ printf("\nREVERSE WAVE:\n");
+ print_wave(V,M,low,hgh,besta);
+#endif
+
+ while (more && lasta <= besta + TRIM_MLAG)
+ { int k, n;
+ int ua, ub;
+ BVEC t;
+ int am, ac, ap;
+ char *a;
+
+ if (low > minp)
+ { low -= 1;
+ NA[low] = NA[low+1];
+ NB[low] = NB[low+1];
+ V[low] = ap = INT32_MAX;
+ }
+ else
+ ap = V[low];
+ if (hgh < maxp)
+ { hgh += 1;
+ NA[hgh] = NA[hgh-1];
+ NB[hgh] = NB[hgh-1];
+ V[hgh] = INT32_MAX;
+ }
+ dif += 1;
+
+ ac = V[hgh+1] = V[low-1] = INT32_MAX;
+ a = aseq + low;
+ t = PATH_INT;
+ n = PATH_LEN;
+ ua = ub = -1;
+ for (k = low; k <= hgh; k++)
+ { int y, m;
+ int ha, hb;
+ int c, d;
+ BVEC b;
+ Pebble *pb;
+
+ am = ac;
+ ac = ap;
+ ap = V[d = k+1];
+
+ if (ac > ap)
+ if (ap > am)
+ { c = am-1;
+ m = n;
+ b = t;
+ ha = ua;
+ hb = ub;
+ }
+ else
+ { c = ap-1;
+ m = M[d];
+ b = T[d];
+ ha = HA[d];
+ hb = HB[d];
+ }
+ else
+ if (ac > am)
+ { c = am-1;
+ m = n;
+ b = t;
+ ha = ua;
+ hb = ub;
+ }
+ else
+ { c = ac-2;
+ m = M[k];
+ b = T[k];
+ ha = HA[k];
+ hb = HB[k];
+ }
+
+ if ((b & PATH_TOP) != 0)
+ m -= 1;
+ b <<= 1;
+
+ y = (c-k) >> 1;
+ while (1)
+ { c = bseq[y];
+ if (c == 4)
+ { more = 0;
+ if (bclip > k)
+ bclip = k;
+ break;
+ }
+ d = a[y];
+ if (c != d)
+ { if (d == 4)
+ { more = 0;
+ aclip = k;
+ }
+ break;
+ }
+ y -= 1;
+ if ((b & PATH_TOP) == 0)
+ m += 1;
+ b = (b << 1) | 1;
+ }
+ c = (y << 1) + k;
+
+ while (y+k <= NA[k])
+ { if (cells[ha].mark > NA[k])
+ { if (avail >= cmax)
+ { cmax = ((int) (avail*1.2)) + 10000;
+ cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+ "Reallocating trace cells");
+ if (cells == NULL)
+ exit (1);
+ }
+#ifdef SHOW_TPS
+ printf(" A %d: %d,%d,%d,%d\n",avail,ha,k,dif,NA[k]); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = ha;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = dif;
+#endif
+ pb->mark = NA[k];
+ ha = avail++;
+ }
+ NA[k] -= TRACE_SPACE;
+ }
+ while (y <= NB[k])
+ { if (cells[hb].mark > NB[k])
+ { if (avail >= cmax)
+ { cmax = ((int) (avail*1.2)) + 10000;
+ cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+ "Reallocating trace cells");
+ if (cells == NULL)
+ exit (1);
+ }
+#ifdef SHOW_TPS
+ printf(" B %d: %d,%d,%d,%d\n",avail,hb,k,dif,NB[k]); fflush(stdout);
+#endif
+ pb = cells+avail;
+ pb->ptr = hb;
+ pb->diag = k;
+#ifdef DELTAS
+ pb->diff = dif;
+#endif
+ pb->mark = NB[k];
+ hb = avail++;
+ }
+ NB[k] -= TRACE_SPACE;
+ }
+
+ if (c < besta)
+ { besta = c;
+ besty = y;
+ if (m >= PATH_AVE)
+ { lasta = c;
+ if (TABLE[b & TRIM_MASK] >= 0)
+ if (TABLE[(b >> TRIM_LEN) & TRIM_MASK] + SCORE[b & TRIM_MASK] >= 0)
+ { trima = c;
+ trimy = y;
+ trimd = dif;
+ trimha = ha;
+ trimhb = hb;
+ }
+ }
+ }
+
+ t = T[k];
+ n = M[k];
+ ua = HA[k];
+ ub = HB[k];
+ V[k] = c;
+ T[k] = b;
+ M[k] = m;
+ HA[k] = ha;
+ HB[k] = hb;
+
+ a += 1;
+ }
+
+ if (more == 0)
+ { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+ more = 1;
+ if (low <= aclip)
+ { low = aclip+1;
+ if (morem <= M[aclip])
+ { morem = M[aclip];
+ morea = V[aclip];
+ morey = (morea - aclip)/2;
+ mored = dif;
+ moreha = HA[aclip];
+ morehb = HB[aclip];
+ }
+ }
+ if (hgh >= bclip)
+ { hgh = bclip-1;
+ if (morem <= M[bclip])
+ { morem = M[bclip];
+ morea = V[bclip];
+ morey = (morea - bclip)/2;
+ mored = dif;
+ moreha = HA[bclip];
+ morehb = HB[bclip];
+ }
+ }
+ aclip = -INT32_MAX;
+ bclip = INT32_MAX;
+ }
+
+ n = besta + WAVE_LAG;
+ while (hgh >= low)
+ if (V[hgh] > n)
+ hgh -= 1;
+ else
+ { while (V[low] > n)
+ low += 1;
+ break;
+ }
+
+#ifdef WAVE_STATS
+ k = (hgh-low)+1;
+ if (k > MAX)
+ MAX = k;
+ TOT += k;
+ NWV += 1;
+#endif
+
+#ifdef DEBUG_WAVE
+ print_wave(V,M,low,hgh,besta);
+#endif
+ }
+
+ { uint16 *atrace = (uint16 *) apath->trace;
+ uint16 *btrace = (uint16 *) bpath->trace;
+ int atlen, btlen;
+ int trimx;
+ int a, b, k, h;
+#ifdef DELTAS
+ int d, e;
+#endif
+
+ if (morem >= 0)
+ { trimx = morea-morey;
+ trimy = morey;
+ trimd = mored;
+ trimha = moreha;
+ trimhb = morehb;
+ }
+ else
+ trimx = trima-trimy;
+
+ atlen = btlen = 0;
+
+ a = -1;
+ for (h = trimha; h >= 0; h = b)
+ { b = cells[h].ptr;
+ cells[h].ptr = a;
+ a = h;
+ }
+ h = a;
+
+#ifdef DELTAS
+
+ k = cells[h].diag;
+ b = cells[h].mark - k;
+ e = 0;
+#ifdef SHOW_TRAIL
+ printf(" A path = (%5d,%5d)\n",b+k,b); fflush(stdout);
+#endif
+ if ((b+k)%TRACE_SPACE != 0)
+ { h = cells[h].ptr;
+ if (h < 0)
+ { a = trimy;
+ d = trimd;
+ }
+ else
+ { k = cells[h].diag;
+ a = cells[h].mark - k;
+ d = cells[h].diff;
+ }
+#ifdef SHOW_TRAIL
+ printf(" +%4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,b-a); fflush(stdout);
+#endif
+ if (apath->tlen == 0)
+ { atrace[--atlen] = (uint16) (b-a);
+ atrace[--atlen] = (uint16) (d-e);
+ }
+ else
+ { atrace[1] = (uint16) (atrace[1] + (b-a));
+ atrace[0] = (uint16) (atrace[0] + (d-e));
+ }
+ b = a;
+ e = d;
+ }
+ if (h >= 0)
+ { for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+ { k = cells[h].diag;
+ a = cells[h].mark - k;
+ atrace[--atlen] = (uint16) (b-a);
+ d = cells[h].diff;
+ atrace[--atlen] = (uint16) (d-e);
+#ifdef SHOW_TRAIL
+ printf(" %4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,b-a); fflush(stdout);
+#endif
+ b = a;
+ e = d;
+ }
+ if (b+k != trimx)
+ { atrace[--atlen] = (uint16) (b-trimy);
+ atrace[--atlen] = (uint16) (trimd-e);
+#ifdef SHOW_TRAIL
+ printf(" (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimy); fflush(stdout);
+#endif
+ }
+ else if (b != trimy)
+ { atrace[atlen+1] = (uint16) (atrace[atlen+1] + (b-trimy));
+ atrace[atlen] = (uint16) (atrace[atlen] + (trimd-e));
+#ifdef SHOW_TRAIL
+ printf(" @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimy); fflush(stdout);
+#endif
+ }
+ }
+
+#else // DELTAS
+
+ k = cells[h].diag;
+ b = cells[h].mark - k;
+#ifdef SHOW_TRAIL
+ printf(" A path = (%5d,%5d)\n",b+k,b); fflush(stdout);
+#endif
+ if ((b+k)%TRACE_SPACE != 0)
+ { h = cells[h].ptr;
+ if (h < 0)
+ a = trimy;
+ else
+ { k = cells[h].diag;
+ a = cells[h].mark - k;
+ }
+#ifdef SHOW_TRAIL
+ printf(" +%4d: (%5d,%5d): %3d\n",h,a+k,a,b-a); fflush(stdout);
+#endif
+ if (apath->tlen == 0)
+ atrace[--atlen] = (uint16) (b-a);
+ else
+ atrace[0] = (uint16) (atrace[0] + (b-a));
+ b = a;
+ }
+ if (h >= 0)
+ { for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+ { k = cells[h].diag;
+ a = cells[h].mark - k;
+ atrace[--atlen] = (uint16) (b-a);
+#ifdef SHOW_TRAIL
+ printf(" %4d: (%5d,%5d): %3d\n",h,a+k,a,b-a); fflush(stdout);
+#endif
+ b = a;
+ }
+ if (b+k != trimx)
+ { atrace[--atlen] = (uint16) (b-trimy);
+#ifdef SHOW_TRAIL
+ printf(" (%5d,%5d): %3d\n",trimx,trimy,b-trimy); fflush(stdout);
+#endif
+ }
+ else if (b != trimy)
+ { atrace[atlen] = (uint16) (atrace[atlen] + (b-trimy));
+#ifdef SHOW_TRAIL
+ printf(" @ (%5d,%5d): %3d\n",trimx,trimy,b-trimy); fflush(stdout);
+#endif
+ }
+ }
+
+#endif // DELTAS
+
+ a = -1;
+ for (h = trimhb; h >= 0; h = b)
+ { b = cells[h].ptr;
+ cells[h].ptr = a;
+ a = h;
+ }
+ h = a;
+
+#ifdef DELTAS
+
+ k = cells[h].diag;
+ b = cells[h].mark + k;
+ e = 0;
+#ifdef SHOW_TRAIL
+ printf(" B path = (%5d,%5d)\n",b,b-k); fflush(stdout);
+#endif
+ if ((b-k)%TRACE_SPACE != boff)
+ { h = cells[h].ptr;
+ if (h < 0)
+ { a = trimx;
+ d = trimd;
+ }
+ else
+ { k = cells[h].diag;
+ a = cells[h].mark + k;
+ d = cells[h].diff;
+ }
+#ifdef SHOW_TRAIL
+ printf(" +%4d: (%5d,%5d): %3d / %3d\n",h,a,a-k,d-e,b-a); fflush(stdout);
+#endif
+ if (bpath->tlen == 0)
+ { btrace[--btlen] = (uint16) (b-a);
+ btrace[--btlen] = (uint16) (b-a);
+ }
+ else
+ { btrace[1] = (uint16) (btrace[1] + (b-a));
+ btrace[0] = (uint16) (btrace[0] + (d-e));
+ }
+ b = a;
+ e = d;
+ }
+
+ if (h >= 0)
+ { for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+ { k = cells[h].diag;
+ a = cells[h].mark + k;
+ btrace[--btlen] = (uint16) (b-a);
+ d = cells[h].diff;
+ btrace[--btlen] = (uint16) (d-e);
+#ifdef SHOW_TRAIL
+ printf(" %4d: (%5d,%5d): %3d / %3d\n",h,a,a-k,d-e,b-a); fflush(stdout);
+#endif
+ b = a;
+ e = d;
+ }
+ if (b-k != trimy)
+ { btrace[--btlen] = (uint16) (b-trimx);
+ btrace[--btlen] = (uint16) (trimd-e);
+#ifdef SHOW_TRAIL
+ printf(" (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimx); fflush(stdout);
+#endif
+ }
+ else if (b != trimx)
+ { btrace[btlen+1] = (uint16) (btrace[btlen+1] + (b-trimx));
+ btrace[btlen] = (uint16) (btrace[btlen] + (trimd-e));
+#ifdef SHOW_TRAIL
+ printf(" @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimx); fflush(stdout);
+#endif
+ }
+ }
+
+#else // DELTAS
+
+ k = cells[h].diag;
+ b = cells[h].mark + k;
+#ifdef SHOW_TRAIL
+ printf(" B path = (%5d,%5d)\n",b,b-k); fflush(stdout);
+#endif
+ if ((b-k)%TRACE_SPACE != boff)
+ { h = cells[h].ptr;
+ if (h < 0)
+ a = trimx;
+ else
+ { k = cells[h].diag;
+ a = cells[h].mark + k;
+ }
+#ifdef SHOW_TRAIL
+ printf(" +%4d: (%5d,%5d): %3d\n",h,a,a-k,b-a); fflush(stdout);
+#endif
+ if (bpath->tlen == 0)
+ btrace[--btlen] = (uint16) (b-a);
+ else
+ btrace[0] = (uint16) (btrace[0] + (b-a));
+ b = a;
+ }
+
+ if (h >= 0)
+ { for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+ { k = cells[h].diag;
+ a = cells[h].mark + k;
+ btrace[--btlen] = (uint16) (b-a);
+#ifdef SHOW_TRAIL
+ printf(" %4d: (%5d,%5d): %3d\n",h,a,a-k,b-a); fflush(stdout);
+#endif
+ b = a;
+ }
+ if (b-k != trimy)
+ { btrace[--btlen] = (uint16) (b-trimx);
+#ifdef SHOW_TRAIL
+ printf(" (%5d,%5d): %3d\n",trimx,trimy,b-trimx); fflush(stdout);
+#endif
+ }
+ else if (b != trimx)
+ { btrace[btlen] = (uint16) (btrace[btlen] + (b-trimx));
+#ifdef SHOW_TRAIL
+ printf(" @ (%5d,%5d): %3d\n",trimx,trimy,b-trimx); fflush(stdout);
+#endif
+ }
+ }
+
+#endif // DELTAS
+
+ apath->abpos = trimx;
+ apath->bbpos = trimy;
+ apath->diffs = apath->diffs + trimd;
+ apath->tlen = apath->tlen - atlen;
+ apath->trace = atrace + atlen;
+ if (COMP(align->flags))
+ { bpath->aepos = align->blen - apath->bbpos;
+ bpath->bepos = align->alen - apath->abpos;
+ }
+ else
+ { bpath->abpos = apath->bbpos;
+ bpath->bbpos = apath->abpos;
+ }
+ bpath->diffs = bpath->diffs + trimd;
+ bpath->tlen = bpath->tlen - btlen;
+ bpath->trace = btrace + btlen;
+ }
+
+ work->cells = (void *) cells;
+ work->celmax = cmax;
+}
+
+
+/* Find the longest local alignment between aseq and bseq through (xcnt,ycnt)
+ See associated .h file for the precise definition of the interface.
+*/
+
+Path *Local_Alignment(Alignment *align, Work_Data *ework, Align_Spec *espec,
+ int xlow, int xhgh, int ycnt)
+{ _Work_Data *work = ( _Work_Data *) ework;
+ _Align_Spec *spec = (_Align_Spec *) espec;
+
+ Path *apath, *bpath;
+
+ { int alen, blen;
+ int maxtp, wsize;
+
+ alen = align->alen;
+ blen = align->blen;
+
+ wsize = (6*sizeof(int) + sizeof(BVEC))*(alen+blen+3);
+ if (wsize >= work->vecmax)
+ enlarge_vector(work,wsize);
+
+ if (alen < blen)
+ maxtp = 2*(blen/spec->trace_space+2);
+ else
+ maxtp = 2*(alen/spec->trace_space+2);
+ wsize = 4*maxtp*sizeof(uint16) + sizeof(Path);
+ if (wsize > work->pntmax)
+ enlarge_points(work,wsize);
+
+ apath = align->path;
+ bpath = (Path *) work->points;
+
+ apath->trace = ((uint16 *) (bpath+1)) + maxtp;
+ bpath->trace = ((uint16 *) apath->trace) + 2*maxtp;
+ }
+
+#ifdef DEBUG_PASSES
+ printf("\n");
+#endif
+
+ { int l, h, a;
+
+ l = xlow-ycnt;
+ h = xhgh-ycnt;
+ a = (xlow+xhgh)/2+ycnt;
+
+ l = forward_wave(work,spec,align,bpath,l,h,a);
+#ifdef DEBUG_PASSES
+ printf("F1 (%d-%d,%d) => (%d,%d) %d\n",xlow,xhgh,ycnt,apath->aepos,apath->bepos,apath->diffs);
+#endif
+
+ reverse_wave(work,spec,align,bpath,l,l,a);
+#ifdef DEBUG_PASSES
+ printf("R1 (%d,%d) => (%d,%d) %d\n",l,ycnt,apath->abpos,apath->bbpos,apath->diffs);
+#endif
+ }
+
+ if (COMP(align->flags))
+ { uint16 *trace = (uint16 *) bpath->trace;
+ uint16 p;
+ int i, j;
+
+#ifdef DELTAS
+ i = bpath->tlen-2;
+ j = 0;
+ while (j < i)
+ { p = trace[i];
+ trace[i] = trace[j];
+ trace[j] = p;
+ p = trace[i+1];
+ trace[i+1] = trace[j+1];
+ trace[j+1] = p;
+ i -= 2;
+ j += 2;
+ }
+#else
+ i = bpath->tlen-1;
+ j = 0;
+ while (j < i)
+ { p = trace[i];
+ trace[i] = trace[j];
+ trace[j] = p;
+ i -= 1;
+ j += 1;
+ }
+#endif
+ }
+
+#ifdef DEBUG_POINTS
+ { uint16 *trace = (uint16 *) apath->trace;
+ int a, h;
+
+ printf("\nA-path (%d,%d)->(%d,%d)",apath->abpos,apath->bbpos,apath->aepos,apath->bepos);
+ printf(" %c\n",(COMP(align->flags) ? 'c' : 'n'));
+ a = apath->bbpos;
+#ifdef DELTAS
+ for (h = 1; h < apath->tlen; h += 2)
+ { int dif = trace[h-1];
+ int del = trace[h];
+ a += del;
+ printf(" %d / %d (%d)\n",dif,del,a);
+ }
+#else
+ for (h = 0; h < apath->tlen; h++)
+ { int del = trace[h];
+ a += del;
+ printf(" %d (%d)\n",del,a);
+ }
+#endif
+ }
+
+ { uint16 *trace = (uint16 *) bpath->trace;
+ int a, h;
+
+ printf("\nB-path (%d,%d)->(%d,%d)",bpath->abpos,bpath->bbpos,bpath->aepos,bpath->bepos);
+ printf(" %c [%d,%d]\n",(COMP(align->flags) ? 'c' : 'n'),align->blen,align->alen);
+ a = bpath->bbpos;
+#ifdef DELTAS
+ for (h = 1; h < bpath->tlen; h += 2)
+ { int dif = trace[h-1];
+ int del = trace[h];
+ a += del;
+ printf(" %d / %d (%d)\n",dif,del,a);
+ }
+#else
+ for (h = 0; h < bpath->tlen; h++)
+ { int del = trace[h];
+ a += del;
+ printf(" %d (%d)\n",del,a);
+ }
+#endif
+ }
+#endif
+
+ return (bpath);
+}
+
+
+/****************************************************************************************\
+* *
+* OVERLAP MANIPULATION *
+* *
+\****************************************************************************************/
+
+static int64 PtrSize = sizeof(void *);
+static int64 OvlIOSize = sizeof(Overlap) - sizeof(void *);
+
+int Read_Overlap(FILE *input, Overlap *ovl)
+{ if (fread( ((char *) ovl) + PtrSize, OvlIOSize, 1, input) != 1)
+ return (1);
+ return (0);
+}
+
+int Read_Trace(FILE *input, Overlap *ovl, int tbytes)
+{ if (tbytes > 0 && ovl->path.tlen > 0)
+ { if (fread(ovl->path.trace, tbytes*ovl->path.tlen, 1, input) != 1)
+ return (1);
+ }
+ return (0);
+}
+
+void Write_Overlap(FILE *output, Overlap *ovl, int tbytes)
+{ fwrite( ((char *) ovl) + PtrSize, OvlIOSize, 1, output);
+ if (ovl->path.trace != NULL)
+ fwrite(ovl->path.trace,tbytes,ovl->path.tlen,output);
+}
+
+void Compress_TraceTo8(Overlap *ovl)
+{ uint16 *t16 = (uint16 *) ovl->path.trace;
+ uint8 *t8 = (uint8 *) ovl->path.trace;
+ int j;
+
+ for (j = 0; j < ovl->path.tlen; j++)
+ t8[j] = (uint8) (t16[j]);
+}
+
+void Decompress_TraceTo16(Overlap *ovl)
+{ uint16 *t16 = (uint16 *) ovl->path.trace;
+ uint8 *t8 = (uint8 *) ovl->path.trace;
+ int j;
+
+ for (j = ovl->path.tlen-1; j >= 0; j--)
+ t16[j] = t8[j];
+}
+
+void Print_Overlap(FILE *output, Overlap *ovl, int tbytes, int indent)
+{ int i;
+
+ fprintf(output,"%*s%d vs. ",indent,"",ovl->aread);
+ if (COMP(ovl->flags))
+ fprintf(output,"c(%d)\n",ovl->bread);
+ else
+ fprintf(output,"%d\n",ovl->bread);
+ fprintf(output,"%*s [%d,%d] vs [%d,%d] w. %d diffs\n",indent,"",
+ ovl->path.abpos,ovl->path.aepos,ovl->path.bbpos,ovl->path.bepos,ovl->path.diffs);
+
+ if (tbytes == 1)
+ { uint8 *trace = (uint8 *) (ovl->path.trace);
+ if (trace != NULL)
+#ifdef DELTAS
+ { int p = ovl->path.bbpos + trace[1];
+ fprintf(output,"%*sTrace: %3d/%5d",indent,"",trace[0],p);
+ for (i = 3; i < ovl->path.tlen; i += 2)
+ { if (i%10 == 0)
+ fprintf(output,"\n%*s",indent+6,"");
+ p += trace[i];
+ fprintf(output," %3d/%5d",trace[i-1],p);
+ }
+#else
+ { int p = ovl->path.bbpos + trace[0];
+ fprintf(output,"%*sTrace: %5d",indent,"",p);
+ for (i = 1; i < ovl->path.tlen; i++)
+ { if (i%10 == 0)
+ fprintf(output,"\n%*s",indent+6,"");
+ p += trace[i];
+ fprintf(output," %5d",p);
+ }
+#endif
+ fprintf(output,"\n");
+ }
+ }
+ else
+ { uint16 *trace = (uint16 *) (ovl->path.trace);
+ if (trace != NULL)
+#ifdef DELTAS
+ { int p = ovl->path.bbpos + trace[1];
+ fprintf(output,"%*sTrace: %3d/%5d",indent,"",trace[0],p);
+ for (i = 3; i < ovl->path.tlen; i += 2)
+ { if (i%10 == 0)
+ fprintf(output,"\n%*s",indent+6,"");
+ p += trace[i];
+ fprintf(output," %3d/%5d",trace[i-1],p);
+ }
+#else
+ { int p = ovl->path.bbpos + trace[0];
+ fprintf(output,"%*sTrace: %5d",indent,"",p);
+ for (i = 1; i < ovl->path.tlen; i++)
+ { if (i%10 == 0)
+ fprintf(output,"\n%*s",indent+6,"");
+ p += trace[i];
+ fprintf(output," %5d",p);
+ }
+#endif
+ fprintf(output,"\n");
+ }
+ }
+}
+
+int Check_Trace_Points(Overlap *ovl, int tspace, int verbose, char *fname)
+{ int i, p;
+
+#ifdef DELTAS
+ if (((ovl->path.aepos-1)/tspace - ovl->path.abpos/tspace)*2 != ovl->path.tlen-2)
+#else
+ if ((ovl->path.aepos-1)/tspace - ovl->path.abpos/tspace != ovl->path.tlen-1)
+#endif
+ { if (verbose)
+ fprintf(stderr," %s: Wrong number of trace points\n",fname);
+ return (1);
+ }
+ p = ovl->path.bbpos;
+ if (tspace <= TRACE_XOVR)
+ { uint8 *trace8 = (uint8 *) ovl->path.trace;
+#ifdef DELTAS
+ for (i = 1; i < ovl->path.tlen; i += 2)
+#else
+ for (i = 0; i < ovl->path.tlen; i++)
+#endif
+ p += trace8[i];
+ }
+ else
+ { uint16 *trace16 = (uint16 *) ovl->path.trace;
+#ifdef DELTAS
+ for (i = 1; i < ovl->path.tlen; i += 2)
+#else
+ for (i = 0; i < ovl->path.tlen; i++)
+#endif
+ p += trace16[i];
+ }
+ if (p != ovl->path.bepos)
+ { if (verbose)
+ fprintf(stderr," %s: Trace point sum != aligned interval\n",fname);
+ return (1);
+ }
+ return (0);
+}
+
+
+void Flip_Alignment(Alignment *align, int full)
+{ char *aseq = align->aseq;
+ char *bseq = align->bseq;
+ int alen = align->alen;
+ int blen = align->blen;
+ Path *path = align->path;
+ int comp = COMP(align->flags);
+
+ int *trace = (int *) path->trace;
+ int tlen = path->tlen;
+
+ int i, j, p;
+
+ if (comp)
+ { p = path->abpos;
+ path->abpos = blen - path->bepos;
+ path->bepos = alen - p;
+ p = path->aepos;
+ path->aepos = blen - path->bbpos;
+ path->bbpos = alen - p;
+
+ if (full)
+ { alen += 2;
+ blen += 2;
+
+ for (i = 0; i < tlen; i++)
+ if ((p = trace[i]) < 0)
+ trace[i] = alen + p;
+ else
+ trace[i] = p - blen;
+
+ i = tlen-1;
+ j = 0;
+ while (j < i)
+ { p = trace[i];
+ trace[i] = trace[j];
+ trace[j] = p;
+ i -= 1;
+ j += 1;
+ }
+
+ alen -= 2;
+ blen -= 2;
+ }
+ }
+ else
+ { p = path->abpos;
+ path->abpos = path->bbpos;
+ path->bbpos = p;
+ p = path->aepos;
+ path->aepos = path->bepos;
+ path->bepos = p;
+
+ if (full)
+ for (i = 0; i < tlen; i++)
+ trace[i] = - (trace[i]);
+ }
+
+ align->aseq = bseq;
+ align->bseq = aseq;
+ align->alen = blen;
+ align->blen = alen;
+}
+
+
+/****************************************************************************************\
+* *
+* ALIGNMENT PRINTING *
+* *
+\****************************************************************************************/
+
+/* Complement the sequence in fragment aseq. The operation does the
+ complementation/reversal in place. Calling it a second time on a
+ given fragment restores it to its original state. */
+
+void Complement_Seq(char *aseq, int len)
+{ char *s, *t;
+ int c;
+
+ s = aseq;
+ t = aseq + (len-1);
+ while (s < t)
+ { c = 3 - *s;
+ *s++ = (char) (3 - *t);
+ *t-- = (char) c;
+ }
+ if (s == t)
+ *s = (char) (3 - *s);
+}
+
+/* Print an alignment to file between a and b given in trace (unpacked).
+ Prefix gives the length of the initial prefix of a that is unaligned. */
+
+static char ToL[8] = { 'a', 'c', 'g', 't', '.', '[', ']', '-' };
+static char ToU[8] = { 'A', 'C', 'G', 'T', '.', '[', ']', '-' };
+
+void Print_Alignment(FILE *file, Alignment *align, Work_Data *ework,
+ int indent, int width, int border, int upper, int coord)
+{ _Work_Data *work = (_Work_Data *) ework;
+ int *trace = align->path->trace;
+ int tlen = align->path->tlen;
+
+ char *Abuf, *Bbuf, *Dbuf;
+ int i, j, o;
+ char *a, *b;
+ char mtag, dtag;
+ int prefa, prefb;
+ int aend, bend;
+ int sa, sb;
+ int match, diff;
+ char *N2A;
+
+ if (trace == NULL) return;
+
+#ifdef SHOW_TRACE
+ fprintf(file,"\nTrace:\n");
+ for (i = 0; i < tlen; i++)
+ fprintf(file," %3d\n",trace[i]);
+#endif
+
+ o = sizeof(char)*3*(width+1);
+ if (o > work->vecmax)
+ enlarge_vector(work,o);
+
+ if (upper)
+ N2A = ToU;
+ else
+ N2A = ToL;
+
+ Abuf = (char *) work->vector;
+ Bbuf = Abuf + (width+1);
+ Dbuf = Bbuf + (width+1);
+
+ aend = align->path->aepos;
+ bend = align->path->bepos;
+
+ Abuf[width] = Bbuf[width] = Dbuf[width] = '\0';
+ /* buffer/output next column */
+#define COLUMN(x,y) \
+{ int u, v; \
+ if (o >= width) \
+ { fprintf(file,"\n"); \
+ fprintf(file,"%*s",indent,""); \
+ if (coord > 0) \
+ { if (sa <= aend) \
+ fprintf(file," %*d",coord,sa); \
+ else \
+ fprintf(file," %*s",coord,""); \
+ fprintf(file," %s\n",Abuf); \
+ fprintf(file,"%*s %*s %s\n",indent,"",coord,"",Dbuf); \
+ fprintf(file,"%*s",indent,""); \
+ if (sb <= bend) \
+ fprintf(file," %*d",coord,sb); \
+ else \
+ fprintf(file," %*s",coord,""); \
+ fprintf(file," %s",Bbuf); \
+ } \
+ else \
+ { fprintf(file," %s\n",Abuf); \
+ fprintf(file,"%*s %s\n",indent,"",Dbuf); \
+ fprintf(file,"%*s %s",indent,"",Bbuf); \
+ } \
+ fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match)); \
+ o = 0; \
+ sa = i; \
+ sb = j; \
+ match = diff = 0; \
+ } \
+ u = (x); \
+ v = (y); \
+ if (u == 4 || v == 4) \
+ Dbuf[o] = ' '; \
+ else if (u == v) \
+ Dbuf[o] = mtag; \
+ else \
+ Dbuf[o] = dtag; \
+ Abuf[o] = N2A[u]; \
+ Bbuf[o] = N2A[v]; \
+ o += 1; \
+}
+
+ a = align->aseq - 1;
+ b = align->bseq - 1;
+
+ o = 0;
+ i = j = 1;
+
+ prefa = align->path->abpos;
+ prefb = align->path->bbpos;
+
+ if (prefa > border)
+ { i = prefa-(border-1);
+ prefa = border;
+ }
+ if (prefb > border)
+ { j = prefb-(border-1);
+ prefb = border;
+ }
+
+ sa = i;
+ sb = j;
+ mtag = ':';
+ dtag = ':';
+
+ while (prefa > prefb)
+ { COLUMN(a[i],4)
+ i += 1;
+ prefa -= 1;
+ }
+ while (prefb > prefa)
+ { COLUMN(4,b[j])
+ j += 1;
+ prefb -= 1;
+ }
+ while (prefa > 0)
+ { COLUMN(a[i],b[j])
+ i += 1;
+ j += 1;
+ prefa -= 1;
+ }
+
+ mtag = '[';
+ if (prefb > 0)
+ COLUMN(5,5)
+
+ mtag = '|';
+ dtag = '*';
+
+ match = diff = 0;
+
+ { int p, c; /* Output columns of alignment til reach trace end */
+
+ for (c = 0; c < tlen; c++)
+ if ((p = trace[c]) < 0)
+ { p = -p;
+ while (i != p)
+ { COLUMN(a[i],b[j])
+ if (a[i] == b[j])
+ match += 1;
+ else
+ diff += 1;
+ i += 1;
+ j += 1;
+ }
+ COLUMN(7,b[j])
+ j += 1;
+ diff += 1;
+ }
+ else
+ { while (j != p)
+ { COLUMN(a[i],b[j])
+ if (a[i] == b[j])
+ match += 1;
+ else
+ diff += 1;
+ i += 1;
+ j += 1;
+ }
+ COLUMN(a[i],7)
+ i += 1;
+ diff += 1;
+ }
+ p = align->path->aepos;
+ while (i <= p)
+ { COLUMN(a[i],b[j])
+ if (a[i] == b[j])
+ match += 1;
+ else
+ diff += 1;
+ i += 1;
+ j += 1;
+ }
+ }
+
+ { int c; /* Output remaining column including unaligned suffix */
+
+ mtag = ']';
+ if (a[i] != 4 && b[j] != 4 && border > 0)
+ COLUMN(6,6)
+
+ mtag = ':';
+ dtag = ':';
+
+ c = 0;
+ while (c < border && (a[i] != 4 || b[j] != 4))
+ { if (a[i] != 4)
+ if (b[j] != 4)
+ { COLUMN(a[i],b[j])
+ i += 1;
+ j += 1;
+ }
+ else
+ { COLUMN(a[i],4)
+ i += 1;
+ }
+ else
+ { COLUMN(4,b[j])
+ j += 1;
+ }
+ c += 1;
+ }
+ }
+
+ /* Print remainder of buffered col.s */
+
+ fprintf(file,"\n");
+ fprintf(file,"%*s",indent,"");
+ if (coord > 0)
+ { if (sa <= aend)
+ fprintf(file," %*d",coord,sa);
+ else
+ fprintf(file," %*s",coord,"");
+ fprintf(file," %.*s\n",o,Abuf);
+ fprintf(file,"%*s %*s %.*s\n",indent,"",coord,"",o,Dbuf);
+ fprintf(file,"%*s",indent,"");
+ if (sb <= bend)
+ fprintf(file," %*d",coord,sb);
+ else
+ fprintf(file," %*s",coord,"");
+ fprintf(file," %.*s",o,Bbuf);
+ }
+ else
+ { fprintf(file," %.*s\n",o,Abuf);
+ fprintf(file,"%*s %.*s\n",indent,"",o,Dbuf);
+ fprintf(file,"%*s %.*s",indent,"",o,Bbuf);
+ }
+ if (diff+match > 0)
+ fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match));
+ else
+ fprintf(file,"\n");
+
+ fflush(file);
+}
+
+void Print_Reference(FILE *file, Alignment *align, Work_Data *ework,
+ int indent, int block, int border, int upper, int coord)
+{ _Work_Data *work = (_Work_Data *) ework;
+ int *trace = align->path->trace;
+ int tlen = align->path->tlen;
+
+ char *Abuf, *Bbuf, *Dbuf;
+ int i, j, o;
+ char *a, *b;
+ char mtag, dtag;
+ int prefa, prefb;
+ int aend, bend;
+ int sa, sb, s0;
+ int match, diff;
+ char *N2A;
+ int vmax;
+
+ if (trace == NULL) return;
+
+#ifdef SHOW_TRACE
+ fprintf(file,"\nTrace:\n");
+ for (i = 0; i < tlen; i++)
+ fprintf(file," %3d\n",trace[i]);
+#endif
+
+ vmax = work->vecmax/3;
+ o = sizeof(char)*6*(block+1);
+ if (o > vmax)
+ { enlarge_vector(work,3*o);
+ vmax = work->vecmax/3;
+ }
+
+ Abuf = (char *) work->vector;
+ Bbuf = Abuf + vmax;
+ Dbuf = Bbuf + vmax;
+
+ if (upper)
+ N2A = ToU;
+ else
+ N2A = ToL;
+
+ aend = align->path->aepos;
+ bend = align->path->bepos;
+
+#define BLOCK(x,y) \
+{ int u, v; \
+ if (i%block == 1 && i != s0 && x != 7 && o > 0) \
+ { fprintf(file,"\n"); \
+ fprintf(file,"%*s",indent,""); \
+ if (coord > 0) \
+ { if (sa <= aend) \
+ fprintf(file," %*d",coord,sa); \
+ else \
+ fprintf(file," %*s",coord,""); \
+ fprintf(file," %.*s\n",o,Abuf); \
+ fprintf(file,"%*s %*s %.*s\n",indent,"",coord,"",o,Dbuf); \
+ fprintf(file,"%*s",indent,""); \
+ if (sb <= bend) \
+ fprintf(file," %*d",coord,sb); \
+ else \
+ fprintf(file," %*s",coord,""); \
+ fprintf(file," %.*s",o,Bbuf); \
+ } \
+ else \
+ { fprintf(file," %.*s\n",o,Abuf); \
+ fprintf(file,"%*s %.*s\n",indent,"",o,Dbuf); \
+ fprintf(file,"%*s %.*s",indent,"",o,Bbuf); \
+ } \
+ fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match)); \
+ o = 0; \
+ sa = i; \
+ sb = j; \
+ match = diff = 0; \
+ } \
+ u = (x); \
+ v = (y); \
+ if (u == 4 || v == 4) \
+ Dbuf[o] = ' '; \
+ else if (u == v) \
+ Dbuf[o] = mtag; \
+ else \
+ Dbuf[o] = dtag; \
+ Abuf[o] = N2A[u]; \
+ Bbuf[o] = N2A[v]; \
+ o += 1; \
+ if (o >= vmax) \
+ { enlarge_vector(work,3*o); \
+ vmax = work->vecmax/3; \
+ memmove(work->vector+2*vmax,Dbuf,o); \
+ memmove(work->vector+vmax,Bbuf,o); \
+ memmove(work->vector,Abuf,o); \
+ Abuf = (char *) work->vector; \
+ Bbuf = Abuf + vmax; \
+ Dbuf = Bbuf + vmax; \
+ } \
+}
+
+ a = align->aseq - 1;
+ b = align->bseq - 1;
+
+ o = 0;
+ i = j = 1;
+
+ prefa = align->path->abpos;
+ prefb = align->path->bbpos;
+
+ if (prefa > border)
+ { i = prefa-(border-1);
+ prefa = border;
+ }
+ if (prefb > border)
+ { j = prefb-(border-1);
+ prefb = border;
+ }
+
+ s0 = i;
+ sa = i;
+ sb = j;
+ mtag = ':';
+ dtag = ':';
+
+ while (prefa > prefb)
+ { BLOCK(a[i],4)
+ i += 1;
+ prefa -= 1;
+ }
+ while (prefb > prefa)
+ { BLOCK(4,b[j])
+ j += 1;
+ prefb -= 1;
+ }
+ while (prefa > 0)
+ { BLOCK(a[i],b[j])
+ i += 1;
+ j += 1;
+ prefa -= 1;
+ }
+
+ mtag = '[';
+ if (prefb > 0)
+ BLOCK(5,5)
+
+ mtag = '|';
+ dtag = '*';
+
+ match = diff = 0;
+
+ { int p, c; /* Output columns of alignment til reach trace end */
+
+ for (c = 0; c < tlen; c++)
+ if ((p = trace[c]) < 0)
+ { p = -p;
+ while (i != p)
+ { BLOCK(a[i],b[j])
+ if (a[i] == b[j])
+ match += 1;
+ else
+ diff += 1;
+ i += 1;
+ j += 1;
+ }
+ BLOCK(7,b[j])
+ j += 1;
+ diff += 1;
+ }
+ else
+ { while (j != p)
+ { BLOCK(a[i],b[j])
+ if (a[i] == b[j])
+ match += 1;
+ else
+ diff += 1;
+ i += 1;
+ j += 1;
+ }
+ BLOCK(a[i],7)
+ i += 1;
+ diff += 1;
+ }
+ p = align->path->aepos;
+ while (i <= p)
+ { BLOCK(a[i],b[j])
+ if (a[i] == b[j])
+ match += 1;
+ else
+ diff += 1;
+ i += 1;
+ j += 1;
+ }
+ }
+
+ { int c; /* Output remaining column including unaligned suffix */
+
+ mtag = ']';
+ if (a[i] != 4 && b[j] != 4 && border > 0)
+ BLOCK(6,6)
+
+ mtag = ':';
+ dtag = ':';
+
+ c = 0;
+ while (c < border && (a[i] != 4 || b[j] != 4))
+ { if (a[i] != 4)
+ if (b[j] != 4)
+ { BLOCK(a[i],b[j])
+ i += 1;
+ j += 1;
+ }
+ else
+ { BLOCK(a[i],4)
+ i += 1;
+ }
+ else
+ { BLOCK(4,b[j])
+ j += 1;
+ }
+ c += 1;
+ }
+ }
+
+ /* Print remainder of buffered col.s */
+
+ fprintf(file,"\n");
+ fprintf(file,"%*s",indent,"");
+ if (coord > 0)
+ { if (sa <= aend)
+ fprintf(file," %*d",coord,sa);
+ else
+ fprintf(file," %*s",coord,"");
+ fprintf(file," %.*s\n",o,Abuf);
+ fprintf(file,"%*s %*s %.*s\n",indent,"",coord,"",o,Dbuf);
+ fprintf(file,"%*s",indent,"");
+ if (sb <= bend)
+ fprintf(file," %*d",coord,sb);
+ else
+ fprintf(file," %*s",coord,"");
+ fprintf(file," %.*s",o,Bbuf);
+ }
+ else
+ { fprintf(file," %.*s\n",o,Abuf);
+ fprintf(file,"%*s %.*s\n",indent,"",o,Dbuf);
+ fprintf(file,"%*s %.*s",indent,"",o,Bbuf);
+ }
+ if (diff+match > 0)
+ fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match));
+ else
+ fprintf(file,"\n");
+
+ fflush(file);
+}
+
+/* Print an ASCII representation of the overlap in align between fragments
+ a and b to given file. */
+
+static inline void repchar(FILE *file, int symbol, int rep)
+{ while (rep-- > 0)
+ fputc(symbol,file);
+}
+
+void Alignment_Cartoon(FILE *file, Alignment *align, int indent, int coord)
+{ int alen = align->alen;
+ int blen = align->blen;
+ Path *path = align->path;
+ int comp = COMP(align->flags);
+ int w;
+
+ fprintf(file,"%*s",indent,"");
+ if (path->abpos > 0)
+ fprintf(file," %*d ",coord,path->abpos);
+ else
+ fprintf(file,"%*s",coord+5,"");
+ if (path->aepos < alen)
+ fprintf(file,"%*s%d",coord+8,"",alen-path->aepos);
+ fprintf(file,"\n");
+
+ fprintf(file,"%*s",indent,"");
+ if (path->abpos > 0)
+ { fprintf(file,"A ");
+ w = Number_Digits((int64) path->abpos);
+ repchar(file,' ',coord-w);
+ repchar(file,'=',w+3);
+ fputc('+',file);
+ repchar(file,'-',coord+5);
+ }
+ else
+ { fprintf(file,"A %*s",coord+4,"");
+ repchar(file,'-',coord+5);
+ }
+
+ if (path->aepos < alen)
+ { fputc('+',file);
+ w = Number_Digits((int64) (alen-path->aepos));
+ repchar(file,'=',w+2);
+ fputc('>',file);
+ repchar(file,' ',w);
+ }
+ else
+ { fputc('>',file);
+ repchar(file,' ',coord+3);
+ }
+
+ { int asub, bsub;
+
+ asub = path->aepos - path->abpos;
+ bsub = path->bepos - path->bbpos;
+ fprintf(file," dif/(len1+len2) = %d/(%d+%d) = %5.2f%%\n",
+ path->diffs,asub,bsub,(200.*path->diffs)/(asub+bsub));
+ }
+
+ { int sym1e, sym2e;
+ int sym1p, sym2p;
+
+ if (comp > 0)
+ { sym1p = '<'; sym2p = '-'; sym1e = '<'; sym2e = '='; }
+ else
+ { sym1p = '-'; sym2p = '>'; sym1e = '='; sym2e = '>'; }
+
+ fprintf(file,"%*s",indent,"");
+ if (path->bbpos > 0)
+ { fprintf(file,"B ");
+ w = Number_Digits((int64) path->bbpos);
+ repchar(file,' ',coord-w);
+ fputc(sym1e,file);
+ repchar(file,'=',w+2);
+ fputc('+',file);
+ repchar(file,'-',coord+5);
+ }
+ else
+ { fprintf(file,"B ");
+ repchar(file,' ',coord+3);
+ fputc(sym1p,file);
+ repchar(file,'-',coord+5);
+ }
+ if (path->bepos < blen)
+ { fprintf(file,"+");
+ w = Number_Digits((int64) (blen-path->bepos));
+ repchar(file,'=',w+2);
+ fprintf(file,"%c\n",sym2e);
+ }
+ else
+ fprintf(file,"%c\n",sym2p);
+ }
+
+ fprintf(file,"%*s",indent,"");
+ if (path->bbpos > 0)
+ fprintf(file," %*d ",coord,path->bbpos);
+ else
+ fprintf(file,"%*s",coord+5,"");
+ if (path->bepos < blen)
+ fprintf(file,"%*s%d",coord+8,"",blen-path->bepos);
+ fprintf(file,"\n");
+
+ fflush(file);
+}
+
+
+/****************************************************************************************\
+* *
+* O(ND) trace algorithm *
+* *
+\****************************************************************************************/
+
+#ifdef DEBUG_AWAVE
+
+static void print_awave(int *V, int low, int hgh)
+{ int k;
+
+ printf(" [%6d,%6d]: ",low,hgh);
+ for (k = low; k <= hgh; k++)
+ printf(" %3d",V[k]);
+ printf("\n");
+ fflush(stdout);
+}
+
+#endif
+
+#ifdef DEBUG_ALIGN
+
+static int depth = 0;
+
+#endif
+
+typedef struct
+ { int *Stop; // Ongoing stack of alignment indels
+ char *Aabs, *Babs; // Absolute base of A and B sequences
+
+ int **PVF, **PHF; // List of waves for iterative np algorithms
+ int mida, midb; // mid point division for mid-point algorithms
+
+ int *VF, *VB; // Forward/Reverse waves for nd algorithms
+ // (defunct: were used for O(nd) algorithms)
+ } Trace_Waves;
+
+static int dandc_nd(char *A, int M, char *B, int N, Trace_Waves *wave)
+{ int x, y;
+ int D;
+
+#ifdef DEBUG_ALIGN
+ printf("%*s %ld,%ld: %d vs %d\n",depth,"",A-wave->Aabs,B-wave->Babs,M,N);
+#endif
+
+ if (M <= 0)
+ { x = (wave->Aabs-A)-1;
+ for (y = 1; y <= N; y++)
+ { *wave->Stop++ = x;
+#ifdef DEBUG_SCRIPT
+ printf("%*s *I %ld(%ld)\n",depth,"",y+(B-wave->Babs),(A-wave->Aabs)+1);
+#endif
+ }
+ return (N);
+ }
+ if (N <= 0)
+ { y = (B-wave->Babs)+1;
+ for (x = 1; x <= M; x++)
+ { *wave->Stop++ = y;
+#ifdef DEBUG_SCRIPT
+ printf("%*s *D %ld(%ld)\n",depth,"",x+(A-wave->Aabs),(B-wave->Babs)+1);
+#endif
+ }
+ return (M);
+ }
+
+ { int *VF = wave->VF;
+ int *VB = wave->VB;
+ int flow; // fhgh == D !
+ int blow, bhgh;
+ char *a;
+
+ y = 0;
+ if (N < M)
+ while (y < N && B[y] == A[y])
+ y += 1;
+ else
+ { while (y < M && B[y] == A[y])
+ y += 1;
+ if (y >= M && N == M)
+ return (0);
+ }
+
+ flow = 0;
+ VF[0] = y;
+ VF[-1] = -2;
+
+ x = N-M;
+ a = A-x;
+ y = N-1;
+ if (N > M)
+ while (y >= x && B[y] == a[y])
+ y -= 1;
+ else
+ while (y >= 0 && B[y] == a[y])
+ y -= 1;
+
+ blow = bhgh = -x;
+ VB += x;
+ VB[blow] = y;
+ VB[blow-1] = N+1;
+
+ for (D = 1; 1; D += 1)
+ { int k, r;
+ int am, ac, ap;
+
+ // Forward wave
+
+ flow -= 1;
+ am = ac = VF[flow-1] = -2;
+
+ a = A + D;
+ x = M - D;
+ for (k = D; k >= flow; k--)
+ { ap = ac;
+ ac = am+1;
+ am = VF[k-1];
+
+ if (ac < am)
+ if (ap < am)
+ y = am;
+ else
+ y = ap;
+ else
+ if (ap < ac)
+ y = ac;
+ else
+ y = ap;
+
+ if (blow <= k && k <= bhgh)
+ { r = VB[k];
+ if (y > r)
+ { D = (D<<1)-1;
+ if (ap > r)
+ y = ap;
+ else if (ac > r)
+ y = ac;
+ else
+ y = r+1;
+ x = k+y;
+ goto OVERLAP2;
+ }
+ }
+
+ if (N < x)
+ while (y < N && B[y] == a[y])
+ y += 1;
+ else
+ while (y < x && B[y] == a[y])
+ y += 1;
+
+ VF[k] = y;
+ a -= 1;
+ x += 1;
+ }
+
+#ifdef DEBUG_AWAVE
+ print_awave(VF,flow,D);
+#endif
+
+ // Reverse Wave
+
+ bhgh += 1;
+ blow -= 1;
+ am = ac = VB[blow-1] = N+1;
+
+ a = A + bhgh;
+ x = -bhgh;
+ for (k = bhgh; k >= blow; k--)
+ { ap = ac+1;
+ ac = am;
+ am = VB[k-1];
+
+ if (ac > am)
+ if (ap > am)
+ y = am;
+ else
+ y = ap;
+ else
+ if (ap > ac)
+ y = ac;
+ else
+ y = ap;
+
+ if (flow <= k && k <= D)
+ { r = VF[k];
+ if (y <= r)
+ { D = (D << 1);
+ if (ap <= r)
+ y = ap;
+ else if (ac <= r)
+ y = ac;
+ else
+ y = r;
+ x = k+y;
+ goto OVERLAP2;
+ }
+ }
+
+ y -= 1;
+ if (x > 0)
+ while (y >= x && B[y] == a[y])
+ y -= 1;
+ else
+ while (y >= 0 && B[y] == a[y])
+ y -= 1;
+
+ VB[k] = y;
+ a -= 1;
+ x += 1;
+ }
+
+#ifdef DEBUG_AWAVE
+ print_awave(VB,blow,bhgh);
+#endif
+ }
+ }
+
+OVERLAP2:
+
+#ifdef DEBUG_ALIGN
+ printf("%*s (%d,%d) @ %d\n",depth,"",x,y,D);
+ fflush(stdout);
+#endif
+ if (D > 1)
+ {
+#ifdef DEBUG_ALIGN
+ depth += 2;
+#endif
+ dandc_nd(A,x,B,y,wave);
+ dandc_nd(A+x,M-x,B+y,N-y,wave);
+#ifdef DEBUG_ALIGN
+ depth -= 2;
+#endif
+ }
+ else if (D == 1)
+ { if (M > N)
+ { *wave->Stop++ = (B-wave->Babs)+y+1;
+#ifdef DEBUG_SCRIPT
+ printf("%*s D %ld(%ld)\n",depth,"",(A-wave->Aabs)+x,(B-wave->Babs)+y+1);
+#endif
+ }
+ else if (M < N)
+ { *wave->Stop++ = (wave->Aabs-A)-x-1;
+#ifdef DEBUG_SCRIPT
+ printf("%*s I %ld(%ld)\n",depth,"",(B-wave->Babs)+y,(A-wave->Aabs)+x+1);
+#endif
+ }
+#ifdef DEBUG_SCRIPT
+ else
+ printf("%*s %ld S %ld\n",depth,"",(wave->Aabs-A)+x,(B-wave->Babs)+y);
+#endif
+ }
+
+ return (D);
+}
+
+
+static void Compute_Trace_ND_ALL(Alignment *align, Work_Data *ework)
+{ _Work_Data *work = (_Work_Data *) ework;
+ Trace_Waves wave;
+
+ int L, D;
+ int asub, bsub;
+ Path *path;
+ int *trace;
+
+ path = align->path;
+ asub = path->aepos-path->abpos;
+ bsub = path->bepos-path->bbpos;
+
+ if (asub < bsub)
+ L = bsub;
+ else
+ L = asub;
+ L *= sizeof(int);
+ if (L > work->tramax)
+ enlarge_trace(work,L);
+
+ trace = wave.Stop = ((int *) work->trace);
+
+ D = 2*(path->diffs + 4)*sizeof(int);
+ if (D > work->vecmax)
+ enlarge_vector(work,D);
+
+ D = (path->diffs+3)/2;
+ wave.VF = ((int *) work->vector) + (D+1);
+ wave.VB = wave.VF + (2*D+1);
+
+ wave.Aabs = align->aseq;
+ wave.Babs = align->bseq;
+
+ path->diffs = dandc_nd(align->aseq+path->abpos,path->aepos-path->abpos,
+ align->bseq+path->bbpos,path->bepos-path->bbpos,&wave);
+ path->trace = trace;
+ path->tlen = wave.Stop - trace;
+}
+
+
+/****************************************************************************************\
+* *
+* O(NP) tracing algorithms *
+* *
+\****************************************************************************************/
+
+/* Iterative O(np) algorithm for finding the alignment between two substrings (specified
+ by a Path record). The variation includes handling substitutions and guarantees
+ to find left-most alignments so that low complexity runs are always aligned in
+ the same way.
+*/
+
+#ifdef DEBUG_ALIGN
+
+static int ToA[4] = { 'a', 'c', 'g', 't' };
+
+#endif
+
+static int iter_np(char *A, int M, char *B, int N, Trace_Waves *wave)
+{ int **PVF = wave->PVF;
+ int **PHF = wave->PHF;
+ int D;
+ int del = M-N;
+
+ { int *F0, *F1, *F2;
+ int *HF;
+ int low, hgh, pos;
+
+#ifdef DEBUG_ALIGN
+ printf("\n%*s BASE %ld,%ld: %d vs %d\n",depth,"",A-wave->Aabs,B-wave->Babs,M,N);
+ printf("%*s A = ",depth,"");
+ for (D = 0; D < M; D++)
+ printf("%c",ToA[(int) A[D]]);
+ printf("\n");
+ printf("%*s B = ",depth,"");
+ for (D = 0; D < N; D++)
+ printf("%c",ToA[(int) B[D]]);
+ printf("\n");
+#endif
+
+ if (del >= 0)
+ { low = 0;
+ hgh = del;
+ }
+ else
+ { low = del;
+ hgh = 0;
+ }
+ if (wave->Aabs == wave->Babs)
+ pos = B-A;
+ else
+ pos = -INT32_MAX;
+
+ F1 = PVF[-2];
+ F0 = PVF[-1];
+
+ for (D = low-1; D <= hgh+1; D++)
+ F1[D] = F0[D] = -2;
+ F0[0] = -1;
+
+ low += 1;
+ hgh -= 1;
+
+ for (D = 0; 1; D += 1)
+ { int k, i, j;
+ int am, ac, ap;
+ char *a;
+
+ F2 = F1;
+ F1 = F0;
+ F0 = PVF[D];
+ HF = PHF[D];
+
+ if ((D & 0x1) == 0)
+ { hgh += 1;
+ low -= 1;
+ if (low <= pos)
+ low += 1;
+ }
+ F0[hgh+1] = F0[low-1] = -2;
+
+#define FS_MOVE(mdir,pdir) \
+ ac = F1[k]+1; \
+ if (ac < am) \
+ if (ap < am) \
+ { HF[k] = mdir; \
+ j = am; \
+ } \
+ else \
+ { HF[k] = pdir; \
+ j = ap; \
+ } \
+ else \
+ if (ap < ac) \
+ { HF[k] = 0; \
+ j = ac; \
+ } \
+ else \
+ { HF[k] = pdir; \
+ j = ap; \
+ } \
+ \
+ if (N < i) \
+ while (j < N && B[j] == a[j]) \
+ j += 1; \
+ else \
+ while (j < i && B[j] == a[j]) \
+ j += 1; \
+ F0[k] = j;
+
+ j = -2;
+ a = A + hgh;
+ i = M - hgh;
+ for (k = hgh; k > del; k--)
+ { ap = j+1;
+ am = F2[k-1];
+ FS_MOVE(-1,4)
+ a -= 1;
+ i += 1;
+ }
+
+ j = -2;
+ a = A + low;
+ i = M - low;
+ for (k = low; k < del; k++)
+ { ap = F2[k+1]+1;
+ am = j;
+ FS_MOVE(2,1)
+ a += 1;
+ i -= 1;
+ }
+
+ ap = F0[del+1]+1;
+ am = j;
+ FS_MOVE(2,4)
+
+#ifdef DEBUG_AWAVE
+ print_awave(F0,low,hgh);
+ print_awave(HF,low,hgh);
+#endif
+
+ if (F0[del] >= N)
+ break;
+ }
+ }
+
+ { int k, h, m, e, c;
+ char *a;
+ int ap = (wave->Aabs-A)-1;
+ int bp = (B-wave->Babs)+1;
+
+ PHF[0][0] = 3;
+
+ c = N;
+ k = del;
+ e = PHF[D][k];
+ PHF[D][k] = 3;
+ while (e != 3)
+ { h = k+e;
+ if (e > 1)
+ h -= 3;
+ else if (e == 0)
+ D -= 1;
+ else
+ D -= 2;
+ if (h < k) // => e = -1 or 2
+ { a = A + k;
+ if (k < 0)
+ m = -k;
+ else
+ m = 0;
+ if (PVF[D][h] <= c)
+ c = PVF[D][h]-1;
+ while (c >= m && a[c] == B[c])
+ c -= 1;
+ if (e < 1) // => edge is 2, others are 1, and 0
+ { if (c <= PVF[D+2][k+1])
+ { e = 4;
+ h = k+1;
+ D = D+2;
+ }
+ else if (c == PVF[D+1][k])
+ { e = 0;
+ h = k;
+ D = D+1;
+ }
+ else
+ PVF[D][h] = c+1;
+ }
+ else // => edge is 0, others are 1, and 2 (if k != del), 0 (otherwise)
+ { if (k == del)
+ m = D;
+ else
+ m = D-2;
+ if (c <= PVF[m][k+1])
+ { if (k == del)
+ e = 4;
+ else
+ e = 1;
+ h = k+1;
+ D = m;
+ }
+ else if (c == PVF[D-1][k])
+ { e = 0;
+ h = k;
+ D = D-1;
+ }
+ else
+ PVF[D][h] = c+1;
+ }
+ }
+ m = PHF[D][h];
+ PHF[D][h] = e;
+ e = m;
+
+ k = h;
+ }
+
+ k = D = 0;
+ e = PHF[D][k];
+ while (e != 3)
+ { h = k-e;
+ c = PVF[D][k];
+ if (e > 1)
+ h += 3;
+ else if (e == 0)
+ D += 1;
+ else
+ D += 2;
+ if (h > k)
+ *wave->Stop++ = bp+c;
+ else if (h < k)
+ *wave->Stop++ = ap-(c+k);
+ k = h;
+ e = PHF[D][h];
+ }
+
+#ifdef DEBUG_SCRIPT
+ k = D = 0;
+ e = PHF[D][k];
+ while (e != 3)
+ { h = k-e;
+ c = PVF[D][k];
+ if (e > 1)
+ h += 3;
+ else if (e == 0)
+ D += 1;
+ else
+ D += 2;
+ if (h > k)
+ printf("%*s D %d(%d)\n",depth,"",(c-k)-(ap-1),c+bp);
+ else if (h < k)
+ printf("%*s I %d(%d)\n",depth,"",c+(bp-1),(c+k)-ap);
+ else
+ printf("%*s %d S %d\n",depth,"",(c+k)-(ap+1),c+(bp-1));
+ k = h;
+ e = PHF[D][h];
+ }
+#endif
+ }
+
+ return (D + abs(del));
+}
+
+static int middle_np(char *A, int M, char *B, int N, Trace_Waves *wave)
+{ int **PVF = wave->PVF;
+ int **PHF = wave->PHF;
+ int D;
+ int del = M-N;
+
+ { int *F0, *F1, *F2;
+ int *HF;
+ int low, hgh, pos;
+
+#ifdef DEBUG_ALIGN
+ printf("\n%*s BASE %ld,%ld: %d vs %d\n",depth,"",A-wave->Aabs,B-wave->Babs,M,N);
+ printf("%*s A = ",depth,"");
+ for (D = 0; D < M; D++)
+ printf("%c",ToA[(int) A[D]]);
+ printf("\n");
+ printf("%*s B = ",depth,"");
+ for (D = 0; D < N; D++)
+ printf("%c",ToA[(int) B[D]]);
+ printf("\n");
+#endif
+
+ if (del >= 0)
+ { low = 0;
+ hgh = del;
+ }
+ else
+ { low = del;
+ hgh = 0;
+ }
+ if (wave->Aabs == wave->Babs)
+ pos = B-A;
+ else
+ pos = -INT32_MAX;
+
+ F1 = PVF[-2];
+ F0 = PVF[-1];
+
+ for (D = low-1; D <= hgh+1; D++)
+ F1[D] = F0[D] = -2;
+ F0[0] = -1;
+
+ low += 1;
+ hgh -= 1;
+
+ for (D = 0; 1; D += 1)
+ { int k, i, j;
+ int am, ac, ap;
+ char *a;
+
+ F2 = F1;
+ F1 = F0;
+ F0 = PVF[D];
+ HF = PHF[D];
+
+ if ((D & 0x1) == 0)
+ { hgh += 1;
+ low -= 1;
+ if (low <= pos)
+ low += 1;
+ }
+ F0[hgh+1] = F0[low-1] = -2;
+
+ j = -2;
+ a = A + hgh;
+ i = M - hgh;
+ for (k = hgh; k > del; k--)
+ { ap = j+1;
+ am = F2[k-1];
+ FS_MOVE(-1,4)
+ a -= 1;
+ i += 1;
+ }
+
+ j = -2;
+ a = A + low;
+ i = M - low;
+ for (k = low; k < del; k++)
+ { ap = F2[k+1]+1;
+ am = j;
+ FS_MOVE(2,1)
+ a += 1;
+ i -= 1;
+ }
+
+ ap = F0[del+1]+1;
+ am = j;
+ FS_MOVE(2,4)
+
+#ifdef DEBUG_AWAVE
+ print_awave(F0,low,hgh);
+ print_awave(HF,low,hgh);
+#endif
+
+ if (F0[del] >= N)
+ break;
+ }
+ }
+
+ { int k, h, m, e, c;
+ int d, f;
+ char *a;
+
+ d = D + abs(del);
+ c = N;
+ k = del;
+ for (f = d/2; d > f; d--)
+ { e = PHF[D][k];
+ h = k+e;
+ if (e > 1)
+ h -= 3;
+ else if (e == 0)
+ D -= 1;
+ else
+ D -= 2;
+ if (h < k) // => e = -1 or 2
+ { a = A + k;
+ if (k < 0)
+ m = -k;
+ else
+ m = 0;
+ if (PVF[D][h] <= c)
+ c = PVF[D][h]-1;
+ while (c >= m && a[c] == B[c])
+ c -= 1;
+ if (e < 1) // => edge is 2, others are 1, and 0
+ { if (c <= PVF[D+2][k+1])
+ { e = 4;
+ h = k+1;
+ D = D+2;
+ }
+ else if (c == PVF[D+1][k])
+ { e = 0;
+ h = k;
+ D = D+1;
+ }
+ else
+ PVF[D][h] = c+1;
+ }
+ else // => edge is 0, others are 1, and 2 (if k != del), 0 (otherwise)
+ { if (k == del)
+ m = D;
+ else
+ m = D-2;
+ if (c <= PVF[m][k+1])
+ { if (k == del)
+ e = 4;
+ else
+ e = 1;
+ h = k+1;
+ D = m;
+ }
+ else if (c == PVF[D-1][k])
+ { e = 0;
+ h = k;
+ D = D-1;
+ }
+ else
+ PVF[D][h] = c+1;
+ }
+ }
+ k = h;
+ }
+
+ wave->midb = (B-wave->Babs) + PVF[D][k];
+ wave->mida = (A-wave->Aabs) + k + PVF[D][k];
+ }
+
+ return (1);
+}
+
+
+/****************************************************************************************\
+* *
+* COMPUTE_TRACE FLAVORS *
+* *
+\****************************************************************************************/
+
+void Compute_Trace_ALL(Alignment *align, Work_Data *ework)
+{ _Work_Data *work = (_Work_Data *) ework;
+ Trace_Waves wave;
+
+ Path *path;
+ char *aseq, *bseq;
+ int M, N;
+
+ path = align->path;
+ aseq = align->aseq;
+ bseq = align->bseq;
+
+ M = path->aepos-path->abpos;
+ N = path->bepos-path->bbpos;
+
+ { int64 s;
+ int d;
+ int dmax;
+ int **PVF, **PHF;
+
+ if (M < N)
+ s = N;
+ else
+ s = M;
+ s *= sizeof(int);
+ if (s > work->tramax)
+ enlarge_trace(work,s);
+
+ dmax = path->diffs - abs(M-N);
+
+ s = (dmax+3)*2*((M+N+3)*sizeof(int) + sizeof(int *));
+
+ if (s > 256000000)
+ { Compute_Trace_ND_ALL(align,ework);
+ return;
+ }
+
+ if (s > work->vecmax)
+ enlarge_vector(work,s);
+
+ wave.PVF = PVF = ((int **) (work->vector)) + 2;
+ wave.PHF = PHF = PVF + (dmax+3);
+
+ s = M+N+3;
+ PVF[-2] = ((int *) (PHF + (dmax+1))) + (N+1);
+ for (d = -1; d <= dmax; d++)
+ PVF[d] = PVF[d-1] + s;
+ PHF[-2] = PVF[dmax] + s;
+ for (d = -1; d <= dmax; d++)
+ PHF[d] = PHF[d-1] + s;
+ }
+
+ wave.Stop = ((int *) work->trace);
+ wave.Aabs = aseq;
+ wave.Babs = bseq;
+
+ path->diffs = iter_np(aseq+path->abpos,M,bseq+path->bbpos,N,&wave);
+ path->trace = work->trace;
+ path->tlen = wave.Stop - ((int *) path->trace);
+}
+
+void Compute_Trace_PTS(Alignment *align, Work_Data *ework, int trace_spacing)
+{ _Work_Data *work = (_Work_Data *) ework;
+ Trace_Waves wave;
+
+ Path *path;
+ char *aseq, *bseq;
+ uint16 *points;
+ int tlen;
+ int ab, bb;
+ int ae, be;
+ int diffs;
+
+ path = align->path;
+ aseq = align->aseq;
+ bseq = align->bseq;
+ tlen = path->tlen;
+ points = (uint16 *) path->trace;
+
+ { int64 s;
+ int d;
+ int M, N;
+ int dmax, nmax;
+ int **PVF, **PHF;
+
+ M = path->aepos-path->abpos;
+ N = path->bepos-path->bbpos;
+ if (M < N)
+ s = N*sizeof(int);
+ else
+ s = M*sizeof(int);
+ if (s > work->tramax)
+ enlarge_trace(work,s);
+
+ nmax = 0;
+#ifdef DELTAS
+ dmax = 0;
+ for (d = 1; d < tlen; d += 2)
+ { if (points[d-1] > dmax)
+ dmax = points[d-1];
+#else
+ for (d = 0; d < tlen; d++)
+ {
+#endif
+ if (points[d] > nmax)
+ nmax = points[d];
+ }
+ if (tlen <= 1)
+ nmax = N;
+#ifdef DELTAS
+ if (points[d-1] > dmax)
+ dmax = points[d-1];
+#else
+ dmax = nmax;
+#endif
+
+ s = (dmax+3)*2*((trace_spacing+nmax+3)*sizeof(int) + sizeof(int *));
+
+ if (s > work->vecmax)
+ enlarge_vector(work,s);
+
+ wave.PVF = PVF = ((int **) (work->vector)) + 2;
+ wave.PHF = PHF = PVF + (dmax+3);
+
+ s = trace_spacing+nmax+3;
+ PVF[-2] = ((int *) (PHF + (dmax+1))) + (nmax+1);
+ for (d = -1; d <= dmax; d++)
+ PVF[d] = PVF[d-1] + s;
+ PHF[-2] = PVF[dmax] + s;
+ for (d = -1; d <= dmax; d++)
+ PHF[d] = PHF[d-1] + s;
+ }
+
+ wave.Stop = (int *) (work->trace);
+ wave.Aabs = aseq;
+ wave.Babs = bseq;
+
+ { int i;
+
+ diffs = 0;
+ ab = path->abpos;
+ ae = (ab/trace_spacing)*trace_spacing;
+ bb = path->bbpos;
+#ifdef DELTAS
+ tlen -= 2;
+ for (i = 1; i < tlen; i += 2)
+#else
+ tlen -= 1;
+ for (i = 0; i < tlen; i++)
+#endif
+ { ae = ae + trace_spacing;
+ be = bb + points[i];
+ diffs += iter_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave);
+ ab = ae;
+ bb = be;
+ }
+ ae = path->aepos;
+ be = path->bepos;
+ diffs += iter_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave);
+ }
+
+ path->trace = work->trace;
+ path->tlen = wave.Stop - ((int *) path->trace);
+ path->diffs = diffs;
+}
+
+void Compute_Trace_MID(Alignment *align, Work_Data *ework, int trace_spacing)
+{ _Work_Data *work = (_Work_Data *) ework;
+ Trace_Waves wave;
+
+ Path *path;
+ char *aseq, *bseq;
+ uint16 *points;
+ int tlen;
+ int ab, bb;
+ int ae, be;
+ int diffs;
+
+ path = align->path;
+ aseq = align->aseq;
+ bseq = align->bseq;
+ tlen = path->tlen;
+ points = (uint16 *) path->trace;
+
+ { int64 s;
+ int d;
+ int M, N;
+ int dmax, nmax;
+ int **PVF, **PHF;
+
+ M = path->aepos-path->abpos;
+ N = path->bepos-path->bbpos;
+ if (M < N)
+ s = N*sizeof(int);
+ else
+ s = M*sizeof(int);
+ if (s > work->tramax)
+ enlarge_trace(work,s);
+
+ nmax = 0;
+#ifdef DELTAS
+ dmax = 0;
+ for (d = 1; d < tlen; d += 2)
+ { if (points[d-1] > dmax)
+ dmax = points[d-1];
+#else
+ for (d = 0; d < tlen; d++)
+ {
+#endif
+ if (points[d] > nmax)
+ nmax = points[d];
+ }
+ if (tlen <= 1)
+ nmax = N;
+#ifdef DELTAS
+ if (points[d-1] > dmax)
+ dmax = points[d-1];
+#else
+ dmax = nmax;
+#endif
+
+ s = (dmax+3)*4*((trace_spacing+nmax+3)*sizeof(int) + sizeof(int *));
+
+ if (s > work->vecmax)
+ enlarge_vector(work,s);
+
+ wave.PVF = PVF = ((int **) (work->vector)) + 2;
+ wave.PHF = PHF = PVF + (dmax+3);
+
+ s = trace_spacing+nmax+3;
+ PVF[-2] = ((int *) (PHF + (dmax+1))) + (nmax+1);
+ for (d = -1; d <= dmax; d++)
+ PVF[d] = PVF[d-1] + s;
+ PHF[-2] = PVF[dmax] + s;
+ for (d = -1; d <= dmax; d++)
+ PHF[d] = PHF[d-1] + s;
+ }
+
+ wave.Stop = ((int *) work->trace);
+ wave.Aabs = aseq;
+ wave.Babs = bseq;
+
+ { int i;
+ int as, bs;
+ int af, bf;
+
+ diffs = 0;
+ ab = as = af = path->abpos;
+ ae = (ab/trace_spacing)*trace_spacing;
+ bb = bs = bf = path->bbpos;
+#ifdef DELTAS
+ tlen -= 2;
+ for (i = 1; i < tlen; i += 2)
+#else
+ tlen -= 1;
+ for (i = 0; i < tlen; i++)
+#endif
+ { ae = ae + trace_spacing;
+ be = bb + points[i];
+ if (middle_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave))
+ { af = wave.mida;
+ bf = wave.midb;
+ diffs += iter_np(aseq+as,af-as,bseq+bs,bf-bs,&wave);
+ ab = ae;
+ bb = be;
+ as = af;
+ bs = bf;
+ }
+ }
+ ae = path->aepos;
+ be = path->bepos;
+ if (middle_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave))
+ { af = wave.mida;
+ bf = wave.midb;
+ diffs += iter_np(aseq+as,af-as,bseq+bs,bf-bs,&wave);
+ as = af;
+ bs = bf;
+ }
+ diffs += iter_np(aseq+af,ae-as,bseq+bf,be-bs,&wave);
+ }
+
+ path->trace = work->trace;
+ path->tlen = wave.Stop - ((int *) path->trace);
+ path->diffs = diffs;
+}
diff --git a/src/cpp/align.h b/src/cpp/align.h
new file mode 100644
index 0000000..d0b2d5b
--- /dev/null
+++ b/src/cpp/align.h
@@ -0,0 +1,331 @@
+/************************************************************************************\
+* *
+* Copyright (c) 2014, Dr. Eugene W. Myers (EWM). 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. *
+* *
+* · The name of EWM may not be used to endorse or promote products derived from *
+* this software without specific prior written permission. *
+* *
+* THIS SOFTWARE IS PROVIDED BY EWM ”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 EWM 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. *
+* *
+* For any issues regarding this software and its use, contact EWM at: *
+* *
+* Eugene W. Myers Jr. *
+* Bautzner Str. 122e *
+* 01099 Dresden *
+* GERMANY *
+* Email: gene.myers at gmail.com *
+* *
+\************************************************************************************/
+
+/*******************************************************************************************
+ *
+ * Local alignment module. Routines for finding local alignments given a seed position,
+ * representing such an l.a. with its interval and a set of pass-thru points, so that
+ * a detailed alignment can be efficiently computed on demand.
+ *
+ * All routines work on a numeric representation of DNA sequences, i.e. 0 for A, 1 for C,
+ * 2 for G, and 3 for T.
+ *
+ * Author: Gene Myers
+ * Date : July 2013
+ *
+ ********************************************************************************************/
+
+#include "DB.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TRACE_XOVR 125 // If the trace spacing is not more than this value, then can
+ // and do compress traces pts to 8-bit unsigned ints
+
+#ifndef _A_MODULE
+
+#define _A_MODULE
+
+/*** PATH ABSTRACTION:
+
+ Coordinates are *between* characters where 0 is the tick just before the first char,
+ 1 is the tick between the first and second character, and so on. Our data structure
+ is called a Path refering to its conceptualization in an edit graph.
+
+ A local alignment is specified by the point '(abpos,bbpos)' at which its path in
+ the underlying edit graph starts, and the point '(aepos,bepos)' at which it ends.
+ In otherwords A[abpos+1..aepos] is aligned to B[bbpos+1..bepos] (assuming X[1] is
+ the *first* character of X).
+
+ There are 'diffs' differences in an optimal local alignment between the beginning and
+ end points of the alignment (if computed by Compute_Trace), or nearly so (if computed
+ by Local_Alignment).
+
+ Optionally, a Path can have additional information about the exact nature of the
+ aligned substrings if the field 'trace' is not NULL. Trace points to either an
+ array of integers (if computed by a Compute_Trace routine), or an array of unsigned
+ short integers (if computed by Local_Alignment).
+
+ If computed by Local_Alignment 'trace' points at a list of 'tlen' (always even) short
+ values:
+
+ d_0, b_0, d_1, b_1, ... d_n-1, b_n-1, d_n, b_n
+
+ to be interpreted as follows. The alignment from (abpos,bbpos) to (aepos,bepos)
+ passes through the n trace points for i in [1,n]:
+
+ (a_i,b_i) where a_i = floor(abpos/TS)*TS + i*TS
+ and b_i = bbpos + (b_0 + b_1 + b_i-1)
+
+ where also let a_0,b_0 = abpos,bbpos and a_(n+1),b_(n+1) = aepos,bepos. That is, the
+ interior (i.e. i != 0 and i != n+1) trace points pass through every TS'th position of
+ the aread where TS is the "trace spacing" employed when finding the alignment (see
+ New_Align_Spec). Typically TS is 100. Then d_i is the number of differences in the
+ portion of the alignment between (a_i,b_i) and (a_i+1,b_i+1). These trace points allow
+ the Compute_Trace routines to efficiently compute the exact alignment between the two
+ reads by efficiently computing exact alignments between consecutive pairs of trace points.
+ Moreover, the diff values give one an idea of the quality of the alignment along every
+ segment of TS symbols of the aread.
+
+ If computed by a Compute_Trace routine, 'trace' points at a list of 'tlen' integers
+ < i1, i2, ... in > that encodes an exact alignment as follows. A negative number j
+ indicates that a dash should be placed before A[-j] and a positive number k indicates
+ that a dash should be placed before B[k], where A and B are the two sequences of the
+ overlap. The indels occur in the trace in the order in which they occur along the
+ alignment. For a good example of how to "decode" a trace into an alignment, see the
+ code for the routine Print_Alignment.
+
+***/
+
+typedef struct
+ { void *trace;
+ int tlen;
+ int diffs;
+ int abpos, bbpos;
+ int aepos, bepos;
+ } Path;
+
+
+/*** ALIGNMENT ABSTRACTION:
+
+ An alignment is modeled by an Alignment record, which in addition to a *pointer* to a
+ 'path', gives pointers to the A and B sequences, their lengths, and indicates whether
+ the B-sequence needs to be complemented ('comp' non-zero if so). The 'trace' pointer
+ of the 'path' subrecord can be either NULL, a list of pass-through points, or an exact
+ trace depending on what routines have been called on the record.
+
+ One can (1) compute a trace, with Compute_Trace, either from scratch if 'path.trace' = NULL,
+ or using the sequence of pass-through points in trace, (2) print an ASCII representation
+ of an alignment, or (3) reverse the roles of A and B, and (4) complement a sequence
+ (which is a reversible process).
+
+ If the alignment record shows the B sequence as complemented, *** THEN IT IS THE
+ RESPONSIBILITY OF THE CALLER *** to make sure that bseq points at a complement of
+ the sequence before calling Compute_Trace or Print_Alignment. Complement_Seq complements
+ the sequence a of length n. The operation does the complementation/reversal in place.
+ Calling it a second time on a given fragment restores it to its original state.
+***/
+
+#define COMP(x) ((x) & 0x1)
+
+#define COMP_FLAG 0x1
+
+typedef struct
+ { Path *path;
+ uint32 flags; /* Pipeline status and complementation flags */
+ char *aseq; /* Pointer to A sequence */
+ char *bseq; /* Pointer to B sequence */
+ int alen; /* Length of A sequence */
+ int blen; /* Length of B sequence */
+ } Alignment;
+
+void Complement_Seq(char *a, int n);
+
+ /* Many routines like Local_Alignment, Compute_Trace, and Print_Alignment need working
+ storage that is more efficiently reused with each call, rather than being allocated anew
+ with each call. Each *thread* can create a Work_Data object with New_Work_Data and this
+ object holds and retains the working storage for routines of this module between calls
+ to the routines. Free_Work_Data frees a Work_Data object and all working storage
+ held by it.
+ */
+
+ typedef void Work_Data;
+
+ Work_Data *New_Work_Data();
+
+ void Free_Work_Data(Work_Data *work);
+
+ /* Local_Alignment seeks local alignments of a quality determined by a number of parameters.
+ These are coded in an Align_Spec object that can be created with New_Align_Spec and
+ freed with Free_Align_Spec when no longer needed. There are 4 essential parameters:
+
+ ave_corr: the average correlation (1 - 2*error_rate) for the sought alignments. For Pacbio
+ data we set this to .70 assuming an average of 15% error in each read.
+ trace_space: the spacing interval for keeping trace points and segment differences (see
+ description of 'trace' for Paths above)
+ freq[4]: a 4-element vector where afreq[0] = frequency of A, f(A), freq[1] = f(C),
+ freq[2] = f(G), and freq[3] = f(T). This vector is part of the header
+ of every HITS database (see db.h).
+
+ If an alignment cannot reach the boundary of the d.p. matrix with this condition (i.e.
+ overlap), then the last/first 30 columns of the alignment are guaranteed to be
+ suffix/prefix positive at correlation ave_corr * g(freq) where g is an empirically
+ measured function that increases from 1 as the entropy of freq decreases.
+
+ You can get back the original parameters used to create an Align_Spec with the simple
+ utility functions below.
+ */
+
+ typedef void Align_Spec;
+
+ Align_Spec *New_Align_Spec(double ave_corr, int trace_space, float *freq);
+
+ void Free_Align_Spec(Align_Spec *spec);
+
+ int Trace_Spacing (Align_Spec *spec);
+ double Average_Correlation(Align_Spec *spec);
+ float *Base_Frequencies (Align_Spec *spec);
+
+ /* Local_Alignment finds the longest significant local alignment between the sequences in
+ 'align' subject to the alignment criterion given by the Align_Spec 'spec' that passes
+ through one of the points '(xlow-xhgh,y)' within the underlying dynamic programming matrix.
+ The path record of 'align' has its 'trace' filled from the point of view of an overlap between
+ the aread and the bread. In addition a Path record from the point of view of the bread
+ versus the aread is returned by the function, with this Path's 'trace' filled in
+ appropriately. The space for the returned path and the two 'trace's are in the
+ working storage supplied by the Work_Data packet and this space is reused with each call,
+ so if one wants to retain the bread-path and the two trace point sequences, then they
+ must be copied to user-allocated storage before calling the routine again.
+ */
+
+ Path *Local_Alignment(Alignment *align, Work_Data *work, Align_Spec *spec,
+ int xlow, int xhgh, int y);
+
+ /* Given a legitimate Alignment object, Compute_Trace_X computes an exact trace for the alignment.
+ If 'path.trace' is non-NULL, then it is assumed to be a sequence of pass-through points
+ and diff levels computed by Local_Alignment. In either case 'path.trace' is set
+ to point at an integer array within the storage of the Work_Data packet encoding an
+ exact optimal trace from the start to end points. If the trace is needed beyond the
+ next call to a routine that sets it, then it should be copied to an array allocated
+ and managed by the caller.
+
+ Compute_Trace_ALL does not require a sequence of pass-through points, as it computes the
+ best alignment between (path->abpos,path->bbpos) and (path->aepos,path->bepos) in the
+ edit graph between the sequences. Compute_Trace_PTS computes a trace by computing the
+ trace between successive pass through points. It is much, much faster than Compute_Trace_ALL
+ but at the tradeoff of not necessarily being optimal as pass-through points are not all
+ perfect. Compute_Trace_MID computes a trace by computing the trace between the mid-points
+ of alignments between two adjacent pairs of pass through points. It is generally twice as
+ slow as Compute_Trace_PTS, but it produces nearer optimal alignments.
+ */
+
+ void Compute_Trace_ALL(Alignment *align, Work_Data *work);
+ void Compute_Trace_PTS(Alignment *align, Work_Data *work, int trace_spacing);
+ void Compute_Trace_MID(Alignment *align, Work_Data *work, int trace_spacing);
+
+ /* Alignment_Cartoon prints an ASCII representation of the overlap relationhip between the
+ two reads of 'align' to the given 'file' indented by 'indent' space. Coord controls
+ the display width of numbers, it must be not less than the width of any number to be
+ displayed.
+
+ If the alignment trace is an exact trace, then one can ask Print_Alignment to print an
+ ASCII representation of the alignment 'align' to the file 'file'. Indent the display
+ by "indent" spaces and put "width" columns per line in the display. Show "border"
+ characters of sequence on each side of the aligned region. If upper is non-zero then
+ display bases in upper case. If coord is greater than 0, then the positions of the
+ first character in A and B in the given row is displayed with a field width given by
+ coord's value.
+
+ Print_Reference is like Print_Alignment but rather than printing exaclty "width" columns
+ per segment, it prints "block" characters of the A sequence in each segment. This results
+ in segments of different lengths, but is convenient when looking at two alignments involving
+ A as segments are guaranteed to cover the same interval of A in a segment.
+
+ Flip_Alignment modifies align so the roles of A and B are reversed. If full is off then
+ the trace is ignored, otherwise the trace must be to a full alignment trace and this trace
+ is also appropriately inverted.
+ */
+
+ void Alignment_Cartoon(FILE *file, Alignment *align, int indent, int coord);
+
+ void Print_Alignment(FILE *file, Alignment *align, Work_Data *work,
+ int indent, int width, int border, int upper, int coord);
+
+ void Print_Reference(FILE *file, Alignment *align, Work_Data *work,
+ int indent, int block, int border, int upper, int coord);
+
+ void Flip_Alignment(Alignment *align, int full);
+
+
+/*** OVERLAP ABSTRACTION:
+
+ Externally, between modules an Alignment is modeled by an "Overlap" record, which
+ (a) replaces the pointers to the two sequences with their ID's in the HITS data bases,
+ (b) does not contain the length of the 2 sequences (must fetch from DB), and
+ (c) contains its path as a subrecord rather than as a pointer (indeed, typically the
+ corresponding Alignment record points at the Overlap's path sub-record). The trace pointer
+ is always to a sequence of trace points and can be either compressed (uint8) or
+ uncompressed (uint16). One can read and write binary records of an "Overlap".
+***/
+
+typedef struct {
+ Path path; /* Path: begin- and end-point of alignment + diffs */
+ uint32 flags; /* Pipeline status and complementation flags */
+ int aread; /* Id # of A sequence */
+ int bread; /* Id # of B sequence */
+} Overlap;
+
+
+ /* Read_Overlap reads the next Overlap record from stream 'input', not including the trace
+ (if any), and without modifying 'ovl's trace pointer. Read_Trace reads the ensuing trace
+ into the memory pointed at by the trace field of 'ovl'. It is assumed to be big enough to
+ accommodate the trace where each value take 'tbytes' bytes (1 if uint8 or 2 if uint16).
+
+ Write_Overlap write 'ovl' to stream 'output' followed by its trace vector (if any) that
+ occupies 'tbytes' bytes per value.
+
+ Print_Overlap prints an ASCII version of the contents of 'ovl' to stream 'output'
+ where the trace occupes 'tbytes' per value and the print out is indented from the left
+ margin by 'indent' spaces.
+
+ Compress_TraceTo8 converts a trace fo 16-bit values to 8-bit values in place, and
+ Decompress_TraceTo16 does the reverse conversion.
+
+ Check_Trace_Points checks that the number of trace points is correct and that the sum
+ of the b-read displacements equals the b-read alignment interval, assuming the trace
+ spacing is 'tspace'. It reports an error message if there is a problem and 'verbose'
+ is non-zero. The 'ovl' came from the file names 'fname'.
+ */
+
+ int Read_Overlap(FILE *input, Overlap *ovl);
+ int Read_Trace(FILE *innput, Overlap *ovl, int tbytes);
+
+ void Write_Overlap(FILE *output, Overlap *ovl, int tbytes);
+ void Print_Overlap(FILE *output, Overlap *ovl, int tbytes, int indent);
+
+ void Compress_TraceTo8(Overlap *ovl);
+ void Decompress_TraceTo16(Overlap *ovl);
+
+ int Check_Trace_Points(Overlap *ovl, int tspace, int verbose, char *fname);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _A_MODULE
diff --git a/src/cpp/boost.mk b/src/cpp/boost.mk
new file mode 100644
index 0000000..aebea4d
--- /dev/null
+++ b/src/cpp/boost.mk
@@ -0,0 +1,18 @@
+# A URI location for a copy of boost headers only
+URI := https://www.dropbox.com/s/g22iayi83p5gbbq/boost_1_58_0-headersonly.tbz2\?dl\=0
+# Obtain the boost URI and extract it into PWD
+GET_BOOST := curl -L $(URI) | tar xjf -
+
+MYPATH__ := $(dir $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
+
+# Three ways to boost: 1) Internal PBI repot, 2) URI, 3) User specified.
+
+ifdef boost
+BOOST_HEADERS := $(boost)
+else
+BOOST_HEADERS := $(MYPATH__)third-party/boost_1_58_0-headersonly
+endif
+
+ifneq ($(wildcard $(PREBUILT)/boost/boost_1_58_0-headersonly/*),)
+BOOST_HEADERS := $(PREBUILT)/boost/boost_1_58_0-headersonly
+endif
diff --git a/src/cpp/dazcon.cpp b/src/cpp/dazcon.cpp
new file mode 100644
index 0000000..2f70ba6
--- /dev/null
+++ b/src/cpp/dazcon.cpp
@@ -0,0 +1,257 @@
+
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+#include <cstdint>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <thread>
+#include <boost/format.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/graphviz.hpp>
+
+#define ELPP_CUSTOM_COUT std::cerr
+#define ELPP_THREAD_SAFE 1
+#include "easylogging++.h"
+#include "tclap/CmdLine.h"
+#include "ProgramOpts.hpp"
+#include "Alignment.hpp"
+#include "AlnGraphBoost.hpp"
+#include "DazAlnProvider.hpp"
+#include "BoundedBuffer.hpp"
+
+INITIALIZE_EASYLOGGINGPP
+
+ProgramOpts popts;
+
+typedef std::vector<dagcon::Alignment> AlnVec;
+
+struct TargetData {
+ std::string targSeq;
+ AlnVec alns;
+};
+
+typedef BoundedBuffer<TargetData> TrgBuf;
+typedef BoundedBuffer<std::string> CnsBuf;
+
+void Reader(TrgBuf& trgBuf, AlnProvider* ap) {
+ try {
+ TargetData td;
+ bool hasNext = true;
+ do {
+ hasNext = ap->nextTarget(td.targSeq, td.alns);
+ //for (auto& aln : td.alns)
+ // std::cerr << aln;
+ if (! td.alns.empty())
+ trgBuf.push(td);
+ } while (hasNext);
+ }
+ catch (PacBio::DagCon::IOException& e) {
+ std::cerr << e.what();
+ exit(1);
+ }
+
+ // write out sentinals, one per consensus thread
+ TargetData sentinel;
+ for (int i=0; i < popts.threads; i++)
+ trgBuf.push(sentinel);
+}
+
+void Consensus(int id, TrgBuf& trgBuf, CnsBuf& cnsBuf) {
+ TargetData td;
+ trgBuf.pop(&td);
+ std::vector<CnsResult> seqs;
+ el::Loggers::getLogger("Consensus");
+
+ while (td.alns.size() > 0) {
+ if (td.alns.size() < popts.minCov) {
+ trgBuf.pop(&td);
+ continue;
+ }
+ boost::format msg("(%d) calling: %s Alignments: %d");
+ CLOG(INFO, "Consensus") << msg % id % td.alns[0].id % td.alns.size();
+
+ AlnGraphBoost ag(td.targSeq);
+ AlnVec alns = td.alns;
+ for (auto it = alns.begin(); it != alns.end(); ++it) {
+ if (it->qstr.length() < popts.minLen) continue;
+ dagcon::Alignment aln = normalizeGaps(*it);
+ // XXX: Shouldn't be needed for dazcon, but causes some infinite
+ // loops in the current consensus code.
+ trimAln(aln, popts.trim);
+ ag.addAln(aln);
+ }
+ CVLOG(3, "Consensus") << "Merging nodes";
+ ag.mergeNodes();
+ CVLOG(3, "Consensus") << "Generating consensus";
+ ag.consensus(seqs, popts.minCov, popts.minLen);
+ for (auto it = seqs.begin(); it != seqs.end(); ++it) {
+ CnsResult result = *it;
+ boost::format fasta(">%s/%d_%d\n%s\n");
+ fasta % alns[0].id % result.range[0] % result.range[1];
+ fasta % result.seq;
+ cnsBuf.push(fasta.str());
+ }
+ trgBuf.pop(&td);
+ }
+ boost::format msg("(%d) ending ...");
+ CLOG(INFO, "Consensus") << msg % id;
+ // write out a sentinal
+ cnsBuf.push("");
+}
+
+void Writer(CnsBuf& cnsBuf) {
+ std::string cns;
+ cnsBuf.pop(&cns);
+ int sentinelCount = 0;
+ while (true) {
+ std::cout << cns;
+ if (cns == "" && ++sentinelCount == popts.threads)
+ break;
+
+ cnsBuf.pop(&cns);
+ }
+}
+
+void parseArgs(int argc, char **argv) {
+
+ try {
+ TCLAP::CmdLine cmd("PBI consensus module", ' ', "0.3");
+ TCLAP::ValueArg<int> threadArg(
+ "j","threads", // short, long name
+ "Number of consensus threads", // description
+ false, 4, // required, default
+ "int", cmd);
+
+ TCLAP::ValueArg<unsigned int> minCovArg(
+ "c","min-coverage",
+ "Minimum coverage for correction",
+ false, 6, "uint", cmd);
+
+ TCLAP::ValueArg<unsigned int> minLenArg(
+ "l","min-len",
+ "Minimum length for correction",
+ false, 500, "uint", cmd);
+
+ TCLAP::ValueArg<unsigned int> trimArg(
+ "t","trim",
+ "Trim alignments on either size",
+ false, 10, "uint", cmd);
+
+ TCLAP::ValueArg<std::string> alnFileArg(
+ "a","align-file",
+ "Path to the alignments file",
+ true,"","string", cmd);
+
+ TCLAP::ValueArg<std::string> seqFileArg(
+ "s","seq-file",
+ "Path to the sequences file",
+ true,"","string", cmd);
+
+ TCLAP::ValueArg<unsigned int> maxHitArg(
+ "m","max-hit",
+ "Maximum number of hits to pass to consensus",
+ false,85,"uint", cmd);
+
+ TCLAP::SwitchArg sortCovArg("x","coverage-sort",
+ "Sort hits by coverage", cmd, false);
+
+ TCLAP::SwitchArg properOvlArg("o","only-proper-overlaps",
+ "Use only 'proper overlaps', i.e., align to the ends", cmd, false);
+
+ TCLAP::SwitchArg verboseArg("v","verbose",
+ "Turns on verbose logging", cmd, false);
+
+ TCLAP::UnlabeledMultiArg<int> targetArgs(
+ "targets", "Limit consensus to list of target ids",
+ false, "list of ints", cmd);
+
+ cmd.parse(argc, argv);
+
+ popts.minCov = minCovArg.getValue();
+ popts.minLen = minLenArg.getValue();
+ popts.trim = trimArg.getValue();
+ popts.alnFile = alnFileArg.getValue();
+ popts.seqFile = seqFileArg.getValue();
+ popts.threads = threadArg.getValue();
+ popts.maxHits = maxHitArg.getValue();
+ popts.sortCov = sortCovArg.getValue();
+ popts.properOvls = properOvlArg.getValue();
+ std::vector<int> tgs = targetArgs.getValue();
+ popts.targets.insert(tgs.begin(), tgs.end());
+ } catch (TCLAP::ArgException& e) {
+ std::cerr << "Error " << e.argId() << ": " << e.error() << std::endl;
+ exit(1);
+ }
+}
+
+int main(int argc, char* argv[]) {
+
+ START_EASYLOGGINGPP(argc, argv);
+ parseArgs(argc, argv);
+
+ LOG(INFO) << "Initializing alignment provider";
+ DazAlnProvider* ap;
+ ap = new DazAlnProvider(popts);
+ TrgBuf trgBuf(20);
+ CnsBuf cnsBuf(10);
+
+ std::thread writerThread(Writer, std::ref(cnsBuf));
+
+ std::vector<std::thread> cnsThreads;
+ for (int i=0; i < popts.threads; i++) {
+ std::thread ct(Consensus, i, std::ref(trgBuf), std::ref(cnsBuf));
+ cnsThreads.push_back(std::move(ct));
+ }
+
+ std::thread readerThread(Reader, std::ref(trgBuf), ap);
+
+ writerThread.join();
+
+ std::vector<std::thread>::iterator it;
+ for (it = cnsThreads.begin(); it != cnsThreads.end(); ++it)
+ it->join();
+
+ readerThread.join();
+
+ delete ap;
+
+ return 0;
+}
diff --git a/src/cpp/main.cpp b/src/cpp/main.cpp
new file mode 100644
index 0000000..d64de00
--- /dev/null
+++ b/src/cpp/main.cpp
@@ -0,0 +1,314 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) 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 Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+// 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.
+
+
+#include <cstdint>
+#include <cassert>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <boost/format.hpp>
+#include <thread>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/graphviz.hpp>
+
+#define ELPP_CUSTOM_COUT std::cerr
+#define ELPP_THREAD_SAFE 1
+#include "easylogging++.h"
+#include "tclap/CmdLine.h"
+#include "Alignment.hpp"
+#include "AlnGraphBoost.hpp"
+#include "BlasrM5AlnProvider.hpp"
+#include "BoundedBuffer.hpp"
+#include "tuples/TupleMetrics.hpp"
+#include "SimpleAligner.hpp"
+#include "ProgramOpts.hpp"
+
+INITIALIZE_EASYLOGGINGPP
+
+ProgramOpts popts;
+
+bool AlignFirst = false;
+
+typedef std::vector<dagcon::Alignment> AlnVec;
+typedef BoundedBuffer<AlnVec> AlnBuf;
+typedef BoundedBuffer<std::string> CnsBuf;
+
+class Reader {
+ AlnBuf* alnBuf_;
+ const std::string fpath_;
+ size_t minCov_;
+ int nCnsThreads_;
+public:
+ Reader(AlnBuf* b, const std::string fpath, size_t minCov) :
+ alnBuf_(b),
+ fpath_(fpath),
+ minCov_(minCov)
+ { }
+
+ void setNumCnsThreads(int n) {
+ nCnsThreads_ = n;
+ }
+
+ void operator()() {
+ el::Logger* logger = el::Loggers::getLogger("Reader");
+ try {
+ AlnProvider* ap;
+ if (fpath_ == "-") {
+ ap = new BlasrM5AlnProvider(&std::cin);
+ } else {
+ ap = new BlasrM5AlnProvider(fpath_);
+ }
+ AlnVec alns;
+ bool hasNext = true;
+ while (hasNext) {
+ hasNext = ap->nextTarget(alns);
+ size_t cov = alns.size();
+ if (cov == 0) continue;
+ if (cov < minCov_) {
+ logger->debug("Coverage requirement not met for %v, coverage: %v",
+ alns[0].id, alns.size());
+ continue;
+ }
+ boost::format msg("Consensus candidate: %s");
+ msg % alns[0].id;
+ logger->debug(msg.str());
+ alnBuf_->push(alns);
+ }
+ }
+ catch (M5Exception::FileOpenError) {
+ logger->error("Error opening file: %s", fpath_);
+ }
+ catch (M5Exception::FormatError err) {
+ logger->error("Format error. Input: %s, Error: %s",
+ fpath_, err.msg);
+ }
+ catch (M5Exception::SortError err) {
+ logger->error("Input file is not sorted by either target or query.");
+ }
+
+ // write out sentinals, one per consensus thread
+ AlnVec sentinel;
+ for (int i=0; i < nCnsThreads_; i++)
+ alnBuf_->push(sentinel);
+ }
+};
+
+class Consensus {
+ AlnBuf* alnBuf_;
+ CnsBuf* cnsBuf_;
+ size_t minLen_;
+ int minWeight_;
+ SimpleAligner aligner;
+public:
+ Consensus(AlnBuf* ab, CnsBuf* cb, size_t minLen, int minWeight) :
+ alnBuf_(ab),
+ cnsBuf_(cb),
+ minLen_(minLen),
+ minWeight_(minWeight)
+ { }
+
+ void operator()() {
+ el::Logger* logger = el::Loggers::getLogger("Consensus");
+ AlnVec alns;
+ alnBuf_->pop(&alns);
+ std::vector<CnsResult> seqs;
+
+ while (alns.size() > 0) {
+ if (alns.size() < popts.minCov) {
+ alnBuf_->pop(&alns);
+ continue;
+ }
+ boost::format msg("Consensus calling: %s Alignments: %d");
+ msg % alns[0].id;
+ msg % alns.size();
+ logger->info(msg.str());
+
+ if (AlignFirst)
+ for_each(alns.begin(), alns.end(), aligner);
+
+ AlnGraphBoost ag(alns[0].tlen);
+ for (auto it = alns.begin(); it != alns.end(); ++it) {
+ if (it->qstr.length() < minLen_) continue;
+ dagcon::Alignment aln = normalizeGaps(*it);
+ trimAln(aln, popts.trim);
+ ag.addAln(aln);
+ }
+ ag.mergeNodes();
+ ag.consensus(seqs, minWeight_, minLen_);
+ for (auto it = seqs.begin(); it != seqs.end(); ++it) {
+ CnsResult result = *it;
+ boost::format fasta(">%s/%d_%d\n%s\n");
+ fasta % alns[0].id % result.range[0] % result.range[1];
+ fasta % result.seq;
+ cnsBuf_->push(fasta.str());
+ }
+
+ alnBuf_->pop(&alns);
+ }
+ // write out a sentinal
+ cnsBuf_->push("");
+ }
+};
+
+class Writer {
+ CnsBuf* cnsBuf_;
+ int nCnsThreads_;
+public:
+ Writer(CnsBuf* cb) : cnsBuf_(cb) {}
+
+ void setNumCnsThreads(int n) {
+ nCnsThreads_ = n;
+ }
+
+ void operator()() {
+ std::string cns;
+ cnsBuf_->pop(&cns);
+ int sentinelCount = 0;
+ while (true) {
+ std::cout << cns;
+ if (cns == "" && ++sentinelCount == nCnsThreads_)
+ break;
+
+ cnsBuf_->pop(&cns);
+ }
+ }
+};
+
+void parseArgs(int argc, char **argv) {
+
+ try {
+ TCLAP::CmdLine cmd("PBI consensus module", ' ', "0.3");
+ TCLAP::ValueArg<int> threadArg(
+ "j","threads", // short, long name
+ "Number of consensus threads", // description
+ false, 4, // required, default
+ "int", cmd);
+
+ TCLAP::ValueArg<unsigned int> minCovArg(
+ "c","min-coverage",
+ "Minimum coverage for correction",
+ false, 6, "uint", cmd);
+
+ TCLAP::ValueArg<unsigned int> minLenArg(
+ "m","min-length",
+ "Minimum length for correction",
+ false, 500, "uint", cmd);
+
+ TCLAP::ValueArg<unsigned int> trimArg(
+ "t","trim",
+ "Trim alignments on either size",
+ false, 50, "uint", cmd);
+
+ TCLAP::SwitchArg alignArg("a","align",
+ "Align sequences before adding to consensus", cmd, false);
+
+ TCLAP::SwitchArg verboseArg("v","verbose",
+ "Turns on verbose logging", cmd, false);
+
+ TCLAP::UnlabeledValueArg<std::string> inputArg(
+ "input", "Input data",
+ true, "-","either file path or stdin", cmd);
+
+ cmd.parse(argc, argv);
+
+ popts.minCov = minCovArg.getValue();
+ popts.minLen = minLenArg.getValue();
+ popts.trim = trimArg.getValue();
+ popts.threads = threadArg.getValue();
+ popts.align = alignArg.getValue();
+ popts.input = inputArg.getValue();
+ } catch (TCLAP::ArgException& e) {
+ std::cerr << "Error " << e.argId() << ": " << e.error() << std::endl;
+ exit(1);
+ }
+}
+
+int main(int argc, char* argv[]) {
+ START_EASYLOGGINGPP(argc, argv);
+ parseArgs(argc, argv);
+
+ el::Logger* logger = el::Loggers::getLogger("default");
+
+ if (popts.align) {
+ dagcon::Alignment::parse = parsePre;
+ AlignFirst = true;
+ }
+
+ AlnBuf alnBuf(30);
+ CnsBuf cnsBuf(30);
+
+ if (popts.threads > 1) {
+ logger->info("Multi-threaded. Input: %v, Threads: %v",
+ popts.input, popts.threads);
+
+ Writer writer(&cnsBuf);
+ writer.setNumCnsThreads(popts.threads);
+ std::thread writerThread(writer);
+
+ std::vector<std::thread> cnsThreads;
+ for (int i=0; i < popts.threads; i++) {
+ Consensus c(&alnBuf, &cnsBuf, popts.minLen, popts.minCov);
+ cnsThreads.push_back(std::thread(c));
+ }
+
+ Reader reader(&alnBuf, popts.input, popts.minCov);
+ reader.setNumCnsThreads(popts.threads);
+ std::thread readerThread(reader);
+
+ writerThread.join();
+ std::vector<std::thread>::iterator it;
+ for (it = cnsThreads.begin(); it != cnsThreads.end(); ++it)
+ it->join();
+
+ readerThread.join();
+ } else {
+ logger->info("Single-threaded. Input: %v", popts.input);
+ Reader reader(&alnBuf, popts.input, popts.minCov);
+ reader.setNumCnsThreads(1);
+ Consensus cns(&alnBuf, &cnsBuf, popts.minLen, popts.minCov);
+ Writer writer(&cnsBuf);
+ writer.setNumCnsThreads(1);
+ reader();
+ cns();
+ writer();
+ }
+
+ return 0;
+}
diff --git a/src/cpp/pbdagcon_wf.sh b/src/cpp/pbdagcon_wf.sh
new file mode 100755
index 0000000..76912da
--- /dev/null
+++ b/src/cpp/pbdagcon_wf.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+#################################################################################$$
+# Copyright (c) 2011-2013, Pacific Biosciences of California, Inc.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted (subject to the limitations in the
+# disclaimer below) 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 Pacific Biosciences nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+# GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+# BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+# 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.
+#################################################################################$$
+
+
+# Simple pbdagcon workflow script. Written for the benefit of running via
+# smrtpipe so I can communicate pipe errors to the task. We're overcoming
+# the limitation of smrtpipe forcing tasks to run serially, enabling a new
+# level of pipelining that's extremely efficient in an imperfect world ...
+# However, direct file I/O is faster by default.
+
+tmp=${tmp-"/tmp"}
+
+trap "rm -f $tmp/aln.$$.pre" EXIT SIGINT
+
+echo "Generating pre-alignments"
+echo "m4topre.py $mym4 $allm4 $subreads ${bestn-24} > $tmp/aln.$$.pre"
+
+# generate pre-alignments to a tmp directory
+m4topre.py $mym4 $allm4 $subreads ${bestn-24} > $tmp/aln.$$.pre || exit $?
+
+echo "Correcting reads"
+# pipe it to consensus and generate fasta
+pbdagcon -c ${cov-8} -a -j ${nproc-15} $tmp/aln.$$.pre | tee ${fasta-"corrected.fa"} | \
+# generate a fastq
+awk '{if($0~/>/){sub(/>/,"@",$0);print;}else{l=length($0);q="";while(l--){q=q "9"}printf("%s\n+\n%s\n",$0,q)}}' > ${fastq-"corrected.fq"}
+
+
+# check the status of each piped command and exit non-zero if found
+for exitval in ${PIPESTATUS[*]}
+do
+ if [ $exitval -gt 0 ]
+ then
+ exit $exitval
+ fi
+done
+
+
+exit 0;
diff --git a/src/cpp/pbi.mk b/src/cpp/pbi.mk
new file mode 100644
index 0000000..db3a232
--- /dev/null
+++ b/src/cpp/pbi.mk
@@ -0,0 +1,22 @@
+# Darwin/Clang is unhappy with -L for a non-existent directory, so we
+# cannot use this file to build on OSX.
+# Instead of a bunch of ifdefs, we can conditionally include this file.
+
+# Defines some variables specific to the PBI build env using relative paths
+BASEDIR ?= ../../../../../..
+PREBUILT := $(BASEDIR)/smrtanalysis/prebuilt.out
+BIFX := $(BASEDIR)/smrtanalysis/bioinformatics
+BLASR ?= $(BIFX)/lib/cpp/alignment
+PBDATA ?= $(BIFX)/lib/cpp/pbdata
+PBBAM := $(BIFX)/staging/PostPrimary/pbbam
+ZLIB := $(PREBUILT)/zlib/zlib-1.2.5/$(OS_STRING2)/lib
+
+ifneq ($(wildcard $(PBBAM)/*),)
+EXTRA_INCDIRS := -I$(PBBAM)/include -I$(PBBAM)/third-party/htslib
+EXTRA_LDFLAGS := -L$(PBBAM)/lib -L$(PBBAM)/third-party/htslib -L$(ZLIB)
+
+# We are moving to BAM, which requires extra lib support when compiling against
+# libblasr. This conditional allows backward compatable compilations with
+# PacificBiosciences/blasr_libcpp.
+EXTRA_LDLIBS = -lpbbam -lhts -lz -lpthread
+endif
diff --git a/src/cpp/third-party/easylogging++.h b/src/cpp/third-party/easylogging++.h
new file mode 100755
index 0000000..f9a67fe
--- /dev/null
+++ b/src/cpp/third-party/easylogging++.h
@@ -0,0 +1,6663 @@
+//
+// Easylogging++ v9.80
+// Single-header only, cross-platform logging library for C++ applications
+//
+// Copyright (c) 2015 muflihun.com
+//
+// This library is released under the MIT Licence.
+// http://easylogging.muflihun.com/licence.php
+//
+// easylogging at muflihun.com
+//
+// https://github.com/easylogging/easyloggingpp
+// http://easylogging.muflihun.com
+// http://muflihun.com
+//
+#ifndef EASYLOGGINGPP_H
+#define EASYLOGGINGPP_H
+// Compilers and C++0x/C++11 Evaluation
+#if defined(__GNUC__)
+# define ELPP_COMPILER_GCC 1
+# define ELPP_GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+# if defined(__GXX_EXPERIMENTAL_CXX0X__)
+# define ELPP_CXX0X 1
+# elif(ELPP_GCC_VERSION >= 40801)
+# define ELPP_CXX11 1
+# endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
+#endif // defined(__GNUC__)
+// Visual C++
+#if defined(_MSC_VER)
+# define ELPP_COMPILER_MSVC 1
+# define ELPP_CRT_DBG_WARNINGS 1
+# if (_MSC_VER == 1600)
+# define ELPP_CXX0X 1
+# elif(_MSC_VER >= 1700)
+# define ELPP_CXX11 1
+# endif // (_MSC_VER == 1600)
+#endif // defined(_MSC_VER)
+// Clang++
+#if defined(__clang__) && (__clang__ == 1)
+# define ELPP_COMPILER_CLANG 1
+# define ELPP_CLANG_VERSION (__clang_major__ * 10000 \
+ + __clang_minor__ * 100 \
+ + __clang_patchlevel__)
+# if (ELPP_CLANG_VERSION >= 30300)
+# define ELPP_CXX11 1
+# endif // (ELPP_CLANG_VERSION >= 30300)
+#endif // defined(__clang__) && (__clang__ == 1)
+// MinGW
+#if defined(__MINGW32__) || defined(__MINGW64__)
+# define ELPP_MINGW 1
+#endif // defined(__MINGW32__) || defined(__MINGW64__)
+// Cygwin
+#if defined(__CYGWIN__) && (__CYGWIN__ == 1)
+# define ELPP_CYGWIN 1
+#endif // defined(__CYGWIN__) && (__CYGWIN__ == 1)
+// Intel C++
+#if defined(__INTEL_COMPILER)
+# define ELPP_COMPILER_INTEL 1
+#endif
+// Operating System Evaluation
+// Windows
+#if defined(_WIN32) || defined(_WIN64)
+# define ELPP_OS_WINDOWS 1
+#endif // defined(_WIN32) || defined(_WIN64)
+// Linux
+#if (defined(__linux) || defined(__linux__))
+# define ELPP_OS_LINUX 1
+#endif // (defined(__linux) || defined(__linux__))
+// Mac
+#if defined(__APPLE__)
+# define ELPP_OS_MAC 1
+#endif // defined(__APPLE__)
+// FreeBSD
+#if defined(__FreeBSD__)
+# define ELPP_OS_FREEBSD 1
+#endif
+// Solaris
+#if defined(__sun)
+# define ELPP_OS_SOLARIS 1
+#endif
+// Unix
+#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS))
+# define ELPP_OS_UNIX 1
+#endif // ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS))
+// Android
+#if defined(__ANDROID__)
+# define ELPP_OS_ANDROID 1
+#endif // defined(__ANDROID__)
+// Evaluating Cygwin as *nix OS
+#if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN
+# undef ELPP_OS_UNIX
+# undef ELPP_OS_LINUX
+# define ELPP_OS_UNIX 1
+# define ELPP_OS_LINUX 1
+#endif // !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN
+#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO)
+# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout
+#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT)
+#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR)
+# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr
+#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT)
+#if !defined(ELPP_INTERNAL_DEBUGGING_ENDL)
+# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl
+#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT)
+#if !defined(ELPP_INTERNAL_DEBUGGING_MSG)
+# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg
+#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT)
+// Internal Assertions and errors
+#if !defined(ELPP_DISABLE_ASSERT)
+# if (defined(ELPP_DEBUG_ASSERT_FAILURE))
+# define ELPP_ASSERT(expr, msg) if (!(expr)) { \
+ std::stringstream internalInfoStream; internalInfoStream << msg; \
+ ELPP_INTERNAL_DEBUGGING_OUT_ERROR \
+ << "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \
+ << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \
+ "ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); }
+# else
+# define ELPP_ASSERT(expr, msg) if (!(expr)) { \
+ std::stringstream internalInfoStream; internalInfoStream << msg; \
+ ELPP_INTERNAL_DEBUGGING_OUT_ERROR\
+ << "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \
+ << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \
+ << ELPP_INTERNAL_DEBUGGING_ENDL; }
+# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE))
+#else
+# define ELPP_ASSERT(x, y)
+#endif //(!defined(ELPP_DISABLE_ASSERT)
+#if ELPP_COMPILER_MSVC
+# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \
+ { char buff[256]; strerror_s(buff, 256, errno); \
+ ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0
+#else
+# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \
+ ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0
+#endif // ELPP_COMPILER_MSVC
+#if defined(ELPP_DEBUG_ERRORS)
+# if !defined(ELPP_INTERNAL_ERROR)
+# define ELPP_INTERNAL_ERROR(msg, pe) { \
+ std::stringstream internalInfoStream; internalInfoStream << "<ERROR> " << msg; \
+ ELPP_INTERNAL_DEBUGGING_OUT_ERROR \
+ << "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \
+ << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \
+ if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0
+# endif
+#else
+# undef ELPP_INTERNAL_INFO
+# define ELPP_INTERNAL_ERROR(msg, pe)
+#endif // defined(ELPP_DEBUG_ERRORS)
+#if (defined(ELPP_DEBUG_INFO))
+# if !(defined(ELPP_INTERNAL_INFO_LEVEL))
+# define ELPP_INTERNAL_INFO_LEVEL 9
+# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL))
+# if !defined(ELPP_INTERNAL_INFO)
+# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \
+ std::stringstream internalInfoStream; internalInfoStream << "<INFO> " << msg; \
+ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \
+ << ELPP_INTERNAL_DEBUGGING_ENDL; }}
+# endif
+#else
+# undef ELPP_INTERNAL_INFO
+# define ELPP_INTERNAL_INFO(lvl, msg)
+#endif // (defined(ELPP_DEBUG_INFO))
+#if defined(ELPP_STACKTRACE_ON_CRASH)
+# if (ELPP_COMPILER_GCC && !ELPP_MINGW)
+# define ELPP_STACKTRACE 1
+# else
+# if ELPP_COMPILER_MSVC
+# pragma message("Stack trace not available for this compiler")
+# else
+# warning "Stack trace not available for this compiler";
+# endif // ELPP_COMPILER_MSVC
+# endif // ELPP_COMPILER_GCC
+#endif // (defined(ELPP_STACKTRACE_ON_CRASH))
+// Miscellaneous macros
+#define ELPP_UNUSED(x) (void)x
+#if ELPP_OS_UNIX
+// Log file permissions for unix-based systems
+# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH
+#endif // ELPP_OS_UNIX
+#if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC
+# if defined(ELPP_EXPORT_SYMBOLS)
+# define ELPP_EXPORT __declspec(dllexport)
+# else
+# define ELPP_EXPORT __declspec(dllimport)
+# endif // defined(ELPP_EXPORT_SYMBOLS)
+#else
+# define ELPP_EXPORT
+#endif // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC
+// Some special functions that are VC++ specific
+#undef STRTOK
+#undef STRERROR
+#undef STRCAT
+#undef STRCPY
+#if ELPP_CRT_DBG_WARNINGS
+# define STRTOK(a, b, c) strtok_s(a, b, c)
+# define STRERROR(a, b, c) strerror_s(a, b, c)
+# define STRCAT(a, b, len) strcat_s(a, len, b)
+# define STRCPY(a, b, len) strcpy_s(a, len, b)
+#else
+# define STRTOK(a, b, c) strtok(a, b)
+# define STRERROR(a, b, c) strerror(c)
+# define STRCAT(a, b, len) strcat(a, b)
+# define STRCPY(a, b, len) strcpy(a, b)
+#endif
+// Compiler specific support evaluations
+#if (!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD)
+# define ELPP_USE_STD_THREADING 1
+#endif // (!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD)
+#undef ELPP_FINAL
+#if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702)
+# define ELPP_FINAL
+#else
+# define ELPP_FINAL final
+#endif // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702)
+#if defined(ELPP_EXPERIMENTAL_ASYNC)
+# define ELPP_ASYNC_LOGGING 1
+#else
+# define ELPP_ASYNC_LOGGING 0
+#endif // defined(ELPP_EXPERIMENTAL_ASYNC)
+#if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING
+# define ELPP_THREADING_ENABLED 1
+#endif // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING
+// Function macro ELPP_FUNC
+#undef ELPP_FUNC
+#if ELPP_COMPILER_MSVC // Visual C++
+# define ELPP_FUNC __FUNCSIG__
+#elif ELPP_COMPILER_GCC // GCC
+# define ELPP_FUNC __PRETTY_FUNCTION__
+#elif ELPP_COMPILER_INTEL // Intel C++
+# define ELPP_FUNC __PRETTY_FUNCTION__
+#elif ELPP_COMPILER_CLANG // Clang++
+# define ELPP_FUNC __PRETTY_FUNCTION__
+#else
+# if defined(__func__)
+# define ELPP_FUNC __func__
+# else
+# define ELPP_FUNC ""
+# endif // defined(__func__)
+#endif // defined(_MSC_VER)
+#undef ELPP_VARIADIC_TEMPLATES_SUPPORTED
+// Keep following line commented until features are fixed
+#if ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800)
+# define ELPP_VARIADIC_TEMPLATES_SUPPORTED 1
+#endif // ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800)
+// Logging Enable/Disable macros
+#if (!defined(ELPP_DISABLE_LOGS))
+# define ELPP_LOGGING_ENABLED 1
+#endif // (!defined(ELPP_DISABLE_LOGS))
+#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG))))
+# define ELPP_DEBUG_LOG 1
+#else
+# define ELPP_DEBUG_LOG 0
+#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG))))
+#if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED))
+# define ELPP_INFO_LOG 1
+#else
+# define ELPP_INFO_LOG 0
+#endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED))
+# define ELPP_WARNING_LOG 1
+#else
+# define ELPP_WARNING_LOG 0
+#endif // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED))
+# define ELPP_ERROR_LOG 1
+#else
+# define ELPP_ERROR_LOG 0
+#endif // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED))
+# define ELPP_FATAL_LOG 1
+#else
+# define ELPP_FATAL_LOG 0
+#endif // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED))
+# define ELPP_TRACE_LOG 1
+#else
+# define ELPP_TRACE_LOG 0
+#endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED))
+# define ELPP_VERBOSE_LOG 1
+#else
+# define ELPP_VERBOSE_LOG 0
+#endif // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!(ELPP_CXX0X || ELPP_CXX11))
+# error "Easylogging++ 9.0+ is only compatible with C++0x (or higher) compliant compiler"
+#endif // (!(ELPP_CXX0X || ELPP_CXX11))
+// Headers
+#if defined(ELPP_SYSLOG)
+# include <syslog.h>
+#endif // defined(ELPP_SYSLOG)
+#include <ctime>
+#include <cstring>
+#include <cstdlib>
+#include <cctype>
+#include <cwchar>
+#include <csignal>
+#include <cerrno>
+#include <cstdarg>
+#if defined(ELPP_UNICODE)
+# include <locale>
+# if ELPP_OS_WINDOWS
+# include <codecvt>
+# endif // ELPP_OS_WINDOWS
+#endif // defined(ELPP_UNICODE)
+#if ELPP_STACKTRACE
+# include <cxxabi.h>
+# include <execinfo.h>
+#endif // ELPP_STACKTRACE
+#if ELPP_OS_ANDROID
+# include <sys/system_properties.h>
+#endif // ELPP_OS_ANDROID
+#if ELPP_OS_UNIX
+# include <sys/stat.h>
+# include <sys/time.h>
+#elif ELPP_OS_WINDOWS
+# include <direct.h>
+# include <windows.h>
+# if defined(WIN32_LEAN_AND_MEAN)
+# if defined(ELPP_WINSOCK2)
+# include <winsock2.h>
+# else
+# include <winsock.h>
+# endif // defined(ELPP_WINSOCK2)
+# endif // defined(WIN32_LEAN_AND_MEAN)
+#endif // ELPP_OS_UNIX
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+#include <functional>
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <memory>
+#include <type_traits>
+#if ELPP_THREADING_ENABLED
+# if ELPP_USE_STD_THREADING
+# include <mutex>
+# include <thread>
+# else
+# if ELPP_OS_UNIX
+# include <pthread.h>
+# endif // ELPP_OS_UNIX
+# endif // ELPP_USE_STD_THREADING
+#endif // ELPP_THREADING_ENABLED
+#if ELPP_ASYNC_LOGGING
+# include <thread>
+# include <queue>
+# include <condition_variable>
+#endif // ELPP_ASYNC_LOGGING
+#if defined(ELPP_STL_LOGGING)
+// For logging STL based templates
+# include <list>
+# include <queue>
+# include <deque>
+# include <set>
+# include <bitset>
+# include <stack>
+# if defined(ELPP_LOG_STD_ARRAY)
+# include <array>
+# endif // defined(ELPP_LOG_STD_ARRAY)
+# if defined(ELPP_LOG_UNORDERED_MAP)
+# include <unordered_map>
+# endif // defined(ELPP_LOG_UNORDERED_MAP)
+# if defined(ELPP_LOG_UNORDERED_SET)
+# include <unordered_set>
+# endif // defined(ELPP_UNORDERED_SET)
+#endif // defined(ELPP_STL_LOGGING)
+#if defined(ELPP_QT_LOGGING)
+// For logging Qt based classes & templates
+# include <QString>
+# include <QByteArray>
+# include <QVector>
+# include <QList>
+# include <QPair>
+# include <QMap>
+# include <QQueue>
+# include <QSet>
+# include <QLinkedList>
+# include <QHash>
+# include <QMultiHash>
+# include <QStack>
+#endif // defined(ELPP_QT_LOGGING)
+#if defined(ELPP_BOOST_LOGGING)
+// For logging boost based classes & templates
+# include <boost/container/vector.hpp>
+# include <boost/container/stable_vector.hpp>
+# include <boost/container/list.hpp>
+# include <boost/container/deque.hpp>
+# include <boost/container/map.hpp>
+# include <boost/container/flat_map.hpp>
+# include <boost/container/set.hpp>
+# include <boost/container/flat_set.hpp>
+#endif // defined(ELPP_BOOST_LOGGING)
+#if defined(ELPP_WXWIDGETS_LOGGING)
+// For logging wxWidgets based classes & templates
+# include <wx/vector.h>
+#endif // defined(ELPP_WXWIDGETS_LOGGING)
+// Forward declarations
+namespace el {
+class Logger;
+class LogMessage;
+class PerformanceTrackingData;
+class Loggers;
+class Helpers;
+template <typename T> class Callback;
+class LogDispatchCallback;
+class PerformanceTrackingCallback;
+class LogDispatchData;
+namespace base {
+class Storage;
+class RegisteredLoggers;
+class PerformanceTracker;
+class MessageBuilder;
+class Writer;
+class PErrorWriter;
+class LogDispatcher;
+class DefaultLogBuilder;
+class DefaultLogDispatchCallback;
+#if ELPP_ASYNC_LOGGING
+class AsyncLogDispatchCallback;
+class AsyncDispatchWorker;
+#endif // ELPP_ASYNC_LOGGING
+class DefaultPerformanceTrackingCallback;
+} // namespace base
+} // namespace el
+/// @brief Easylogging++ entry namespace
+namespace el {
+/// @brief Namespace containing base/internal functionality used by Easylogging++
+namespace base {
+/// @brief Data types used by Easylogging++
+namespace type {
+#undef ELPP_LITERAL
+#undef ELPP_STRLEN
+#undef ELPP_COUT
+#if defined(ELPP_UNICODE)
+# define ELPP_LITERAL(txt) L##txt
+# define ELPP_STRLEN wcslen
+# if defined ELPP_CUSTOM_COUT
+# define ELPP_COUT ELPP_CUSTOM_COUT
+# else
+# define ELPP_COUT std::wcout
+# endif // defined ELPP_CUSTOM_COUT
+typedef wchar_t char_t;
+typedef std::wstring string_t;
+typedef std::wstringstream stringstream_t;
+typedef std::wfstream fstream_t;
+typedef std::wostream ostream_t;
+#else
+# define ELPP_LITERAL(txt) txt
+# define ELPP_STRLEN strlen
+# if defined ELPP_CUSTOM_COUT
+# define ELPP_COUT ELPP_CUSTOM_COUT
+# else
+# define ELPP_COUT std::cout
+# endif // defined ELPP_CUSTOM_COUT
+typedef char char_t;
+typedef std::string string_t;
+typedef std::stringstream stringstream_t;
+typedef std::fstream fstream_t;
+typedef std::ostream ostream_t;
+#endif // defined(ELPP_UNICODE)
+#if defined(ELPP_CUSTOM_COUT_LINE)
+# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine)
+#else
+# define ELPP_COUT_LINE(logLine) logLine << std::flush
+#endif // defined(ELPP_CUSTOM_COUT_LINE)
+typedef unsigned short EnumType;
+typedef std::shared_ptr<base::Storage> StoragePointer;
+typedef int VerboseLevel;
+typedef std::shared_ptr<LogDispatchCallback> LogDispatchCallbackPtr;
+typedef std::shared_ptr<PerformanceTrackingCallback> PerformanceTrackingCallbackPtr;
+} // namespace type
+/// @brief Internal helper class that prevent copy constructor for class
+///
+/// @detail When using this class simply inherit it privately
+class NoCopy {
+protected:
+ NoCopy(void) {}
+private:
+ NoCopy(const NoCopy&);
+ NoCopy& operator=(const NoCopy&);
+};
+/// @brief Internal helper class that makes all default constructors private.
+///
+/// @detail This prevents initializing class making it static unless an explicit constructor is declared.
+/// When using this class simply inherit it privately
+class StaticClass {
+private:
+ StaticClass(void);
+ StaticClass(const StaticClass&);
+ StaticClass& operator=(const StaticClass&);
+};
+} // namespace base
+/// @brief Represents enumeration for severity level used to determine level of logging
+///
+/// @detail With Easylogging++, developers may disable or enable any level regardless of
+/// what the severity is. Or they can choose to log using hierarchical logging flag
+enum class Level : base::type::EnumType {
+ /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels
+ Global = 1,
+ /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs.
+ Trace = 2,
+ /// @brief Informational events most useful for developers to debug application
+ Debug = 4,
+ /// @brief Severe error information that will presumably abort application
+ Fatal = 8,
+ /// @brief Information representing errors in application but application will keep running
+ Error = 16,
+ /// @brief Useful when application has potentially harmful situtaions
+ Warning = 32,
+ /// @brief Information that can be highly useful and vary with verbose logging level.
+ Verbose = 64,
+ /// @brief Mainly useful to represent current progress of application
+ Info = 128,
+ /// @brief Represents unknown level
+ Unknown = 1010
+};
+/// @brief Static class that contains helper functions for el::Level
+class LevelHelper : base::StaticClass {
+public:
+ /// @brief Represents minimum valid level. Useful when iterating through enum.
+ static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(Level::Trace);
+ /// @brief Represents maximum valid level. This is used internally and you should not need it.
+ static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(Level::Info);
+ /// @brief Casts level to int, useful for iterating through enum.
+ static base::type::EnumType castToInt(Level level) {
+ return static_cast<base::type::EnumType>(level);
+ }
+ /// @brief Casts int(ushort) to level, useful for iterating through enum.
+ static Level castFromInt(base::type::EnumType l) {
+ return static_cast<Level>(l);
+ }
+ /// @brief Converts level to associated const char*
+ /// @return Upper case string based level.
+ static const char* convertToString(Level level) {
+ // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
+ if (level == Level::Global) return "GLOBAL";
+ if (level == Level::Debug) return "DEBUG";
+ if (level == Level::Info) return "INFO";
+ if (level == Level::Warning) return "WARNING";
+ if (level == Level::Error) return "ERROR";
+ if (level == Level::Fatal) return "FATAL";
+ if (level == Level::Verbose) return "VERBOSE";
+ if (level == Level::Trace) return "TRACE";
+ return "UNKNOWN";
+ }
+ /// @brief Converts from levelStr to Level
+ /// @param levelStr Upper case string based level.
+ /// Lower case is also valid but providing upper case is recommended.
+ static Level convertFromString(const char* levelStr) {
+ if ((strcmp(levelStr, "GLOBAL") == 0) || (strcmp(levelStr, "global") == 0))
+ return Level::Global;
+ if ((strcmp(levelStr, "DEBUG") == 0) || (strcmp(levelStr, "debug") == 0))
+ return Level::Debug;
+ if ((strcmp(levelStr, "INFO") == 0) || (strcmp(levelStr, "info") == 0))
+ return Level::Info;
+ if ((strcmp(levelStr, "WARNING") == 0) || (strcmp(levelStr, "warning") == 0))
+ return Level::Warning;
+ if ((strcmp(levelStr, "ERROR") == 0) || (strcmp(levelStr, "error") == 0))
+ return Level::Error;
+ if ((strcmp(levelStr, "FATAL") == 0) || (strcmp(levelStr, "fatal") == 0))
+ return Level::Fatal;
+ if ((strcmp(levelStr, "VERBOSE") == 0) || (strcmp(levelStr, "verbose") == 0))
+ return Level::Verbose;
+ if ((strcmp(levelStr, "TRACE") == 0) || (strcmp(levelStr, "trace") == 0))
+ return Level::Trace;
+ return Level::Unknown;
+ }
+ /// @brief Applies specified function to each level starting from startIndex
+ /// @param startIndex initial value to start the iteration from. This is passed as pointer and
+ /// is left-shifted so this can be used inside function (fn) to represent current level.
+ /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels.
+ static inline void forEachLevel(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) {
+ base::type::EnumType lIndexMax = LevelHelper::kMaxValid;
+ do {
+ if (fn()) {
+ break;
+ }
+ *startIndex = static_cast<base::type::EnumType>(*startIndex << 1);
+ } while (*startIndex <= lIndexMax);
+ }
+};
+/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect
+/// of logging
+enum class ConfigurationType : base::type::EnumType {
+ /// @brief Determines whether or not corresponding level and logger of logging is enabled
+ /// You may disable all logs by using el::Level::Global
+ Enabled = 1,
+ /// @brief Whether or not to write corresponding log to log file
+ ToFile = 2,
+ /// @brief Whether or not to write corresponding level and logger log to standard output.
+ /// By standard output meaning termnal, command prompt etc
+ ToStandardOutput = 4,
+ /// @brief Determines format of logging corresponding level and logger.
+ Format = 8,
+ /// @brief Determines log file (full path) to write logs to for correponding level and logger
+ Filename = 16,
+ /// @brief Specifies milliseconds width. Width can be within range (1-6)
+ MillisecondsWidth = 32,
+ /// @brief Determines whether or not performance tracking is enabled.
+ ///
+ /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger
+ PerformanceTracking = 64,
+ /// @brief Specifies log file max size.
+ ///
+ /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will
+ /// be truncated and re-initiated.
+ MaxLogFileSize = 128,
+ /// @brief Specifies number of log entries to hold until we flush pending log data
+ LogFlushThreshold = 256,
+ /// @brief Represents unknown configuration
+ Unknown = 1010
+};
+/// @brief Static class that contains helper functions for el::ConfigurationType
+class ConfigurationTypeHelper : base::StaticClass {
+public:
+ /// @brief Represents minimum valid configuration type. Useful when iterating through enum.
+ static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(ConfigurationType::Enabled);
+ /// @brief Represents maximum valid configuration type. This is used internally and you should not need it.
+ static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(ConfigurationType::MaxLogFileSize);
+ /// @brief Casts configuration type to int, useful for iterating through enum.
+ static base::type::EnumType castToInt(ConfigurationType configurationType) {
+ return static_cast<base::type::EnumType>(configurationType);
+ }
+ /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum.
+ static ConfigurationType castFromInt(base::type::EnumType c) {
+ return static_cast<ConfigurationType>(c);
+ }
+ /// @brief Converts configuration type to associated const char*
+ /// @returns Upper case string based configuration type.
+ static const char* convertToString(ConfigurationType configurationType) {
+ // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
+ if (configurationType == ConfigurationType::Enabled) return "ENABLED";
+ if (configurationType == ConfigurationType::Filename) return "FILENAME";
+ if (configurationType == ConfigurationType::Format) return "FORMAT";
+ if (configurationType == ConfigurationType::ToFile) return "TO_FILE";
+ if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT";
+ if (configurationType == ConfigurationType::MillisecondsWidth) return "MILLISECONDS_WIDTH";
+ if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING";
+ if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE";
+ if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD";
+ return "UNKNOWN";
+ }
+ /// @brief Converts from configStr to ConfigurationType
+ /// @param configStr Upper case string based configuration type.
+ /// Lower case is also valid but providing upper case is recommended.
+ static ConfigurationType convertFromString(const char* configStr) {
+ if ((strcmp(configStr, "ENABLED") == 0) || (strcmp(configStr, "enabled") == 0))
+ return ConfigurationType::Enabled;
+ if ((strcmp(configStr, "TO_FILE") == 0) || (strcmp(configStr, "to_file") == 0))
+ return ConfigurationType::ToFile;
+ if ((strcmp(configStr, "TO_STANDARD_OUTPUT") == 0) || (strcmp(configStr, "to_standard_output") == 0))
+ return ConfigurationType::ToStandardOutput;
+ if ((strcmp(configStr, "FORMAT") == 0) || (strcmp(configStr, "format") == 0))
+ return ConfigurationType::Format;
+ if ((strcmp(configStr, "FILENAME") == 0) || (strcmp(configStr, "filename") == 0))
+ return ConfigurationType::Filename;
+ if ((strcmp(configStr, "MILLISECONDS_WIDTH") == 0) || (strcmp(configStr, "milliseconds_width") == 0))
+ return ConfigurationType::MillisecondsWidth;
+ if ((strcmp(configStr, "PERFORMANCE_TRACKING") == 0) || (strcmp(configStr, "performance_tracking") == 0))
+ return ConfigurationType::PerformanceTracking;
+ if ((strcmp(configStr, "MAX_LOG_FILE_SIZE") == 0) || (strcmp(configStr, "max_log_file_size") == 0))
+ return ConfigurationType::MaxLogFileSize;
+ if ((strcmp(configStr, "LOG_FLUSH_THRESHOLD") == 0) || (strcmp(configStr, "log_flush_threshold") == 0))
+ return ConfigurationType::LogFlushThreshold;
+ return ConfigurationType::Unknown;
+ }
+ /// @brief Applies specified function to each configuration type starting from startIndex
+ /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted
+ /// so this can be used inside function (fn) to represent current configuration type.
+ /// @param fn function to apply with each configuration type.
+ /// This bool represent whether or not to stop iterating through configurations.
+ static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) {
+ base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid;
+ do {
+ if (fn()) {
+ break;
+ }
+ *startIndex = static_cast<base::type::EnumType>(*startIndex << 1);
+ } while (*startIndex <= cIndexMax);
+ }
+};
+/// @brief Flags used while writing logs. This flags are set by user
+enum class LoggingFlag : base::type::EnumType {
+ /// @brief Makes sure we have new line for each container log entry
+ NewLineForContainer = 1,
+ /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose
+ /// logging is allowed via that module.
+ AllowVerboseIfModuleNotSpecified = 2,
+ /// @brief When handling crashes by default, detailed crash reason will be logged as well
+ LogDetailedCrashReason = 4,
+ /// @brief Allows to disable application abortion when logged using FATAL level
+ DisableApplicationAbortOnFatalLog = 8,
+ /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default
+ ImmediateFlush = 16,
+ /// @brief Enables strict file rolling
+ StrictLogFileSizeCheck = 32,
+ /// @brief Make terminal output colorful for supported terminals
+ ColoredTerminalOutput = 64,
+ /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network")
+ MultiLoggerSupport = 128,
+ /// @brief Disables comparing performance tracker's checkpoints
+ DisablePerformanceTrackingCheckpointComparison = 256,
+ /// @brief Disable VModules
+ DisableVModules = 512,
+ /// @brief Disable VModules extensions
+ DisableVModulesExtensions = 1024,
+ /// @brief Enables hierarchical logging
+ HierarchicalLogging = 2048,
+ /// @brief Creates logger automatically when not available
+ CreateLoggerAutomatically = 4096,
+ /// @brief Adds spaces b/w logs that separated by left-shift operator
+ AutoSpacing = 8192,
+ /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only)
+ FixedTimeFormat = 16384
+};
+namespace base {
+/// @brief Namespace containing constants used internally.
+namespace consts {
+ // Level log values - These are values that are replaced in place of %level format specifier
+ static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO ");
+ static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG");
+ static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARN ");
+ static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR");
+ static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL");
+ static const base::type::char_t* kVerboseLevelLogValue = ELPP_LITERAL("VER");
+ static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE");
+ static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I");
+ static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D");
+ static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W");
+ static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E");
+ static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F");
+ static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V");
+ static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T");
+ // Format specifiers - These are used to define log format
+ static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app");
+ static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger");
+ static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread");
+ static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level");
+ static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort");
+ static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime");
+ static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file");
+ static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase");
+ static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line");
+ static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc");
+ static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func");
+ static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user");
+ static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host");
+ static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg");
+ static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel");
+ static const char* kDateTimeFormatSpecifierForFilename = "%datetime";
+ // Date/time
+ static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
+ static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+ static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August",
+ "September", "October", "November", "December" };
+ static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g";
+ static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m";
+ static const int kYearBase = 1900;
+ static const char* kAm = "AM";
+ static const char* kPm = "PM";
+ // Miscellaneous constants
+ static const char* kDefaultLoggerId = "default";
+ static const char* kPerformanceLoggerId = "performance";
+ static const char* kSysLogLoggerId = "syslog";
+ static const char* kNullPointer = "nullptr";
+ static const char kFormatSpecifierChar = '%';
+#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
+ static const char kFormatSpecifierCharValue = 'v';
+#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
+ static const unsigned int kMaxLogPerContainer = 100;
+ static const unsigned int kMaxLogPerCounter = 100000;
+ static const unsigned int kDefaultMillisecondsWidth = 3;
+ static const base::type::VerboseLevel kMaxVerboseLevel = 9;
+ static const char* kUnknownUser = "user";
+ static const char* kUnknownHost = "unknown-host";
+#if defined(ELPP_DEFAULT_LOG_FILE)
+ static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE;
+#else
+# if ELPP_OS_UNIX
+# if ELPP_OS_ANDROID
+ static const char* kDefaultLogFile = "logs/myeasylog.log";
+# else
+ static const char* kDefaultLogFile = "logs/myeasylog.log";
+# endif // ELPP_OS_ANDROID
+# elif ELPP_OS_WINDOWS
+ static const char* kDefaultLogFile = "logs\\myeasylog.log";
+# endif // ELPP_OS_UNIX
+#endif // defined(ELPP_DEFAULT_LOG_FILE)
+#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+ static const char* kDefaultLogFileParam = "--default-log-file";
+#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+#if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+ static const char* kLoggingFlagsParam = "--logging-flags";
+#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+#if ELPP_OS_WINDOWS
+ static const char* kFilePathSeperator = "\\";
+#else
+ static const char* kFilePathSeperator = "/";
+#endif // ELPP_OS_WINDOWS
+ static const char* kValidLoggerIdSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._";
+ static const char* kConfigurationComment = "##";
+ static const char* kConfigurationLevel = "*";
+ static const char* kConfigurationLoggerId = "--";
+ static const std::size_t kSourceFilenameMaxLength = 100;
+ static const std::size_t kSourceLineMaxLength = 10;
+ static const Level kPerformanceTrackerDefaultLevel = Level::Info;
+ const struct {
+ double value;
+ const base::type::char_t* unit;
+ } kTimeFormats[] = {
+ { 1000.0f, ELPP_LITERAL("mis") },
+ { 1000.0f, ELPP_LITERAL("ms") },
+ { 60.0f, ELPP_LITERAL("seconds") },
+ { 60.0f, ELPP_LITERAL("minutes") },
+ { 24.0f, ELPP_LITERAL("hours") },
+ { 7.0f, ELPP_LITERAL("days") }
+ };
+ static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]);
+ const struct {
+ int numb;
+ const char* name;
+ const char* brief;
+ const char* detail;
+ } kCrashSignals[] = {
+ // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..)
+ { SIGABRT, "SIGABRT", "Abnormal termination",
+ "Program was abnormally terminated." },
+ { SIGFPE, "SIGFPE", "Erroneous arithmetic operation",
+ "Arithemetic operation issue such as division by zero or operation resulting in overflow." },
+ { SIGILL, "SIGILL", "Illegal instruction",
+ "Generally due to a corruption in the code or to an attempt to execute data."},
+ { SIGSEGV, "SIGSEGV", "Invalid access to memory",
+ "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." },
+ { SIGINT, "SIGINT", "Interactive attention signal",
+ "Interruption generated (generally) by user or operating system." },
+ };
+ static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]);
+} // namespace consts
+} // namespace base
+typedef std::function<void(const char*, std::size_t)> PreRollOutCallback;
+namespace base {
+static inline void defaultPreRollOutCallback(const char*, std::size_t) {}
+/// @brief Enum to represent timestamp unit
+enum class TimestampUnit : base::type::EnumType {
+ Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5
+};
+/// @brief Format flags used to determine specifiers that are active for performance improvements.
+enum class FormatFlags : base::type::EnumType {
+ DateTime = 1<<1, LoggerId = 1<<2, File = 1<<3, Line = 1<<4, Location = 1<<5, Function = 1<<6,
+ User = 1<<7, Host = 1<<8, LogMessage = 1<<9, VerboseLevel = 1<<10, AppName = 1<<11, ThreadId = 1<<12,
+ Level = 1<<13, FileBase = 1<<14, LevelShort = 1<<15
+};
+/// @brief A milliseconds width class containing actual width and offset for date/time
+class MillisecondsWidth {
+public:
+ MillisecondsWidth(void) { init(base::consts::kDefaultMillisecondsWidth); }
+ explicit MillisecondsWidth(int width) { init(width); }
+ bool operator==(const MillisecondsWidth& msWidth) { return m_width == msWidth.m_width && m_offset == msWidth.m_offset; }
+ int m_width; unsigned int m_offset;
+private:
+ void init(int width) {
+ if (width < 1 || width > 6) {
+ width = base::consts::kDefaultMillisecondsWidth;
+ }
+ m_width = width;
+ switch (m_width) {
+ case 3: m_offset = 1000; break;
+ case 4: m_offset = 100; break;
+ case 5: m_offset = 10; break;
+ case 6: m_offset = 1; break;
+ default: m_offset = 1000; break;
+ }
+ }
+};
+/// @brief Namespace containing utility functions/static classes used internally
+namespace utils {
+/// @brief Deletes memory safely and points to null
+template <typename T>
+static inline
+typename std::enable_if<std::is_pointer<T*>::value, void>::type
+safeDelete(T*& pointer) {
+ if (pointer == nullptr)
+ return;
+ delete pointer;
+ pointer = nullptr;
+}
+/// @brief Gets value of const char* but if it is nullptr, a string nullptr is returned
+static inline const char* charPtrVal(const char* pointer) {
+ return pointer == nullptr ? base::consts::kNullPointer : pointer;
+}
+/// @brief Aborts application due with user-defined status
+static inline void abort(int status, const std::string& reason = std::string()) {
+ // Both status and reason params are there for debugging with tools like gdb etc
+ ELPP_UNUSED(status);
+ ELPP_UNUSED(reason);
+#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
+ // Ignore msvc critical error dialog - break instead (on debug mode)
+ _asm int 3
+#else
+ ::abort();
+#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
+}
+/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation
+/// Use these function as <pre>flag = bitwise::Or<MyEnum>(MyEnum::val1, flag);</pre>
+namespace bitwise {
+template <typename Enum>
+static inline base::type::EnumType And(Enum e, base::type::EnumType flag) {
+ return static_cast<base::type::EnumType>(flag) & static_cast<base::type::EnumType>(e);
+}
+template <typename Enum>
+static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) {
+ return static_cast<base::type::EnumType>(flag) & ~(static_cast<base::type::EnumType>(e));
+}
+template <typename Enum>
+static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) {
+ return static_cast<base::type::EnumType>(flag) | static_cast<base::type::EnumType>(e);
+}
+} // namespace bitwise
+template <typename Enum>
+static inline void addFlag(Enum e, base::type::EnumType* flag) {
+ *flag = base::utils::bitwise::Or<Enum>(e, *flag);
+}
+template <typename Enum>
+static inline void removeFlag(Enum e, base::type::EnumType* flag) {
+ *flag = base::utils::bitwise::Not<Enum>(e, *flag);
+}
+template <typename Enum>
+static inline bool hasFlag(Enum e, base::type::EnumType flag) {
+ return base::utils::bitwise::And<Enum>(e, flag) > 0x0;
+}
+} // namespace utils
+namespace threading {
+#if ELPP_THREADING_ENABLED
+# if !ELPP_USE_STD_THREADING
+namespace internal {
+/// @brief A mutex wrapper for compiler that dont yet support std::mutex
+class Mutex : base::NoCopy {
+public:
+ Mutex(void) {
+# if ELPP_OS_UNIX
+ pthread_mutex_init(&m_underlyingMutex, nullptr);
+# elif ELPP_OS_WINDOWS
+ InitializeCriticalSection(&m_underlyingMutex);
+# endif // ELPP_OS_UNIX
+ }
+
+ virtual ~Mutex(void) {
+# if ELPP_OS_UNIX
+ pthread_mutex_destroy(&m_underlyingMutex);
+# elif ELPP_OS_WINDOWS
+ DeleteCriticalSection(&m_underlyingMutex);
+# endif // ELPP_OS_UNIX
+ }
+
+ inline void lock(void) {
+# if ELPP_OS_UNIX
+ pthread_mutex_lock(&m_underlyingMutex);
+# elif ELPP_OS_WINDOWS
+ EnterCriticalSection(&m_underlyingMutex);
+# endif // ELPP_OS_UNIX
+ }
+
+ inline bool try_lock(void) {
+# if ELPP_OS_UNIX
+ return (pthread_mutex_trylock(&m_underlyingMutex) == 0);
+# elif ELPP_OS_WINDOWS
+ return TryEnterCriticalSection(&m_underlyingMutex);
+# endif // ELPP_OS_UNIX
+ }
+
+ inline void unlock(void) {
+# if ELPP_OS_UNIX
+ pthread_mutex_unlock(&m_underlyingMutex);
+# elif ELPP_OS_WINDOWS
+ LeaveCriticalSection(&m_underlyingMutex);
+# endif // ELPP_OS_UNIX
+ }
+
+private:
+# if ELPP_OS_UNIX
+ pthread_mutex_t m_underlyingMutex;
+# elif ELPP_OS_WINDOWS
+ CRITICAL_SECTION m_underlyingMutex;
+# endif // ELPP_OS_UNIX
+};
+/// @brief Scoped lock for compiler that dont yet support std::lock_guard
+template <typename M>
+class ScopedLock : base::NoCopy {
+public:
+ explicit ScopedLock(M& mutex) {
+ m_mutex = &mutex;
+ m_mutex->lock();
+ }
+
+ virtual ~ScopedLock(void) {
+ m_mutex->unlock();
+ }
+private:
+ M* m_mutex;
+ ScopedLock(void);
+};
+} // namespace internal
+/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned.
+static inline std::string getCurrentThreadId(void) {
+ std::stringstream ss;
+# if (ELPP_OS_WINDOWS)
+ ss << GetCurrentThreadId();
+# endif // (ELPP_OS_WINDOWS)
+ return ss.str();
+}
+static inline void msleep(int) {
+ // No implementation for non std::thread version
+}
+typedef base::threading::internal::Mutex Mutex;
+typedef base::threading::internal::ScopedLock<base::threading::Mutex> ScopedLock;
+# else
+/// @brief Gets ID of currently running threading using std::this_thread::get_id()
+static inline std::string getCurrentThreadId(void) {
+ std::stringstream ss;
+ ss << std::this_thread::get_id();
+ return ss.str();
+}
+static inline void msleep(int ms) {
+ // Only when async logging enabled - this is because async is strict on compiler
+#if ELPP_ASYNC_LOGGING
+ std::this_thread::sleep_for(std::chrono::milliseconds(ms));
+#endif // ELPP_ASYNC_LOGGING
+}
+typedef std::mutex Mutex;
+typedef std::lock_guard<std::mutex> ScopedLock;
+# endif // !ELPP_USE_STD_THREADING
+#else
+namespace internal {
+/// @brief Mutex wrapper used when multi-threading is disabled.
+class NoMutex : base::NoCopy {
+public:
+ NoMutex(void) {}
+ inline void lock(void) {}
+ inline bool try_lock(void) { return true; }
+ inline void unlock(void) {}
+};
+/// @brief Lock guard wrapper used when multi-threading is disabled.
+template <typename Mutex>
+class NoScopedLock : base::NoCopy {
+public:
+ explicit NoScopedLock(Mutex&) {
+ }
+ virtual ~NoScopedLock(void) {
+ }
+private:
+ NoScopedLock(void);
+};
+} // namespace internal
+static inline std::string getCurrentThreadId(void) {
+ return std::string();
+}
+static inline void msleep(int) {
+ // No custom implementation
+}
+typedef base::threading::internal::NoMutex Mutex;
+typedef base::threading::internal::NoScopedLock<base::threading::Mutex> ScopedLock;
+#endif // ELPP_THREADING_ENABLED
+/// @brief Base of thread safe class, this class is inheritable-only
+class ThreadSafe {
+public:
+ virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); }
+ virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); }
+ virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; }
+protected:
+ ThreadSafe(void) {}
+ virtual ~ThreadSafe(void) {}
+private:
+ base::threading::Mutex m_mutex;
+};
+} // namespace threading
+namespace utils {
+class File : base::StaticClass {
+public:
+ /// @brief Creates new out file stream for specified filename.
+ /// @return Pointer to newly created fstream or nullptr
+ static base::type::fstream_t* newFileStream(const std::string& filename) {
+ base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(),
+ base::type::fstream_t::out | base::type::fstream_t::app);
+#if defined(ELPP_UNICODE)
+ std::locale elppUnicodeLocale("");
+#if ELPP_OS_WINDOWS
+ std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16<wchar_t>);
+ elppUnicodeLocale = elppUnicodeLocaleWindows;
+#endif
+ fs->imbue(elppUnicodeLocale);
+#endif // defined(ELPP_UNICODE)
+ if (fs->is_open()) {
+ fs->flush();
+ } else {
+ base::utils::safeDelete(fs);
+ ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true);
+ }
+ return fs;
+ }
+
+ /// @brief Gets size of file provided in stream
+ static std::size_t getSizeOfFile(base::type::fstream_t* fs) {
+ if (fs == nullptr) {
+ return 0;
+ }
+ std::streampos currPos = fs->tellg();
+ fs->seekg(0, fs->end);
+ std::size_t size = static_cast<std::size_t>(fs->tellg());
+ fs->seekg(currPos);
+ return size;
+ }
+
+ /// @brief Determines whether or not provided path exist in current file system
+ static inline bool pathExists(const char* path, bool considerFile = false) {
+ if (path == nullptr) {
+ return false;
+ }
+#if ELPP_OS_UNIX
+ ELPP_UNUSED(considerFile);
+ struct stat st;
+ return (stat(path, &st) == 0);
+#elif ELPP_OS_WINDOWS
+ DWORD fileType = GetFileAttributesA(path);
+ if (fileType == INVALID_FILE_ATTRIBUTES) {
+ return false;
+ }
+ return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true);
+#endif // ELPP_OS_UNIX
+ }
+
+ /// @brief Creates specified path on file system
+ /// @param path Path to create.
+ static bool createPath(const std::string& path) {
+ if (path.empty()) {
+ return false;
+ }
+ if (base::utils::File::pathExists(path.c_str())) {
+ return true;
+ }
+ int status = -1;
+
+ char* currPath = const_cast<char*>(path.c_str());
+ std::string builtPath = std::string();
+#if ELPP_OS_UNIX
+ if (path[0] == '/') {
+ builtPath = "/";
+ }
+ currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0);
+#elif ELPP_OS_WINDOWS
+ // Use secure functions API
+ char* nextTok_ = nullptr;
+ currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_);
+ ELPP_UNUSED(nextTok_);
+#endif // ELPP_OS_UNIX
+ while (currPath != nullptr) {
+ builtPath.append(currPath);
+ builtPath.append(base::consts::kFilePathSeperator);
+#if ELPP_OS_UNIX
+ status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS);
+ currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0);
+#elif ELPP_OS_WINDOWS
+ status = _mkdir(builtPath.c_str());
+ currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_);
+#endif // ELPP_OS_UNIX
+ }
+ if (status == -1) {
+ ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true);
+ return false;
+ }
+ return true;
+ }
+ /// @brief Extracts path of filename with leading slash
+ static std::string extractPathFromFilename(const std::string& fullPath,
+ const char* seperator = base::consts::kFilePathSeperator) {
+ if ((fullPath == "") || (fullPath.find(seperator) == std::string::npos)) {
+ return fullPath;
+ }
+ std::size_t lastSlashAt = fullPath.find_last_of(seperator);
+ if (lastSlashAt == 0) {
+ return std::string(seperator);
+ }
+ return fullPath.substr(0, lastSlashAt + 1);
+ }
+ /// @brief builds stripped filename and puts it in buff
+ static void buildStrippedFilename(const char* filename, char buff[],
+ std::size_t limit = base::consts::kSourceFilenameMaxLength) {
+ std::size_t sizeOfFilename = strlen(filename);
+ if (sizeOfFilename >= limit) {
+ filename += (sizeOfFilename - limit);
+ if (filename[0] != '.' && filename[1] != '.') { // prepend if not already
+ filename += 3; // 3 = '..'
+ STRCAT(buff, "..", limit);
+ }
+ }
+ STRCAT(buff, filename, limit);
+ }
+ /// @brief builds base filename and puts it in buff
+ static void buildBaseFilename(const std::string& fullPath, char buff[],
+ std::size_t limit = base::consts::kSourceFilenameMaxLength,
+ const char* seperator = base::consts::kFilePathSeperator) {
+ const char *filename = fullPath.c_str();
+ std::size_t lastSlashAt = fullPath.find_last_of(seperator);
+ filename += lastSlashAt ? lastSlashAt+1 : 0;
+ std::size_t sizeOfFilename = strlen(filename);
+ if (sizeOfFilename >= limit) {
+ filename += (sizeOfFilename - limit);
+ if (filename[0] != '.' && filename[1] != '.') { // prepend if not already
+ filename += 3; // 3 = '..'
+ STRCAT(buff, "..", limit);
+ }
+ }
+ STRCAT(buff, filename, limit);
+ }
+};
+/// @brief String utilities helper class used internally. You should not use it.
+class Str : base::StaticClass {
+public:
+ /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues.
+ static inline bool isDigit(char c) {
+ return c >= '0' && c <= '9';
+ }
+
+ /// @brief Matches wildcards, '*' and '?' only supported.
+ static bool wildCardMatch(const char* str, const char* pattern) {
+ while (*pattern) {
+ switch (*pattern) {
+ case '?':
+ if (!*str)
+ return false;
+ ++str;
+ ++pattern;
+ break;
+ case '*':
+ if (wildCardMatch(str, pattern + 1))
+ return true;
+ if (*str && wildCardMatch(str + 1, pattern))
+ return true;
+ return false;
+ break;
+ default:
+ if (*str++ != *pattern++)
+ return false;
+ break;
+ }
+ }
+ return !*str && !*pattern;
+ }
+
+ /// @brief Trims string from start
+ /// @param [in,out] str String to trim
+ static inline std::string& ltrim(std::string& str) {
+ str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(std::ptr_fun<int, int>(&std::isspace))));
+ return str;
+ }
+
+ /// @brief Trim string from end
+ /// @param [in,out] str String to trim
+ static inline std::string& rtrim(std::string& str) {
+ str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun<int, int>(&std::isspace))).base(), str.end());
+ return str;
+ }
+
+ /// @brief Trims string from left and right
+ /// @param [in,out] str String to trim
+ static inline std::string& trim(std::string& str) {
+ return ltrim(rtrim(str));
+ }
+
+ /// @brief Determines whether or not str starts with specified string
+ /// @param str String to check
+ /// @param start String to check against
+ /// @return Returns true if starts with specified string, false otherwise
+ static inline bool startsWith(const std::string& str, const std::string& start) {
+ return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0);
+ }
+
+ /// @brief Determines whether or not str ends with specified string
+ /// @param str String to check
+ /// @param end String to check against
+ /// @return Returns true if ends with specified string, false otherwise
+ static inline bool endsWith(const std::string& str, const std::string& end) {
+ return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0);
+ }
+
+ /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance.
+ /// @param [in,out] str String to replace from
+ /// @param replaceWhat Character to replace
+ /// @param replaceWith Character to replace with
+ /// @return Modified version of str
+ static inline std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith) {
+ std::replace(str.begin(), str.end(), replaceWhat, replaceWith);
+ return str;
+ }
+
+ /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place
+ /// @param str String to replace from
+ /// @param replaceWhat Character to replace
+ /// @param replaceWith Character to replace with
+ /// @return Modified (original) str
+ static inline std::string& replaceAll(std::string& str, const std::string& replaceWhat, // NOLINT
+ const std::string& replaceWith) {
+ if (replaceWhat == replaceWith)
+ return str;
+ std::size_t foundAt = std::string::npos;
+ while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) {
+ str.replace(foundAt, replaceWhat.length(), replaceWith);
+ }
+ return str;
+ }
+
+ static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT
+ const base::type::string_t& replaceWith) {
+ std::size_t foundAt = base::type::string_t::npos;
+ while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) {
+ if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) {
+ str.erase(foundAt > 0 ? foundAt - 1 : 0, 1);
+ ++foundAt;
+ } else {
+ str.replace(foundAt, replaceWhat.length(), replaceWith);
+ return;
+ }
+ }
+ }
+#if defined(ELPP_UNICODE)
+ static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT
+ const std::string& replaceWith) {
+ replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end()));
+ }
+#endif // defined(ELPP_UNICODE)
+ /// @brief Converts string to uppercase
+ /// @param str String to convert
+ /// @return Uppercase string
+ static inline std::string& toUpper(std::string& str) {
+ std::transform(str.begin(), str.end(), str.begin(), ::toupper);
+ return str;
+ }
+
+ /// @brief Compares cstring equality - uses strcmp
+ static inline bool cStringEq(const char* s1, const char* s2) {
+ if (s1 == nullptr && s2 == nullptr) return true;
+ if (s1 == nullptr || s2 == nullptr) return false;
+ return strcmp(s1, s2) == 0;
+ }
+
+ /// @brief Compares cstring equality (case-insensitive) - uses toupper(char)
+ /// Dont use strcasecmp because of CRT (VC++)
+ static bool cStringCaseEq(const char* s1, const char* s2) {
+ if (s1 == nullptr && s2 == nullptr) return true;
+ if (s1 == nullptr || s2 == nullptr) return false;
+ if (strlen(s1) != strlen(s2)) return false;
+ while (*s1 != '\0' && *s2 != '\0') {
+ if (::toupper(*s1) != ::toupper(*s2)) return false;
+ ++s1;
+ ++s2;
+ }
+ return true;
+ }
+
+ /// @brief Returns true if c exist in str
+ static inline bool contains(const char* str, char c) {
+ for (; *str; ++str) {
+ if (*str == c)
+ return true;
+ }
+ return false;
+ }
+
+ static inline char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true) {
+ char localBuff[10] = "";
+ char* p = localBuff + sizeof(localBuff) - 2;
+ if (n > 0) {
+ for (; n > 0 && p > localBuff && len > 0; n /= 10, --len)
+ *--p = static_cast<char>(n % 10 + '0');
+ } else {
+ *--p = '0';
+ --len;
+ }
+ if (zeroPadded)
+ while (p > localBuff && len-- > 0) *--p = static_cast<char>('0');
+ return addToBuff(p, buf, bufLim);
+ }
+
+ static inline char* addToBuff(const char* str, char* buf, const char* bufLim) {
+ while ((buf < bufLim) && ((*buf = *str++) != '\0'))
+ ++buf;
+ return buf;
+ }
+
+ static inline char* clearBuff(char buff[], std::size_t lim) {
+ STRCPY(buff, "", lim);
+ ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro
+ return buff;
+ }
+
+ /// @brief Converst wchar* to char*
+ /// NOTE: Need to free return value after use!
+ static char* wcharPtrToCharPtr(const wchar_t* line) {
+ std::size_t len_ = wcslen(line) + 1;
+ char* buff_ = static_cast<char*>(malloc(len_ + 1));
+# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
+ std::wcstombs(buff_, line, len_);
+# elif ELPP_OS_WINDOWS
+ std::size_t convCount_ = 0;
+ mbstate_t mbState_;
+ ::memset(static_cast<void*>(&mbState_), 0, sizeof(mbState_));
+ wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_);
+# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
+ return buff_;
+ }
+};
+/// @brief Operating System helper static class used internally. You should not use it.
+class OS : base::StaticClass {
+public:
+#if ELPP_OS_WINDOWS
+ /// @brief Gets environment variables for Windows based OS.
+ /// We are not using <code>getenv(const char*)</code> because of CRT deprecation
+ /// @param varname Variable name to get environment variable value for
+ /// @return If variable exist the value of it otherwise nullptr
+ static const char* getWindowsEnvironmentVariable(const char* varname) {
+ const DWORD bufferLen = 50;
+ static char buffer[bufferLen];
+ if (GetEnvironmentVariableA(varname, buffer, bufferLen)) {
+ return buffer;
+ }
+ return nullptr;
+ }
+#endif // ELPP_OS_WINDOWS
+#if ELPP_OS_ANDROID
+ /// @brief Reads android property value
+ static inline std::string getProperty(const char* prop) {
+ char propVal[PROP_VALUE_MAX + 1];
+ int ret = __system_property_get(prop, propVal);
+ return ret == 0 ? std::string() : std::string(propVal);
+ }
+
+ /// @brief Reads android device name
+ static std::string getDeviceName(void) {
+ std::stringstream ss;
+ std::string manufacturer = getProperty("ro.product.manufacturer");
+ std::string model = getProperty("ro.product.model");
+ if (manufacturer.empty() || model.empty()) {
+ return std::string();
+ }
+ ss << manufacturer << "-" << model;
+ return ss.str();
+ }
+#endif // ELPP_OS_ANDROID
+
+ /// @brief Runs command on terminal and returns the output.
+ ///
+ /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned.
+ /// @param command Bash command
+ /// @return Result of bash output or empty string if no result found.
+ static const std::string getBashOutput(const char* command) {
+#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
+ if (command == nullptr) {
+ return std::string();
+ }
+ FILE* proc = nullptr;
+ if ((proc = popen(command, "r")) == nullptr) {
+ ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true);
+ return std::string();
+ }
+ char hBuff[4096];
+ if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) {
+ pclose(proc);
+ if (hBuff[strlen(hBuff) - 1] == '\n') {
+ hBuff[strlen(hBuff) - 1] = '\0';
+ }
+ return std::string(hBuff);
+ }
+ return std::string();
+#else
+ ELPP_UNUSED(command);
+ return std::string();
+#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
+ }
+
+ /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++)
+ /// @param variableName Environment variable name
+ /// @param defaultVal If no environment variable or value found the value to return by default
+ /// @param alternativeBashCommand If environment variable not found what would be alternative bash command
+ /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami'
+ static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, const char* alternativeBashCommand = nullptr) {
+#if ELPP_OS_UNIX
+ const char* val = getenv(variableName);
+#elif ELPP_OS_WINDOWS
+ const char* val = getWindowsEnvironmentVariable(variableName);
+#endif // ELPP_OS_UNIX
+ if ((val == nullptr) || ((strcmp(val, "") == 0))) {
+#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
+ // Try harder on unix-based systems
+ std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand);
+ if (valBash.empty()) {
+ return std::string(defaultVal);
+ } else {
+ return valBash;
+ }
+#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX
+ ELPP_UNUSED(alternativeBashCommand);
+ return std::string(defaultVal);
+#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
+ }
+ return std::string(val);
+ }
+ /// @brief Gets current username.
+ static inline std::string currentUser(void) {
+#if ELPP_OS_UNIX && !ELPP_OS_ANDROID
+ return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami");
+#elif ELPP_OS_WINDOWS
+ return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser);
+#elif ELPP_OS_ANDROID
+ ELPP_UNUSED(base::consts::kUnknownUser);
+ return std::string("android");
+#else
+ return std::string();
+#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID
+ }
+
+ /// @brief Gets current host name or computer name.
+ ///
+ /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen
+ static inline std::string currentHost(void) {
+#if ELPP_OS_UNIX && !ELPP_OS_ANDROID
+ return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname");
+#elif ELPP_OS_WINDOWS
+ return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost);
+#elif ELPP_OS_ANDROID
+ ELPP_UNUSED(base::consts::kUnknownHost);
+ return getDeviceName();
+#else
+ return std::string();
+#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID
+ }
+ /// @brief Whether or not terminal supports colors
+ static inline bool termSupportsColor(void) {
+ std::string term = getEnvironmentVariable("TERM", "");
+ return term == "xterm" || term == "xterm-color" || term == "xterm-256color" ||
+ term == "screen" || term == "linux" || term == "cygwin";
+ }
+};
+extern std::string s_currentUser;
+extern std::string s_currentHost;
+extern bool s_termSupportsColor;
+#define ELPP_INITI_BASIC_DECLR \
+ namespace el {\
+ namespace base {\
+ namespace utils {\
+ std::string s_currentUser = el::base::utils::OS::currentUser(); \
+ std::string s_currentHost = el::base::utils::OS::currentHost(); \
+ bool s_termSupportsColor = el::base::utils::OS::termSupportsColor(); \
+ }\
+ }\
+ }
+/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str
+class DateTime : base::StaticClass {
+public:
+ /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current millisecond.
+ ///
+ /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided
+ /// @param [in,out] tv Pointer that gets updated
+ static void gettimeofday(struct timeval* tv) {
+#if ELPP_OS_WINDOWS
+ if (tv != nullptr) {
+# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
+ const unsigned __int64 delta_ = 11644473600000000Ui64;
+# else
+ const unsigned __int64 delta_ = 11644473600000000ULL;
+# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
+ const double secOffSet = 0.000001;
+ const unsigned long usecOffSet = 1000000;
+ FILETIME fileTime;
+ GetSystemTimeAsFileTime(&fileTime);
+ unsigned __int64 present = 0;
+ present |= fileTime.dwHighDateTime;
+ present = present << 32;
+ present |= fileTime.dwLowDateTime;
+ present /= 10; // mic-sec
+ // Subtract the difference
+ present -= delta_;
+ tv->tv_sec = static_cast<long>(present * secOffSet);
+ tv->tv_usec = static_cast<long>(present % usecOffSet);
+ }
+#else
+ ::gettimeofday(tv, nullptr);
+#endif // ELPP_OS_WINDOWS
+ }
+
+ /// @brief Gets current date and time with milliseconds.
+ /// @param format User provided date/time format
+ /// @param msWidth A pointer to base::MillisecondsWidth from configuration (non-null)
+ /// @returns string based date time in specified format.
+ static inline std::string getDateTime(const char* format, const base::MillisecondsWidth* msWidth) {
+ struct timeval currTime;
+ gettimeofday(&currTime);
+ struct ::tm timeInfo;
+ buildTimeInfo(&currTime, &timeInfo);
+ const int kBuffSize = 30;
+ char buff_[kBuffSize] = "";
+ parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast<std::size_t>(currTime.tv_usec / msWidth->m_offset), msWidth);
+ return std::string(buff_);
+ }
+
+ /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc
+ static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit) {
+ double result = static_cast<double>(time);
+ base::type::EnumType start = static_cast<base::type::EnumType>(timestampUnit);
+ const base::type::char_t* unit = base::consts::kTimeFormats[start].unit;
+ for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) {
+ if (result <= base::consts::kTimeFormats[i].value) {
+ break;
+ }
+ result /= base::consts::kTimeFormats[i].value;
+ unit = base::consts::kTimeFormats[i + 1].unit;
+ }
+ base::type::stringstream_t ss;
+ ss << result << " " << unit;
+ return ss.str();
+ }
+
+ /// @brief Gets time difference in milli/micro second depending on timestampUnit
+ static inline unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, base::TimestampUnit timestampUnit) {
+ if (timestampUnit == base::TimestampUnit::Microsecond) {
+ return static_cast<unsigned long long>(static_cast<unsigned long long>(1000000 * endTime.tv_sec + endTime.tv_usec) -
+ static_cast<unsigned long long>(1000000 * startTime.tv_sec + startTime.tv_usec));
+ } else {
+ return static_cast<unsigned long long>((((endTime.tv_sec - startTime.tv_sec) * 1000000) + (endTime.tv_usec - startTime.tv_usec)) / 1000);
+ }
+ }
+
+private:
+ static inline struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) {
+#if ELPP_OS_UNIX
+ time_t rawTime = currTime->tv_sec;
+ ::localtime_r(&rawTime, timeInfo);
+ return timeInfo;
+#else
+# if ELPP_COMPILER_MSVC
+ ELPP_UNUSED(currTime);
+ time_t t;
+ _time64(&t);
+ localtime_s(timeInfo, &t);
+ return timeInfo;
+# else
+ // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method
+ time_t rawTime = currTime->tv_sec;
+ struct tm* tmInf = localtime(&rawTime);
+ *timeInfo = *tmInf;
+ return timeInfo;
+# endif // ELPP_COMPILER_MSVC
+#endif // ELPP_OS_UNIX
+ }
+ static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo,
+ std::size_t msec, const base::MillisecondsWidth* msWidth) {
+ const char* bufLim = buf + bufSz;
+ for (; *format; ++format) {
+ if (*format == base::consts::kFormatSpecifierChar) {
+ switch (*++format) {
+ case base::consts::kFormatSpecifierChar: // Escape
+ break;
+ case '\0': // End
+ --format;
+ break;
+ case 'd': // Day
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim);
+ continue;
+ case 'a': // Day of week (short)
+ buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim);
+ continue;
+ case 'A': // Day of week (long)
+ buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim);
+ continue;
+ case 'M': // month
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim);
+ continue;
+ case 'b': // month (short)
+ buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim);
+ continue;
+ case 'B': // month (long)
+ buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim);
+ continue;
+ case 'y': // year (two digits)
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim);
+ continue;
+ case 'Y': // year (four digits)
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim);
+ continue;
+ case 'h': // hour (12-hour)
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim);
+ continue;
+ case 'H': // hour (24-hour)
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim);
+ continue;
+ case 'm': // minute
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim);
+ continue;
+ case 's': // second
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim);
+ continue;
+ case 'z': // milliseconds
+ case 'g':
+ buf = base::utils::Str::convertAndAddToBuff(msec, msWidth->m_width, buf, bufLim);
+ continue;
+ case 'F': // AM/PM
+ buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim);
+ continue;
+ default:
+ continue;
+ }
+ }
+ if (buf == bufLim) break;
+ *buf++ = *format;
+ }
+ return buf;
+ }
+};
+/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..)
+class CommandLineArgs {
+public:
+ CommandLineArgs(void) {
+ setArgs(0, static_cast<char**>(nullptr));
+ }
+ CommandLineArgs(int argc, const char** argv) {
+ setArgs(argc, argv);
+ }
+ CommandLineArgs(int argc, char** argv) {
+ setArgs(argc, argv);
+ }
+ virtual ~CommandLineArgs(void) {}
+ /// @brief Sets arguments and parses them
+ inline void setArgs(int argc, const char** argv) {
+ setArgs(argc, const_cast<char**>(argv));
+ }
+ /// @brief Sets arguments and parses them
+ inline void setArgs(int argc, char** argv) {
+ m_params.clear();
+ m_paramsWithValue.clear();
+ if (argc == 0 || argv == nullptr) {
+ return;
+ }
+ m_argc = argc;
+ m_argv = argv;
+ for (int i = 1; i < m_argc; ++i) {
+ const char* v = (strstr(m_argv[i], "="));
+ if (v != nullptr && strlen(v) > 0) {
+ std::string key = std::string(m_argv[i]);
+ key = key.substr(0, key.find_first_of('='));
+ if (hasParamWithValue(key.c_str())) {
+ ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value ["
+ << getParamValue(key.c_str()) << "]");
+ } else {
+ m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1)));
+ }
+ }
+ if (v == nullptr) {
+ if (hasParam(m_argv[i])) {
+ ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists");
+ } else {
+ m_params.push_back(std::string(m_argv[i]));
+ }
+ }
+ }
+ }
+ /// @brief Returns true if arguments contain paramKey with a value (seperated by '=')
+ inline bool hasParamWithValue(const char* paramKey) const {
+ return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end();
+ }
+ /// @brief Returns value of arguments
+ /// @see hasParamWithValue(const char*)
+ inline const char* getParamValue(const char* paramKey) const {
+ return m_paramsWithValue.find(std::string(paramKey))->second.c_str();
+ }
+ /// @brief Return true if arguments has a param (not having a value) i,e without '='
+ inline bool hasParam(const char* paramKey) const {
+ return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end();
+ }
+ /// @brief Returns true if no params available. This exclude argv[0]
+ inline bool empty(void) const {
+ return m_params.empty() && m_paramsWithValue.empty();
+ }
+ /// @brief Returns total number of arguments. This exclude argv[0]
+ inline std::size_t size(void) const {
+ return m_params.size() + m_paramsWithValue.size();
+ }
+ inline friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) {
+ for (int i = 1; i < c.m_argc; ++i) {
+ os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]");
+ if (i < c.m_argc - 1) {
+ os << ELPP_LITERAL(" ");
+ }
+ }
+ return os;
+ }
+
+private:
+ int m_argc;
+ char** m_argv;
+ std::map<std::string, std::string> m_paramsWithValue;
+ std::vector<std::string> m_params;
+};
+/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type.
+///
+/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement
+/// unregisterAll() and deepCopy(const AbstractRegistry<T_Ptr, Container>&) and write registerNew() method according to container
+/// and few more methods; get() to find element, unregister() to unregister single entry.
+/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation.
+template <typename T_Ptr, typename Container>
+class AbstractRegistry : public base::threading::ThreadSafe {
+public:
+ typedef typename Container::iterator iterator;
+ typedef typename Container::const_iterator const_iterator;
+
+ /// @brief Default constructor
+ AbstractRegistry(void) {}
+
+ /// @brief Move constructor that is useful for base classes
+ AbstractRegistry(AbstractRegistry&& sr) {
+ if (this == &sr) {
+ return;
+ }
+ unregisterAll();
+ m_list = std::move(sr.m_list);
+ }
+
+ bool operator==(const AbstractRegistry<T_Ptr, Container>& other) {
+ if (size() != other.size()) {
+ return false;
+ }
+ for (std::size_t i = 0; i < m_list.size(); ++i) {
+ if (m_list.at(i) != other.m_list.at(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool operator!=(const AbstractRegistry<T_Ptr, Container>& other) {
+ if (size() != other.size()) {
+ return true;
+ }
+ for (std::size_t i = 0; i < m_list.size(); ++i) {
+ if (m_list.at(i) != other.m_list.at(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// @brief Assignment move operator
+ AbstractRegistry& operator=(AbstractRegistry&& sr) {
+ if (this == &sr) {
+ return *this;
+ }
+ unregisterAll();
+ m_list = std::move(sr.m_list);
+ return *this;
+ }
+
+ virtual ~AbstractRegistry(void) {
+ }
+
+ /// @return Iterator pointer from start of repository
+ virtual inline iterator begin(void) ELPP_FINAL {
+ return m_list.begin();
+ }
+
+ /// @return Iterator pointer from end of repository
+ virtual inline iterator end(void) ELPP_FINAL {
+ return m_list.end();
+ }
+
+
+ /// @return Constant iterator pointer from start of repository
+ virtual inline const_iterator cbegin(void) const ELPP_FINAL {
+ return m_list.cbegin();
+ }
+
+ /// @return End of repository
+ virtual inline const_iterator cend(void) const ELPP_FINAL {
+ return m_list.cend();
+ }
+
+ /// @return Whether or not repository is empty
+ virtual inline bool empty(void) const ELPP_FINAL {
+ return m_list.empty();
+ }
+
+ /// @return Size of repository
+ virtual inline std::size_t size(void) const ELPP_FINAL {
+ return m_list.size();
+ }
+
+ /// @brief Returns underlying container by reference
+ virtual inline Container& list(void) ELPP_FINAL {
+ return m_list;
+ }
+
+ /// @brief Returns underlying container by constant reference.
+ virtual inline const Container& list(void) const ELPP_FINAL {
+ return m_list;
+ }
+
+ /// @brief Unregisters all the pointers from current repository.
+ virtual void unregisterAll(void) = 0;
+
+protected:
+ virtual void deepCopy(const AbstractRegistry<T_Ptr, Container>&) = 0;
+ void reinitDeepCopy(const AbstractRegistry<T_Ptr, Container>& sr) {
+ unregisterAll();
+ deepCopy(sr);
+ }
+
+private:
+ Container m_list;
+};
+
+/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version)
+///
+/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these functions)
+/// of AbstractRegistry<T_Ptr, Container>. Any implementation of this class should be
+/// explicitly (by using lock functions)
+template <typename T_Ptr, typename T_Key = const char*>
+class Registry : public AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>> {
+public:
+ typedef typename Registry<T_Ptr, T_Key>::iterator iterator;
+ typedef typename Registry<T_Ptr, T_Key>::const_iterator const_iterator;
+
+ Registry(void) {}
+
+ /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor.
+ Registry(const Registry& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() {
+ if (this == &sr) {
+ return;
+ }
+ this->reinitDeepCopy(sr);
+ }
+
+ /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element
+ /// @see unregisterAll()
+ /// @see deepCopy(const AbstractRegistry&)
+ Registry& operator=(const Registry& sr) {
+ if (this == &sr) {
+ return *this;
+ }
+ this->reinitDeepCopy(sr);
+ return *this;
+ }
+
+ virtual ~Registry(void) {
+ unregisterAll();
+ }
+
+protected:
+ virtual inline void unregisterAll(void) ELPP_FINAL {
+ if (!this->empty()) {
+ for (auto&& curr : this->list()) {
+ base::utils::safeDelete(curr.second);
+ }
+ this->list().clear();
+ }
+ }
+
+ /// @brief Registers new registry to repository.
+ virtual inline void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL {
+ unregister(uniqKey);
+ this->list().insert(std::make_pair(uniqKey, ptr));
+ }
+
+ /// @brief Unregisters single entry mapped to specified unique key
+ inline void unregister(const T_Key& uniqKey) {
+ T_Ptr* existing = get(uniqKey);
+ if (existing != nullptr) {
+ base::utils::safeDelete(existing);
+ this->list().erase(uniqKey);
+ }
+ }
+
+ /// @brief Gets pointer from repository. If none found, nullptr is returned.
+ inline T_Ptr* get(const T_Key& uniqKey) {
+ iterator it = this->list().find(uniqKey);
+ return it == this->list().end()
+ ? nullptr
+ : it->second;
+ }
+
+private:
+ virtual inline void deepCopy(const AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>>& sr) ELPP_FINAL {
+ for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) {
+ registerNew(it->first, new T_Ptr(*it->second));
+ }
+ }
+};
+
+/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version)
+///
+/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry<T_Ptr, Container>. Any implementation of this class
+/// should be made thread-safe explicitly
+template <typename T_Ptr, typename Pred>
+class RegistryWithPred : public AbstractRegistry<T_Ptr, std::vector<T_Ptr*>> {
+public:
+ typedef typename RegistryWithPred<T_Ptr, Pred>::iterator iterator;
+ typedef typename RegistryWithPred<T_Ptr, Pred>::const_iterator const_iterator;
+
+ RegistryWithPred(void) {
+ }
+
+ virtual ~RegistryWithPred(void) {
+ unregisterAll();
+ }
+
+ /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor.
+ RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() {
+ if (this == &sr) {
+ return;
+ }
+ this->reinitDeepCopy(sr);
+ }
+
+ /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element
+ /// @see unregisterAll()
+ /// @see deepCopy(const AbstractRegistry&)
+ RegistryWithPred& operator=(const RegistryWithPred& sr) {
+ if (this == &sr) {
+ return *this;
+ }
+ this->reinitDeepCopy(sr);
+ return *this;
+ }
+
+ friend inline base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) {
+ for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) {
+ os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n");
+ }
+ return os;
+ }
+
+protected:
+ virtual inline void unregisterAll(void) ELPP_FINAL {
+ if (!this->empty()) {
+ for (auto&& curr : this->list()) {
+ base::utils::safeDelete(curr);
+ }
+ this->list().clear();
+ }
+ }
+
+ virtual void unregister(T_Ptr*& ptr) ELPP_FINAL {
+ if (ptr) {
+ iterator iter = this->begin();
+ for (; iter != this->end(); ++iter) {
+ if (ptr == *iter) {
+ break;
+ }
+ }
+ if (iter != this->end() && *iter != nullptr) {
+ this->list().erase(iter);
+ base::utils::safeDelete(*iter);
+ }
+ }
+ }
+
+ virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL {
+ this->list().push_back(ptr);
+ }
+
+ /// @brief Gets pointer from repository with speicifed arguments. Arguments are passed to predicate
+ /// in order to validate pointer.
+ template <typename T, typename T2>
+ inline T_Ptr* get(const T& arg1, const T2 arg2) {
+ iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2));
+ if (iter != this->list().end() && *iter != nullptr) {
+ return *iter;
+ }
+ return nullptr;
+ }
+
+private:
+ virtual inline void deepCopy(const AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>& sr) {
+ for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) {
+ registerNew(new T_Ptr(**it));
+ }
+ }
+};
+
+} // namespace utils
+} // namespace base
+/// @brief Base of Easylogging++ friendly class
+///
+/// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const`
+class Loggable {
+public:
+ virtual ~Loggable(void) {}
+ virtual void log(el::base::type::ostream_t&) const = 0;
+private:
+ friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) {
+ loggable.log(os);
+ return os;
+ }
+};
+namespace base {
+/// @brief Represents log format containing flags and date format. This is used internally to start initial log
+class LogFormat : public Loggable {
+public:
+ LogFormat(void) :
+ m_level(Level::Unknown),
+ m_userFormat(base::type::string_t()),
+ m_format(base::type::string_t()),
+ m_dateTimeFormat(std::string()),
+ m_flags(0x0) {
+ }
+
+ LogFormat(Level level, const base::type::string_t& format)
+ : m_level(level), m_userFormat(format) {
+ parseFromFormat(m_userFormat);
+ }
+
+ LogFormat(const LogFormat& logFormat) {
+ m_level = logFormat.m_level;
+ m_userFormat = logFormat.m_userFormat;
+ m_format = logFormat.m_format;
+ m_dateTimeFormat = logFormat.m_dateTimeFormat;
+ m_flags = logFormat.m_flags;
+ }
+
+ LogFormat(LogFormat&& logFormat) {
+ m_level = std::move(logFormat.m_level);
+ m_userFormat = std::move(logFormat.m_userFormat);
+ m_format = std::move(logFormat.m_format);
+ m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat);
+ m_flags = std::move(logFormat.m_flags);
+ }
+
+ LogFormat& operator=(const LogFormat& logFormat) {
+ m_level = logFormat.m_level;
+ m_userFormat = logFormat.m_userFormat;
+ m_dateTimeFormat = logFormat.m_dateTimeFormat;
+ m_flags = logFormat.m_flags;
+ return *this;
+ }
+
+ virtual ~LogFormat(void) {
+ }
+
+ inline bool operator==(const LogFormat& other) {
+ return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format &&
+ m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags;
+ }
+
+ /// @brief Updates format to be used while logging.
+ /// @param userFormat User provided format
+ void parseFromFormat(const base::type::string_t& userFormat) {
+ // We make copy because we will be changing the format
+ // i.e, removing user provided date format from original format
+ // and then storing it.
+ base::type::string_t formatCopy = userFormat;
+ m_flags = 0x0;
+ auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) {
+ std::size_t foundAt = base::type::string_t::npos;
+ while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos){
+ if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) {
+ if (hasFlag(flag)) {
+ // If we already have flag we remove the escape chars so that '%%' is turned to '%'
+ // even after specifier resolution - this is because we only replaceFirst specifier
+ formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1);
+ ++foundAt;
+ }
+ } else {
+ if (!hasFlag(flag)) addFlag(flag);
+ }
+ }
+ };
+ conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName);
+ conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level);
+ conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort);
+ conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId);
+ conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId);
+ conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File);
+ conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase);
+ conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line);
+ conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location);
+ conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function);
+ conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User);
+ conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host);
+ conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage);
+ conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel);
+ // For date/time we need to extract user's date format first
+ std::size_t dateIndex = std::string::npos;
+ if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) {
+ while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
+ dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1);
+ }
+ if (dateIndex != std::string::npos) {
+ addFlag(base::FormatFlags::DateTime);
+ updateDateFormat(dateIndex, formatCopy);
+ }
+ }
+ m_format = formatCopy;
+ updateFormatSpec();
+ }
+
+ inline Level level(void) const {
+ return m_level;
+ }
+
+ inline const base::type::string_t& userFormat(void) const {
+ return m_userFormat;
+ }
+
+ inline const base::type::string_t& format(void) const {
+ return m_format;
+ }
+
+ inline const std::string& dateTimeFormat(void) const {
+ return m_dateTimeFormat;
+ }
+
+ inline base::type::EnumType flags(void) const {
+ return m_flags;
+ }
+
+ inline bool hasFlag(base::FormatFlags flag) const {
+ return base::utils::hasFlag(flag, m_flags);
+ }
+
+ virtual void log(el::base::type::ostream_t& os) const {
+ os << m_format;
+ }
+
+protected:
+ /// @brief Updates date time format if available in currFormat.
+ /// @param index Index where %datetime, %date or %time was found
+ /// @param [in,out] currFormat current format that is being used to format
+ virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL {
+ if (hasFlag(base::FormatFlags::DateTime)) {
+ index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier);
+ }
+ const base::type::char_t* ptr = currFormat.c_str() + index;
+ if ((currFormat.size() > index) && (ptr[0] == '{')) {
+ // User has provided format for date/time
+ ++ptr;
+ int count = 1; // Start by 1 in order to remove starting brace
+ std::stringstream ss;
+ for (; *ptr; ++ptr, ++count) {
+ if (*ptr == '}') {
+ ++count; // In order to remove ending brace
+ break;
+ }
+ ss << *ptr;
+ }
+ currFormat.erase(index, count);
+ m_dateTimeFormat = ss.str();
+ } else {
+ // No format provided, use default
+ if (hasFlag(base::FormatFlags::DateTime)) {
+ m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat);
+ }
+ }
+ }
+
+ /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level
+ virtual void updateFormatSpec(void) ELPP_FINAL {
+ // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
+ if (m_level == Level::Debug) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kDebugLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kDebugLevelShortLogValue);
+ } else if (m_level == Level::Info) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kInfoLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kInfoLevelShortLogValue);
+ } else if (m_level == Level::Warning) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kWarningLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kWarningLevelShortLogValue);
+ } else if (m_level == Level::Error) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kErrorLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kErrorLevelShortLogValue);
+ } else if (m_level == Level::Fatal) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kFatalLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kFatalLevelShortLogValue);
+ } else if (m_level == Level::Verbose) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kVerboseLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kVerboseLevelShortLogValue);
+ } else if (m_level == Level::Trace) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kTraceLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kTraceLevelShortLogValue);
+ }
+ if (hasFlag(base::FormatFlags::User)) {
+ std::string s = base::utils::s_currentUser;
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier,
+ base::utils::s_currentUser);
+ }
+ if (hasFlag(base::FormatFlags::Host)) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier,
+ base::utils::s_currentHost);
+ }
+ // Ignore Level::Global and Level::Unknown
+ }
+
+ inline void addFlag(base::FormatFlags flag) {
+ base::utils::addFlag(flag, &m_flags);
+ }
+
+private:
+ Level m_level;
+ base::type::string_t m_userFormat;
+ base::type::string_t m_format;
+ std::string m_dateTimeFormat;
+ base::type::EnumType m_flags;
+ friend class el::Logger; // To resolve loggerId format specifier easily
+};
+} // namespace base
+/// @brief Resolving function for format specifier
+typedef std::function<const char*(void)> FormatSpecifierValueResolver;
+/// @brief User-provided custom format specifier
+/// @see el::Helpers::installCustomFormatSpecifier
+/// @see FormatSpecifierValueResolver
+class CustomFormatSpecifier {
+public:
+ CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) :
+ m_formatSpecifier(formatSpecifier), m_resolver(resolver) {}
+ inline const char* formatSpecifier(void) const { return m_formatSpecifier; }
+ inline const FormatSpecifierValueResolver& resolver(void) const { return m_resolver; }
+ inline bool operator==(const char* formatSpecifier) {
+ return strcmp(m_formatSpecifier, formatSpecifier) == 0;
+ }
+
+private:
+ const char* m_formatSpecifier;
+ FormatSpecifierValueResolver m_resolver;
+};
+/// @brief Represents single configuration that has representing level, configuration type and a string based value.
+///
+/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside quotes
+/// and will be parsed later.
+///
+/// Consider some examples below:
+/// * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true");
+/// * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048");
+/// * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log");
+class Configuration : public Loggable {
+public:
+ Configuration(const Configuration& c) :
+ m_level(c.m_level),
+ m_configurationType(c.m_configurationType),
+ m_value(c.m_value) {
+ }
+
+ Configuration& operator=(const Configuration& c) {
+ m_level = c.m_level;
+ m_configurationType = c.m_configurationType;
+ m_value = c.m_value;
+ return *this;
+ }
+
+ virtual ~Configuration(void) {
+ }
+
+ /// @brief Full constructor used to sets value of configuration
+ Configuration(Level level, ConfigurationType configurationType, const std::string& value) :
+ m_level(level),
+ m_configurationType(configurationType),
+ m_value(value) {
+ }
+
+ /// @brief Gets level of current configuration
+ inline Level level(void) const {
+ return m_level;
+ }
+
+ /// @brief Gets configuration type of current configuration
+ inline ConfigurationType configurationType(void) const {
+ return m_configurationType;
+ }
+
+ /// @brief Gets string based configuration value
+ inline const std::string& value(void) const {
+ return m_value;
+ }
+
+ /// @brief Set string based configuration value
+ /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values
+ /// use them in quotes. They will be parsed when configuring
+ inline void setValue(const std::string& value) {
+ m_value = value;
+ }
+
+ virtual inline void log(el::base::type::ostream_t& os) const {
+ os << LevelHelper::convertToString(m_level)
+ << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType)
+ << ELPP_LITERAL(" = ") << m_value.c_str();
+ }
+
+ /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it.
+ class Predicate {
+ public:
+ Predicate(Level level, ConfigurationType configurationType) :
+ m_level(level),
+ m_configurationType(configurationType) {
+ }
+
+ inline bool operator()(const Configuration* conf) const {
+ return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType));
+ }
+
+ private:
+ Level m_level;
+ ConfigurationType m_configurationType;
+ };
+
+private:
+ Level m_level;
+ ConfigurationType m_configurationType;
+ std::string m_value;
+};
+
+/// @brief Thread-safe Configuration repository
+///
+/// @detail This repository represents configurations for all the levels and configuration type mapped to a value.
+class Configurations : public base::utils::RegistryWithPred<Configuration, Configuration::Predicate> {
+public:
+ /// @brief Default constructor with empty repository
+ Configurations(void) :
+ m_configurationFile(std::string()),
+ m_isFromFile(false) {
+ }
+
+ /// @brief Constructor used to set configurations using configuration file.
+ /// @param configurationFile Full path to configuration file
+ /// @param useDefaultsForRemaining Lets you set the remaining configurations to default.
+ /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to.
+ /// @see parseFromFile(const std::string&, Configurations* base)
+ /// @see setRemainingToDefault()
+ Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, Configurations* base = nullptr) :
+ m_configurationFile(configurationFile),
+ m_isFromFile(false) {
+ parseFromFile(configurationFile, base);
+ if (useDefaultsForRemaining) {
+ setRemainingToDefault();
+ }
+ }
+
+ virtual ~Configurations(void) {
+ }
+
+ /// @brief Parses configuration from file.
+ /// @param configurationFile Full path to configuration file
+ /// @param base Configurations to base new configuration repository off. This value is used when you want to use
+ /// existing Configurations to base all the values and then set rest of configuration via configuration file.
+ /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you
+ /// do not proceed without successful parse.
+ inline bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr) {
+ // We initial assertion with true because if we have assertion diabled, we want to pass this
+ // check and if assertion is enabled we will have values re-assigned any way.
+ bool assertionPassed = true;
+ ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)),
+ "Configuration file [" << configurationFile << "] does not exist!");
+ if (!assertionPassed) {
+ return false;
+ }
+ bool success = Parser::parseFromFile(configurationFile, this, base);
+ m_isFromFile = success;
+ return success;
+ }
+
+ /// @brief Parse configurations from configuration string.
+ ///
+ /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary
+ /// new line characters are provided.
+ /// @param base Configurations to base new configuration repository off. This value is used when you want to use
+ /// existing Configurations to base all the values and then set rest of configuration via configuration text.
+ /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you
+ /// do not proceed without successful parse.
+ inline bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr) {
+ bool success = Parser::parseFromText(configurationsString, this, base);
+ if (success) {
+ m_isFromFile = false;
+ }
+ return success;
+ }
+
+ /// @brief Sets configuration based-off an existing configurations.
+ /// @param base Pointer to existing configurations.
+ inline void setFromBase(Configurations* base) {
+ if (base == nullptr || base == this) {
+ return;
+ }
+ base::threading::ScopedLock scopedLock(base->lock());
+ for (Configuration*& conf : base->list()) {
+ set(conf);
+ }
+ }
+
+ /// @brief Determines whether or not specified configuration type exists in the repository.
+ ///
+ /// @detail Returns as soon as first level is found.
+ /// @param configurationType Type of configuration to check existence for.
+ bool hasConfiguration(ConfigurationType configurationType) {
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ bool result = false;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) {
+ result = true;
+ }
+ return result;
+ });
+ return result;
+ }
+
+ /// @brief Determines whether or not specified configuration type exists for specified level
+ /// @param level Level to check
+ /// @param configurationType Type of configuration to check existence for.
+ inline bool hasConfiguration(Level level, ConfigurationType configurationType) {
+ base::threading::ScopedLock scopedLock(lock());
+#if ELPP_COMPILER_INTEL
+ // We cant specify template types here, Intel C++ throws compilation error
+ // "error: type name is not allowed"
+ return RegistryWithPred::get(level, configurationType) != nullptr;
+#else
+ return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType) != nullptr;
+#endif // ELPP_COMPILER_INTEL
+ }
+
+ /// @brief Sets value of configuration for specified level.
+ ///
+ /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types
+ /// ConfigurationType::MillisecondsWidth and ConfigurationType::PerformanceTracking will be ignored if not set for
+ /// Level::Global because these configurations are not dependant on level.
+ /// @param level Level to set configuration for (el::Level).
+ /// @param configurationType Type of configuration (el::ConfigurationType)
+ /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string
+ /// from users' point of view. This is then parsed later to be used internally.
+ /// @see Configuration::setValue(const std::string& value)
+ /// @see el::Level
+ /// @see el::ConfigurationType
+ inline void set(Level level, ConfigurationType configurationType, const std::string& value) {
+ base::threading::ScopedLock scopedLock(lock());
+ unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex
+ if (level == Level::Global) {
+ unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either
+ }
+ }
+
+ /// @brief Sets single configuration based on other single configuration.
+ /// @see set(Level level, ConfigurationType configurationType, const std::string& value)
+ inline void set(Configuration* conf) {
+ if (conf == nullptr) {
+ return;
+ }
+ set(conf->level(), conf->configurationType(), conf->value());
+ }
+
+ inline Configuration* get(Level level, ConfigurationType configurationType) {
+ base::threading::ScopedLock scopedLock(lock());
+ return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType);
+ }
+
+ /// @brief Sets configuration for all levels.
+ /// @param configurationType Type of configuration
+ /// @param value String based value
+ /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value)
+ inline void setGlobally(ConfigurationType configurationType, const std::string& value) {
+ setGlobally(configurationType, value, false);
+ }
+
+ /// @brief Clears repository so that all the configurations are unset
+ inline void clear(void) {
+ base::threading::ScopedLock scopedLock(lock());
+ unregisterAll();
+ }
+
+ /// @brief Gets configuration file used in parsing this configurations.
+ ///
+ /// @detail If this repository was set manually or by text this returns empty string.
+ inline const std::string& configurationFile(void) const {
+ return m_configurationFile;
+ }
+
+ /// @brief Sets configurations to "factory based" configurations.
+ void setToDefault(void) {
+ setGlobally(ConfigurationType::Enabled, std::string("true"), true);
+#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true);
+#else
+ ELPP_UNUSED(base::consts::kDefaultLogFile);
+#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ setGlobally(ConfigurationType::ToFile, std::string("true"), true);
+ setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true);
+ setGlobally(ConfigurationType::MillisecondsWidth, std::string("3"), true);
+ setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true);
+ setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true);
+ setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true);
+
+ setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true);
+ set(Level::Debug, ConfigurationType::Format, std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
+ // INFO and WARNING are set to default by Level::Global
+ set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+ set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+ set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
+ set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
+ }
+
+ /// @brief Lets you set the remaining configurations to default.
+ ///
+ /// @detail By remaining, it means that the level/type a configuration does not exist for.
+ /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets
+ /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e,
+ /// true. If you dont do this explicitley (either by calling this function or by using second param in Constructor
+ /// and try to access a value, an error is thrown
+ void setRemainingToDefault(void) {
+ base::threading::ScopedLock scopedLock(lock());
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true"));
+#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile));
+#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::ToFile, std::string("true"));
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true"));
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::MillisecondsWidth, std::string("3"));
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true"));
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0"));
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+ unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format,
+ std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
+ // INFO and WARNING are set to default by Level::Global
+ unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+ unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+ unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
+ unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
+ }
+
+ /// @brief Parser used internally to parse configurations from file or text.
+ ///
+ /// @detail This class makes use of base::utils::Str.
+ /// You should not need this unless you are working on some tool for Easylogging++
+ class Parser : base::StaticClass {
+ public:
+ /// @brief Parses configuration from file.
+ /// @param configurationFile Full path to configuration file
+ /// @param sender Sender configurations pointer. Usually 'this' is used from calling class
+ /// @param base Configurations to base new configuration repository off. This value is used when you want to use
+ /// existing Configurations to base all the values and then set rest of configuration via configuration file.
+ /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you
+ /// do not proceed without successful parse.
+ static bool parseFromFile(const std::string& configurationFile, Configurations* sender, Configurations* base = nullptr) {
+ sender->setFromBase(base);
+ std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in);
+ ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing.");
+ bool parsedSuccessfully = false;
+ std::string line = std::string();
+ Level currLevel = Level::Unknown;
+ std::string currConfigStr = std::string();
+ std::string currLevelStr = std::string();
+ while (fileStream_.good()) {
+ std::getline(fileStream_, line);
+ parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
+ ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
+ }
+ return parsedSuccessfully;
+ }
+
+ /// @brief Parse configurations from configuration string.
+ ///
+ /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary
+ /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you
+ /// do not proceed without successful parse (This is recommended)
+ /// @param configurationsString
+ /// @param sender Sender configurations pointer. Usually 'this' is used from calling class
+ /// @param base Configurations to base new configuration repository off. This value is used when you want to use
+ /// existing Configurations to base all the values and then set rest of configuration via configuration text.
+ /// @return True if successfully parsed, false otherwise.
+ static bool parseFromText(const std::string& configurationsString, Configurations* sender, Configurations* base = nullptr) {
+ sender->setFromBase(base);
+ bool parsedSuccessfully = false;
+ std::stringstream ss(configurationsString);
+ std::string line = std::string();
+ Level currLevel = Level::Unknown;
+ std::string currConfigStr = std::string();
+ std::string currLevelStr = std::string();
+ while (std::getline(ss, line)) {
+ parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
+ ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
+ }
+ return parsedSuccessfully;
+ }
+
+ private:
+ friend class el::Loggers;
+ static void ignoreComments(std::string* line) {
+ std::size_t foundAt = 0;
+ std::size_t quotesStart = line->find("\"");
+ std::size_t quotesEnd = std::string::npos;
+ if (quotesStart != std::string::npos) {
+ quotesEnd = line->find("\"", quotesStart + 1);
+ while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') {
+ // Do not erase slash yet - we will erase it in parseLine(..) while loop
+ quotesEnd = line->find("\"", quotesEnd + 2);
+ }
+ }
+ if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) {
+ if (foundAt < quotesEnd) {
+ foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1);
+ }
+ *line = line->substr(0, foundAt);
+ }
+ }
+ static inline bool isLevel(const std::string& line) {
+ return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel));
+ }
+
+ static inline bool isComment(const std::string& line) {
+ return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment));
+ }
+
+ static inline bool isConfig(const std::string& line) {
+ std::size_t assignment = line.find('=');
+ return line != "" &&
+ (line[0] >= 65 || line[0] <= 90 || line[0] >= 97 || line[0] <= 122) &&
+ (assignment != std::string::npos) &&
+ (line.size() > assignment);
+ }
+
+ static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, Configurations* conf) {
+ ConfigurationType currConfig = ConfigurationType::Unknown;
+ std::string currValue = std::string();
+ *line = base::utils::Str::trim(*line);
+ if (isComment(*line)) return true;
+ ignoreComments(line);
+ *line = base::utils::Str::trim(*line);
+ if (line->empty()) {
+ // Comment ignored
+ return true;
+ }
+ if (isLevel(*line)) {
+ if (line->size() <= 2) {
+ return true;
+ }
+ *currLevelStr = line->substr(1, line->size() - 2);
+ *currLevelStr = base::utils::Str::toUpper(*currLevelStr);
+ *currLevelStr = base::utils::Str::trim(*currLevelStr);
+ *currLevel = LevelHelper::convertFromString(currLevelStr->c_str());
+ return true;
+ }
+ if (isConfig(*line)) {
+ std::size_t assignment = line->find('=');
+ *currConfigStr = line->substr(0, assignment);
+ *currConfigStr = base::utils::Str::toUpper(*currConfigStr);
+ *currConfigStr = base::utils::Str::trim(*currConfigStr);
+ currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str());
+ currValue = line->substr(assignment + 1);
+ currValue = base::utils::Str::trim(currValue);
+ std::size_t quotesStart = currValue.find("\"", 0);
+ std::size_t quotesEnd = std::string::npos;
+ if (quotesStart != std::string::npos) {
+ quotesEnd = currValue.find("\"", quotesStart + 1);
+ while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') {
+ currValue = currValue.erase(quotesEnd - 1, 1);
+ quotesEnd = currValue.find("\"", quotesEnd + 2);
+ }
+ }
+ if (quotesStart != std::string::npos && quotesEnd != std::string::npos) {
+ // Quote provided - check and strip if valid
+ ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in ["
+ << currConfigStr << "]");
+ ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]");
+ if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) {
+ // Explicit check in case if assertion is disabled
+ currValue = currValue.substr(quotesStart + 1, quotesEnd - 1);
+ }
+ }
+ }
+ ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]");
+ ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]");
+ if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) {
+ return false; // unrecognizable level or config
+ }
+ conf->set(*currLevel, currConfig, currValue);
+ return true;
+ }
+ };
+
+private:
+ std::string m_configurationFile;
+ bool m_isFromFile;
+ friend class el::Loggers;
+
+ /// @brief Unsafely sets configuration if does not already exist
+ void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) {
+ Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType);
+ if (conf == nullptr) {
+ unsafeSet(level, configurationType, value);
+ }
+ }
+
+ /// @brief Thread unsafe set
+ void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) {
+ Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType);
+ if (conf == nullptr) {
+ registerNew(new Configuration(level, configurationType, value));
+ } else {
+ conf->setValue(value);
+ }
+ if (level == Level::Global) {
+ unsafeSetGlobally(configurationType, value, false);
+ }
+ }
+
+ /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true
+ /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value)
+ void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) {
+ if (includeGlobalLevel) {
+ set(Level::Global, configurationType, value);
+ }
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ set(LevelHelper::castFromInt(lIndex), configurationType, value);
+ return false; // Do not break lambda function yet as we need to set all levels regardless
+ });
+ }
+
+ /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true
+ /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value)
+ void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) {
+ if (includeGlobalLevel) {
+ unsafeSet(Level::Global, configurationType, value);
+ }
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value);
+ return false; // Do not break lambda function yet as we need to set all levels regardless
+ });
+ }
+};
+
+namespace base {
+typedef std::shared_ptr<base::type::fstream_t> FileStreamPtr;
+typedef std::map<std::string, FileStreamPtr> LogStreamsReferenceMap;
+/// @brief Configurations with data types.
+///
+/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations.
+/// This is to perform faster while writing logs using correct configurations.
+///
+/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class)
+class TypedConfigurations : public base::threading::ThreadSafe {
+public:
+ /// @brief Constructor to initialize (construct) the object off el::Configurations
+ /// @param configurations Configurations pointer/reference to base this typed configurations off.
+ /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference()
+ TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference) {
+ m_configurations = configurations;
+ m_logStreamsReference = logStreamsReference;
+ build(m_configurations);
+ }
+
+ TypedConfigurations(const TypedConfigurations& other) {
+ this->m_configurations = other.m_configurations;
+ this->m_logStreamsReference = other.m_logStreamsReference;
+ build(m_configurations);
+ }
+
+ virtual ~TypedConfigurations(void) {
+ }
+
+ const Configurations* configurations(void) const {
+ return m_configurations;
+ }
+
+ inline bool enabled(Level level) {
+ return getConfigByVal<bool>(level, &m_enabledMap, "enabled");
+ }
+
+ inline bool toFile(Level level) {
+ return getConfigByVal<bool>(level, &m_toFileMap, "toFile");
+ }
+
+ inline const std::string& filename(Level level) {
+ return getConfigByRef<std::string>(level, &m_filenameMap, "filename");
+ }
+
+ inline bool toStandardOutput(Level level) {
+ return getConfigByVal<bool>(level, &m_toStandardOutputMap, "toStandardOutput");
+ }
+
+ inline const base::LogFormat& logFormat(Level level) {
+ return getConfigByRef<base::LogFormat>(level, &m_logFormatMap, "logFormat");
+ }
+
+ inline const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global) {
+ return getConfigByRef<base::MillisecondsWidth>(level, &m_millisecondsWidthMap, "millisecondsWidth");
+ }
+
+ inline bool performanceTracking(Level level = Level::Global) {
+ return getConfigByVal<bool>(level, &m_performanceTrackingMap, "performanceTracking");
+ }
+
+ inline base::type::fstream_t* fileStream(Level level) {
+ return getConfigByRef<base::FileStreamPtr>(level, &m_fileStreamMap, "fileStream").get();
+ }
+
+ inline std::size_t maxLogFileSize(Level level) {
+ return getConfigByVal<std::size_t>(level, &m_maxLogFileSizeMap, "maxLogFileSize");
+ }
+
+ inline std::size_t logFlushThreshold(Level level) {
+ return getConfigByVal<std::size_t>(level, &m_logFlushThresholdMap, "logFlushThreshold");
+ }
+
+private:
+ Configurations* m_configurations;
+ std::map<Level, bool> m_enabledMap;
+ std::map<Level, bool> m_toFileMap;
+ std::map<Level, std::string> m_filenameMap;
+ std::map<Level, bool> m_toStandardOutputMap;
+ std::map<Level, base::LogFormat> m_logFormatMap;
+ std::map<Level, base::MillisecondsWidth> m_millisecondsWidthMap;
+ std::map<Level, bool> m_performanceTrackingMap;
+ std::map<Level, base::FileStreamPtr> m_fileStreamMap;
+ std::map<Level, std::size_t> m_maxLogFileSizeMap;
+ std::map<Level, std::size_t> m_logFlushThresholdMap;
+ base::LogStreamsReferenceMap* m_logStreamsReference;
+
+ friend class el::Helpers;
+ friend class el::base::MessageBuilder;
+ friend class el::base::Writer;
+ friend class el::base::DefaultLogDispatchCallback;
+ friend class el::base::LogDispatcher;
+
+ template <typename Conf_T>
+ inline Conf_T getConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) {
+ base::threading::ScopedLock scopedLock(lock());
+ return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope
+ }
+
+ template <typename Conf_T>
+ inline Conf_T& getConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) {
+ base::threading::ScopedLock scopedLock(lock());
+ return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope
+ }
+
+ template <typename Conf_T>
+ inline Conf_T unsafeGetConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) {
+ ELPP_UNUSED(confName);
+ typename std::map<Level, Conf_T>::const_iterator it = confMap->find(level);
+ if (it == confMap->end()) {
+ try {
+ return confMap->at(Level::Global);
+ } catch (...) {
+ ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level ["
+ << LevelHelper::convertToString(level) << "]"
+ << std::endl << "Please ensure you have properly configured logger.", false);
+ return Conf_T();
+ }
+ }
+ return it->second;
+ }
+
+ template <typename Conf_T>
+ inline Conf_T& unsafeGetConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) {
+ ELPP_UNUSED(confName);
+ typename std::map<Level, Conf_T>::iterator it = confMap->find(level);
+ if (it == confMap->end()) {
+ try {
+ return confMap->at(Level::Global);
+ } catch (...) {
+ ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level ["
+ << LevelHelper::convertToString(level) << "]"
+ << std::endl << "Please ensure you have properly configured logger.", false);
+ }
+ }
+ return it->second;
+ }
+
+ template <typename Conf_T>
+ void setValue(Level level, const Conf_T& value, std::map<Level, Conf_T>* confMap, bool includeGlobalLevel = true) {
+ // If map is empty and we are allowed to add into generic level (Level::Global), do it!
+ if (confMap->empty() && includeGlobalLevel) {
+ confMap->insert(std::make_pair(Level::Global, value));
+ return;
+ }
+ // If same value exist in generic level already, dont add it to explicit level
+ typename std::map<Level, Conf_T>::iterator it = confMap->find(Level::Global);
+ if (it != confMap->end() && it->second == value) {
+ return;
+ }
+ // Now make sure we dont double up values if we really need to add it to explicit level
+ it = confMap->find(level);
+ if (it == confMap->end()) {
+ // Value not found for level, add new
+ confMap->insert(std::make_pair(level, value));
+ } else {
+ // Value found, just update value
+ confMap->at(level) = value;
+ }
+ }
+
+ void build(Configurations* configurations) {
+ base::threading::ScopedLock scopedLock(lock());
+ auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming
+ base::utils::Str::trim(boolStr);
+ return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1");
+ };
+ std::vector<Configuration*> withFileSizeLimit;
+ for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
+ Configuration* conf = *it;
+ // We cannot use switch on strong enums because Intel C++ dont support them yet
+ if (conf->configurationType() == ConfigurationType::Enabled) {
+ setValue(conf->level(), getBool(conf->value()), &m_enabledMap);
+ } else if (conf->configurationType() == ConfigurationType::ToFile) {
+ setValue(conf->level(), getBool(conf->value()), &m_toFileMap);
+ } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) {
+ setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap);
+ } else if (conf->configurationType() == ConfigurationType::Filename) {
+ // We do not yet configure filename but we will configure in another
+ // loop. This is because if file cannot be created, we will force ToFile
+ // to be false. Because configuring logger is not necessarily performance
+ // sensative operation, we can live with another loop; (by the way this loop
+ // is not very heavy either)
+ } else if (conf->configurationType() == ConfigurationType::Format) {
+ setValue(conf->level(), base::LogFormat(conf->level(),
+ base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap);
+ } else if (conf->configurationType() == ConfigurationType::MillisecondsWidth) {
+ setValue(Level::Global,
+ base::MillisecondsWidth(static_cast<int>(getULong(conf->value()))), &m_millisecondsWidthMap);
+ } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) {
+ setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap);
+ } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) {
+ setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_maxLogFileSizeMap);
+#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ withFileSizeLimit.push_back(conf);
+#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) {
+ setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_logFlushThresholdMap);
+ }
+ }
+ // As mentioned early, we will now set filename configuration in separate loop to deal with non-existent files
+ for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
+ Configuration* conf = *it;
+ if (conf->configurationType() == ConfigurationType::Filename) {
+ insertFile(conf->level(), conf->value());
+ }
+ }
+ for (std::vector<Configuration*>::iterator conf = withFileSizeLimit.begin();
+ conf != withFileSizeLimit.end(); ++conf) {
+ // This is not unsafe as mutex is locked in currect scope
+ unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback);
+ }
+ }
+
+ unsigned long getULong(std::string confVal) {
+ bool valid = true;
+ base::utils::Str::trim(confVal);
+ valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(),
+ [](char c) { return !base::utils::Str::isDigit(c); }) == confVal.end();
+ if (!valid) {
+ valid = false;
+ ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]");
+ return 0;
+ }
+ return atol(confVal.c_str());
+ }
+
+ std::string resolveFilename(const std::string& filename) {
+ std::string resultingFilename = filename;
+ std::size_t dateIndex = std::string::npos;
+ std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename);
+ if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) {
+ while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
+ dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1);
+ }
+ if (dateIndex != std::string::npos) {
+ const char* ptr = resultingFilename.c_str() + dateIndex;
+ // Goto end of specifier
+ ptr += dateTimeFormatSpecifierStr.size();
+ std::string fmt;
+ if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) {
+ // User has provided format for date/time
+ ++ptr;
+ int count = 1; // Start by 1 in order to remove starting brace
+ std::stringstream ss;
+ for (; *ptr; ++ptr, ++count) {
+ if (*ptr == '}') {
+ ++count; // In order to remove ending brace
+ break;
+ }
+ ss << *ptr;
+ }
+ resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count);
+ fmt = ss.str();
+ } else {
+ fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename);
+ }
+ base::MillisecondsWidth msWidth(3);
+ std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &msWidth);
+ base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename
+ base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now);
+ }
+ }
+ return resultingFilename;
+ }
+
+ void insertFile(Level level, const std::string& fullFilename) {
+ std::string resolvedFilename = resolveFilename(fullFilename);
+ if (resolvedFilename.empty()) {
+ std::cerr << "Could not load empty file for logging, please re-check your configurations for level ["
+ << LevelHelper::convertToString(level) << "]";
+ }
+ std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator);
+ if (filePath.size() < resolvedFilename.size()) {
+ base::utils::File::createPath(filePath);
+ }
+ auto create = [&](Level level) {
+ base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename);
+ base::type::fstream_t* fs = nullptr;
+ if (filestreamIter == m_logStreamsReference->end()) {
+ // We need a completely new stream, nothing to share with
+ fs = base::utils::File::newFileStream(resolvedFilename);
+ m_filenameMap.insert(std::make_pair(level, resolvedFilename));
+ m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs)));
+ m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level))));
+ } else {
+ // Woops! we have an existing one, share it!
+ m_filenameMap.insert(std::make_pair(level, filestreamIter->first));
+ m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second)));
+ fs = filestreamIter->second.get();
+ }
+ if (fs == nullptr) {
+ // We display bad file error from newFileStream()
+ ELPP_INTERNAL_ERROR("Setting [TO_FILE] of ["
+ << LevelHelper::convertToString(level) << "] to FALSE", false);
+ setValue(level, false, &m_toFileMap);
+ }
+ };
+ // If we dont have file conf for any level, create it for Level::Global first
+ // otherwise create for specified level
+ create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level);
+ }
+
+ bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) {
+ base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get();
+ if (fs == nullptr) {
+ return true;
+ }
+ std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize");
+ std::size_t currFileSize = base::utils::File::getSizeOfFile(fs);
+ if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) {
+ std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename");
+ ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level ["
+ << LevelHelper::convertToString(level) << "]");
+ fs->close();
+ PreRollOutCallback(fname.c_str(), currFileSize);
+ fs->open(fname, std::fstream::out | std::fstream::trunc);
+ return true;
+ }
+ return false;
+ }
+
+ bool validateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) {
+ base::threading::ScopedLock scopedLock(lock());
+ return unsafeValidateFileRolling(level, PreRollOutCallback);
+ }
+};
+/// @brief Class that keeps record of current line hit for occasional logging
+class HitCounter {
+public:
+ HitCounter(void) :
+ m_filename(""),
+ m_lineNumber(0),
+ m_hitCounts(0) {
+ }
+
+ HitCounter(const char* filename, unsigned long int lineNumber) :
+ m_filename(filename),
+ m_lineNumber(lineNumber),
+ m_hitCounts(0) {
+ }
+
+ HitCounter(const HitCounter& hitCounter) :
+ m_filename(hitCounter.m_filename),
+ m_lineNumber(hitCounter.m_lineNumber),
+ m_hitCounts(hitCounter.m_hitCounts) {
+ }
+
+ HitCounter& operator=(const HitCounter& hitCounter) {
+ m_filename = hitCounter.m_filename;
+ m_lineNumber = hitCounter.m_lineNumber;
+ m_hitCounts = hitCounter.m_hitCounts;
+ return *this;
+ }
+
+ virtual ~HitCounter(void) {
+ }
+
+ /// @brief Resets location of current hit counter
+ inline void resetLocation(const char* filename, unsigned long int lineNumber) {
+ m_filename = filename;
+ m_lineNumber = lineNumber;
+ }
+
+ /// @brief Validates hit counts and resets it if necessary
+ inline void validateHitCounts(std::size_t n) {
+ if (m_hitCounts >= base::consts::kMaxLogPerCounter) {
+ m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0);
+ }
+ ++m_hitCounts;
+ }
+
+ inline const char* filename(void) const {
+ return m_filename;
+ }
+
+ inline unsigned long int lineNumber(void) const {
+ return m_lineNumber;
+ }
+
+ inline std::size_t hitCounts(void) const {
+ return m_hitCounts;
+ }
+
+ inline void increment(void) {
+ ++m_hitCounts;
+ }
+
+ class Predicate {
+ public:
+ Predicate(const char* filename, unsigned long int lineNumber)
+ : m_filename(filename),
+ m_lineNumber(lineNumber) {
+ }
+ inline bool operator()(const HitCounter* counter) {
+ return ((counter != nullptr) &&
+ (strcmp(counter->m_filename, m_filename) == 0) &&
+ (counter->m_lineNumber == m_lineNumber));
+ }
+
+ private:
+ const char* m_filename;
+ unsigned long int m_lineNumber;
+ };
+
+private:
+ const char* m_filename;
+ unsigned long int m_lineNumber;
+ std::size_t m_hitCounts;
+};
+/// @brief Repository for hit counters used across the application
+class RegisteredHitCounters : public base::utils::RegistryWithPred<base::HitCounter, base::HitCounter::Predicate> {
+public:
+ /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one
+ /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
+ bool validateEveryN(const char* filename, unsigned long int lineNumber, std::size_t n) {
+ base::threading::ScopedLock scopedLock(lock());
+ base::HitCounter* counter = get(filename, lineNumber);
+ if (counter == nullptr) {
+ registerNew(counter = new base::HitCounter(filename, lineNumber));
+ }
+ counter->validateHitCounts(n);
+ bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0);
+ return result;
+ }
+
+ /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one
+ /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
+ bool validateAfterN(const char* filename, unsigned long int lineNumber, std::size_t n) {
+ base::threading::ScopedLock scopedLock(lock());
+ base::HitCounter* counter = get(filename, lineNumber);
+ if (counter == nullptr) {
+ registerNew(counter = new base::HitCounter(filename, lineNumber));
+ }
+ // Do not use validateHitCounts here since we do not want to reset counter here
+ // Note the >= instead of > because we are incrementing
+ // after this check
+ if (counter->hitCounts() >= n)
+ return true;
+ counter->increment();
+ return false;
+ }
+
+ /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one
+ /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
+ bool validateNTimes(const char* filename, unsigned long int lineNumber, std::size_t n) {
+ base::threading::ScopedLock scopedLock(lock());
+ base::HitCounter* counter = get(filename, lineNumber);
+ if (counter == nullptr) {
+ registerNew(counter = new base::HitCounter(filename, lineNumber));
+ }
+ counter->increment();
+ // Do not use validateHitCounts here since we do not want to reset counter here
+ if (counter->hitCounts() <= n)
+ return true;
+ return false;
+ }
+
+ /// @brief Gets hit counter registered at specified position
+ inline const base::HitCounter* getCounter(const char* filename, unsigned long int lineNumber) {
+ base::threading::ScopedLock scopedLock(lock());
+ return get(filename, lineNumber);
+ }
+};
+/// @brief Action to be taken for dispatching
+enum class DispatchAction : base::type::EnumType {
+ None = 1, NormalLog = 2, SysLog = 4
+};
+} // namespace base
+template <typename T>
+class Callback : protected base::threading::ThreadSafe {
+public:
+ Callback(void) : m_enabled(true) {}
+ inline bool enabled(void) const { return m_enabled; }
+ inline void setEnabled(bool enabled) {
+ base::threading::ScopedLock scopedLock(lock());
+ m_enabled = enabled;
+ }
+protected:
+ virtual void handle(const T* handlePtr) = 0;
+private:
+ bool m_enabled;
+};
+class LogDispatchData {
+public:
+ LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {}
+ inline const LogMessage* logMessage(void) const { return m_logMessage; }
+ inline base::DispatchAction dispatchAction(void) const { return m_dispatchAction; }
+private:
+ LogMessage* m_logMessage;
+ base::DispatchAction m_dispatchAction;
+ friend class base::LogDispatcher;
+
+ inline void setLogMessage(LogMessage* logMessage) { m_logMessage = logMessage; }
+ inline void setDispatchAction(base::DispatchAction dispatchAction) { m_dispatchAction = dispatchAction; }
+};
+class LogDispatchCallback : public Callback<LogDispatchData> {
+private:
+ friend class base::LogDispatcher;
+};
+class PerformanceTrackingCallback : public Callback<PerformanceTrackingData> {
+private:
+ friend class base::PerformanceTracker;
+};
+class LogBuilder : base::NoCopy {
+public:
+ virtual ~LogBuilder(void) { ELPP_INTERNAL_INFO(3, "Destroying log builder...")}
+ virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0;
+ void convertToColoredOutput(base::type::string_t* logLine, Level level) {
+ if (!base::utils::s_termSupportsColor) return;
+ const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m");
+ if (level == Level::Error || level == Level::Fatal)
+ *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor;
+ else if (level == Level::Warning)
+ *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor;
+ }
+private:
+ friend class el::base::DefaultLogDispatchCallback;
+};
+typedef std::shared_ptr<LogBuilder> LogBuilderPtr;
+/// @brief Represents a logger holding ID and configurations we need to write logs
+///
+/// @detail This class does not write logs itself instead its used by writer to read configuations from.
+class Logger : public base::threading::ThreadSafe, public Loggable {
+public:
+ Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) :
+ m_id(id),
+ m_typedConfigurations(nullptr),
+ m_parentApplicationName(std::string()),
+ m_isConfigured(false),
+ m_logStreamsReference(logStreamsReference) {
+ initUnflushedCount();
+ }
+
+ Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMap* logStreamsReference) :
+ m_id(id),
+ m_typedConfigurations(nullptr),
+ m_parentApplicationName(std::string()),
+ m_isConfigured(false),
+ m_logStreamsReference(logStreamsReference) {
+ initUnflushedCount();
+ configure(configurations);
+ }
+
+ Logger(const Logger& logger) {
+ base::utils::safeDelete(m_typedConfigurations);
+ m_id = logger.m_id;
+ m_typedConfigurations = logger.m_typedConfigurations;
+ m_parentApplicationName = logger.m_parentApplicationName;
+ m_isConfigured = logger.m_isConfigured;
+ m_configurations = logger.m_configurations;
+ m_unflushedCount = logger.m_unflushedCount;
+ m_logStreamsReference = logger.m_logStreamsReference;
+ }
+
+ Logger& operator=(const Logger& logger) {
+ base::utils::safeDelete(m_typedConfigurations);
+ m_id = logger.m_id;
+ m_typedConfigurations = logger.m_typedConfigurations;
+ m_parentApplicationName = logger.m_parentApplicationName;
+ m_isConfigured = logger.m_isConfigured;
+ m_configurations = logger.m_configurations;
+ m_unflushedCount = logger.m_unflushedCount;
+ m_logStreamsReference = logger.m_logStreamsReference;
+ return *this;
+ }
+
+ virtual ~Logger(void) {
+ base::utils::safeDelete(m_typedConfigurations);
+ }
+
+ virtual inline void log(el::base::type::ostream_t& os) const {
+ os << m_id.c_str();
+ }
+
+ /// @brief Configures the logger using specified configurations.
+ void configure(const Configurations& configurations) {
+ m_isConfigured = false; // we set it to false in case if we fail
+ initUnflushedCount();
+ if (m_typedConfigurations != nullptr) {
+ Configurations* c = const_cast<Configurations*>(m_typedConfigurations->configurations());
+ if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) {
+ // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE
+ flush();
+ }
+ }
+ base::threading::ScopedLock scopedLock(lock());
+ if (m_configurations != configurations) {
+ m_configurations.setFromBase(const_cast<Configurations*>(&configurations));
+ }
+ base::utils::safeDelete(m_typedConfigurations);
+ m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference);
+ resolveLoggerFormatSpec();
+ m_isConfigured = true;
+ }
+
+ /// @brief Reconfigures logger using existing configurations
+ inline void reconfigure(void) {
+ ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]");
+ configure(m_configurations);
+ }
+
+ inline const std::string& id(void) const {
+ return m_id;
+ }
+
+ inline const std::string& parentApplicationName(void) const {
+ return m_parentApplicationName;
+ }
+
+ inline void setParentApplicationName(const std::string& parentApplicationName) {
+ m_parentApplicationName = parentApplicationName;
+ }
+
+ inline Configurations* configurations(void) {
+ return &m_configurations;
+ }
+
+ inline base::TypedConfigurations* typedConfigurations(void) {
+ return m_typedConfigurations;
+ }
+
+ static inline bool isValidId(const std::string& id) {
+ for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) {
+ if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ /// @brief Flushes logger to sync all log files for all levels
+ inline void flush(void) {
+ ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels");
+ base::threading::ScopedLock scopedLock(lock());
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ flush(LevelHelper::castFromInt(lIndex), nullptr);
+ return false;
+ });
+ }
+
+ inline void flush(Level level, base::type::fstream_t* fs) {
+ if (fs == nullptr && m_typedConfigurations->toFile(level)) {
+ fs = m_typedConfigurations->fileStream(level);
+ }
+ if (fs != nullptr) {
+ fs->flush();
+ m_unflushedCount.find(level)->second = 0;
+ }
+ }
+
+ inline bool isFlushNeeded(Level level) {
+ return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level);
+ }
+
+ inline LogBuilder* logBuilder(void) const {
+ return m_logBuilder.get();
+ }
+
+ inline void setLogBuilder(const LogBuilderPtr& logBuilder) {
+ m_logBuilder = logBuilder;
+ }
+
+ inline bool enabled(Level level) const {
+ return m_typedConfigurations->enabled(level);
+ }
+
+#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
+# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\
+ template <typename T, typename... Args>\
+ inline void FUNCTION_NAME(const char*, const T&, const Args&...);\
+ template <typename T>\
+ inline void FUNCTION_NAME(const T&);
+
+ template <typename T, typename... Args>
+ inline void verbose(int, const char*, const T&, const Args&...);
+
+ template <typename T>
+ inline void verbose(int, const T&);
+
+ LOGGER_LEVEL_WRITERS_SIGNATURES(info)
+ LOGGER_LEVEL_WRITERS_SIGNATURES(debug)
+ LOGGER_LEVEL_WRITERS_SIGNATURES(warn)
+ LOGGER_LEVEL_WRITERS_SIGNATURES(error)
+ LOGGER_LEVEL_WRITERS_SIGNATURES(fatal)
+ LOGGER_LEVEL_WRITERS_SIGNATURES(trace)
+# undef LOGGER_LEVEL_WRITERS_SIGNATURES
+#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
+private:
+ std::string m_id;
+ base::TypedConfigurations* m_typedConfigurations;
+ base::type::stringstream_t m_stream;
+ std::string m_parentApplicationName;
+ bool m_isConfigured;
+ Configurations m_configurations;
+ std::map<Level, unsigned int> m_unflushedCount;
+ base::LogStreamsReferenceMap* m_logStreamsReference;
+ LogBuilderPtr m_logBuilder;
+
+ friend class el::LogMessage;
+ friend class el::Loggers;
+ friend class el::Helpers;
+ friend class el::base::RegisteredLoggers;
+ friend class el::base::DefaultLogDispatchCallback;
+ friend class el::base::MessageBuilder;
+ friend class el::base::Writer;
+ friend class el::base::PErrorWriter;
+ friend class el::base::Storage;
+ friend class el::base::PerformanceTracker;
+ friend class el::base::LogDispatcher;
+
+ Logger(void);
+
+#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
+ template <typename T, typename... Args>
+ void log_(Level, int, const char*, const T&, const Args&...);
+
+ template <typename T>
+ inline void log_(Level, int, const T&);
+
+ template <typename T, typename... Args>
+ void log(Level, const char*, const T&, const Args&...);
+
+ template <typename T>
+ inline void log(Level, const T&);
+#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
+
+ void initUnflushedCount(void) {
+ m_unflushedCount.clear();
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0));
+ return false;
+ });
+ }
+
+ inline base::type::stringstream_t& stream(void) {
+ return m_stream;
+ }
+
+ void resolveLoggerFormatSpec(void) const {
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ base::LogFormat* logFormat =
+ const_cast<base::LogFormat*>(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex)));
+ base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id);
+ return false;
+ });
+ }
+};
+namespace base {
+/// @brief Loggers repository
+class RegisteredLoggers : public base::utils::Registry<Logger, std::string> {
+public:
+ explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) :
+ m_defaultLogBuilder(defaultLogBuilder) {
+ m_defaultConfigurations.setToDefault();
+ }
+
+ virtual ~RegisteredLoggers(void) {
+ flushAll();
+ }
+
+ inline void setDefaultConfigurations(const Configurations& configurations) {
+ base::threading::ScopedLock scopedLock(lock());
+ m_defaultConfigurations.setFromBase(const_cast<Configurations*>(&configurations));
+ }
+
+ inline Configurations* defaultConfigurations(void) {
+ return &m_defaultConfigurations;
+ }
+
+ Logger* get(const std::string& id, bool forceCreation = true) {
+ base::threading::ScopedLock scopedLock(lock());
+ Logger* logger_ = base::utils::Registry<Logger, std::string>::get(id);
+ if (logger_ == nullptr && forceCreation) {
+ bool validId = Logger::isValidId(id);
+ if (!validId) {
+ ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger.");
+ return nullptr;
+ }
+ logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference);
+ logger_->m_logBuilder = m_defaultLogBuilder;
+ registerNew(id, logger_);
+ }
+ return logger_;
+ }
+
+ bool remove(const std::string& id) {
+ if (id == "default") {
+ return false;
+ }
+ Logger* logger = base::utils::Registry<Logger, std::string>::get(id);
+ if (logger != nullptr) {
+ unregister(logger);
+ }
+ return true;
+ }
+
+ inline bool has(const std::string& id) {
+ return get(id, false) != nullptr;
+ }
+
+ inline void unregister(Logger*& logger) {
+ base::threading::ScopedLock scopedLock(lock());
+ base::utils::Registry<Logger, std::string>::unregister(logger->id());
+ }
+
+ inline base::LogStreamsReferenceMap* logStreamsReference(void) {
+ return &m_logStreamsReference;
+ }
+
+ inline void flushAll(void) {
+ ELPP_INTERNAL_INFO(1, "Flushing all log files");
+ base::threading::ScopedLock scopedLock(lock());
+ for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin();
+ it != m_logStreamsReference.end(); ++it) {
+ if (it->second.get() == nullptr) continue;
+ it->second->flush();
+ }
+ }
+
+private:
+ LogBuilderPtr m_defaultLogBuilder;
+ Configurations m_defaultConfigurations;
+ base::LogStreamsReferenceMap m_logStreamsReference;
+ friend class el::base::Storage;
+};
+/// @brief Represents registries for verbose logging
+class VRegistry : base::NoCopy, public base::threading::ThreadSafe {
+public:
+ explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) {
+ }
+
+ /// @brief Sets verbose level. Accepted range is 0-9
+ inline void setLevel(base::type::VerboseLevel level) {
+ base::threading::ScopedLock scopedLock(lock());
+ if (level < 0)
+ m_level = 0;
+ else if (level > 9)
+ m_level = base::consts::kMaxVerboseLevel;
+ else
+ m_level = level;
+ }
+
+ inline base::type::VerboseLevel level(void) const {
+ return m_level;
+ }
+
+ inline void clearModules(void) {
+ base::threading::ScopedLock scopedLock(lock());
+ m_modules.clear();
+ }
+
+ void setModules(const char* modules) {
+ base::threading::ScopedLock scopedLock(lock());
+ auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) {
+ if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) {
+ std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev)));
+ ss.str(std::string(""));
+ ss << chr;
+ }
+ if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) {
+ std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx)));
+ ss.str(std::string(""));
+ ss << chr;
+ }
+ ss << sfx;
+ };
+ auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) {
+ if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) {
+ addSuffix(ss, ".h", nullptr);
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".c", ".h");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".cpp", ".c");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".cc", ".cpp");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".cxx", ".cc");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".-inl.h", ".cxx");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".hxx", ".-inl.h");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".hpp", ".hxx");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".hh", ".hpp");
+ }
+ m_modules.insert(std::make_pair(ss.str(), level));
+ };
+ bool isMod = true;
+ bool isLevel = false;
+ std::stringstream ss;
+ int level = -1;
+ for (; *modules; ++modules) {
+ switch (*modules) {
+ case '=':
+ isLevel = true;
+ isMod = false;
+ break;
+ case ',':
+ isLevel = false;
+ isMod = true;
+ if (!ss.str().empty() && level != -1) {
+ insert(ss, level);
+ ss.str(std::string(""));
+ level = -1;
+ }
+ break;
+ default:
+ if (isMod) {
+ ss << *modules;
+ } else if (isLevel) {
+ if (isdigit(*modules)) {
+ level = static_cast<base::type::VerboseLevel>(*modules) - 48;
+ }
+ }
+ break;
+ }
+ }
+ if (!ss.str().empty() && level != -1) {
+ insert(ss, level);
+ }
+ }
+
+ bool allowed(base::type::VerboseLevel vlevel, const char* file) {
+ base::threading::ScopedLock scopedLock(lock());
+ if (m_modules.empty() || file == nullptr) {
+ return vlevel <= m_level;
+ } else {
+ std::map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin();
+ for (; it != m_modules.end(); ++it) {
+ if (base::utils::Str::wildCardMatch(file, it->first.c_str())) {
+ return vlevel <= it->second;
+ }
+ }
+ if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ inline const std::map<std::string, base::type::VerboseLevel>& modules(void) const {
+ return m_modules;
+ }
+
+ void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) {
+ if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") ||
+ commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) {
+ setLevel(base::consts::kMaxVerboseLevel);
+ } else if (commandLineArgs->hasParamWithValue("--v")) {
+ setLevel(atoi(commandLineArgs->getParamValue("--v")));
+ } else if (commandLineArgs->hasParamWithValue("--V")) {
+ setLevel(atoi(commandLineArgs->getParamValue("--V")));
+ } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) {
+ setModules(commandLineArgs->getParamValue("-vmodule"));
+ } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) {
+ setModules(commandLineArgs->getParamValue("-VMODULE"));
+ }
+ }
+
+ /// @brief Whether or not vModules enabled
+ inline bool vModulesEnabled(void) {
+ return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags);
+ }
+
+private:
+ base::type::VerboseLevel m_level;
+ base::type::EnumType* m_pFlags;
+ std::map<std::string, base::type::VerboseLevel> m_modules;
+};
+} // namespace base
+class LogMessage {
+public:
+ LogMessage(Level level, const std::string& file, unsigned long int line, const std::string& func,
+ base::type::VerboseLevel verboseLevel, Logger* logger) :
+ m_level(level), m_file(file), m_line(line), m_func(func),
+ m_verboseLevel(verboseLevel), m_logger(logger), m_message(std::move(logger->stream().str())) {
+ }
+ inline Level level(void) const { return m_level; }
+ inline const std::string& file(void) const { return m_file; }
+ inline unsigned long int line(void) const { return m_line; } // NOLINT
+ inline const std::string& func(void) const { return m_func; }
+ inline base::type::VerboseLevel verboseLevel(void) const { return m_verboseLevel; }
+ inline Logger* logger(void) const { return m_logger; }
+ inline const base::type::string_t& message(void) const { return m_message; }
+private:
+ Level m_level;
+ std::string m_file;
+ unsigned long int m_line;
+ std::string m_func;
+ base::type::VerboseLevel m_verboseLevel;
+ Logger* m_logger;
+ base::type::string_t m_message;
+};
+namespace base {
+#if ELPP_ASYNC_LOGGING
+class AsyncLogItem {
+public:
+ explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine)
+ : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {}
+ virtual ~AsyncLogItem() {}
+ inline LogMessage* logMessage(void) { return &m_logMessage; }
+ inline LogDispatchData* data(void) { return &m_dispatchData; }
+ inline base::type::string_t logLine(void) { return m_logLine; }
+private:
+ LogMessage m_logMessage;
+ LogDispatchData m_dispatchData;
+ base::type::string_t m_logLine;
+};
+class AsyncLogQueue : public base::threading::ThreadSafe {
+public:
+ virtual ~AsyncLogQueue() {
+ ELPP_INTERNAL_INFO(6, "~AsyncLogQueue");
+ }
+
+ inline AsyncLogItem next(void) {
+ base::threading::ScopedLock scopedLock(lock());
+ AsyncLogItem result = m_queue.front();
+ m_queue.pop();
+ return result;
+ }
+
+ inline void push(const AsyncLogItem& item) {
+ base::threading::ScopedLock scopedLock(lock());
+ m_queue.push(item);
+ }
+ inline void pop(void) {
+ base::threading::ScopedLock scopedLock(lock());
+ m_queue.pop();
+ }
+ inline AsyncLogItem front(void) {
+ base::threading::ScopedLock scopedLock(lock());
+ return m_queue.front();
+ }
+ inline bool empty(void) {
+ base::threading::ScopedLock scopedLock(lock());
+ return m_queue.empty();
+ }
+private:
+ std::queue<AsyncLogItem> m_queue;
+};
+class IWorker {
+public:
+ virtual ~IWorker() {}
+ virtual void start() = 0;
+};
+#endif // ELPP_ASYNC_LOGGING
+/// @brief Easylogging++ management storage
+class Storage : base::NoCopy, public base::threading::ThreadSafe {
+public:
+#if ELPP_ASYNC_LOGGING
+ Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) :
+#else
+ explicit Storage(const LogBuilderPtr& defaultLogBuilder) :
+#endif // ELPP_ASYNC_LOGGING
+ m_registeredHitCounters(new base::RegisteredHitCounters()),
+ m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)),
+ m_flags(0x0),
+ m_vRegistry(new base::VRegistry(0, &m_flags)),
+#if ELPP_ASYNC_LOGGING
+ m_asyncLogQueue(new base::AsyncLogQueue()),
+ m_asyncDispatchWorker(asyncDispatchWorker),
+#endif // ELPP_ASYNC_LOGGING
+ m_preRollOutCallback(base::defaultPreRollOutCallback) {
+ // Register default logger
+ m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId));
+ // Register performance logger and reconfigure format
+ Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId));
+ performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg"));
+ performanceLogger->reconfigure();
+#if defined(ELPP_SYSLOG)
+ // Register syslog logger and reconfigure format
+ Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId));
+ sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg"));
+ sysLogLogger->reconfigure();
+#else
+ ELPP_UNUSED(base::consts::kSysLogLoggerId);
+#endif // defined(ELPP_SYSLOG)
+ addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified);
+#if ELPP_ASYNC_LOGGING
+ installLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback"));
+#else
+ installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback"));
+#endif // ELPP_ASYNC_LOGGING
+ installPerformanceTrackingCallback<base::DefaultPerformanceTrackingCallback>(std::string("DefaultPerformanceTrackingCallback"));
+ ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized");
+#if ELPP_ASYNC_LOGGING
+ m_asyncDispatchWorker->start();
+#endif // ELPP_ASYNC_LOGGING
+ }
+
+ virtual ~Storage(void) {
+ ELPP_INTERNAL_INFO(4, "Destroying storage");
+#if ELPP_ASYNC_LOGGING
+ ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous");
+ uninstallLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback"));
+ installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback"));
+ ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker");
+ base::utils::safeDelete(m_asyncDispatchWorker);
+ ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue");
+ base::utils::safeDelete(m_asyncLogQueue);
+#endif // ELPP_ASYNC_LOGGING
+ ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters");
+ base::utils::safeDelete(m_registeredHitCounters);
+ ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers");
+ base::utils::safeDelete(m_registeredLoggers);
+ ELPP_INTERNAL_INFO(5, "Destroying vRegistry");
+ base::utils::safeDelete(m_vRegistry);
+ }
+
+ inline bool validateEveryNCounter(const char* filename, unsigned long int lineNumber, std::size_t occasion) {
+ return hitCounters()->validateEveryN(filename, lineNumber, occasion);
+ }
+
+ inline bool validateAfterNCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT
+ return hitCounters()->validateAfterN(filename, lineNumber, n);
+ }
+
+ inline bool validateNTimesCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT
+ return hitCounters()->validateNTimes(filename, lineNumber, n);
+ }
+
+ inline base::RegisteredHitCounters* hitCounters(void) const {
+ return m_registeredHitCounters;
+ }
+
+ inline base::RegisteredLoggers* registeredLoggers(void) const {
+ return m_registeredLoggers;
+ }
+
+ inline base::VRegistry* vRegistry(void) const {
+ return m_vRegistry;
+ }
+
+#if ELPP_ASYNC_LOGGING
+ inline base::AsyncLogQueue* asyncLogQueue(void) const {
+ return m_asyncLogQueue;
+ }
+#endif // ELPP_ASYNC_LOGGING
+
+ inline const base::utils::CommandLineArgs* commandLineArgs(void) const {
+ return &m_commandLineArgs;
+ }
+
+ inline void addFlag(LoggingFlag flag) {
+ base::utils::addFlag(flag, &m_flags);
+ }
+
+ inline void removeFlag(LoggingFlag flag) {
+ base::utils::removeFlag(flag, &m_flags);
+ }
+
+ inline bool hasFlag(LoggingFlag flag) const {
+ return base::utils::hasFlag(flag, m_flags);
+ }
+
+ inline base::type::EnumType flags(void) const {
+ return m_flags;
+ }
+
+ inline void setFlags(base::type::EnumType flags) {
+ m_flags = flags;
+ }
+
+ inline void setPreRollOutCallback(const PreRollOutCallback& callback) {
+ m_preRollOutCallback = callback;
+ }
+
+ inline void unsetPreRollOutCallback(void) {
+ m_preRollOutCallback = base::defaultPreRollOutCallback;
+ }
+
+ inline PreRollOutCallback& preRollOutCallback(void) {
+ return m_preRollOutCallback;
+ }
+
+ inline bool hasCustomFormatSpecifier(const char* formatSpecifier) {
+ base::threading::ScopedLock scopedLock(lock());
+ return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(),
+ formatSpecifier) != m_customFormatSpecifiers.end();
+ }
+
+ inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) {
+ if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) {
+ return;
+ }
+ base::threading::ScopedLock scopedLock(lock());
+ m_customFormatSpecifiers.push_back(customFormatSpecifier);
+ }
+
+ inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) {
+ base::threading::ScopedLock scopedLock(lock());
+ std::vector<CustomFormatSpecifier>::iterator it = std::find(m_customFormatSpecifiers.begin(),
+ m_customFormatSpecifiers.end(), formatSpecifier);
+ if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) {
+ m_customFormatSpecifiers.erase(it);
+ return true;
+ }
+ return false;
+ }
+
+ const std::vector<CustomFormatSpecifier>* customFormatSpecifiers(void) const {
+ return &m_customFormatSpecifiers;
+ }
+
+ inline void setLoggingLevel(Level level) {
+ m_loggingLevel = level;
+ }
+
+ template <typename T>
+ inline bool installLogDispatchCallback(const std::string& id) {
+ return installCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks);
+ }
+
+ template <typename T>
+ inline void uninstallLogDispatchCallback(const std::string& id) {
+ uninstallCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks);
+ }
+ template <typename T>
+ inline T* logDispatchCallback(const std::string& id) {
+ return callback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks);
+ }
+
+ template <typename T>
+ inline bool installPerformanceTrackingCallback(const std::string& id) {
+ return installCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks);
+ }
+
+ template <typename T>
+ inline void uninstallPerformanceTrackingCallback(const std::string& id) {
+ uninstallCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks);
+ }
+
+ template <typename T>
+ inline T* performanceTrackingCallback(const std::string& id) {
+ return callback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks);
+ }
+private:
+ base::RegisteredHitCounters* m_registeredHitCounters;
+ base::RegisteredLoggers* m_registeredLoggers;
+ base::type::EnumType m_flags;
+ base::VRegistry* m_vRegistry;
+#if ELPP_ASYNC_LOGGING
+ base::AsyncLogQueue* m_asyncLogQueue;
+ base::IWorker* m_asyncDispatchWorker;
+#endif // ELPP_ASYNC_LOGGING
+ base::utils::CommandLineArgs m_commandLineArgs;
+ PreRollOutCallback m_preRollOutCallback;
+ std::map<std::string, base::type::LogDispatchCallbackPtr> m_logDispatchCallbacks;
+ std::map<std::string, base::type::PerformanceTrackingCallbackPtr> m_performanceTrackingCallbacks;
+ std::vector<CustomFormatSpecifier> m_customFormatSpecifiers;
+ Level m_loggingLevel;
+
+ friend class el::Helpers;
+ friend class el::base::DefaultLogDispatchCallback;
+ friend class el::LogBuilder;
+ friend class el::base::MessageBuilder;
+ friend class el::base::Writer;
+ friend class el::base::PerformanceTracker;
+ friend class el::base::LogDispatcher;
+
+ void setApplicationArguments(int argc, char** argv) {
+ m_commandLineArgs.setArgs(argc, argv);
+ m_vRegistry->setFromArgs(commandLineArgs());
+ // default log file
+#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+ if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) {
+ Configurations c;
+ c.setGlobally(ConfigurationType::Filename, std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam)));
+ registeredLoggers()->setDefaultConfigurations(c);
+ for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin();
+ it != registeredLoggers()->end(); ++it) {
+ it->second->configure(c);
+ }
+ }
+#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+#if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+ if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) {
+ m_flags = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam));
+ }
+#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+ }
+
+ inline void setApplicationArguments(int argc, const char** argv) {
+ setApplicationArguments(argc, const_cast<char**>(argv));
+ }
+
+ template <typename T, typename TPtr>
+ inline bool installCallback(const std::string& id, std::map<std::string, TPtr>* mapT) {
+ if (mapT->find(id) == mapT->end()) {
+ mapT->insert(std::make_pair(id, TPtr(new T())));
+ return true;
+ }
+ return false;
+ }
+
+ template <typename T, typename TPtr>
+ inline void uninstallCallback(const std::string& id, std::map<std::string, TPtr>* mapT) {
+ if (mapT->find(id) != mapT->end()) {
+ mapT->erase(id);
+ }
+ }
+
+ template <typename T, typename TPtr>
+ inline T* callback(const std::string& id, std::map<std::string, TPtr>* mapT) {
+ typename std::map<std::string, TPtr>::iterator iter = mapT->find(id);
+ if (iter != mapT->end()) {
+ return static_cast<T*>(iter->second.get());
+ }
+ return nullptr;
+ }
+};
+extern ELPP_EXPORT base::type::StoragePointer elStorage;
+#define ELPP el::base::elStorage
+class DefaultLogDispatchCallback : public LogDispatchCallback {
+protected:
+ void handle(const LogDispatchData* data) {
+ m_data = data;
+ dispatch(std::move(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(),
+ m_data->dispatchAction() == base::DispatchAction::NormalLog)));
+ }
+private:
+ const LogDispatchData* m_data;
+ void dispatch(base::type::string_t&& logLine) {
+ if (m_data->dispatchAction() == base::DispatchAction::NormalLog) {
+ if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) {
+ base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream(m_data->logMessage()->level());
+ if (fs != nullptr) {
+ fs->write(logLine.c_str(), logLine.size());
+ if (fs->fail()) {
+ ELPP_INTERNAL_ERROR("Unable to write log to file ["
+ << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n"
+ << "Few possible reasons (could be something else):\n" << " * Permission denied\n"
+ << " * Disk full\n" << " * Disk is not writable", true);
+ } else {
+ if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) {
+ m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs);
+ }
+ }
+ } else {
+ ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] "
+ << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: "
+ << m_data->logMessage()->logger()->id() << "]", false);
+ }
+ }
+ if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) {
+ if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
+ m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level());
+ ELPP_COUT << ELPP_COUT_LINE(logLine);
+ }
+ }
+#if defined(ELPP_SYSLOG)
+ else if (m_data->dispatchAction() == base::DispatchAction::SysLog) {
+ // Determine syslog priority
+ int sysLogPriority = 0;
+ if (m_data->logMessage()->level() == Level::Fatal)
+ sysLogPriority = LOG_EMERG;
+ else if (m_data->logMessage()->level() == Level::Error)
+ sysLogPriority = LOG_ERR;
+ else if (m_data->logMessage()->level() == Level::Warning)
+ sysLogPriority = LOG_WARNING;
+ else if (m_data->logMessage()->level() == Level::Info)
+ sysLogPriority = LOG_INFO;
+ else if (m_data->logMessage()->level() == Level::Debug)
+ sysLogPriority = LOG_DEBUG;
+ else
+ sysLogPriority = LOG_NOTICE;
+# if defined(ELPP_UNICODE)
+ char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str());
+ syslog(sysLogPriority, "%s", line);
+ free(line);
+# else
+ syslog(sysLogPriority, "%s", logLine.c_str());
+# endif
+ }
+#endif // defined(ELPP_SYSLOG)
+ }
+};
+#if ELPP_ASYNC_LOGGING
+class AsyncLogDispatchCallback : public LogDispatchCallback {
+protected:
+ void handle(const LogDispatchData* data) {
+ base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(), data->dispatchAction() == base::DispatchAction::NormalLog);
+ if (data->dispatchAction() == base::DispatchAction::NormalLog && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) {
+ if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
+ data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level());
+ ELPP_COUT << ELPP_COUT_LINE(logLine);
+ }
+ // Save resources and only queue if we want to write to file otherwise just ignore handler
+ if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) {
+ ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine));
+ }
+ }
+};
+class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe {
+public:
+ AsyncDispatchWorker() {
+ setContinueRunning(false);
+ }
+
+ virtual ~AsyncDispatchWorker() {
+ setContinueRunning(false);
+ ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue");
+ clean();
+ ELPP_INTERNAL_INFO(6, "Log queue cleaned");
+ }
+
+ inline bool clean() {
+ std::mutex m;
+ std::unique_lock<std::mutex> lk(m);
+ cv.wait(lk, []{ return !ELPP->asyncLogQueue()->empty(); });
+ emptyQueue();
+ lk.unlock();
+ cv.notify_one();
+ return ELPP->asyncLogQueue()->empty();
+ }
+
+ inline void emptyQueue() {
+ while (!ELPP->asyncLogQueue()->empty()) {
+ AsyncLogItem data = ELPP->asyncLogQueue()->next();
+ handle(&data);
+ base::threading::msleep(100);
+ }
+ }
+
+ virtual inline void start() {
+ base::threading::msleep(5000); // Wait extra few seconds
+ setContinueRunning(true);
+ std::thread t1(&AsyncDispatchWorker::runner, this);
+ t1.join();
+ }
+
+ void handle(AsyncLogItem* logItem) {
+ LogDispatchData* data = logItem->data();
+ LogMessage* logMessage = logItem->logMessage();
+ Logger* logger = logMessage->logger();
+ base::TypedConfigurations* conf = logger->typedConfigurations();
+ base::type::string_t logLine = logItem->logLine();
+ if (data->dispatchAction() == base::DispatchAction::NormalLog) {
+ if (conf->toFile(logMessage->level())) {
+ base::type::fstream_t* fs = conf->fileStream(logMessage->level());
+ if (fs != nullptr) {
+ fs->write(logLine.c_str(), logLine.size());
+ if (fs->fail()) {
+ ELPP_INTERNAL_ERROR("Unable to write log to file ["
+ << conf->filename(logMessage->level()) << "].\n"
+ << "Few possible reasons (could be something else):\n" << " * Permission denied\n"
+ << " * Disk full\n" << " * Disk is not writable", true);
+ } else {
+ if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) {
+ logger->flush(logMessage->level(), fs);
+ }
+ }
+ } else {
+ ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] "
+ << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false);
+ }
+ }
+ }
+# if defined(ELPP_SYSLOG)
+ else if (data->dispatchAction() == base::DispatchAction::SysLog) {
+ // Determine syslog priority
+ int sysLogPriority = 0;
+ if (logMessage->level() == Level::Fatal)
+ sysLogPriority = LOG_EMERG;
+ else if (logMessage->level() == Level::Error)
+ sysLogPriority = LOG_ERR;
+ else if (logMessage->level() == Level::Warning)
+ sysLogPriority = LOG_WARNING;
+ else if (logMessage->level() == Level::Info)
+ sysLogPriority = LOG_INFO;
+ else if (logMessage->level() == Level::Debug)
+ sysLogPriority = LOG_DEBUG;
+ else
+ sysLogPriority = LOG_NOTICE;
+# if defined(ELPP_UNICODE)
+ char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str());
+ syslog(sysLogPriority, "%s", line);
+ free(line);
+# else
+ syslog(sysLogPriority, "%s", logLine.c_str());
+# endif
+ }
+# endif // defined(ELPP_SYSLOG)
+ }
+
+ void run() {
+ while (continueRunning()) {
+ emptyQueue();
+ base::threading::msleep(10); // 10ms
+ }
+ }
+
+ static void* runner(void *context) {
+ static_cast<AsyncDispatchWorker*>(context)->run();
+ return NULL;
+ }
+
+ void setContinueRunning(bool value) {
+ base::threading::ScopedLock scopedLock(m_continueRunningMutex);
+ m_continueRunning = value;
+ }
+ bool continueRunning(void) {
+ return m_continueRunning;
+ }
+private:
+ std::condition_variable cv;
+ bool m_continueRunning;
+ base::threading::Mutex m_continueRunningMutex;
+};
+#endif // ELPP_ASYNC_LOGGING
+} // namespace base
+namespace base {
+class DefaultLogBuilder : public LogBuilder {
+public:
+ base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const {
+ base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations();
+ const base::LogFormat* logFormat = &tc->logFormat(logMessage->level());
+ base::type::string_t logLine = logFormat->format();
+ char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = "";
+ const char* bufLim = buff + sizeof(buff);
+ if (logFormat->hasFlag(base::FormatFlags::AppName)) {
+ // App name
+ base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier,
+ logMessage->logger()->parentApplicationName());
+ }
+ if (logFormat->hasFlag(base::FormatFlags::ThreadId)) {
+ // Thread ID
+ base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier,
+ base::threading::getCurrentThreadId());
+ }
+ if (logFormat->hasFlag(base::FormatFlags::DateTime)) {
+ // DateTime
+ base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier,
+ base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(),
+ &tc->millisecondsWidth(logMessage->level())));
+ }
+ if (logFormat->hasFlag(base::FormatFlags::Function)) {
+ // Function
+ base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func());
+ }
+ if (logFormat->hasFlag(base::FormatFlags::File)) {
+ // File
+ char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength);
+ base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff);
+ buf = base::utils::Str::addToBuff(buff, buf, bufLim);
+ base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff));
+ }
+ if (logFormat->hasFlag(base::FormatFlags::FileBase)) {
+ // FileBase
+ char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength);
+ base::utils::File::buildBaseFilename(logMessage->file(), buff);
+ buf = base::utils::Str::addToBuff(buff, buf, bufLim);
+ base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff));
+ }
+ if (logFormat->hasFlag(base::FormatFlags::Line)) {
+ // Line
+ char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength);
+ buf = base::utils::Str::convertAndAddToBuff(logMessage->line(),
+ base::consts::kSourceLineMaxLength, buf, bufLim, false);
+ base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff));
+ }
+ if (logFormat->hasFlag(base::FormatFlags::Location)) {
+ // Location
+ char* buf = base::utils::Str::clearBuff(buff,
+ base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength);
+ base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff);
+ buf = base::utils::Str::addToBuff(buff, buf, bufLim);
+ buf = base::utils::Str::addToBuff(":", buf, bufLim);
+ buf = base::utils::Str::convertAndAddToBuff(logMessage->line(),
+ base::consts::kSourceLineMaxLength, buf, bufLim, false);
+ base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff));
+ }
+ if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) {
+ // Verbose level
+ char* buf = base::utils::Str::clearBuff(buff, 1);
+ buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false);
+ base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff));
+ }
+ if (logFormat->hasFlag(base::FormatFlags::LogMessage)) {
+ // Log message
+ base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message());
+ }
+#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
+ for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin();
+ it != ELPP->customFormatSpecifiers()->end(); ++it) {
+ std::string fs(it->formatSpecifier());
+ base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end());
+ base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, std::string(it->resolver()()));
+ }
+#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
+ if (appendNewLine) logLine += ELPP_LITERAL("\n");
+ return logLine;
+ }
+};
+/// @brief Dispatches log messages
+class LogDispatcher : base::NoCopy {
+public:
+ LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) :
+ m_proceed(proceed),
+ m_logMessage(std::move(logMessage)),
+ m_dispatchAction(std::move(dispatchAction)) {
+ }
+
+ void dispatch(void) {
+ if (m_proceed && m_dispatchAction == base::DispatchAction::None) {
+ m_proceed = false;
+ }
+ if (!m_proceed) {
+ return;
+ }
+ // We minimize the time of ELPP's lock - this lock is released after log is written
+ base::threading::ScopedLock scopedLock(ELPP->lock());
+ base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations;
+ if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) {
+ tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback());
+ }
+ LogDispatchCallback* callback = nullptr;
+ LogDispatchData data;
+ for (const std::pair<std::string, base::type::LogDispatchCallbackPtr>& h
+ : ELPP->m_logDispatchCallbacks) {
+ callback = h.second.get();
+ if (callback != nullptr && callback->enabled()) {
+ data.setLogMessage(&m_logMessage);
+ data.setDispatchAction(m_dispatchAction);
+ callback->acquireLock();
+ callback->handle(&data);
+ callback->releaseLock();
+ }
+ }
+ }
+
+private:
+ bool m_proceed;
+ LogMessage m_logMessage;
+ base::DispatchAction m_dispatchAction;
+};
+#if defined(ELPP_STL_LOGGING)
+/// @brief Workarounds to write some STL logs
+///
+/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers
+/// of same type and provide iterator interface and pass it on to writeIterator().
+/// Remember, this is passed by value in constructor so that we dont change original containers.
+/// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer))
+namespace workarounds {
+/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T
+template <typename T, typename Container>
+class IterableContainer {
+public:
+ typedef typename Container::iterator iterator;
+ typedef typename Container::const_iterator const_iterator;
+ IterableContainer(void) {}
+ virtual ~IterableContainer(void) {}
+ iterator begin(void) { return getContainer().begin(); }
+ iterator end(void) { return getContainer().end(); }
+private:
+ virtual Container& getContainer(void) = 0;
+};
+/// @brief Implements IterableContainer and provides iterable std::priority_queue class
+template<typename T, typename Container = std::vector<T>, typename Comparator = std::less<typename Container::value_type>>
+class IterablePriorityQueue : public IterableContainer<T, Container>, public std::priority_queue<T, Container, Comparator> {
+public:
+ IterablePriorityQueue(std::priority_queue<T, Container, Comparator> queue_) {
+ std::size_t count_ = 0;
+ while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) {
+ this->push(queue_.top());
+ queue_.pop();
+ }
+ }
+private:
+ inline Container& getContainer(void) {
+ return this->c;
+ }
+};
+/// @brief Implements IterableContainer and provides iterable std::queue class
+template<typename T, typename Container = std::deque<T>>
+class IterableQueue : public IterableContainer<T, Container>, public std::queue<T, Container> {
+public:
+ IterableQueue(std::queue<T, Container> queue_) {
+ std::size_t count_ = 0;
+ while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) {
+ this->push(queue_.front());
+ queue_.pop();
+ }
+ }
+private:
+ inline Container& getContainer(void) {
+ return this->c;
+ }
+};
+/// @brief Implements IterableContainer and provides iterable std::stack class
+template<typename T, typename Container = std::deque<T>>
+class IterableStack : public IterableContainer<T, Container>, public std::stack<T, Container> {
+public:
+ IterableStack(std::stack<T, Container> stack_) {
+ std::size_t count_ = 0;
+ while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) {
+ this->push(stack_.top());
+ stack_.pop();
+ }
+ }
+private:
+ inline Container& getContainer(void) {
+ return this->c;
+ }
+};
+} // namespace workarounds
+#endif // defined(ELPP_STL_LOGGING)
+// Log message builder
+class MessageBuilder {
+public:
+ MessageBuilder(void) : m_logger(nullptr), m_containerLogSeperator(ELPP_LITERAL("")) {}
+ void initialize(Logger* logger) {
+ m_logger = logger;
+ m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ?
+ ELPP_LITERAL("\n ") : ELPP_LITERAL(", ");
+ }
+
+# define ELPP_SIMPLE_LOG(LOG_TYPE)\
+ inline MessageBuilder& operator<<(LOG_TYPE msg) {\
+ m_logger->stream() << msg;\
+ if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {\
+ m_logger->stream() << " ";\
+ }\
+ return *this;\
+ }
+
+ inline MessageBuilder& operator<<(const std::string& msg) {
+ return operator<<(msg.c_str());
+ }
+ ELPP_SIMPLE_LOG(char)
+ ELPP_SIMPLE_LOG(bool)
+ ELPP_SIMPLE_LOG(signed short)
+ ELPP_SIMPLE_LOG(unsigned short)
+ ELPP_SIMPLE_LOG(signed int)
+ ELPP_SIMPLE_LOG(unsigned int)
+ ELPP_SIMPLE_LOG(signed long)
+ ELPP_SIMPLE_LOG(unsigned long)
+ ELPP_SIMPLE_LOG(float)
+ ELPP_SIMPLE_LOG(double)
+ ELPP_SIMPLE_LOG(char*)
+ ELPP_SIMPLE_LOG(const char*)
+ ELPP_SIMPLE_LOG(const void*)
+ ELPP_SIMPLE_LOG(long double)
+ inline MessageBuilder& operator<<(const std::wstring& msg) {
+ return operator<<(msg.c_str());
+ }
+ inline MessageBuilder& operator<<(const wchar_t* msg) {
+ if (msg == nullptr) {
+ m_logger->stream() << base::consts::kNullPointer;
+ return *this;
+ }
+# if defined(ELPP_UNICODE)
+ m_logger->stream() << msg;
+# else
+ char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg);
+ m_logger->stream() << buff_;
+ free(buff_);
+# endif
+ if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {
+ m_logger->stream() << " ";
+ }
+ return *this;
+ }
+ // ostream manipulators
+ inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) {
+ m_logger->stream() << OStreamMani;
+ return *this;
+ }
+#define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \
+ template <typename T> \
+ inline MessageBuilder& operator<<(const temp<T>& template_inst) { \
+ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \
+ }
+#define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp) \
+ template <typename T1, typename T2> \
+ inline MessageBuilder& operator<<(const temp<T1, T2>& template_inst) { \
+ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \
+ }
+#define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp) \
+ template <typename T1, typename T2, typename T3> \
+ inline MessageBuilder& operator<<(const temp<T1, T2, T3>& template_inst) { \
+ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \
+ }
+#define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp) \
+ template <typename T1, typename T2, typename T3, typename T4> \
+ inline MessageBuilder& operator<<(const temp<T1, T2, T3, T4>& template_inst) { \
+ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \
+ }
+#define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp) \
+ template <typename T1, typename T2, typename T3, typename T4, typename T5> \
+ inline MessageBuilder& operator<<(const temp<T1, T2, T3, T4, T5>& template_inst) { \
+ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \
+ }
+
+#if defined(ELPP_STL_LOGGING)
+ ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector)
+ ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list)
+ ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque)
+ ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set)
+ ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset)
+ ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map)
+ ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap)
+ template <class T, class Container>
+ inline MessageBuilder& operator<<(const std::queue<T, Container>& queue_) {
+ base::workarounds::IterableQueue<T, Container> iterableQueue_ =
+ static_cast<base::workarounds::IterableQueue<T, Container> >(queue_);
+ return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size());
+ }
+ template <class T, class Container>
+ inline MessageBuilder& operator<<(const std::stack<T, Container>& stack_) {
+ base::workarounds::IterableStack<T, Container> iterableStack_ =
+ static_cast<base::workarounds::IterableStack<T, Container> >(stack_);
+ return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size());
+ }
+ template <class T, class Container, class Comparator>
+ inline MessageBuilder& operator<<(const std::priority_queue<T, Container, Comparator>& priorityQueue_) {
+ base::workarounds::IterablePriorityQueue<T, Container, Comparator> iterablePriorityQueue_ =
+ static_cast<base::workarounds::IterablePriorityQueue<T, Container, Comparator> >(priorityQueue_);
+ return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size());
+ }
+ template <class First, class Second>
+ inline MessageBuilder& operator<<(const std::pair<First, Second>& pair_) {
+ m_logger->stream() << ELPP_LITERAL("(");
+ operator << (static_cast<First>(pair_.first));
+ m_logger->stream() << ELPP_LITERAL(", ");
+ operator << (static_cast<Second>(pair_.second));
+ m_logger->stream() << ELPP_LITERAL(")");
+ return *this;
+ }
+ template <std::size_t Size>
+ inline MessageBuilder& operator<<(const std::bitset<Size>& bitset_) {
+ m_logger->stream() << ELPP_LITERAL("[");
+ operator << (bitset_.to_string());
+ m_logger->stream() << ELPP_LITERAL("]");
+ return *this;
+ }
+# if defined(ELPP_LOG_STD_ARRAY)
+ template <class T, std::size_t Size>
+ inline MessageBuilder& operator<<(const std::array<T, Size>& array) {
+ return writeIterator(array.begin(), array.end(), array.size());
+ }
+# endif // defined(ELPP_LOG_STD_ARRAY)
+# if defined(ELPP_LOG_UNORDERED_MAP)
+ ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map)
+ ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap)
+# endif // defined(ELPP_LOG_UNORDERED_MAP)
+# if defined(ELPP_LOG_UNORDERED_SET)
+ ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set)
+ ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset)
+# endif // defined(ELPP_LOG_UNORDERED_SET)
+#endif // defined(ELPP_STL_LOGGING)
+#if defined(ELPP_QT_LOGGING)
+ inline MessageBuilder& operator<<(const QString& msg) {
+# if defined(ELPP_UNICODE)
+ m_logger->stream() << msg.toStdWString();
+# else
+ m_logger->stream() << msg.toStdString();
+# endif // defined(ELPP_UNICODE)
+ return *this;
+ }
+ inline MessageBuilder& operator<<(const QByteArray& msg) {
+ return operator << (QString(msg));
+ }
+ inline MessageBuilder& operator<<(const QStringRef& msg) {
+ return operator<<(msg.toString());
+ }
+ inline MessageBuilder& operator<<(qint64 msg) {
+# if defined(ELPP_UNICODE)
+ m_logger->stream() << QString::number(msg).toStdWString();
+# else
+ m_logger->stream() << QString::number(msg).toStdString();
+# endif // defined(ELPP_UNICODE)
+ return *this;
+ }
+ inline MessageBuilder& operator<<(quint64 msg) {
+# if defined(ELPP_UNICODE)
+ m_logger->stream() << QString::number(msg).toStdWString();
+# else
+ m_logger->stream() << QString::number(msg).toStdString();
+# endif // defined(ELPP_UNICODE)
+ return *this;
+ }
+ inline MessageBuilder& operator<<(QChar msg) {
+ m_logger->stream() << msg.toLatin1();
+ return *this;
+ }
+ inline MessageBuilder& operator<<(const QLatin1String& msg) {
+ m_logger->stream() << msg.latin1();
+ return *this;
+ }
+ ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList)
+ ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector)
+ ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue)
+ ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet)
+ ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList)
+ ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack)
+ template <typename First, typename Second>
+ inline MessageBuilder& operator<<(const QPair<First, Second>& pair_) {
+ m_logger->stream() << ELPP_LITERAL("(");
+ operator << (static_cast<First>(pair_.first));
+ m_logger->stream() << ELPP_LITERAL(", ");
+ operator << (static_cast<Second>(pair_.second));
+ m_logger->stream() << ELPP_LITERAL(")");
+ return *this;
+ }
+ template <typename K, typename V>
+ inline MessageBuilder& operator<<(const QMap<K, V>& map_) {
+ m_logger->stream() << ELPP_LITERAL("[");
+ QList<K> keys = map_.keys();
+ typename QList<K>::const_iterator begin = keys.begin();
+ typename QList<K>::const_iterator end = keys.end();
+ int max_ = static_cast<int>(base::consts::kMaxLogPerContainer); // to prevent warning
+ for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) {
+ m_logger->stream() << ELPP_LITERAL("(");
+ operator << (static_cast<K>(*begin));
+ m_logger->stream() << ELPP_LITERAL(", ");
+ operator << (static_cast<V>(map_.value(*begin)));
+ m_logger->stream() << ELPP_LITERAL(")");
+ m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL(""));
+ }
+ if (begin != end) {
+ m_logger->stream() << ELPP_LITERAL("...");
+ }
+ m_logger->stream() << ELPP_LITERAL("]");
+ return *this;
+ }
+ template <typename K, typename V>
+ inline MessageBuilder& operator<<(const QMultiMap<K, V>& map_) {
+ operator << (static_cast<QMap<K, V>>(map_));
+ return *this;
+ }
+ template <typename K, typename V>
+ inline MessageBuilder& operator<<(const QHash<K, V>& hash_) {
+ m_logger->stream() << ELPP_LITERAL("[");
+ QList<K> keys = hash_.keys();
+ typename QList<K>::const_iterator begin = keys.begin();
+ typename QList<K>::const_iterator end = keys.end();
+ int max_ = static_cast<int>(base::consts::kMaxLogPerContainer); // prevent type warning
+ for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) {
+ m_logger->stream() << ELPP_LITERAL("(");
+ operator << (static_cast<K>(*begin));
+ m_logger->stream() << ELPP_LITERAL(", ");
+ operator << (static_cast<V>(hash_.value(*begin)));
+ m_logger->stream() << ELPP_LITERAL(")");
+ m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL(""));
+ }
+ if (begin != end) {
+ m_logger->stream() << ELPP_LITERAL("...");
+ }
+ m_logger->stream() << ELPP_LITERAL("]");
+ return *this;
+ }
+ template <typename K, typename V>
+ inline MessageBuilder& operator<<(const QMultiHash<K, V>& multiHash_) {
+ operator << (static_cast<QHash<K, V>>(multiHash_));
+ return *this;
+ }
+#endif // defined(ELPP_QT_LOGGING)
+#if defined(ELPP_BOOST_LOGGING)
+ ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector)
+ ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector)
+ ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list)
+ ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque)
+ ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map)
+ ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map)
+ ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set)
+ ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set)
+#endif // defined(ELPP_BOOST_LOGGING)
+
+/// @brief Macro used internally that can be used externally to make containers easylogging++ friendly
+///
+/// @detail This macro expands to write an ostream& operator<< for container. This container is expected to
+/// have begin() and end() methods that return respective iterators
+/// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets
+/// @param SizeMethod Method used to get size of container.
+/// @param ElementInstance Instance of element to be fed out. Insance name is "elem". See WXELPP_ENABLED macro
+/// for an example usage
+#define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \
+ el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) {\
+ const el::base::type::char_t* sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? \
+ ELPP_LITERAL("\n ") : ELPP_LITERAL(", ");\
+ ContainerType::const_iterator elem = container.begin();\
+ ContainerType::const_iterator endElem = container.end();\
+ std::size_t size_ = container.SizeMethod; \
+ ss << ELPP_LITERAL("[");\
+ for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) { \
+ ss << ElementInstance;\
+ ss << ((i < size_ - 1) ? sep : ELPP_LITERAL(""));\
+ }\
+ if (elem != endElem) {\
+ ss << ELPP_LITERAL("...");\
+ }\
+ ss << ELPP_LITERAL("]");\
+ return ss;\
+ }
+#if defined(ELPP_WXWIDGETS_LOGGING)
+ ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector)
+# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem))
+# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem))
+# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \
+ ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")")
+#else
+# define ELPP_WX_PTR_ENABLED(ContainerType)
+# define ELPP_WX_ENABLED(ContainerType)
+# define ELPP_WX_HASH_MAP_ENABLED(ContainerType)
+#endif // defined(ELPP_WXWIDGETS_LOGGING)
+ // Other classes
+ template <class Class>
+ ELPP_SIMPLE_LOG(const Class&)
+#undef ELPP_SIMPLE_LOG
+#undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG
+#undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG
+#undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG
+#undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG
+#undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG
+private:
+ Logger* m_logger;
+ const base::type::char_t* m_containerLogSeperator;
+
+ template<class Iterator>
+ inline MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) {
+ m_logger->stream() << ELPP_LITERAL("[");
+ for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) {
+ operator << (*begin_);
+ m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeperator : ELPP_LITERAL(""));
+ }
+ if (begin_ != end_) {
+ m_logger->stream() << ELPP_LITERAL("...");
+ }
+ m_logger->stream() << ELPP_LITERAL("]");
+ if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {
+ m_logger->stream() << " ";
+ }
+ return *this;
+ }
+};
+/// @brief Writes nothing - Used when certain log is disabled
+class NullWriter : base::NoCopy {
+public:
+ NullWriter(void) {}
+
+ // Null manipulator
+ inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) {
+ return *this;
+ }
+
+ template <typename T>
+ inline NullWriter& operator<<(const T&) {
+ return *this;
+ }
+};
+/// @brief Main entry point of each logging
+class Writer : base::NoCopy {
+public:
+ Writer(Level level, const char* file, unsigned long int line,
+ const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog,
+ base::type::VerboseLevel verboseLevel = 0) :
+ m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel),
+ m_proceed(false), m_dispatchAction(dispatchAction) {
+ }
+
+ virtual ~Writer(void) {
+ processDispatch();
+ }
+
+ template <typename T>
+ inline Writer& operator<<(const T& log) {
+#if ELPP_LOGGING_ENABLED
+ if (m_proceed) {
+ m_messageBuilder << log;
+ }
+#endif // ELPP_LOGGING_ENABLED
+ return *this;
+ }
+
+ inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) {
+#if ELPP_LOGGING_ENABLED
+ if (m_proceed) {
+ m_messageBuilder << log;
+ }
+#endif // ELPP_LOGGING_ENABLED
+ return *this;
+ }
+
+ Writer& construct(Logger* logger, bool needLock = true) {
+ m_logger = logger;
+ initializeLogger(logger->id(), false, needLock);
+ m_messageBuilder.initialize(m_logger);
+ return *this;
+ }
+
+ Writer& construct(int count, const char* loggerIds, ...) {
+ if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) {
+ va_list loggersList;
+ va_start(loggersList, loggerIds);
+ const char* id = loggerIds;
+ for (int i = 0; i < count; ++i) {
+ m_loggerIds.push_back(std::string(id));
+ id = va_arg(loggersList, const char*);
+ }
+ va_end(loggersList);
+ initializeLogger(m_loggerIds.at(0));
+ } else {
+ initializeLogger(std::string(loggerIds));
+ }
+ m_messageBuilder.initialize(m_logger);
+ return *this;
+ }
+protected:
+ Level m_level;
+ const char* m_file;
+ const unsigned long int m_line;
+ const char* m_func;
+ base::type::VerboseLevel m_verboseLevel;
+ Logger* m_logger;
+ bool m_proceed;
+ base::MessageBuilder m_messageBuilder;
+ base::DispatchAction m_dispatchAction;
+ std::vector<std::string> m_loggerIds;
+ friend class el::Helpers;
+
+ void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true) {
+ if (lookup) {
+ m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically));
+ }
+ if (m_logger == nullptr) {
+ ELPP->acquireLock();
+ if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) {
+ // Somehow default logger has been unregistered. Not good! Register again
+ ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId));
+ }
+ ELPP->releaseLock(); // Need to unlock it for next writer
+ Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
+ << "Logger [" << loggerId << "] is not registered yet!";
+ m_proceed = false;
+ } else {
+ if (needLock) {
+ m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because
+ // m_proceed can be changed by lines below
+ }
+ if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) {
+ m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) :
+ LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel);
+ } else {
+ m_proceed = m_logger->enabled(m_level);
+ }
+ }
+ }
+
+ void processDispatch() {
+#if ELPP_LOGGING_ENABLED
+ if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) {
+ bool firstDispatched = false;
+ base::type::string_t logMessage;
+ std::size_t i = 0;
+ do {
+ if (m_proceed) {
+ if (firstDispatched) {
+ m_logger->stream() << logMessage;
+ } else {
+ firstDispatched = true;
+ if (m_loggerIds.size() > 1) {
+ logMessage = m_logger->stream().str();
+ }
+ }
+ triggerDispatch();
+ } else if (m_logger != nullptr) {
+ m_logger->stream().str(ELPP_LITERAL(""));
+ m_logger->releaseLock();
+ }
+ if (i + 1 < m_loggerIds.size()) {
+ initializeLogger(m_loggerIds.at(i + 1));
+ }
+ } while (++i < m_loggerIds.size());
+ } else {
+ if (m_proceed) {
+ triggerDispatch();
+ } else if (m_logger != nullptr) {
+ m_logger->stream().str(ELPP_LITERAL(""));
+ m_logger->releaseLock();
+ }
+ }
+#else
+ if (m_logger != nullptr) {
+ m_logger->stream().str(ELPP_LITERAL(""));
+ m_logger->releaseLock();
+ }
+#endif // ELPP_LOGGING_ENABLED
+ }
+
+ void triggerDispatch(void) {
+ if (m_proceed) {
+ base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel,
+ m_logger), m_dispatchAction).dispatch();
+ }
+ if (m_logger != nullptr) {
+ m_logger->stream().str(ELPP_LITERAL(""));
+ m_logger->releaseLock();
+ }
+ if (m_proceed && m_level == Level::Fatal
+ && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) {
+ base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
+ << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]";
+ std::stringstream reasonStream;
+ reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]"
+ << " If you wish to disable 'abort on fatal log' please use "
+ << "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)";
+ base::utils::abort(1, reasonStream.str());
+ }
+ m_proceed = false;
+ }
+};
+class PErrorWriter : public base::Writer {
+public:
+ PErrorWriter(Level level, const char* file, unsigned long int line,
+ const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog,
+ base::type::VerboseLevel verboseLevel = 0) :
+ base::Writer(level, file, line, func, dispatchAction, verboseLevel) {
+ }
+
+ virtual ~PErrorWriter(void) {
+ if (m_proceed) {
+#if ELPP_COMPILER_MSVC
+ char buff[256];
+ strerror_s(buff, 256, errno);
+ m_logger->stream() << ": " << buff << " [" << errno << "]";
+#else
+ m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]";
+#endif
+ }
+ }
+};
+} // namespace base
+// Logging from Logger class. Why this is here? Because we have Storage and Writer class available
+#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
+ template <typename T, typename... Args>
+ void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) {
+ base::MessageBuilder b;
+ b.initialize(this);
+ while (*s) {
+ if (*s == base::consts::kFormatSpecifierChar) {
+ if (*(s + 1) == base::consts::kFormatSpecifierChar) {
+ ++s;
+ } else {
+ if (*(s + 1) == base::consts::kFormatSpecifierCharValue) {
+ ++s;
+ b << value;
+ log_(level, vlevel, ++s, args...);
+ return;
+ }
+ }
+ }
+ b << *s++;
+ }
+ ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false);
+ }
+ template <typename T>
+ inline void Logger::log_(Level level, int vlevel, const T& log) {
+ if (level == Level::Verbose) {
+ if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) {
+ base::Writer(Level::Verbose, "FILE", 0, "FUNCTION",
+ base::DispatchAction::NormalLog, vlevel).construct(this, false) << log;
+ } else {
+ stream().str(ELPP_LITERAL(""));
+ }
+ } else {
+ base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log;
+ }
+ }
+ template <typename T, typename... Args>
+ void Logger::log(Level level, const char* s, const T& value, const Args&... args) {
+ base::threading::ScopedLock scopedLock(lock());
+ log_(level, 0, s, value, args...);
+ }
+ template <typename T>
+ inline void Logger::log(Level level, const T& log) {
+ base::threading::ScopedLock scopedLock(lock());
+ log_(level, 0, log);
+ }
+# if ELPP_VERBOSE_LOG
+ template <typename T, typename... Args>
+ inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) {
+ base::threading::ScopedLock scopedLock(lock());
+ log_(el::Level::Verbose, vlevel, s, value, args...);
+ }
+ template <typename T>
+ inline void Logger::verbose(int vlevel, const T& log) {
+ base::threading::ScopedLock scopedLock(lock());
+ log_(el::Level::Verbose, vlevel, log);
+ }
+# else
+ template <typename T, typename... Args>
+ inline void Logger::verbose(int, const char*, const T&, const Args&...) {
+ return;
+ }
+ template <typename T>
+ inline void Logger::verbose(int, const T&) {
+ return;
+ }
+# endif // ELPP_VERBOSE_LOG
+# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\
+ template <typename T, typename... Args>\
+ inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) {\
+ log(LOG_LEVEL, s, value, args...);\
+ }\
+ template <typename T>\
+ inline void Logger::FUNCTION_NAME(const T& value) {\
+ log(LOG_LEVEL, value);\
+ }
+# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\
+ template <typename T, typename... Args>\
+ inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) {\
+ return;\
+ }\
+ template <typename T>\
+ inline void Logger::FUNCTION_NAME(const T&) {\
+ return;\
+ }
+
+# if ELPP_INFO_LOG
+ LOGGER_LEVEL_WRITERS(info, Level::Info)
+# else
+ LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info)
+# endif // ELPP_INFO_LOG
+# if ELPP_DEBUG_LOG
+ LOGGER_LEVEL_WRITERS(debug, Level::Debug)
+# else
+ LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug)
+# endif // ELPP_DEBUG_LOG
+# if ELPP_WARNING_LOG
+ LOGGER_LEVEL_WRITERS(warn, Level::Warning)
+# else
+ LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning)
+# endif // ELPP_WARNING_LOG
+# if ELPP_ERROR_LOG
+ LOGGER_LEVEL_WRITERS(error, Level::Error)
+# else
+ LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error)
+# endif // ELPP_ERROR_LOG
+# if ELPP_FATAL_LOG
+ LOGGER_LEVEL_WRITERS(fatal, Level::Fatal)
+# else
+ LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal)
+# endif // ELPP_FATAL_LOG
+# if ELPP_TRACE_LOG
+ LOGGER_LEVEL_WRITERS(trace, Level::Trace)
+# else
+ LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace)
+# endif // ELPP_TRACE_LOG
+# undef LOGGER_LEVEL_WRITERS
+# undef LOGGER_LEVEL_WRITERS_DISABLED
+#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
+#if ELPP_COMPILER_MSVC
+# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs
+# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__))
+# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\
+ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#else
+# if ELPP_COMPILER_CLANG
+# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+# else
+# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+# endif // ELPP_COMPILER_CLANG
+#endif // ELPP_COMPILER_MSVC
+#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \
+ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \
+ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \
+ if (ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion)) \
+ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \
+ if (ELPP->validateAfterNCounter(__FILE__, __LINE__, n)) \
+ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \
+ if (ELPP->validateNTimesCounter(__FILE__, __LINE__, n)) \
+ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#undef ELPP_CURR_FILE_PERFORMANCE_LOGGER
+#if defined(ELPP_PERFORMANCE_LOGGER)
+# define ELPP_CURR_FILE_PERFORMANCE_LOGGER ELPP_PERFORMANCE_LOGGER
+#else
+# define ELPP_CURR_FILE_PERFORMANCE_LOGGER el::base::consts::kPerformanceLoggerId
+#endif
+class PerformanceTrackingData {
+public:
+ enum class DataType : base::type::EnumType {
+ Checkpoint = 1, Complete = 2
+ };
+ // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*)
+ explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr),
+ m_dataType(dataType), m_file(""), m_line(0), m_func("") {}
+ inline const std::string* blockName(void) const;
+ inline const struct timeval* startTime(void) const;
+ inline const struct timeval* endTime(void) const;
+ inline const struct timeval* lastCheckpointTime(void) const;
+ inline const base::PerformanceTracker* performanceTracker(void) const { return m_performanceTracker; }
+ inline PerformanceTrackingData::DataType dataType(void) const { return m_dataType; }
+ inline bool firstCheckpoint(void) const { return m_firstCheckpoint; }
+ inline std::string checkpointId(void) const { return m_checkpointId; }
+ inline const char* file(void) const { return m_file; }
+ inline unsigned long int line(void) const { return m_line; }
+ inline const char* func(void) const { return m_func; }
+ inline const base::type::string_t* formattedTimeTaken() const { return &m_formattedTimeTaken; }
+ inline const std::string& loggerId(void) const;
+private:
+ base::PerformanceTracker* m_performanceTracker;
+ base::type::string_t m_formattedTimeTaken;
+ PerformanceTrackingData::DataType m_dataType;
+ bool m_firstCheckpoint;
+ std::string m_checkpointId;
+ const char* m_file;
+ unsigned long int m_line;
+ const char* m_func;
+ inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) {
+ m_performanceTracker = performanceTracker;
+ m_firstCheckpoint = firstCheckpoint;
+ }
+
+ friend class el::base::PerformanceTracker;
+};
+namespace base {
+/// @brief Represents performanceTracker block of code that conditionally adds performance status to log
+/// either when goes outside the scope of when checkpoint() is called
+class PerformanceTracker : public base::threading::ThreadSafe, public Loggable {
+public:
+ PerformanceTracker(const std::string& blockName,
+ base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond,
+ const std::string& loggerId = std::string(ELPP_CURR_FILE_PERFORMANCE_LOGGER),
+ bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel) :
+ m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog),
+ m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) {
+#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+ // We store it locally so that if user happen to change configuration by the end of scope
+ // or before calling checkpoint, we still depend on state of configuraton at time of construction
+ el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false);
+ m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level);
+ if (m_enabled) {
+ base::utils::DateTime::gettimeofday(&m_startTime);
+ }
+#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+ }
+ /// @brief Copy constructor
+ PerformanceTracker(const PerformanceTracker& t) :
+ m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog),
+ m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled),
+ m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) {
+ }
+ virtual ~PerformanceTracker(void) {
+#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+ if (m_enabled) {
+ base::threading::ScopedLock scopedLock(lock());
+ if (m_scopedLog) {
+ base::utils::DateTime::gettimeofday(&m_endTime);
+ base::type::string_t formattedTime = getFormattedTimeTaken();
+ PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete);
+ data.init(this);
+ data.m_formattedTimeTaken = formattedTime;
+ PerformanceTrackingCallback* callback = nullptr;
+ for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h
+ : ELPP->m_performanceTrackingCallbacks) {
+ callback = h.second.get();
+ if (callback != nullptr && callback->enabled()) {
+ callback->acquireLock();
+ callback->handle(&data);
+ callback->releaseLock();
+ }
+ }
+ }
+ }
+#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING)
+ }
+ /// @brief A checkpoint for current performanceTracker block.
+ void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, unsigned long int line = __LINE__, const char* func = "") {
+#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+ if (m_enabled) {
+ base::threading::ScopedLock scopedLock(lock());
+ base::utils::DateTime::gettimeofday(&m_endTime);
+ base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL("");
+ PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint);
+ data.init(this);
+ data.m_checkpointId = id;
+ data.m_file = file;
+ data.m_line = line;
+ data.m_func = func;
+ data.m_formattedTimeTaken = formattedTime;
+ PerformanceTrackingCallback* callback = nullptr;
+ for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h
+ : ELPP->m_performanceTrackingCallbacks) {
+ callback = h.second.get();
+ if (callback != nullptr && callback->enabled()) {
+ callback->acquireLock();
+ callback->handle(&data);
+ callback->releaseLock();
+ }
+ }
+ base::utils::DateTime::gettimeofday(&m_lastCheckpointTime);
+ m_hasChecked = true;
+ m_lastCheckpointId = id;
+ }
+#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+ ELPP_UNUSED(id);
+ ELPP_UNUSED(file);
+ ELPP_UNUSED(line);
+ ELPP_UNUSED(func);
+ }
+ inline Level level(void) const { return m_level; }
+private:
+ std::string m_blockName;
+ base::TimestampUnit m_timestampUnit;
+ std::string m_loggerId;
+ bool m_scopedLog;
+ Level m_level;
+ bool m_hasChecked;
+ std::string m_lastCheckpointId;
+ bool m_enabled;
+ struct timeval m_startTime, m_endTime, m_lastCheckpointTime;
+
+ PerformanceTracker(void);
+
+ friend class el::PerformanceTrackingData;
+ friend class base::DefaultPerformanceTrackingCallback;
+
+ const inline base::type::string_t getFormattedTimeTaken() const {
+ return getFormattedTimeTaken(m_startTime);
+ }
+
+ const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const {
+ if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) {
+ base::type::stringstream_t ss;
+ ss << base::utils::DateTime::getTimeDifference(m_endTime,
+ startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast<base::type::EnumType>(m_timestampUnit)].unit;
+ return ss.str();
+ }
+ return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime,
+ startTime, m_timestampUnit), m_timestampUnit);
+ }
+
+ virtual inline void log(el::base::type::ostream_t& os) const {
+ os << getFormattedTimeTaken();
+ }
+};
+class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback {
+protected:
+ void handle(const PerformanceTrackingData* data) {
+ m_data = data;
+ base::type::stringstream_t ss;
+ if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) {
+ ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << *m_data->formattedTimeTaken() << ELPP_LITERAL("]");
+ } else {
+ ss << ELPP_LITERAL("Performance checkpoint");
+ if (!m_data->checkpointId().empty()) {
+ ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]");
+ }
+ ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << *m_data->performanceTracker();
+ if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) && m_data->performanceTracker()->m_hasChecked) {
+ ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from ");
+ if (m_data->performanceTracker()->m_lastCheckpointId.empty()) {
+ ss << ELPP_LITERAL("last checkpoint");
+ } else {
+ ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'");
+ }
+ ss << ELPP_LITERAL(")]");
+ } else {
+ ss << ELPP_LITERAL("]");
+ }
+ }
+ el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, m_data->loggerId().c_str()) << ss.str();
+ }
+private:
+ const PerformanceTrackingData* m_data;
+};
+} // namespace base
+inline const std::string* PerformanceTrackingData::blockName() const {
+ return const_cast<const std::string*>(&m_performanceTracker->m_blockName);
+}
+inline const struct timeval* PerformanceTrackingData::startTime() const {
+ return const_cast<const struct timeval*>(&m_performanceTracker->m_startTime);
+}
+inline const struct timeval* PerformanceTrackingData::endTime() const {
+ return const_cast<const struct timeval*>(&m_performanceTracker->m_endTime);
+}
+inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const {
+ return const_cast<const struct timeval*>(&m_performanceTracker->m_lastCheckpointTime);
+}
+inline const std::string& PerformanceTrackingData::loggerId(void) const { return m_performanceTracker->m_loggerId; }
+namespace base {
+/// @brief Contains some internal debugging tools like crash handler and stack tracer
+namespace debug {
+class StackTrace : base::NoCopy {
+public:
+ static const std::size_t kMaxStack = 64;
+ static const std::size_t kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew()
+ class StackTraceEntry {
+ public:
+ StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr) {
+ m_index = index;
+ m_location = std::string(loc);
+ m_demangled = std::string(demang);
+ m_hex = std::string(hex);
+ m_addr = std::string(addr);
+ }
+ StackTraceEntry(std::size_t index, char* loc) {
+ m_index = index;
+ m_location = std::string(loc);
+ }
+ std::size_t m_index;
+ std::string m_location;
+ std::string m_demangled;
+ std::string m_hex;
+ std::string m_addr;
+ friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si) {
+ ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled
+ << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr;
+ return ss;
+ }
+
+ private:
+ StackTraceEntry(void);
+ };
+
+ StackTrace(void) {
+ generateNew();
+ }
+
+ virtual ~StackTrace(void) {
+ }
+
+ inline std::vector<StackTraceEntry>& getLatestStack(void) {
+ return m_stack;
+ }
+
+ friend inline std::ostream& operator<<(std::ostream& os, const StackTrace& st) {
+ std::vector<StackTraceEntry>::const_iterator it = st.m_stack.begin();
+ while (it != st.m_stack.end()) {
+ os << " " << *it++ << "\n";
+ }
+ return os;
+ }
+
+private:
+ std::vector<StackTraceEntry> m_stack;
+
+ void generateNew(void) {
+#if ELPP_STACKTRACE
+ m_stack.clear();
+ void* stack[kMaxStack];
+ std::size_t size = backtrace(stack, kMaxStack);
+ char** strings = backtrace_symbols(stack, size);
+ if (size > kStackStart) { // Skip StackTrace c'tor and generateNew
+ for (std::size_t i = kStackStart; i < size; ++i) {
+ char* mangName = nullptr;
+ char* hex = nullptr;
+ char* addr = nullptr;
+ for (char* c = strings[i]; *c; ++c) {
+ switch (*c) {
+ case '(':
+ mangName = c;
+ break;
+ case '+':
+ hex = c;
+ break;
+ case ')':
+ addr = c;
+ break;
+ }
+ }
+ // Perform demangling if parsed properly
+ if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) {
+ *mangName++ = '\0';
+ *hex++ = '\0';
+ *addr++ = '\0';
+ int status = 0;
+ char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status);
+ // if demangling is successful, output the demangled function name
+ if (status == 0) {
+ // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html)
+ StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr);
+ m_stack.push_back(entry);
+ } else {
+ // Not successful - we will use mangled name
+ StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr);
+ m_stack.push_back(entry);
+ }
+ free(demangName);
+ } else {
+ StackTraceEntry entry(i - 1, strings[i]);
+ m_stack.push_back(entry);
+ }
+ }
+ }
+ free(strings);
+#else
+ ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler");
+#endif // ELPP_STACKTRACE
+ }
+};
+static std::string crashReason(int sig) {
+ std::stringstream ss;
+ bool foundReason = false;
+ for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) {
+ if (base::consts::kCrashSignals[i].numb == sig) {
+ ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal";
+ if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) {
+ ss << std::endl <<
+ " " << base::consts::kCrashSignals[i].brief << std::endl <<
+ " " << base::consts::kCrashSignals[i].detail;
+ }
+ foundReason = true;
+ }
+ }
+ if (!foundReason) {
+ ss << "Application has crashed due to unknown signal [" << sig << "]";
+ }
+ return ss.str();
+}
+/// @brief Logs reason of crash from sig
+static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) {
+ std::stringstream ss;
+ ss << "CRASH HANDLED; ";
+ ss << crashReason(sig);
+#if ELPP_STACKTRACE
+ if (stackTraceIfAvailable) {
+ ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace();
+ }
+#else
+ ELPP_UNUSED(stackTraceIfAvailable);
+#endif // ELPP_STACKTRACE
+ ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str();
+}
+static inline void crashAbort(int sig) {
+ base::utils::abort(sig);
+}
+/// @brief Default application crash handler
+///
+/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program.
+static inline void defaultCrashHandler(int sig) {
+ base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId);
+ base::debug::crashAbort(sig);
+}
+/// @brief Handles unexpected crashes
+class CrashHandler : base::NoCopy {
+public:
+ typedef void (*Handler)(int);
+
+ explicit CrashHandler(bool useDefault) {
+ if (useDefault) {
+ setHandler(defaultCrashHandler);
+ }
+ }
+ explicit CrashHandler(const Handler& cHandler) {
+ setHandler(cHandler);
+ }
+ void setHandler(const Handler& cHandler) {
+ m_handler = cHandler;
+#if defined(ELPP_HANDLE_SIGABRT)
+ int i = 0; // SIGABRT is at base::consts::kCrashSignals[0]
+#else
+ int i = 1;
+#endif // defined(ELPP_HANDLE_SIGABRT)
+ for (; i < base::consts::kCrashSignalsCount; ++i) {
+ m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler);
+ }
+ }
+
+private:
+ Handler m_handler;
+};
+} // namespace debug
+} // namespace base
+extern base::debug::CrashHandler elCrashHandler;
+#define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \
+ el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, const ClassType& ClassInstance)
+/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor
+class SysLogInitializer {
+public:
+ SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) {
+#if defined(ELPP_SYSLOG)
+ openlog(processIdent, options, facility);
+#else
+ ELPP_UNUSED(processIdent);
+ ELPP_UNUSED(options);
+ ELPP_UNUSED(facility);
+#endif // defined(ELPP_SYSLOG)
+ }
+ virtual ~SysLogInitializer(void) {
+#if defined(ELPP_SYSLOG)
+ closelog();
+#endif // defined(ELPP_SYSLOG)
+ }
+};
+#define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac)
+/// @brief Static helpers for developers
+class Helpers : base::StaticClass {
+public:
+ /// @brief Shares logging repository (base::Storage)
+ static inline void setStorage(base::type::StoragePointer storage) {
+ ELPP = storage;
+ }
+ /// @return Main storage repository
+ static inline base::type::StoragePointer storage() {
+ return ELPP;
+ }
+ /// @brief Sets application arguments and figures out whats active for logging and whats not.
+ static inline void setArgs(int argc, char** argv) {
+ ELPP->setApplicationArguments(argc, argv);
+ }
+ /// @copydoc setArgs(int argc, char** argv)
+ static inline void setArgs(int argc, const char** argv) {
+ ELPP->setApplicationArguments(argc, const_cast<char**>(argv));
+ }
+ /// @brief Overrides default crash handler and installs custom handler.
+ /// @param crashHandler A functor with no return type that takes single int argument.
+ /// Handler is a typedef with specification: void (*Handler)(int)
+ static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) {
+ el::elCrashHandler.setHandler(crashHandler);
+ }
+ /// @brief Abort due to crash with signal in parameter
+ /// @param sig Crash signal
+ static inline void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0) {
+ std::stringstream ss;
+ ss << base::debug::crashReason(sig).c_str();
+ ss << " - [Called el::Helpers::crashAbort(" << sig << ")]";
+ if (sourceFile != nullptr && strlen(sourceFile) > 0) {
+ ss << " - Source: " << sourceFile;
+ if (line > 0)
+ ss << ":" << line;
+ else
+ ss << " (line number not specified)";
+ }
+ base::utils::abort(sig, ss.str());
+ }
+ /// @brief Logs reason of crash as per sig
+ /// @param sig Crash signal
+ /// @param stackTraceIfAvailable Includes stack trace if available
+ /// @param level Logging level
+ /// @param logger Logger to use for logging
+ static inline void logCrashReason(int sig, bool stackTraceIfAvailable = false,
+ Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId) {
+ el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger);
+ }
+ /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out
+ /// (can be useful for backing up)
+ static inline void installPreRollOutCallback(const PreRollOutCallback& callback) {
+ ELPP->setPreRollOutCallback(callback);
+ }
+ /// @brief Uninstalls pre rollout callback
+ static inline void uninstallPreRollOutCallback(void) {
+ ELPP->unsetPreRollOutCallback();
+ }
+ /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched
+ template <typename T>
+ static inline bool installLogDispatchCallback(const std::string& id) {
+ return ELPP->installLogDispatchCallback<T>(id);
+ }
+ /// @brief Uninstalls log dispatch callback
+ template <typename T>
+ static inline void uninstallLogDispatchCallback(const std::string& id) {
+ ELPP->uninstallLogDispatchCallback<T>(id);
+ }
+ template <typename T>
+ static inline T* logDispatchCallback(const std::string& id) {
+ return ELPP->logDispatchCallback<T>(id);
+ }
+ /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished
+ template <typename T>
+ static inline bool installPerformanceTrackingCallback(const std::string& id) {
+ return ELPP->installPerformanceTrackingCallback<T>(id);
+ }
+ /// @brief Uninstalls post performance tracking handler
+ template <typename T>
+ static inline void uninstallPerformanceTrackingCallback(const std::string& id) {
+ ELPP->uninstallPerformanceTrackingCallback<T>(id);
+ }
+ template <typename T>
+ static inline T* performanceTrackingCallback(const std::string& id) {
+ return ELPP->performanceTrackingCallback<T>(id);
+ }
+ /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const
+ template <typename T>
+ static std::string convertTemplateToStdString(const T& templ) {
+ el::Logger* logger =
+ ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId);
+ if (logger == nullptr) {
+ return std::string();
+ }
+ base::MessageBuilder b;
+ b.initialize(logger);
+ logger->acquireLock();
+ b << templ;
+#if defined(ELPP_UNICODE)
+ std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end());
+#else
+ std::string s = logger->stream().str();
+#endif // defined(ELPP_UNICODE)
+ logger->stream().str(ELPP_LITERAL(""));
+ logger->releaseLock();
+ return s;
+ }
+ /// @brief Returns command line arguments (pointer) provided to easylogging++
+ static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) {
+ return ELPP->commandLineArgs();
+ }
+ /// @brief Installs user defined format specifier and handler
+ static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) {
+ ELPP->installCustomFormatSpecifier(customFormatSpecifier);
+ }
+ /// @brief Uninstalls user defined format specifier and handler
+ static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) {
+ return ELPP->uninstallCustomFormatSpecifier(formatSpecifier);
+ }
+ /// @brief Returns true if custom format specifier is installed
+ static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) {
+ return ELPP->hasCustomFormatSpecifier(formatSpecifier);
+ }
+ static inline void validateFileRolling(Logger* logger, Level level) {
+ if (logger == nullptr) return;
+ logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback());
+ }
+};
+/// @brief Static helpers to deal with loggers and their configurations
+class Loggers : base::StaticClass {
+public:
+ /// @brief Gets existing or registers new logger
+ static inline Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true) {
+ base::threading::ScopedLock scopedLock(ELPP->lock());
+ return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable);
+ }
+ /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister
+ /// loggers initialized / used by third-party libs.
+ static inline bool unregisterLogger(const std::string& identity) {
+ base::threading::ScopedLock scopedLock(ELPP->lock());
+ return ELPP->registeredLoggers()->remove(identity);
+ }
+ /// @brief Whether or not logger with id is registered
+ static inline bool hasLogger(const std::string& identity) {
+ base::threading::ScopedLock scopedLock(ELPP->lock());
+ return ELPP->registeredLoggers()->has(identity);
+ }
+ /// @brief Reconfigures specified logger with new configurations
+ static inline Logger* reconfigureLogger(Logger* logger, const Configurations& configurations) {
+ if (!logger) return nullptr;
+ logger->configure(configurations);
+ return logger;
+ }
+ /// @brief Reconfigures logger with new configurations after looking it up using identity
+ static inline Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations) {
+ return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations);
+ }
+ /// @brief Reconfigures logger's single configuration
+ static inline Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType,
+ const std::string& value) {
+ Logger* logger = Loggers::getLogger(identity);
+ if (logger == nullptr) {
+ return nullptr;
+ }
+ logger->configurations()->set(Level::Global, configurationType, value);
+ logger->reconfigure();
+ return logger;
+ }
+ /// @brief Reconfigures all the existing loggers with new configurations
+ static inline void reconfigureAllLoggers(const Configurations& configurations) {
+ for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
+ it != ELPP->registeredLoggers()->end(); ++it) {
+ Loggers::reconfigureLogger(it->second, configurations);
+ }
+ }
+ /// @brief Reconfigures single configuration for all the loggers
+ static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) {
+ reconfigureAllLoggers(Level::Global, configurationType, value);
+ }
+ /// @brief Reconfigures single configuration for all the loggers for specified level
+ static inline void reconfigureAllLoggers(Level level, ConfigurationType configurationType,
+ const std::string& value) {
+ for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
+ it != ELPP->registeredLoggers()->end(); ++it) {
+ Logger* logger = it->second;
+ logger->configurations()->set(level, configurationType, value);
+ logger->reconfigure();
+ }
+ }
+ /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers
+ static inline void setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers = false) {
+ ELPP->registeredLoggers()->setDefaultConfigurations(configurations);
+ if (reconfigureExistingLoggers) {
+ Loggers::reconfigureAllLoggers(configurations);
+ }
+ }
+ /// @brief Returns current default
+ static inline const Configurations* defaultConfigurations(void) {
+ return ELPP->registeredLoggers()->defaultConfigurations();
+ }
+ /// @brief Returns log stream reference pointer if needed by user
+ static inline const base::LogStreamsReferenceMap* logStreamsReference(void) {
+ return ELPP->registeredLoggers()->logStreamsReference();
+ }
+ /// @brief Default typed configuration based on existing defaultConf
+ static base::TypedConfigurations defaultTypedConfigurations(void) {
+ return base::TypedConfigurations(
+ ELPP->registeredLoggers()->defaultConfigurations(),
+ ELPP->registeredLoggers()->logStreamsReference());
+ }
+ /// @brief Populates all logger IDs in current repository.
+ /// @param [out] targetList List of fill up.
+ static inline std::vector<std::string>* populateAllLoggerIds(std::vector<std::string>* targetList) {
+ targetList->clear();
+ for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin();
+ it != ELPP->registeredLoggers()->list().end(); ++it) {
+ targetList->push_back(it->first);
+ }
+ return targetList;
+ }
+ /// @brief Sets configurations from global configuration file.
+ static void configureFromGlobal(const char* globalConfigurationFilePath) {
+ std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in);
+ ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath
+ << "] for parsing.");
+ std::string line = std::string();
+ std::stringstream ss;
+ Logger* logger = nullptr;
+ auto configure = [&](void) {
+ ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str()
+ << "\n--------------");
+ Configurations c;
+ c.parseFromText(ss.str());
+ logger->configure(c);
+ };
+ while (gcfStream.good()) {
+ std::getline(gcfStream, line);
+ ELPP_INTERNAL_INFO(1, "Parsing line: " << line);
+ base::utils::Str::trim(line);
+ if (Configurations::Parser::isComment(line)) continue;
+ Configurations::Parser::ignoreComments(&line);
+ base::utils::Str::trim(line);
+ if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) {
+ if (!ss.str().empty() && logger != nullptr) {
+ configure();
+ }
+ ss.str(std::string(""));
+ line = line.substr(2);
+ base::utils::Str::trim(line);
+ if (line.size() > 1) {
+ ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'");
+ logger = getLogger(line);
+ }
+ } else {
+ ss << line << "\n";
+ }
+ }
+ if (!ss.str().empty() && logger != nullptr) {
+ configure();
+ }
+ }
+ /// @brief Configures loggers using command line arg. Ensure you have already set command line args,
+ /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger.
+ /// If true is returned that does not mean it has been configured successfully, it only means that it
+ /// has attempeted to configure logger using configuration file provided in argument
+ static inline bool configureFromArg(const char* argKey) {
+#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
+ ELPP_UNUSED(argKey);
+#else
+ if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) {
+ return false;
+ }
+ configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey));
+#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
+ return true;
+ }
+ /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered
+ static inline void flushAll(void) {
+ ELPP->registeredLoggers()->flushAll();
+ }
+ /// @brief Adds logging flag used internally.
+ static inline void addFlag(LoggingFlag flag) {
+ ELPP->addFlag(flag);
+ }
+ /// @brief Removes logging flag used internally.
+ static inline void removeFlag(LoggingFlag flag) {
+ ELPP->removeFlag(flag);
+ }
+ /// @brief Determines whether or not certain flag is active
+ static inline bool hasFlag(LoggingFlag flag) {
+ return ELPP->hasFlag(flag);
+ }
+ /// @brief Adds flag and removes it when scope goes out
+ class ScopedAddFlag {
+ public:
+ ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { Loggers::addFlag(m_flag); }
+ ~ScopedAddFlag(void) { Loggers::removeFlag(m_flag); }
+ private:
+ LoggingFlag m_flag;
+ };
+ /// @brief Removes flag and add it when scope goes out
+ class ScopedRemoveFlag {
+ public:
+ ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { Loggers::removeFlag(m_flag); }
+ ~ScopedRemoveFlag(void) { Loggers::addFlag(m_flag); }
+ private:
+ LoggingFlag m_flag;
+ };
+ /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging)
+ static inline void setLoggingLevel(Level level) {
+ ELPP->setLoggingLevel(level);
+ }
+ /// @brief Sets verbose level on the fly
+ static inline void setVerboseLevel(base::type::VerboseLevel level) {
+ ELPP->vRegistry()->setLevel(level);
+ }
+ /// @brief Gets current verbose level
+ static inline base::type::VerboseLevel verboseLevel(void) {
+ return ELPP->vRegistry()->level();
+ }
+ /// @brief Sets vmodules as specified (on the fly)
+ static inline void setVModules(const char* modules) {
+ if (ELPP->vRegistry()->vModulesEnabled()) {
+ ELPP->vRegistry()->setModules(modules);
+ }
+ }
+ /// @brief Clears vmodules
+ static inline void clearVModules(void) {
+ ELPP->vRegistry()->clearModules();
+ }
+};
+class VersionInfo : base::StaticClass {
+public:
+ /// @brief Current version number
+ static inline const std::string version(void) { return std::string("9.80"); }
+ /// @brief Release date of current version
+ static inline const std::string releaseDate(void) { return std::string("08-01-2015 0850hrs"); }
+};
+} // namespace el
+#undef VLOG_IS_ON
+/// @brief Determines whether verbose logging is on for specified level current file.
+#define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__))
+#undef TIMED_BLOCK
+#undef TIMED_SCOPE
+#undef TIMED_FUNC
+#undef ELPP_MIN_UNIT
+#if defined(ELPP_PERFORMANCE_MICROSECONDS)
+# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond
+#else
+# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond
+#endif // (defined(ELPP_PERFORMANCE_MICROSECONDS))
+/// @brief Performance tracked scope. Performance gets written when goes out of scope using
+/// 'performance' logger.
+///
+/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint();
+/// @see el::base::PerformanceTracker
+/// @see el::base::PerformanceTracker::checkpoint
+// Note: Do not surround this definition with null macro because of obj instance
+#define TIMED_SCOPE(obj, blockname) el::base::PerformanceTracker obj(blockname, ELPP_MIN_UNIT)
+#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::PerformanceTracker timer; } obj = { 0, \
+ el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT) }; obj.i < 1; ++obj.i)
+/// @brief Performance tracked function. Performance gets written when goes out of scope using
+/// 'performance' logger.
+///
+/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint();
+/// @see el::base::PerformanceTracker
+/// @see el::base::PerformanceTracker::checkpoint
+#define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC)
+#undef PERFORMANCE_CHECKPOINT
+#undef PERFORMANCE_CHECKPOINT_WITH_ID
+#define PERFORMANCE_CHECKPOINT(obj) obj.checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC)
+#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj.checkpoint(id, __FILE__, __LINE__, ELPP_FUNC)
+#undef ELPP_COUNTER
+#undef ELPP_COUNTER_POS
+/// @brief Gets hit counter for file/line
+#define ELPP_COUNTER (ELPP->hitCounters()->getCounter(__FILE__, __LINE__))
+/// @brief Gets hit counter position for file/line, -1 if not registered yet
+#define ELPP_COUNTER_POS (ELPP_COUNTER == nullptr ? -1 : ELPP_COUNTER->hitCounts())
+// Undef levels to support LOG(LEVEL)
+#undef INFO
+#undef WARNING
+#undef DEBUG
+#undef ERROR
+#undef FATAL
+#undef TRACE
+#undef VERBOSE
+// Undef existing
+#undef CINFO
+#undef CWARNING
+#undef CDEBUG
+#undef CFATAL
+#undef CERROR
+#undef CTRACE
+#undef CVERBOSE
+#undef CINFO_IF
+#undef CWARNING_IF
+#undef CDEBUG_IF
+#undef CERROR_IF
+#undef CFATAL_IF
+#undef CTRACE_IF
+#undef CVERBOSE_IF
+#undef CINFO_EVERY_N
+#undef CWARNING_EVERY_N
+#undef CDEBUG_EVERY_N
+#undef CERROR_EVERY_N
+#undef CFATAL_EVERY_N
+#undef CTRACE_EVERY_N
+#undef CVERBOSE_EVERY_N
+#undef CINFO_AFTER_N
+#undef CWARNING_AFTER_N
+#undef CDEBUG_AFTER_N
+#undef CERROR_AFTER_N
+#undef CFATAL_AFTER_N
+#undef CTRACE_AFTER_N
+#undef CVERBOSE_AFTER_N
+#undef CINFO_N_TIMES
+#undef CWARNING_N_TIMES
+#undef CDEBUG_N_TIMES
+#undef CERROR_N_TIMES
+#undef CFATAL_N_TIMES
+#undef CTRACE_N_TIMES
+#undef CVERBOSE_N_TIMES
+// Normal logs
+#if ELPP_INFO_LOG
+# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__)
+#else
+# define CINFO(writer, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__)
+#else
+# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__)
+#else
+# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__)
+#else
+# define CERROR(writer, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__)
+#else
+# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__)
+#else
+# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\
+ el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#else
+# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_VERBOSE_LOG
+// Conditional logs
+#if ELPP_INFO_LOG
+# define CINFO_IF(writer, condition_, dispatchAction, ...) \
+ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__)
+#else
+# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+# define CWARNING_IF(writer, condition_, dispatchAction, ...)\
+ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__)
+#else
+# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\
+ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__)
+#else
+# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+# define CERROR_IF(writer, condition_, dispatchAction, ...)\
+ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__)
+#else
+# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+# define CFATAL_IF(writer, condition_, dispatchAction, ...)\
+ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__)
+#else
+# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+# define CTRACE_IF(writer, condition_, dispatchAction, ...)\
+ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__)
+#else
+# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \
+ el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#else
+# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_VERBOSE_LOG
+// Occasional logs
+#if ELPP_INFO_LOG
+# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\
+ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__)
+#else
+# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\
+ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__)
+#else
+# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\
+ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__)
+#else
+# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\
+ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__)
+#else
+# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\
+ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__)
+#else
+# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\
+ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__)
+#else
+# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\
+ CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__)
+#else
+# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_VERBOSE_LOG
+// After N logs
+#if ELPP_INFO_LOG
+# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__)
+#else
+# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__)
+#else
+# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__)
+#else
+# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__)
+#else
+# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__)
+#else
+# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__)
+#else
+# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\
+ CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__)
+#else
+# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_VERBOSE_LOG
+// N Times logs
+#if ELPP_INFO_LOG
+# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__)
+#else
+# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__)
+#else
+# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__)
+#else
+# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__)
+#else
+# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__)
+#else
+# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\
+ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__)
+#else
+# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\
+ CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__)
+#else
+# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter()
+#endif // ELPP_VERBOSE_LOG
+//
+// Custom Loggers - Requires (level, dispatchAction, loggerId/s)
+//
+// undef existing
+#undef CLOG
+#undef CLOG_VERBOSE
+#undef CVLOG
+#undef CLOG_IF
+#undef CLOG_VERBOSE_IF
+#undef CVLOG_IF
+#undef CLOG_EVERY_N
+#undef CVLOG_EVERY_N
+#undef CLOG_AFTER_N
+#undef CVLOG_AFTER_N
+#undef CLOG_N_TIMES
+#undef CVLOG_N_TIMES
+// Normal logs
+#define CLOG(LEVEL, ...)\
+ C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG(vlevel, ...) CVERBOSE(el::base::Writer, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+// Conditional logs
+#define CLOG_IF(condition, LEVEL, ...)\
+ C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_IF(condition, vlevel, ...)\
+ CVERBOSE_IF(el::base::Writer, condition, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+// Hit counts based logs
+#define CLOG_EVERY_N(n, LEVEL, ...)\
+ C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_EVERY_N(n, vlevel, ...)\
+ CVERBOSE_EVERY_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CLOG_AFTER_N(n, LEVEL, ...)\
+ C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_AFTER_N(n, vlevel, ...)\
+ CVERBOSE_AFTER_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CLOG_N_TIMES(n, LEVEL, ...)\
+ C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_N_TIMES(n, vlevel, ...)\
+ CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+//
+// Default Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros
+//
+// undef existing
+#undef LOG
+#undef VLOG
+#undef LOG_IF
+#undef VLOG_IF
+#undef LOG_EVERY_N
+#undef VLOG_EVERY_N
+#undef LOG_AFTER_N
+#undef VLOG_AFTER_N
+#undef LOG_N_TIMES
+#undef VLOG_N_TIMES
+#undef ELPP_CURR_FILE_LOGGER_ID
+#if defined(ELPP_DEFAULT_LOGGER)
+# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER
+#else
+# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId
+#endif
+#undef ELPP_TRACE
+#define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID)
+// Normal logs
+#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG(vlevel) CVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Conditional logs
+#define LOG_IF(condition, LEVEL) CLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_IF(condition, vlevel) CVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Hit counts based logs
+#define LOG_EVERY_N(n, LEVEL) CLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_EVERY_N(n, vlevel) CVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+#define LOG_AFTER_N(n, LEVEL) CLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_AFTER_N(n, vlevel) CVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+#define LOG_N_TIMES(n, LEVEL) CLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_N_TIMES(n, vlevel) CVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Generic PLOG()
+#undef CPLOG
+#undef CPLOG_IF
+#undef PLOG
+#undef PLOG_IF
+#undef DCPLOG
+#undef DCPLOG_IF
+#undef DPLOG
+#undef DPLOG_IF
+#define CPLOG(LEVEL, ...)\
+ C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CPLOG_IF(condition, LEVEL, ...)\
+ C##LEVEL##_IF(el::base::PErrorWriter, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define DCPLOG(LEVEL, ...)\
+ if (ELPP_DEBUG_LOG) C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define DCPLOG_IF(condition, LEVEL, ...)\
+ C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define PLOG(LEVEL) CPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define PLOG_IF(condition, LEVEL) CPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DPLOG(LEVEL) DCPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DPLOG_IF(condition, LEVEL) DCPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+// Generic SYSLOG()
+#undef CSYSLOG
+#undef CSYSLOG_IF
+#undef CSYSLOG_EVERY_N
+#undef CSYSLOG_AFTER_N
+#undef CSYSLOG_N_TIMES
+#undef SYSLOG
+#undef SYSLOG_IF
+#undef SYSLOG_EVERY_N
+#undef SYSLOG_AFTER_N
+#undef SYSLOG_N_TIMES
+#undef DCSYSLOG
+#undef DCSYSLOG_IF
+#undef DCSYSLOG_EVERY_N
+#undef DCSYSLOG_AFTER_N
+#undef DCSYSLOG_N_TIMES
+#undef DSYSLOG
+#undef DSYSLOG_IF
+#undef DSYSLOG_EVERY_N
+#undef DSYSLOG_AFTER_N
+#undef DSYSLOG_N_TIMES
+#if defined(ELPP_SYSLOG)
+# define CSYSLOG(LEVEL, ...)\
+ C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__)
+# define CSYSLOG_IF(condition, LEVEL, ...)\
+ C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__)
+# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId)
+# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId)
+# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId)
+# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId)
+# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId)
+# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__)
+# define DCSYSLOG_IF(condition, LEVEL, ...)\
+ C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__)
+# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\
+ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\
+ if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\
+ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId)
+# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId)
+# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId)
+# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId)
+# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId)
+#else
+# define CSYSLOG(LEVEL, ...) el::base::NullWriter()
+# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter()
+# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter()
+# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter()
+# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter()
+# define SYSLOG(LEVEL) el::base::NullWriter()
+# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter()
+# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter()
+# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter()
+# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter()
+# define DCSYSLOG(LEVEL, ...) el::base::NullWriter()
+# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter()
+# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter()
+# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter()
+# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter()
+# define DSYSLOG(LEVEL) el::base::NullWriter()
+# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter()
+# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter()
+# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter()
+# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter()
+#endif // defined(ELPP_SYSLOG)
+//
+// Custom Debug Only Loggers - Requires (level, loggerId/s)
+//
+// undef existing
+#undef DCLOG
+#undef DCVLOG
+#undef DCLOG_IF
+#undef DCVLOG_IF
+#undef DCLOG_EVERY_N
+#undef DCVLOG_EVERY_N
+#undef DCLOG_AFTER_N
+#undef DCVLOG_AFTER_N
+#undef DCLOG_N_TIMES
+#undef DCVLOG_N_TIMES
+// Normal logs
+#define DCLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG(LEVEL, __VA_ARGS__)
+#define DCLOG_VERBOSE(vlevel, ...) if (ELPP_DEBUG_LOG) CLOG_VERBOSE(vlevel, __VA_ARGS__)
+#define DCVLOG(vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG(vlevel, __VA_ARGS__)
+// Conditional logs
+#define DCLOG_IF(condition, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_IF(condition, LEVEL, __VA_ARGS__)
+#define DCVLOG_IF(condition, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_IF(condition, vlevel, __VA_ARGS__)
+// Hit counts based logs
+#define DCLOG_EVERY_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_EVERY_N(n, LEVEL, __VA_ARGS__)
+#define DCVLOG_EVERY_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_EVERY_N(n, vlevel, __VA_ARGS__)
+#define DCLOG_AFTER_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_AFTER_N(n, LEVEL, __VA_ARGS__)
+#define DCVLOG_AFTER_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_AFTER_N(n, vlevel, __VA_ARGS__)
+#define DCLOG_N_TIMES(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_N_TIMES(n, LEVEL, __VA_ARGS__)
+#define DCVLOG_N_TIMES(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_N_TIMES(n, vlevel, __VA_ARGS__)
+//
+// Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros
+//
+// undef existing
+#undef DLOG
+#undef DVLOG
+#undef DLOG_IF
+#undef DVLOG_IF
+#undef DLOG_EVERY_N
+#undef DVLOG_EVERY_N
+#undef DLOG_AFTER_N
+#undef DVLOG_AFTER_N
+#undef DLOG_N_TIMES
+#undef DVLOG_N_TIMES
+// Normal logs
+#define DLOG(LEVEL) DCLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG(vlevel) DCVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Conditional logs
+#define DLOG_IF(condition, LEVEL) DCLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG_IF(condition, vlevel) DCVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Hit counts based logs
+#define DLOG_EVERY_N(n, LEVEL) DCLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG_EVERY_N(n, vlevel) DCVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+#define DLOG_AFTER_N(n, LEVEL) DCLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+#define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Check macros
+#undef CCHECK
+#undef CPCHECK
+#undef CCHECK_EQ
+#undef CCHECK_NE
+#undef CCHECK_LT
+#undef CCHECK_GT
+#undef CCHECK_LE
+#undef CCHECK_GE
+#undef CCHECK_BOUNDS
+#undef CCHECK_NOTNULL
+#undef CCHECK_STRCASEEQ
+#undef CCHECK_STRCASENE
+#undef CHECK
+#undef PCHECK
+#undef CHECK_EQ
+#undef CHECK_NE
+#undef CHECK_LT
+#undef CHECK_GT
+#undef CHECK_LE
+#undef CHECK_GE
+#undef CHECK_BOUNDS
+#undef CHECK_NOTNULL
+#undef CHECK_STRCASEEQ
+#undef CHECK_STRCASENE
+#define CCHECK(condition, ...) CLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] "
+#define CPCHECK(condition, ...) CPLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] "
+#define CHECK(condition) CCHECK(condition, ELPP_CURR_FILE_LOGGER_ID)
+#define PCHECK(condition) CPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID)
+#define CCHECK_EQ(a, b, ...) CCHECK(a == b, __VA_ARGS__)
+#define CCHECK_NE(a, b, ...) CCHECK(a != b, __VA_ARGS__)
+#define CCHECK_LT(a, b, ...) CCHECK(a < b, __VA_ARGS__)
+#define CCHECK_GT(a, b, ...) CCHECK(a > b, __VA_ARGS__)
+#define CCHECK_LE(a, b, ...) CCHECK(a <= b, __VA_ARGS__)
+#define CCHECK_GE(a, b, ...) CCHECK(a >= b, __VA_ARGS__)
+#define CCHECK_BOUNDS(val, min, max, ...) CCHECK(val >= min && val <= max, __VA_ARGS__)
+#define CHECK_EQ(a, b) CCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_NE(a, b) CCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_LT(a, b) CCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_GT(a, b) CCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_LE(a, b) CCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_GE(a, b) CCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_BOUNDS(val, min, max) CCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID)
+namespace el {
+namespace base {
+namespace utils {
+template <typename T>
+static T* checkNotNull(T* ptr, const char* name, const char* loggers, ...) {
+ CLOG_IF(ptr == nullptr, FATAL, loggers) << "Check failed: [" << name << " != nullptr]";
+ return ptr;
+}
+} // namespace utils
+} // namespace base
+} // namespace el
+#define CCHECK_NOTNULL(ptr, ...) el::base::utils::checkNotNull(ptr, #ptr, __VA_ARGS__)
+#define CCHECK_STREQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \
+ << "Check failed: [" << #str1 << " == " << #str2 << "] "
+#define CCHECK_STRNE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \
+ << "Check failed: [" << #str1 << " != " << #str2 << "] "
+#define CCHECK_STRCASEEQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \
+ << "Check failed: [" << #str1 << " == " << #str2 << "] "
+#define CCHECK_STRCASENE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \
+ << "Check failed: [" << #str1 << " != " << #str2 << "] "
+#define CHECK_NOTNULL(ptr) CCHECK_NOTNULL(ptr, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STREQ(str1, str2) CCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STRNE(str1, str2) CCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STRCASEEQ(str1, str2) CCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STRCASENE(str1, str2) CCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#undef DCCHECK
+#undef DCCHECK_EQ
+#undef DCCHECK_NE
+#undef DCCHECK_LT
+#undef DCCHECK_GT
+#undef DCCHECK_LE
+#undef DCCHECK_GE
+#undef DCCHECK_BOUNDS
+#undef DCCHECK_NOTNULL
+#undef DCCHECK_STRCASEEQ
+#undef DCCHECK_STRCASENE
+#undef DCPCHECK
+#undef DCHECK
+#undef DCHECK_EQ
+#undef DCHECK_NE
+#undef DCHECK_LT
+#undef DCHECK_GT
+#undef DCHECK_LE
+#undef DCHECK_GE
+#undef DCHECK_BOUNDS_
+#undef DCHECK_NOTNULL
+#undef DCHECK_STRCASEEQ
+#undef DCHECK_STRCASENE
+#undef DPCHECK
+#define DCCHECK(condition, ...) if (ELPP_DEBUG_LOG) CCHECK(condition, __VA_ARGS__)
+#define DCCHECK_EQ(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_EQ(a, b, __VA_ARGS__)
+#define DCCHECK_NE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_NE(a, b, __VA_ARGS__)
+#define DCCHECK_LT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LT(a, b, __VA_ARGS__)
+#define DCCHECK_GT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GT(a, b, __VA_ARGS__)
+#define DCCHECK_LE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LE(a, b, __VA_ARGS__)
+#define DCCHECK_GE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GE(a, b, __VA_ARGS__)
+#define DCCHECK_BOUNDS(val, min, max, ...) if (ELPP_DEBUG_LOG) CCHECK_BOUNDS(val, min, max, __VA_ARGS__)
+#define DCCHECK_NOTNULL(ptr, ...) if (ELPP_DEBUG_LOG) CCHECK_NOTNULL(ptr, __VA_ARGS__)
+#define DCCHECK_STREQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STREQ(str1, str2, __VA_ARGS__)
+#define DCCHECK_STRNE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRNE(str1, str2, __VA_ARGS__)
+#define DCCHECK_STRCASEEQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASEEQ(str1, str2, __VA_ARGS__)
+#define DCCHECK_STRCASENE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASENE(str1, str2, __VA_ARGS__)
+#define DCPCHECK(condition, ...) if (ELPP_DEBUG_LOG) CPCHECK(condition, __VA_ARGS__)
+#define DCHECK(condition) DCCHECK(condition, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_EQ(a, b) DCCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_NE(a, b) DCCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_LT(a, b) DCCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_GT(a, b) DCCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_LE(a, b) DCCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_GE(a, b) DCCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_BOUNDS(val, min, max) DCCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_NOTNULL(ptr) DCCHECK_NOTNULL(ptr, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_STREQ(str1, str2) DCCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_STRNE(str1, str2) DCCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID)
+#if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING)
+# define ELPP_USE_DEF_CRASH_HANDLER false
+#else
+# define ELPP_USE_DEF_CRASH_HANDLER true
+#endif // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING)
+#define ELPP_CRASH_HANDLER_INIT
+#define ELPP_INIT_EASYLOGGINGPP(val)\
+ ELPP_INITI_BASIC_DECLR\
+ namespace el {\
+ namespace base {\
+ el::base::type::StoragePointer elStorage(val);\
+ }\
+ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\
+ }
+
+#if ELPP_ASYNC_LOGGING
+# define INITIALIZE_EASYLOGGINGPP\
+ ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\
+ new el::base::AsyncDispatchWorker()))\
+
+#else
+# define INITIALIZE_EASYLOGGINGPP\
+ ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder())))
+#endif // ELPP_ASYNC_LOGGING
+#define INITIALIZE_NULL_EASYLOGGINGPP\
+ ELPP_INITI_BASIC_DECLR\
+ namespace el {\
+ namespace base {\
+ el::base::type::StoragePointer elStorage;\
+ }\
+ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\
+ }
+// NOTE: no ELPP_INITI_BASIC_DECLR when sharing - causes double free corruption on external symbols
+#define SHARE_EASYLOGGINGPP(initializedStorage)\
+ namespace el {\
+ namespace base {\
+ el::base::type::StoragePointer elStorage(initializedStorage);\
+ }\
+ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\
+ }
+
+#if defined(ELPP_UNICODE)
+# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale(""))
+#else
+# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv)
+#endif // defined(ELPP_UNICODE)
+#endif // EASYLOGGINGPP_H
diff --git a/src/filterm4.py b/src/filterm4.py
new file mode 100755
index 0000000..eb5d9ac
--- /dev/null
+++ b/src/filterm4.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+
+# Filters for unique, highest scoring subread query/target pairs from an m4
+# file. Helps get rid of chimeras, at the cost of some yield.
+
+import sys
+from collections import namedtuple
+
+M4Record = namedtuple('M4Record', ('qname tname score pctsimilarity qstrand '
+ 'qstart qend qseqlength tstrand tstart '
+ 'tend tseqlength mapqv'))
+
+
+class Count(object):
+ """Tracks record count for original and filtered"""
+ def __init__(self):
+ self.orig = 0
+ self.filt = 0
+
+ def __repr__(self):
+ return "Record count: original=%i, filtered=%i\n" % \
+ (self.orig, self.filt)
+
+
+def printUniq(qgroup, count):
+ top = dict()
+ for q in qgroup:
+ m = M4Record._make(q.split())
+ k = "%s%s" % (m.qname, m.tname)
+ if k in top:
+ n = M4Record._make(top[k].split())
+ if int(m.score) < int(n.score):
+ top[k] = q
+ else:
+ top[k] = q
+
+ for r in top.values():
+ count.filt += 1
+ print r,
+
+ qgroup[:] = []
+
+
+def main():
+ m4file = sys.argv[1]
+ m4Hndl = open(m4file)
+ qgroup = []
+ curr = ''
+ count = Count()
+ for rec in m4Hndl:
+ count.orig += 1
+ m = M4Record._make(rec.split())
+ if curr != m.qname:
+ printUniq(qgroup, count)
+ qgroup.append(rec)
+ curr = m.qname
+ else:
+ qgroup.append(rec)
+
+ printUniq(qgroup, count)
+ sys.stderr.write(str(count))
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/src/m4topre.py b/src/m4topre.py
new file mode 100755
index 0000000..ad0f983
--- /dev/null
+++ b/src/m4topre.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+"""Super-simple converter from blasr m4 alignments to pbdagcon 'pre'
+alignments. For use in the pre-assembler dagcon workflow.
+"""
+
+import sys
+import heapq
+import string # pylint: disable=W0402
+from itertools import ifilter
+from collections import namedtuple, defaultdict
+import numpy as np
+from pbcore.io.FastaIO import FastaReader
+
+# qname tname score pctsimilarity qstrand qstart qend qseqlength tstrand tstart
+# ... tend tseqlength mapqv
+#
+# store only fields we need
+__m4fields__ = [0, 1, 2, 5, 6, 8, 9, 10, 11]
+M4RECORD = namedtuple(
+ 'M4RECORD', 'qname tname score qstart qend tstrand tstart tend tseqlength')
+
+__tuplfy__ = M4RECORD._make # pylint: disable=W0212
+
+# dna compliment
+__rc__ = string.maketrans('actgACTG', 'tgacTGAC')
+
+
+def parse_m4(rec):
+ """Parse in the m4 file, returning a list of records"""
+ return [y for (x, y) in enumerate(rec.split()) if x in __m4fields__]
+
+
+def rating(rec):
+ """Rates the alignment for by length and score (revisit at some point)"""
+ score = -int(rec.score)
+ alen = int(rec.tend) - int(rec.tstart)
+ return score + alen
+
+
+def schwartzian(rec):
+ """Provides a schwartzian transform for the given record, used for
+ sorting
+ """
+ flds = rec.split()
+ return (flds[1], float(flds[2]), rec)
+
+
+def sort_targ_score(recs):
+ """Sorts the list in place by target id (string), then score (float)"""
+ recs[:] = [schwartzian(x) for x in recs]
+ recs.sort()
+ recs[:] = [rec for (target, score, rec) in recs] # pylint: disable=W0612
+
+
+def rescore(recs):
+ """Rescore alignments using coverage based statistics"""
+ prev = ""
+ cov = np.zeros(1)
+ for idx, rec in enumerate(recs):
+ fields = rec.split()
+ rec = __tuplfy__(fields)
+ if rec.tname != prev:
+ prev = rec.tname
+ cov = np.zeros(int(rec.tseqlength), dtype=np.float16)
+
+ if rec.tstrand:
+ start = int(rec.tseqlength) - int(rec.tend)
+ end = int(rec.tseqlength) - int(rec.tstart)
+ else:
+ start = int(rec.tstart)
+ end = int(rec.tend)
+
+ cov[start:end] += 1
+ score = np.sum(1/cov[start:end])
+ fields[2] = str(-score)
+ recs[idx] = " ".join(fields)
+
+
+def bestn_true(recstr, myq):
+ """Checks if the record falls inside bestn (used when blasr is chunked)"""
+ rec = __tuplfy__(recstr.split())
+ rate = rating(rec)
+ return rate in myq[rec.qname[32:]].top
+
+
+class AlnLimiter(object): # pylint: disable=R0903
+ """Functor that returns alignments until some count is reached. Alignments
+ should be sorted.
+ """
+ def __init__(self, limit=76):
+ self.count = 0
+ self.target = ''
+ self.limit = limit
+
+ def __call__(self, rec):
+ target = rec.split()[1]
+ if target != self.target:
+ self.count = 0
+ self.target = target
+ self.count += 1
+ return self.count < self.limit
+
+
+class TopAlignments(object): # pylint: disable=R0903
+ """Tracks the top alignments for a given query, used for bestn calc"""
+ bestn = 10
+
+ def __init__(self):
+ self.top = [0] * TopAlignments.bestn
+
+ def __call__(self):
+ return # noop
+
+ def add(self, aln):
+ """Adds an alignment to a bounded list, kicking out another if
+ necessary
+ """
+ heapq.heappushpop(self.top, aln)
+
+
+def main(): # pylint: disable=R0914
+ """Drives the program"""
+ mym4 = sys.argv[1]
+ allm4 = sys.argv[2]
+ reads = sys.argv[3]
+ TopAlignments.bestn = int(sys.argv[4])
+
+ # tracks bestn
+ my_queries = defaultdict(TopAlignments)
+ my_m4recs = []
+
+ # load my m4 chunk
+ m4h = open(mym4)
+ rec_add = my_m4recs.append
+ for line in m4h:
+ flds = parse_m4(line)
+ rec = __tuplfy__(flds)
+ rate = rating(rec)
+ my_queries[rec.qname[32:]].add(rate)
+ rec_add(' '.join(flds))
+
+ m4h.close()
+
+ # if we're chunked locate relevant alignments
+ if mym4 != allm4:
+ # assuming fofn here
+ m4files = [x.rstrip() for x in open(allm4) if x.rstrip() != mym4]
+ for m4f in m4files:
+ m4h = open(m4f)
+ for recstr in m4h:
+ rec = __tuplfy__(parse_m4(recstr))
+ if rec.qname[32:] in my_queries:
+ rate = rating(rec)
+ my_queries[rec.qname[32:]].add(rate)
+ m4h.close()
+
+ # remove alignments that fall outside of bestn
+ my_m4recs[:] = [x for x in my_m4recs if bestn_true(x, my_queries)]
+
+ # sort by target name/score
+ sort_targ_score(my_m4recs)
+
+ # rescore based on coverage
+ rescore(my_m4recs)
+
+ # sort one more time be new score
+ sort_targ_score(my_m4recs)
+
+ # take a max number of alignments for each target
+ limiter = AlnLimiter()
+ my_m4recs[:] = [x for x in ifilter(limiter, my_m4recs)]
+
+ # load only related sequences
+ seqs = {}
+ frea = FastaReader(reads)
+ for fent in frea:
+ if fent.name[32:] in my_queries:
+ seqs[fent.name] = fent.sequence
+
+ # may or may not help
+ del my_queries
+
+ # generate pre-alignments
+ for recstr in my_m4recs:
+ rec = __tuplfy__(recstr.split())
+
+ # Bug 24538, rare case missing self hit
+ if rec.tname not in seqs:
+ msg = "Warning: skipping query %s target %s\n"
+ sys.stderr.write(msg % (rec.qname, rec.tname))
+ continue
+
+ qst = int(rec.qstart)
+ qnd = int(rec.qend)
+ qseq = seqs[rec.qname][qst:qnd]
+ strand = '-' if rec.tstrand == '1' else '+'
+ tst = int(rec.tstart)
+ tnd = int(rec.tend)
+ if strand == '+':
+ tseq = seqs[rec.tname][tst:tnd]
+ else:
+ tseq = seqs[rec.tname].translate(__rc__)[::-1][tst:tnd]
+
+ print ' '.join([rec.qname, rec.tname, strand,
+ rec.tseqlength, str(tst), str(tnd), qseq, tseq])
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/src/q-sense.py b/src/q-sense.py
new file mode 100755
index 0000000..72e51f1
--- /dev/null
+++ b/src/q-sense.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+
+#################################################################################$$
+# Copyright (c) 2011-2013, Pacific Biosciences of California, Inc.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted (subject to the limitations in the
+# disclaimer below) 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 Pacific Biosciences nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+# GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+# BIOSCIENCES AND ITS 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 PACIFIC BIOSCIENCES OR ITS
+# 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.
+#################################################################################$$
+
+
+import sys
+import os
+import logging
+import pkg_resources
+
+from pbcore.util.ToolRunner import PBMultiToolRunner
+from pbcore.io import FastaReader
+from pbtools.pbdagcon.q_sense import *
+
+try:
+ __p4revision__ = "$Revision: #15 $"
+ __p4change__ = "$Change: 115421 $"
+ revNum = int(__p4revision__.strip("$").split(" ")[1].strip("#"))
+ changeNum = int(__p4change__.strip("$").split(":")[-1])
+ __version__ = "%s-r%d-c%d" % ( pkg_resources.require("pbtools.pbdagcon")[0].version, revNum, changeNum )
+except:
+ __version__ = "pbtools.pbdagcon-github"
+
+
+class Consensus(PBMultiToolRunner):
+
+ def __init__(self):
+ desc = ["Making consensus sequence from a group reads storing in a fasta file. "
+ "All of the reads are expected to cover most of the target templates that gets sequenced. "
+ "If the reads have broad read length distribution and not all of them cover the same region of "
+ "a template, this code won't generate correct result. "
+ "This code is designed for getting consensus up to the length of reads (~10k). "
+ "It is not optimized for getting consensus for larger templates."]
+ super(Consensus, self).__init__('\n'.join(desc))
+
+ subparsers = self.subParsers
+
+ desc = ['using self-self alignment for getting the seed sequence for constructing consensus']
+ parser_d = subparsers.add_parser('d', help = "generate consensus using the alignments between the input sequneces to find seed sequence",
+ description = "\n".join(desc))
+ parser_d.add_argument('input', metavar = 'input.fasta',
+ help = 'an input fasta file')
+
+ desc = ['using a reference file as seed for consensus']
+ parser_r = subparsers.add_parser('r', help = "using a reference fasta as the seed sequence",
+ description = "\n".join(desc))
+ parser_r.add_argument('input', metavar = 'input.fasta',
+ help = 'an input fasta file')
+ parser_r.add_argument('ref', metavar = 'ref.fasta',
+ help = 'a reference fasta file')
+
+ for subp in (parser_r, parser_d):
+ subp.add_argument('-o', '--output', metavar = 'file-name', dest = 'out_file_name', default = "g_consensus",
+ help = 'consensus output filename')
+ subp.add_argument('-d', '--output_dir', metavar = 'directory-name', dest = 'out_dir_name', default = "./",
+ help = 'consensus output working directory')
+ subp.add_argument('--cname', metavar = 'consensus-seq-name', dest = 'consensus_seq_name', default = "consensus",
+ help = 'consensus sequence name')
+ subp.add_argument('--enable_hp_correction', action='store_true', default = False, dest="enable_hp_corr",
+ help = 'enable aggressive homopolymer missing errot detection and correction')
+ subp.add_argument('--mark_lower_case', action='store_true', default = False, dest="mark_lower_case",
+ help = 'mark low quality consensus base with lower case letter')
+ subp.add_argument('--hp_correction_th', dest = 'entropy_th', default = 0.65,
+ help = 'homopolymer missing correction entropy threshold')
+ subp.add_argument('--n_iter', default = 4, dest = 'niter',
+ help = 'number of iteration of consensus correction')
+ subp.add_argument('--min_cov', default = 8, dest = 'min_cov',
+ help = 'minimum coverage for generate consensus')
+ subp.add_argument('--max_cov', default = 60, dest = 'max_cov',
+ help = 'maximum coverage for generate consensus')
+ subp.add_argument('--max_n_reads', default = 150, dest = 'max_num_reads',
+ help = 'the maximum number of reads used for consensus')
+ subp.add_argument('--nproc', default = 4, dest = 'nproc',
+ help = 'the number cpu core used by blasr, default to 4 cores')
+ subp.add_argument('--dump_dag_info', action='store_true', default = False, dest="dump_dag_info",
+ help = 'dump the information of the dag, including a pileup view of the alignments')
+
+ def getVersion(self):
+ return __version__
+
+ def denovoConsensus(self):
+ prefix = self.args.out_file_name.split(".")
+ input_fasta_name = self.args.input
+
+ rid,s =best_template_by_blasr(input_fasta_name)
+ if len(prefix) > 1:
+ prefix = ".".join(prefix[:-1])
+ else:
+ prefix = ".".join(prefix)
+ full_prefix = os.path.join(self.args.out_dir_name, prefix)
+ with open("%s_ref.fa" % full_prefix, "w") as f:
+ print >>f ,">%s_ref" % self.args.consensus_seq_name
+ print >>f, s
+ hp_corr = True if self.args.enable_hp_corr else False
+ mark_lower_case = True if self.args.mark_lower_case else False
+ generate_consensus(input_fasta_name, "%s_ref.fa" % full_prefix, full_prefix, self.args.consensus_seq_name,
+ hp_corr, int(self.args.niter),
+ int(self.args.max_num_reads),
+ float(self.args.entropy_th),
+ self.args.dump_dag_info,
+ int(self.args.min_cov),
+ int(self.args.max_cov),
+ mark_lower_case,
+ int(self.args.nproc))
+
+ def refConsensus(self):
+ input_fasta_name = self.args.input
+ prefix = self.args.out_file_name.split(".")
+ if len(prefix) > 1:
+ prefix = ".".join(prefix[:-1])
+ else:
+ prefix = ".".join(prefix)
+ full_prefix = os.path.join(self.args.out_dir_name, prefix)
+ hp_corr = True if self.args.enable_hp_corr else False
+ mark_lower_case = True if self.args.mark_lower_case else False
+ generate_consensus(input_fasta_name, self.args.ref, full_prefix, self.args.consensus_seq_name,
+ hp_corr, int(self.args.niter),
+ int(self.args.max_num_reads),
+ float(self.args.entropy_th),
+ self.args.dump_dag_info,
+ int(self.args.min_cov),
+ int(self.args.max_cov),
+ mark_lower_case,
+ int(self.args.nproc))
+
+ def run(self):
+ logging.debug("Arguments" + str(self.args))
+ if self.args.subCommand == 'd':
+ self.denovoConsensus()
+ elif self.args.subCommand == 'r':
+ self.refConsensus()
+
+if __name__ == '__main__':
+ sys.exit(Consensus().start())
+
diff --git a/src/tests/test_aligngraph.py b/src/tests/test_aligngraph.py
new file mode 100755
index 0000000..dfa8e89
--- /dev/null
+++ b/src/tests/test_aligngraph.py
@@ -0,0 +1,204 @@
+from nose.tools import assert_equal
+from nose import SkipTest
+import random
+from pbtools.pbdagcon.aligngraph import *
+
+def generate_simulated_reads(pi=None, pd=None, n = 4):
+ import random
+ random.seed(42)
+ seq = "ATATTTGGC"
+ seq1 = "ATAGCCGGC"
+ seq2 = "ATACCCGGC"
+ seq3 = "ATATCCGGC"
+ seq4 = "ATATCGGC"
+ if pi == None:
+ pi = 0.03
+ if pd == None:
+ pd = 0.03
+ out_seq = []
+ for i in range(n):
+ c = 0
+ s = []
+ if i % 4 == 0:
+ ss = seq1
+ elif i % 4 == 1:
+ ss = seq2
+ elif i % 4 == 2:
+ ss = seq3
+ else:
+ ss = seq4
+ while 1:
+ if random.uniform(0,1) < pi:
+ s.append(random.choice( ("A","G","C","T") ) )
+ continue
+ if random.uniform(0,1) < pd:
+ c += 1
+ continue
+ if c < len(ss):
+ s.append(ss[c])
+ c += 1
+ else:
+ break
+ out_seq.append( "".join(s) )
+ return seq, out_seq
+
+class TestPhiCoeff:
+ def test_phi_coeff(self):
+ # assert_equal(expected, phi_coeff(xvec, yvec))
+ raise SkipTest # TODO: implement your test here
+
+class TestConvertMismatches:
+ def test_convert_mismatches(self):
+ assert_equal( ('C-AC', 'CG-C'), convert_mismatches("CAC","CGC") )
+ assert_equal( ('CAACAT', 'CAA--T'), convert_mismatches("CAACAT","C-A-AT" ) )
+ assert_equal( ('CCG--T', 'CCGACT'), convert_mismatches("-C--CGT","CCGAC-T") )
+
+class TestAlnEdge:
+ def test___init__(self):
+ # aln_edge = AlnEdge(in_node, out_node)
+ raise SkipTest # TODO: implement your test here
+
+ def test___repr__(self):
+ # aln_edge = AlnEdge(in_node, out_node)
+ # assert_equal(expected, aln_edge.__repr__())
+ raise SkipTest # TODO: implement your test here
+
+ def test_add_to_score(self):
+ # aln_edge = AlnEdge(in_node, out_node)
+ # assert_equal(expected, aln_edge.add_to_score(s))
+ raise SkipTest # TODO: implement your test here
+
+ def test_increase_count(self):
+ # aln_edge = AlnEdge(in_node, out_node)
+ # assert_equal(expected, aln_edge.increase_count())
+ raise SkipTest # TODO: implement your test here
+
+ def test_set_score(self):
+ # aln_edge = AlnEdge(in_node, out_node)
+ # assert_equal(expected, aln_edge.set_score(s))
+ raise SkipTest # TODO: implement your test here
+
+class TestAlnNode:
+ def test___init__(self):
+ # aln_node = AlnNode(base)
+ raise SkipTest # TODO: implement your test here
+
+ def test___repr__(self):
+ # aln_node = AlnNode(base)
+ # assert_equal(expected, aln_node.__repr__())
+ raise SkipTest # TODO: implement your test here
+
+ def test_add_in_edge(self):
+ # aln_node = AlnNode(base)
+ # assert_equal(expected, aln_node.add_in_edge(in_edge))
+ raise SkipTest # TODO: implement your test here
+
+ def test_addout_edge(self):
+ # aln_node = AlnNode(base)
+ # assert_equal(expected, aln_node.addout_edge(out_edge))
+ raise SkipTest # TODO: implement your test here
+
+ def test_increase_weight(self):
+ # aln_node = AlnNode(base)
+ # assert_equal(expected, aln_node.increase_weight(w))
+ raise SkipTest # TODO: implement your test here
+
+class TestAlnGraph:
+ def test___init__(self):
+ backbone_seq, reads = generate_simulated_reads()
+ aln_graph = AlnGraph(backbone_seq)
+ assert len(aln_graph.nodes) == len(backbone_seq) + 2
+
+ def test_add_alignment(self):
+ aln_graph = AlnGraph("ATATTAGGC")
+ alns = [((0, 9, 'A-TAGCCGGC'), (2, 9, 'ATTA---GGC')),
+ ((0, 10, 'ATA-TACCGAG-'), (0, 9, 'ATATTA--G-GC')),
+ ((0, 10, 'ATCATCC--GGC'), (0, 9, 'AT-AT--TAGGC')),
+ ((0, 9, 'ATA-TACGGC'), (0, 9, 'ATATTA-GGC'))]
+ for aln in alns:
+ aln_graph.add_alignment( aln )
+ assert len(aln_graph.nodes) != 0
+ assert len(aln_graph.edges) != 0
+
+
+ def test_add_edge(self):
+ # aln_graph = AlnGraph(backbone_seq)
+ # assert_equal(expected, aln_graph.add_edge(edge))
+ raise SkipTest # TODO: implement your test here
+
+ def test_add_node(self):
+ # aln_graph = AlnGraph(backbone_seq)
+ # assert_equal(expected, aln_graph.add_node(node))
+ raise SkipTest # TODO: implement your test here
+
+ def test_delete_edge(self):
+ # aln_graph = AlnGraph(backbone_seq)
+ # assert_equal(expected, aln_graph.delete_edge(edge))
+ raise SkipTest # TODO: implement your test here
+
+ def test_delete_node(self):
+ # aln_graph = AlnGraph(backbone_seq)
+ # assert_equal(expected, aln_graph.delete_node(node))
+ raise SkipTest # TODO: implement your test here
+
+ def test_find_best_path(self):
+ # aln_graph = AlnGraph(backbone_seq)
+ # assert_equal(expected, aln_graph.find_best_path())
+ raise SkipTest # TODO: implement your test here
+
+ def test_generate_consensus(self):
+ # aln_graph = AlnGraph(backbone_seq)
+ # assert_equal(expected, aln_graph.generate_consensus())
+ raise SkipTest # TODO: implement your test here
+
+ def test_merge_in_nodes(self):
+ # aln_graph = AlnGraph(backbone_seq)
+ # assert_equal(expected, aln_graph.merge_in_nodes(nodes, node))
+ raise SkipTest # TODO: implement your test here
+
+ def test_merge_nodes(self):
+ aln_graph = AlnGraph("ATAATTGGC")
+ alns = [((0, 9, 'ATAG--CTGGC'), (0, 9, 'ATA-AT-TGGC')),
+ ((0, 9, 'ATAG--CTGGC'), (0, 9, 'ATA-AT-TGGC')),
+ ((0, 9, 'ATAG-TTGGC'), (0, 9, 'ATA-ATTGGC')),
+ ((0, 9, 'ATAG-TTGGC'), (0, 9, 'ATA-ATTGGC')),
+ ((0, 9, 'ATAG--CTGGC'), (0, 9, 'ATA-AT-TGGC'))]
+ for aln in alns:
+ aln_graph.add_alignment( aln )
+ aln_graph.merge_nodes()
+
+ def test_merge_out_nodes(self):
+ # aln_graph = AlnGraph(backbone_seq)
+ # assert_equal(expected, aln_graph.merge_out_nodes(node, nodes))
+ raise SkipTest # TODO: implement your test here
+
+ def test_output_consensus_fasta(self):
+ # aln_graph = AlnGraph(backbone_seq)
+ # assert_equal(expected, aln_graph.output_consensus_fasta(fn, rID))
+ raise SkipTest # TODO: implement your test here
+
+ def test_track_path(self):
+ # aln_graph = AlnGraph(backbone_seq)
+ # assert_equal(expected, aln_graph.track_path(seq, node))
+ raise SkipTest # TODO: implement your test here
+
+class TestOutputDot:
+ def test_output_dot(self):
+ # assert_equal(expected, output_dot(aln_graph, fn, r))
+ raise SkipTest # TODO: implement your test here
+
+class TestOutputDot2:
+ def test_output_dot_2(self):
+ # assert_equal(expected, output_dot_2(aln_graph, fn))
+ raise SkipTest # TODO: implement your test here
+
+class TestGenerateSimulatedReads:
+ def test_generate_simulated_reads(self):
+ # assert_equal(expected, generate_simulated_reads())
+ raise SkipTest # TODO: implement your test here
+
+class TestSimpleTest:
+ def test_simple_test(self):
+ # assert_equal(expected, simple_test())
+ raise SkipTest # TODO: implement your test here
+
diff --git a/test/cpp/AlignmentTest.cpp b/test/cpp/AlignmentTest.cpp
new file mode 100644
index 0000000..6cd3bc8
--- /dev/null
+++ b/test/cpp/AlignmentTest.cpp
@@ -0,0 +1,78 @@
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <gtest/gtest.h>
+#include <boost/graph/adjacency_list.hpp>
+#include "Alignment.hpp"
+
+using namespace dagcon;
+
+TEST(Alignment, Normalize) {
+ Alignment a, b;
+ a.start = 1;
+
+ // test mismatch
+ b.qstr = "CAC";
+ b.tstr = "CGC";
+ b = normalizeGaps(b);
+ EXPECT_EQ("C-AC", b.qstr);
+ EXPECT_EQ("CG-C", b.tstr);
+
+ // test shifting gaps on read
+ b.qstr = "-C--CGT";
+ b.tstr = "CCGAC-T";
+ b = normalizeGaps(b);
+ EXPECT_EQ("CCG--T", b.qstr);
+ EXPECT_EQ("CCGACT", b.tstr);
+
+ // another gap reference push
+ a.tstr = "ATATTA---GGC";
+ a.qstr = "ATAT-AGCCGGC";
+
+ b = a;
+ b = normalizeGaps(b);
+ // query remains unchanged
+ EXPECT_EQ(a.qstr, b.qstr);
+ // target shifts the G bases to the left
+ EXPECT_EQ("ATATTAG--G-C", b.tstr);
+}
+
+TEST(Alignment, ParseBasic) {
+ std::ifstream file("basic.m5");
+ Alignment aln;
+ file >> aln;
+ EXPECT_EQ(1, aln.start);
+ EXPECT_EQ("CAC", aln.qstr);
+ EXPECT_EQ("CGC", aln.tstr);
+
+ file >> aln;
+ EXPECT_EQ(1, aln.start);
+ EXPECT_EQ("AATTGGCC", aln.qstr);
+ EXPECT_EQ("GGCCAATT", aln.tstr);
+}
+
+TEST(Alignment, ParseQuery) {
+ std::ifstream file("parsequery.m5");
+ Alignment aln;
+ Alignment::groupByTarget = false;
+ file >> aln;
+ EXPECT_EQ("m130110_062238_00114_c100480560100000001823075906281381_s1_p0/311/1102_3151",
+ aln.id);
+ EXPECT_EQ(2049, aln.tlen);
+ EXPECT_EQ(8, aln.start);
+ EXPECT_EQ("CTGCATGCT", aln.tstr.substr(0,9));
+ EXPECT_EQ("CTGCA--CT", aln.qstr.substr(0,9));
+}
+
+TEST(AlnGraphBoostTest, Trim) {
+ dagcon::Alignment aln;
+ aln.tstr = "GATGAAGCCGGGG---TTACAGGCATGGATGTGGATAACGTCGAGTA-C-AGTC-GTA----TTC--C-TGCAGGGGTTGACG-TTTT-CCACCATCGCACGCCGGGACCATCACCGT-TAT-GAAGATTCAGCA-CCGGGG-GCGGCTGAATGATTTTCTCCTGATGGCCATTGACGGAGGATGATTGCCCGGCCGGAC";
+ aln.qstr = "GATGAAGC-GGGGGCGTTACAG-CATGGATGT------CG---AGTAACG-GTCAGTACCAGTTCATCCTGCAGG--TTGACGGTTTTTCCACCATCGCACGCCGGGACCATCACCGTGTATCGAAGATTCA-CAAC-GGGGC-CG-CTGAATGATTTTCTC-TG-TG-CCAT-GACGGAGGATGAT-GCCCG-CCGGA-";
+ aln.start = 1;
+ aln.strand = '-';
+
+ trimAln(aln, 14);
+ EXPECT_EQ("TACAGGCATGGATGTGGATAACGTCGAGTA-C-AGTC-GTA----TTC--C-TGCAGGGGTTGACG-TTTT-CCACCATCGCACGCCGGGACCATCACCGT-TAT-GAAGATTCAGCA-CCGGGG-GCGGCTGAATGATTTTCTCCTGATGGCCATTGACGGAGGATGA", aln.tstr);
+ EXPECT_EQ("TACAG-CATGGATGT------CG---AGTAACG-GTCAGTACCAGTTCATCCTGCAGG--TTGACGGTTTTTCCACCATCGCACGCCGGGACCATCACCGTGTATCGAAGATTCA-CAAC-GGGGC-CG-CTGAATGATTTTCTC-TG-TG-CCAT-GACGGAGGATGA", aln.qstr);
+ EXPECT_EQ(15, aln.start);
+}
diff --git a/test/cpp/AlnGraphBoostTest.cpp b/test/cpp/AlnGraphBoostTest.cpp
new file mode 100644
index 0000000..8e2fee8
--- /dev/null
+++ b/test/cpp/AlnGraphBoostTest.cpp
@@ -0,0 +1,56 @@
+#include <string>
+#include <sstream>
+#include <map>
+#include <vector>
+#include <gtest/gtest.h>
+#include <boost/graph/adjacency_list.hpp>
+#include "Alignment.hpp"
+#include "AlnGraphBoost.hpp"
+
+TEST(AlnGraphBoostTest, RawConsensus) {
+ std::string backbone = "ATATTAGGC";
+ AlnGraphBoost ag(backbone);
+ dagcon::Alignment *algs = new dagcon::Alignment[5];
+
+ algs[0].tstr = "ATATTA---GGC";
+ algs[0].qstr = "ATAT-AGCCGGC";
+
+ algs[1].tstr = "ATATTA-GGC";
+ algs[1].qstr = "ATAT-ACGGC";
+
+ algs[2].tstr = "AT-ATTA--GGC";
+ algs[2].qstr = "ATCAT--CCGGC";
+
+ algs[3].tstr = "ATATTA--G-GC";
+ algs[3].qstr = "ATAT-ACCGAG-";
+
+ algs[4].tstr = "ATATTA---GGC";
+ algs[4].qstr = "ATAT-AGCCGGC";
+
+ for(int i=0; i < 5; i++) {
+ dagcon::Alignment& ra = algs[i];
+ ra.id = "target";
+ ra.tlen = 9;
+ ra.start = 1;
+ }
+ ag.addAln(algs[0]);
+ ag.addAln(algs[1]);
+ ag.addAln(algs[2]);
+ ag.addAln(algs[3]);
+ ag.addAln(algs[4]);
+
+ ag.mergeNodes();
+ std::string expected = "ATATAGCCGGC";
+ const std::string actual = ag.consensus();
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(AlnGraphBoostTest, DanglingNodes) {
+ AlnGraphBoost ag(12);
+ dagcon::Alignment a;
+ a.tstr = "C-GCGGA-T-G-";
+ a.qstr = "CCGCGG-G-A-T";
+
+ ag.addAln(a);
+ EXPECT_FALSE(ag.danglingNodes());
+}
diff --git a/test/cpp/Makefile b/test/cpp/Makefile
new file mode 100644
index 0000000..5a72813
--- /dev/null
+++ b/test/cpp/Makefile
@@ -0,0 +1,77 @@
+.PHONY: all check test_target_hit test_alngraph test_alignment test_simple_aligner
+# project source code
+SRCDIR := ../../src/cpp
+
+include $(SRCDIR)/pbi.mk
+include $(SRCDIR)/boost.mk
+include gtest.mk
+
+GTEST_CPPFLAGS += -isystem $(GTEST_DIR)/include
+GTEST_CXXFLAGS += -g -Wall -Wextra -pthread
+
+INCDIRS := -I$(PBDATA) -I$(BLASR) -I$(BOOST_HEADERS) -I$(GTEST_DIR)/include -I$(GTEST_DIR) $(EXTRA_INCDIRS)
+LDFLAGS := -L$(PBDATA) -L$(BLASR) $(EXTRA_LDFLAGS)
+LDLIBS := -lpbdata -lblasr -lpthread ${EXTRA_LDLIBS}
+
+CXXFLAGS := -O3 -std=c++11 $(INCDIRS) -I$(SRCDIR)
+
+GTEST_OBJECTS := gtest-all.o gtest_main.o
+DAZCON_OBJECTS := $(SRCDIR)/DB.o $(SRCDIR)/align.o $(SRCDIR)/Alignment.o \
+ $(SRCDIR)/DazAlnProvider.o
+
+PBDAGCON_OBJECTS := $(SRCDIR)/AlnGraphBoost.o $(SRCDIR)/Alignment.o \
+ $(SRCDIR)/SimpleAligner.o
+vpath %.cc $(gtest_version)/src
+
+BUILDMSG = "=== Building $@ ==="
+
+all: check
+
+check: test_target_hit test_alngraph test_alignment test_simple_aligner
+
+# XXX: need to mock out db_ in order to run test_target
+
+test_target_hit: $(GTEST_OBJECTS) $(DAZCON_OBJECTS) TargetHitTest.o
+ @echo $(BUILDMSG)
+ $(CXX) $^ -o $@ -lpthread
+ ./$@
+
+test_target: $(GTEST_OBJECTS) $(DAZCON_OBJECTS) TargetTest.o
+ @echo $(BUILDMSG)
+ $(CXX) $^ -o $@ -lpthread
+ ./$@
+
+test_alngraph: $(GTEST_OBJECTS) $(PBDAGCON_OBJECTS) AlnGraphBoostTest.o
+ @echo $(BUILDMSG)
+ $(CXX) $^ -static -o $@ $(LDFLAGS) $(LDLIBS)
+ ./$@
+
+test_alignment: $(GTEST_OBJECTS) $(PBDAGCON_OBJECTS) AlignmentTest.o
+ @echo $(BUILDMSG)
+ $(CXX) $^ -static -o $@ $(LDFLAGS) $(LDLIBS)
+ ./$@
+
+test_simple_aligner: $(GTEST_OBJECTS) $(PBDAGCON_OBJECTS) SimpleAlignerTest.o
+ @echo $(BUILDMSG)
+ $(CXX) $^ -static -o $@ $(LDFLAGS) $(LDLIBS)
+ ./$@
+
+$(SRCDIR)/AlnGraphBoost.o: $(BOOST_HEADERS)
+
+$(GTEST_OBJECTS): $(GTEST_DIR)
+ $(CXX) $(GTEST_CPPFLAGS) -I$(GTEST_DIR) $(GTEST_CXXFLAGS) -c $</src/$(@:.o=.cc)
+
+$(BOOST_HEADERS):
+ @echo Fetching boost headers from $(URI)
+ cd $(SRCDIR)/third-party && $(GET_BOOST)
+
+$(GTEST_DIR):
+ @echo Fetching gtest from $(gtest_uri)
+ $(get_gtest)
+ unzip -q $(gtest_version).zip
+ $(RM) $(gtest_version).zip
+
+clean:
+ $(RM) $(SRCDIR)/*.o
+ $(RM) *.o
+ $(RM) test_*
diff --git a/test/cpp/SimpleAlignerTest.cpp b/test/cpp/SimpleAlignerTest.cpp
new file mode 100644
index 0000000..2499de1
--- /dev/null
+++ b/test/cpp/SimpleAlignerTest.cpp
@@ -0,0 +1,21 @@
+#include <cstring>
+#include <string>
+#include <algorithm>
+#include <gtest/gtest.h>
+#include "Alignment.hpp"
+#include "SimpleAligner.hpp"
+
+TEST(SimpleAligner, align) {
+ SimpleAligner sa;
+ dagcon::Alignment a;
+ a.id = "test";
+ a.start = 765;
+ a.end = 826;
+ a.tlen = 2092;
+ a.strand = '-';
+ a.tstr = "ACAGAGATGCAAGGTAAAGTACAATTGAAAAACTAACCTCTTCCAGCGAGACTTATAGCGA";
+ a.qstr = "ACAGAAGATGAAGGTAAATACAATGAAAAAACTACCTCGGTTCCAGCGAGAACTATAGCGA";
+ sa.align(a);
+ EXPECT_EQ("TCGCTATAAGT-CTCGCTGGAA--GAGGTTAGTTTTT-CAATTGTACTTTACCTTGCATCT-CTGT", a.tstr);
+ EXPECT_EQ(1267, a.start);
+}
diff --git a/test/cpp/TargetHitTest.cpp b/test/cpp/TargetHitTest.cpp
new file mode 100644
index 0000000..758251a
--- /dev/null
+++ b/test/cpp/TargetHitTest.cpp
@@ -0,0 +1,77 @@
+#include <gtest/gtest.h>
+#include <DazAlnProvider.hpp>
+
+TEST(TargetHitTest, single_overlap_perfect) {
+ Record rec;
+ Overlap ovl;
+ rec.ovl = ovl;
+
+ Path path;
+ path.abpos = 27;
+ path.aepos = 7013;
+ path.bbpos = 1231;
+ path.bepos = 8217;
+ path.diffs = 0;
+
+ rec.ovl.path = path;
+
+ TargetHit th;
+ th.add(rec);
+ th.computeOvlScore();
+ EXPECT_FLOAT_EQ(6986, th.ovlScore);
+}
+
+TEST(TargetHitTest, single_overlap_inaccurate) {
+ Record rec;
+ Overlap ovl;
+ rec.ovl = ovl;
+
+ Path path;
+ path.abpos = 10;
+ path.aepos = 5000;
+ path.bbpos = 2000;
+ path.bepos = 6000;
+ path.diffs = 230;
+
+ rec.ovl.path = path;
+
+ TargetHit th;
+ th.add(rec);
+ th.computeOvlScore();
+ EXPECT_FLOAT_EQ(3770, th.ovlScore);
+}
+
+TEST(TargetHitTest, multi_overlap_inaccurate) {
+ Record r1;
+ Overlap o1;
+ r1.ovl = o1;
+
+ Path p1;
+ p1.abpos = 10;
+ p1.aepos = 5000;
+ p1.bbpos = 2000;
+ p1.bepos = 6000;
+ p1.diffs = 230;
+ r1.ovl.path = p1;
+
+ Record r2;
+ Overlap o2;
+ r2.ovl = o2;
+
+ Path p2;
+ p2.abpos = 5005;
+ p2.aepos = 7005;
+ p2.bbpos = 6001;
+ p2.bepos = 7995;
+ p2.diffs = 53;
+ r2.ovl.path = p2;
+
+ TargetHit th;
+
+ th.add(r1);
+ th.computeOvlScore();
+ EXPECT_FLOAT_EQ(3770, th.ovlScore);
+ th.add(r2);
+ th.computeOvlScore();
+ EXPECT_FLOAT_EQ(4721, th.ovlScore);
+}
diff --git a/test/cpp/TargetTest.cpp b/test/cpp/TargetTest.cpp
new file mode 100644
index 0000000..f06f526
--- /dev/null
+++ b/test/cpp/TargetTest.cpp
@@ -0,0 +1,22 @@
+#include <gtest/gtest.h>
+#include <DazAlnProvider.hpp>
+
+TEST(TargetTest, add_record) {
+ Record r1, r2, r3;
+ Overlap o1, o2, o3;
+
+ o1.aread = 1; o1.bread = 3; o1.flags = 0;
+ o2.aread = 1; o2.bread = 3; o2.flags = 0;
+ o3.aread = 2; o3.bread = 3; o3.flags = 1;
+
+ r1.ovl = o1;
+ r2.ovl = o2;
+ r3.ovl = o3;
+
+ Target t;
+ t.addRecord(r1);
+ t.addRecord(r2);
+ t.addRecord(r3);
+
+ EXPECT_EQ(2, t.hits.size());
+}
diff --git a/test/cpp/basic.m5 b/test/cpp/basic.m5
new file mode 100644
index 0000000..7971f66
--- /dev/null
+++ b/test/cpp/basic.m5
@@ -0,0 +1,2 @@
+id 3 0 3 + ref 3 0 3 + -40645 8129 0 0 0 254 CAC |-| CGC
+id 3 0 3 + ref 3 0 3 - -40645 8129 0 0 0 254 GGCCAATT |-| AATTGGCC
diff --git a/test/cpp/gtest.mk b/test/cpp/gtest.mk
new file mode 100644
index 0000000..c5b2fee
--- /dev/null
+++ b/test/cpp/gtest.mk
@@ -0,0 +1,10 @@
+# user-specified location of gtest
+ifdef gtest
+GTEST_DIR := $(gtest)
+else
+# download/unpack a version from the inter-web
+gtest_version := gtest-1.7.0
+gtest_uri := https://googletest.googlecode.com/files/$(gtest_version).zip
+get_gtest := curl -O $(gtest_uri)
+GTEST_DIR := $(gtest_version)
+endif
diff --git a/test/cpp/parsequery.m5 b/test/cpp/parsequery.m5
new file mode 100644
index 0000000..8c5038a
--- /dev/null
+++ b/test/cpp/parsequery.m5
@@ -0,0 +1 @@
+m130110_062238_00114_c100480560100000001823075906281381_s1_p0/311/1102_3151/0_2049 2049 7 1640 + m130110_062238_00114_c100480560100000001823075906281381_s1_p0/311/1102_3151 2049 493 2044 - -3997 1303 93 237 155 0 CTGCATGCT-GTGACTCTGGATAGAGTTTTCTGCCGCATGTTAATTGTAATCAAGACCGTTTAGTAAGTTCTTATCGATTATCATTACGTTTC-ATCG-C-T-ATTATCGTGGTTTTGGTAGGTCAATGGTGTTCAGGTGTGCATTGTTGTTGATGTATTTCTGCAGTATCGATTTCTGTTCTGTATCGCTGGCAT-TGTTG-TCATCAATAGG-AGATGGTAGTC--TGGTTTTTG-----------T-TAATAGATTATACGC-TGTGATAATGCT [...]
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/pbdagcon.git
More information about the debian-med-commit
mailing list