[med-svn] [Git][med-team/libbioparser-dev][upstream] New upstream version 3.0.12

Michael R. Crusoe gitlab at salsa.debian.org
Fri Jan 1 14:58:31 GMT 2021



Michael R. Crusoe pushed to branch upstream at Debian Med / libbioparser-dev


Commits:
77b7ed91 by Michael R. Crusoe at 2021-01-01T14:54:45+01:00
New upstream version 3.0.12
- - - - -


19 changed files:

- .gitmodules
- .travis.yml
- CMakeLists.txt
- README.md
- − include/bioparser/bioparser.hpp
- + include/bioparser/fasta_parser.hpp
- + include/bioparser/fastq_parser.hpp
- + include/bioparser/mhap_parser.hpp
- + include/bioparser/paf_parser.hpp
- + include/bioparser/parser.hpp
- + include/bioparser/sam_parser.hpp
- − test/bioparser_test.cpp
- − test/bioparser_test_config.h.in
- + test/fasta_parser_test.cpp
- + test/fastq_parser_test.cpp
- + test/mhap_parser_test.cpp
- + test/paf_parser_test.cpp
- + test/parser_test.cpp
- + test/sam_parser_test.cpp


Changes:

=====================================
.gitmodules
=====================================
@@ -1,6 +1,4 @@
-[submodule "vendor/googletest"]
-	path = vendor/googletest
-	url = https://github.com/google/googletest
-[submodule "vendor/zlib"]
-	path = vendor/zlib
-	url = https://github.com/madler/zlib
+
+[submodule "vendor/biosoup"]
+	path = vendor/biosoup
+	url = https://github.com/rvaser/biosoup


=====================================
.travis.yml
=====================================
@@ -2,42 +2,49 @@ dist: trusty
 
 language: cpp
 
-compiler:
-    - clang
-    - gcc
+matrix:
+  include:
+    - name: "GCC 4.8 (Linux)"  # GCC 4.8.5 & CMake 3.9.2
+      os: linux
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - g++-4.8
+            - cmake
+      env:
+        - SET_COMPILER="export CC=gcc-4.8 && export CXX=g++-4.8"
+
+    - name: "Clang 3.5 (Linux)"  # Clang 3.5.0 & CMake 3.9.2
+      os: linux
+      addons:
+        apt:
+          sources:
+            - llvm-toolchain-trusty-3.5
+          packages:
+            - clang-3.5
+            - cmake
+      env:
+        - SET_COMPILER="export CC=clang-3.5 && export CXX=clang++-3.5"
+
+    - name: "Clang Xcode 9.0 (OSX)"  # Clang 9.0.0 & CMake 3.9.2
+      os: osx
+      osx_image: xcode9
 
 before_install:
-    # cmake 3.2
-    - sudo add-apt-repository ppa:george-edison55/cmake-3.x -y
-
-    # g++4.8.1
-    - if [ "$CXX" == "g++" ]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; fi
-
-    # clang 3.4
-    - if [ "$CXX" == "clang++" ]; then sudo add-apt-repository -y ppa:h-rayflood/llvm; fi
-
-    - sudo apt-get update -qq
+  - eval "${SET_COMPILER}"
+  - git clone https://github.com/google/googletest && cd googletest && mkdir build && cd build && git checkout 703bd9c
+  - cmake -DCMAKE_CXX_FLAGS="-std=c++11" .. && make && sudo make install
+  - cd ../../
 
 install:
-    # cmake 3.2
-    - sudo apt-get install cmake cmake-data
-
-    # g++4.8.1
-    - if [ "$CXX" == "g++" ]; then sudo apt-get install -qq g++-4.8; fi
-    - if [ "$CXX" == "g++" ]; then export CXX="g++-4.8"; fi
-
-    # clang 3.4
-    - if [ "$CXX" == "clang++" ]; then sudo apt-get install --allow-unauthenticated -qq clang-3.4; fi
-    - if [ "$CXX" == "clang++" ]; then export CXX="clang++-3.4"; fi
+  - mkdir build && cd build
+  - cmake -Dbioparser_build_tests=ON -DCMAKE_BUILD_TYPE=Release .. && make
 
 script:
-    - mkdir build
-    - cd build
-    - cmake -Dbioparser_build_tests=ON -DCMAKE_BUILD_TYPE=Release ..
-    - make
-    - ./bin/bioparser_test
+  - ./bin/bioparser_test
 
 notifications:
-    email:
-        on_success: change
-        on_failure: always
+  email:
+    on_failure: always


=====================================
CMakeLists.txt
=====================================
@@ -1,41 +1,43 @@
-cmake_minimum_required(VERSION 3.2)
-project(bioparser)
+cmake_minimum_required(VERSION 3.9)
 
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
+project(bioparser VERSION 3.0.12
+                  LANGUAGES CXX
+                  DESCRIPTION "Bioparser is a c++ header only parsing library for several formats in bioinformatics (FASTA/Q, MHAP/PAF/SAM), with support for zlib compressed files.")
 
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
 set(CMAKE_CXX_STANDARD 11)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
 
-option(bioparser_build_tests "Build bioparser unit tests" OFF)
-
-add_library(bioparser INTERFACE)
-
-target_include_directories(bioparser INTERFACE
-    ${PROJECT_SOURCE_DIR}/include
-    ${PROJECT_SOURCE_DIR}/vendor/zlib
-    ${PROJECT_BINARY_DIR}/vendor/zlib)
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
 
-if (NOT TARGET zlib)
-    add_subdirectory(vendor/zlib EXCLUDE_FROM_ALL)
-endif()
+find_package(ZLIB REQUIRED)
+add_library(${PROJECT_NAME} INTERFACE)
+target_link_libraries(${PROJECT_NAME} INTERFACE
+  ZLIB::ZLIB)
 
-target_link_libraries(bioparser INTERFACE zlibstatic)
+target_include_directories(${PROJECT_NAME} INTERFACE
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
 
+option(bioparser_build_tests "Build bioparser unit tests" OFF)
 if (bioparser_build_tests)
-    set(bioparser_test_data_path ${PROJECT_SOURCE_DIR}/test/data/)
-    configure_file(${PROJECT_SOURCE_DIR}/test/bioparser_test_config.h.in
-        ${PROJECT_BINARY_DIR}/config/bioparser_test_config.h)
-    include_directories(${PROJECT_BINARY_DIR}/config)
-
-    add_executable(bioparser_test test/bioparser_test.cpp)
-
-    if (NOT TARGET gtest_main)
-        add_subdirectory(vendor/googletest/googletest EXCLUDE_FROM_ALL)
-    endif()
-
-    target_link_libraries(bioparser_test bioparser gtest_main)
-endif()
+  find_package(GTest REQUIRED)
+  if (NOT TARGET biosoup)
+    add_subdirectory(vendor/biosoup EXCLUDE_FROM_ALL)
+  endif ()
+  add_executable(${PROJECT_NAME}_test
+    test/parser_test.cpp
+    test/fasta_parser_test.cpp
+    test/fastq_parser_test.cpp
+    test/mhap_parser_test.cpp
+    test/paf_parser_test.cpp
+    test/sam_parser_test.cpp)
+  target_link_libraries(${PROJECT_NAME}_test
+    ${PROJECT_NAME}
+    biosoup
+    GTest::Main)
+  target_compile_definitions(${PROJECT_NAME}_test PRIVATE
+    BIOPARSER_DATA_PATH="${PROJECT_SOURCE_DIR}/test/data/")
+endif ()


=====================================
README.md
=====================================
@@ -1,184 +1,185 @@
 # Bioparser
 
 [![Latest GitHub release](https://img.shields.io/github/release/rvaser/bioparser.svg)](https://github.com/rvaser/bioparser/releases/latest)
-[![Build status for gcc/clang](https://travis-ci.org/rvaser/bioparser.svg?branch=master)](https://travis-ci.org/rvaser/bioparser)
+[![Build status for gcc/clang](https://travis-ci.com/rvaser/bioparser.svg?branch=master)](https://travis-ci.com/rvaser/bioparser)
 
-Bioparser is a c++ implementation of parsers for several bioinformatics formats. It consists of only one header file containing template parsers for FASTA, FASTQ, MHAP, PAF and SAM format. It also supports compressed files with gzip.
+Bioparser is a c++ header only parsing library for several formats in bioinformatics (FASTA/Q, MHAP/PAF/SAM), with support for zlib compressed files.
 
-## Dependencies
-1. gcc 4.8+ or clang 3.4+
-2. (optional) cmake 3.2+
+## Usage
 
-## Installation
-To build Bioparser unit tests run the following commands:
+If you would like to add bioparser to your project via CMake, add the following:
+```cmake
+if (NOT TARGET bioparser)
+  add_subdirectory(<path_to_submodules>/bioparser EXCLUDE_FROM_ALL)
+endif ()
+target_link_libraries(<your_exe> bioparser)
+```
 
-```bash
-git clone --recursive https://github.com/rvaser/bioparser.git bioparser
-cd bioparser
-mkdir build
-cd build
-cmake -Dbioparser_build_tests=ON -DCMAKE_BUILD_TYPE=Release ..
-make
+If you are not using CMake, include the appropriate header file directly to your project and link with zlib.
+
+#### Dependencies
+- gcc 4.8+ or clang 3.5+
+- (optional) cmake 3.9+
+- zlib
+
+## Examples
+
+### FASTA parser
+
+```cpp
+#include "bioparser/fasta_parser.hpp"
+
+struct Sequence {  // or any other name
+ public:
+  Sequence(  // required arguments
+      const char*, std::uint32_t,
+      const char*, std::uint32_t) {
+    // implementation
+  }
+}
+auto p = bioparser::Parser<Sequence>::Create<bioparser::FastaParser>(path);
+
+// parse whole file
+auto s = p->Parse(-1);
 ```
 
-After installation, an executable named `bioparser_test` will be created in `build/bin`.
+### FASTQ parser
 
-## Usage
+```cpp
+#include "bioparser/fastq_parser.hpp"
+
+struct Sequence {  // or any other name
+ public:
+  Sequence(  // required arguments
+      const char*, std::uint32_t,
+      const char*, std::uint32_t,
+      const char*, std::uint32_t) {
+    // implementation
+  }
+}
+auto p = bioparser::Parser<Sequence>::Create<bioparser::FastqParser>(path);
+
+// parse in chunks
+std::vector<std::unique_ptr<Sequence>> s;
+std::uint32_t chunk_size = 500 * 1024 * 1024;  // 500 MB
+for (auto t = p->parse(chunk_size); !t.empty(); t = p->parse(chunk_size)) {
+  s.insert(
+      s.end(),
+      std::make_move_iterator(t.begin()),
+      std::make_move_iterator(t.end()));
+}
+```
+
+### MHAP parser
 
-If you would like to add bioparser to your project, add the following commands to your CMakeLists.txt file: `add_subdirectory(vendor/bioparser EXCLUDE_FROM_ALL)` and `target_link_libraries(your_exe bioparser)`. If you are not using cmake, include the header `bioparser.hpp` to your project and install zlib on your machine.
+```cpp
+#include "bioparser/mhap_parser.hpp"
+
+struct Overlap {  // or any other name
+ public:
+  Overlap(  // required arguments
+      std::uint64_t,
+      std::uint64_t,
+      double error,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t) {
+    // implementation
+  }
+}
+auto p = bioparser::Parser<Overlap>::Create<bioparser::MhapParser>(path);
 
-For details on how to use the parsers in your code, please look at the examples bellow:
+// parse whole file
+auto o = p->Parse(-1);
+```
 
-*Note:* Bioparser by default trims sequence headers to the first white space. To disable this behavior pass `false` to the last argument of member functions `bioparser::Parser:parse`.
+### PAF parser
 
 ```cpp
-// define a class for sequences in FASTA format
-class Example1 {
-public:
-    // required signature for the constructor
-    Example1(
-        const char* name, std::uint32_t name_length,
-        const char* sequence, std::uint32_t sequence_length) {
-        // your implementation
-    }
-};
-
-std::vector<std::unique_ptr<Example1>> fasta_objects;
-auto fasta_parser = bioparser::createParser<bioparser::FastaParser, Example1>(path_to_file);
-// read the whole file
-fasta_parser->parse(fasta_objects, -1);
-
-// define a class for sequences in FASTQ format
-class Example2 {
-public:
-    // required signature for the constructor
-    Example2(
-        const char* name, std::uint32_t name_length,
-        const char* sequence, std::uint32_t sequence_length,
-        const char* quality, std::uint32_t quality_length) {
-        // your implementation
-    }
-};
-
-std::vector<std::unique_ptr<Example2>> fastq_objects;
-auto fastq_parser = bioparser::createParser<bioparser::FastqParser, Example2>(path_to_file2);
-// read a predefined size of bytes
-std::uint64_t size_in_bytes = 500 * 1024 * 1024; // 500 MB
-while (true) {
-    auto status = fastq_parser->parse(fastq_objects, size_in_bytes);
-    // do some work with objects
-    if (status == false) {
-        break;
-    }
+#include "bioparser/paf_parser.hpp"
+
+struct Overlap {  // or any other name
+ public:
+  Overlap(  // required arguments
+      const char*, std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      char,
+      const char*, std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      std::uint32_t) {
+    // implementation
+  }
 }
+auto p = bioparser::Parser<Overlap>::Create<bioparser::PafParser>(path);
+
+// parse whole file
+auto o = p->Parse(-1);
+```
 
-// define a class for overlaps in MHAP format
-class Example3 {
-public:
-    // required signature for the constructor
-    Example3(
-        std::uint64_t a_id,
-        std::uint64_t b_id,
-        double eq_bases_perc,
-        std::uint32_t minmers,
-        std::uint32_t a_rc,
-        std::uint32_t a_begin,
-        std::uint32_t a_end,
-        std::uint32_t a_length,
-        std::uint32_t b_rc,
-        std::uint32_t b_begin,
-        std::uint32_t b_end,
-        std::uint32_t b_length) {
-        // your implementation
-    }
-};
-
-std::vector<std::unique_ptr<Example3>> mhap_objects;
-auto mhap_parser = bioparser::createParser<bioparser::MhapParser, Example3>(path_to_file3);
-mhap_parser->parse(mhap_objects, -1);
-
-// define a class for overlaps in PAF format or add a constructor to existing overlap class
-Example3::Example3(
-    const char* q_name, std::uint32_t q_name_length,
-    std::uint32_t q_length,
-    std::uint32_t q_begin,
-    std::uint32_t q_end,
-    char orientation,
-    const char* t_name, std::uint32_t t_name_length,
-    std::uint32_t t_length,
-    std::uint32_t t_begin,
-    std::uint32_t t_end,
-    std::uint32_t matching_bases,
-    std::uint32_t overlap_length,
-    std::uint32_t mapping_quality) {
-    // your implementation
+### SAM parser
+
+```cpp
+#include "bioparser/sam_parser.hpp"
+
+struct Overlap {  // or any other name
+ public:
+  Overlap(  // required arguments
+      const char*, std::uint32_t,
+      std::uint32_t,
+      const char*, std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      const char*, std::uint32_t,
+      const char*, std::uint32_t,
+      std::uint32_t,
+      std::uint32_t,
+      const char*, std::uint32_t,
+      const char*, std::uint32_t) {
+    // implementation
+  }
 }
+auto p = bioparser::Parser<Overlap>::Create<bioparser::SamParser>(path);
 
-std::vector<std::unique_ptr<ExampleClass3>> paf_objects;
-auto paf_parser = bioparser::createParser<bioparser::PafParser, ExampleClass3>(path_to_file4);
-paf_parser->parse(paf_objects, -1);
-
-// define a class for alignments in SAM format
-class Example4 {
-public:
-    // required signature for the constructor
-    Example4(
-        const char* q_name, std::uint32_t q_name_length,
-        std::uint32_t flag,
-        const char* t_name, std::uint32_t t_name_length,
-        std::uint32_t t_begin,
-        std::uint32_t mapping_quality,
-        const char* cigar, std::uint32_t cigar_length,
-        const char* t_next_name, std::uint32_t t_next_name_length,
-        std::uint32_t t_next_begin,
-        std::uint32_t template_length,
-        const char* sequence, std::uint32_t sequence_length,
-        const char* quality, std::uint32_t quality_length) {
-        // your implementation
-    }
-};
-
-std::vector<std::unique_ptr<Example4>> sam_objects;
-auto sam_parser = bioparser::createParser<bioparser::SamParser, Example4>(path_to_file5);
-sam_parser->parse(sam_objects, -1);
+// parse whole file
+auto o = p->Parse(-1);
 ```
-If your class has a **private** constructor with the required signature, format your classes in the following way:
+
+**Note**: If your class has a private constructor, add one of the following lines to your class definition:
 
 ```cpp
-class Example1 {
-public:
-    friend bioparser::FastaParser<Example1>;
-private:
-    Example1(...) {
-        ...
-    }
-};
-
-class Example2 {
-public:
-    friend bioparser::FastqParser<Example2>;
-private:
-    Example2(...) {
-        ...
-    }
-};
-
-class Example3 {
-public:
-    friend bioparser::MhapParser<Example3>;
-    friend bioparser::PafParser<Example3>;
-private:
-    Example3(...) {
-        ...
-    }
-};
-
-class Example4 {
-public:
-    friend bioparser::SamParser<Example4>;
-private:
-    Example4(...) {
-        ...
-    }
-};
+friend bioparser::FastaParser<Sequence>;
+friend bioparser::FastqParser<Sequence>;
+friend bioparser::MhapParser<Overlap>;
+friend bioparser::PafParser<Overlap>;
+friend bioparser::SamParser<Overlap>;
 ```
+
+## Unit tests
+
+To build and run bioparser unit tests run the following commands:
+
+```bash
+git clone --recursive https://github.com/rvaser/bioparser.git bioparser
+cd bioparser && mkdir build && cd build
+cmake -Dbioparser_build_tests=ON -DCMAKE_BUILD_TYPE=Release .. && make
+./bin/bioparser_test
+```
+
+#### Dependencies
+- gtest
+
+## Acknowledgement
+
+This work has been supported in part by the Croatian Science Foundation under the project Single genome and metagenome assembly (IP-2018-01-5886)..


=====================================
include/bioparser/bioparser.hpp deleted
=====================================
@@ -1,876 +0,0 @@
-/*!
- * @file bioparser.hpp
- *
- * @brief Bioparser header file
- */
-
-#pragma once
-
-#include <cstdint>
-#include <cstring>
-#include <exception>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "zlib.h"
-
-namespace bioparser {
-
-static const std::string version = "v2.1.2";
-
-/*!
- * @brief Parser absctract class
- */
-template<class T>
-class Parser;
-
-template<template<class> class P, class T>
-std::unique_ptr<Parser<T>> createParser(const std::string& path);
-
-/*!
- * @brief Parser specializations
- */
-template<class T>
-class FastaParser;
-
-template<class T>
-class FastqParser;
-
-template<class T>
-class MhapParser;
-
-template<class T>
-class PafParser;
-
-template<class T>
-class SamParser;
-
-/*!
- * @brief Parser definitions
- */
-template<class T>
-class Parser {
-public:
-    virtual ~Parser() = 0;
-
-    void reset();
-
-    virtual bool parse(std::vector<std::unique_ptr<T>>& dst,
-        std::uint64_t max_bytes, bool trim = true) = 0;
-
-    bool parse(std::vector<std::shared_ptr<T>>& dst, std::uint64_t max_bytes,
-        bool trim = true);
-protected:
-    Parser(gzFile input_file, std::uint32_t storage_size);
-    Parser(const Parser&) = delete;
-    const Parser& operator=(const Parser&) = delete;
-
-    std::unique_ptr<gzFile_s, int(*)(gzFile)> input_file_;
-    std::vector<char> buffer_;
-    std::uint32_t buffer_ptr_;
-    std::uint32_t buffer_bytes_;
-    std::vector<char> storage_;
-};
-
-template<class T>
-class FastaParser: public Parser<T> {
-public:
-    ~FastaParser();
-
-    bool parse(std::vector<std::unique_ptr<T>>& dst,
-        std::uint64_t max_bytes, bool trim = true) override;
-
-    friend std::unique_ptr<Parser<T>>
-        createParser<bioparser::FastaParser, T>(const std::string& path);
-private:
-    FastaParser(gzFile input_file);
-    FastaParser(const FastaParser&) = delete;
-    const FastaParser& operator=(const FastaParser&) = delete;
-};
-
-template<class T>
-class FastqParser: public Parser<T> {
-public:
-    ~FastqParser();
-
-    bool parse(std::vector<std::unique_ptr<T>>& dst,
-        std::uint64_t max_bytes, bool trim = true) override;
-
-    friend std::unique_ptr<Parser<T>>
-        createParser<bioparser::FastqParser, T>(const std::string& path);
-private:
-    FastqParser(gzFile input_file);
-    FastqParser(const FastqParser&) = delete;
-    const FastqParser& operator=(const FastqParser&) = delete;
-};
-
-template<class T>
-class MhapParser: public Parser<T> {
-public:
-    ~MhapParser();
-
-    bool parse(std::vector<std::unique_ptr<T>>& dst,
-        std::uint64_t max_bytes, bool trim = true) override;
-
-    friend std::unique_ptr<Parser<T>>
-        createParser<bioparser::MhapParser, T>(const std::string& path);
-private:
-    MhapParser(gzFile input_file);
-    MhapParser(const MhapParser&) = delete;
-    const MhapParser& operator=(const MhapParser&) = delete;
-};
-
-template<class T>
-class PafParser: public Parser<T> {
-public:
-    ~PafParser();
-
-    bool parse(std::vector<std::unique_ptr<T>>& dst,
-        std::uint64_t max_bytes, bool trim = true) override;
-
-    friend std::unique_ptr<Parser<T>>
-        createParser<bioparser::PafParser, T>(const std::string& path);
-private:
-    PafParser(gzFile input_file);
-    PafParser(const PafParser&) = delete;
-    const PafParser& operator=(const PafParser&) = delete;
-};
-
-template<class T>
-class SamParser: public Parser<T> {
-public:
-    ~SamParser();
-
-    bool parse(std::vector<std::unique_ptr<T>>& dst,
-        std::uint64_t max_bytes, bool trim = true) override;
-
-    friend std::unique_ptr<Parser<T>>
-        createParser<bioparser::SamParser, T>(const std::string& path);
-private:
-    SamParser(gzFile input_file);
-    SamParser(const SamParser&) = delete;
-    const SamParser& operator=(const SamParser&) = delete;
-};
-
-/*!
- * @brief Implementation
- */
-inline void rightStrip(const char* src, std::uint32_t& src_length) {
-    while (src_length > 0 && isspace(src[src_length - 1])) {
-        --src_length;
-    }
-}
-
-inline void rightStripHard(const char* src, std::uint32_t& src_length) {
-    for (std::uint32_t i = 0; i < src_length; ++i) {
-        if (isspace(src[i])) {
-            src_length = i;
-            break;
-        }
-    }
-}
-
-template<template<class> class P, class T>
-inline std::unique_ptr<Parser<T>> createParser(const std::string& path) {
-
-    auto input_file = gzopen(path.c_str(), "r");
-    if (input_file == nullptr) {
-        throw std::invalid_argument("[bioparser::createParser] error: "
-            "unable to open file " + path + "!");
-    }
-
-    return std::unique_ptr<Parser<T>>(new P<T>(input_file));
-}
-
-template<class T>
-inline Parser<T>::Parser(gzFile input_file, std::uint32_t storage_size)
-        : input_file_(input_file, gzclose), buffer_(65536, 0),
-        buffer_ptr_(0), buffer_bytes_(0), storage_(storage_size, 0) {
-}
-
-template<class T>
-inline Parser<T>::~Parser() {
-}
-
-template<class T>
-inline void Parser<T>::reset() {
-    gzseek(input_file_.get(), 0, SEEK_SET);
-    buffer_ptr_ = 0;
-    buffer_bytes_ = 0;
-}
-
-template<class T>
-inline bool Parser<T>::parse(std::vector<std::shared_ptr<T>>& dst,
-    std::uint64_t max_bytes, bool trim) {
-
-    std::vector<std::unique_ptr<T>> tmp;
-    auto ret = parse_objects(tmp, max_bytes, trim);
-
-    dst.reserve(dst.size() + tmp.size());
-    for (auto& it: tmp) {
-        dst.emplace_back(std::move(it));
-    }
-    return ret;
-}
-
-template<class T>
-inline FastaParser<T>::FastaParser(gzFile input_file)
-        : Parser<T>(input_file, 4194304) {
-}
-
-template<class T>
-inline FastaParser<T>::~FastaParser() {
-}
-
-template<class T>
-inline bool FastaParser<T>::parse(std::vector<std::unique_ptr<T>>& dst,
-    std::uint64_t max_bytes, bool trim) {
-
-    auto input_file = this->input_file_.get();
-    bool is_eof = false;
-
-    std::uint64_t total_bytes = 0;
-    std::uint32_t line_num = 0;
-    std::uint32_t storage_ptr = 0;
-    std::uint32_t data_ptr = 0;
-
-    auto create_T = [&] () -> void {
-
-        if (data_ptr == 0) {
-            throw std::invalid_argument("[bioparser::FastaParser] error: "
-                "invalid file format!");
-        }
-
-        std::uint32_t name_len = data_ptr;
-        if (trim) {
-            rightStripHard(&(this->storage_[0]), name_len);
-        } else {
-            rightStrip(&(this->storage_[0]), name_len);
-        }
-        std::uint32_t data_len = storage_ptr - data_ptr;
-        rightStrip(&(this->storage_[data_ptr]), data_len);
-
-        if (name_len == 0 || this->storage_[0] != '>' || data_len == 0) {
-            throw std::invalid_argument("[bioparser::FastaParser] error: "
-                "invalid file format!");
-        }
-
-        dst.emplace_back(std::unique_ptr<T>(new T(
-            (const char*) &(this->storage_[1]), name_len - 1,
-            (const char*) &(this->storage_[data_ptr]), data_len)));
-
-        total_bytes += storage_ptr;
-        storage_ptr = 0;
-        data_ptr = 0;
-    };
-
-    while (true) {
-
-        std::uint32_t begin_ptr = this->buffer_ptr_;
-        for (; this->buffer_ptr_ < this->buffer_bytes_; ++this->buffer_ptr_) {
-            auto c = this->buffer_[this->buffer_ptr_];
-            if (c == '\n') {
-                std::memcpy(&this->storage_[storage_ptr],
-                    &this->buffer_[begin_ptr],
-                    this->buffer_ptr_ - begin_ptr);
-                storage_ptr += this->buffer_ptr_ - begin_ptr;
-                begin_ptr = this->buffer_ptr_ + 1;
-                if (line_num == 0) {
-                    data_ptr = storage_ptr;
-                    line_num = 1;
-                }
-            } else if (line_num == 1 && c == '>') {
-                line_num = 0;
-                create_T();
-                if (total_bytes >= max_bytes) {
-                    return true;
-                }
-            }
-        }
-        if (begin_ptr < this->buffer_ptr_) {
-            std::memcpy(&this->storage_[storage_ptr],
-                &this->buffer_[begin_ptr],
-                this->buffer_ptr_ - begin_ptr);
-            storage_ptr += this->buffer_ptr_ - begin_ptr;
-        }
-        this->buffer_ptr_ = 0;
-
-        if (is_eof) {
-            break;
-        }
-
-        this->buffer_bytes_ = gzread(input_file, this->buffer_.data(),
-            this->buffer_.size());
-        is_eof = this->buffer_bytes_ < this->buffer_.size();
-
-        if (storage_ptr + this->buffer_bytes_ > this->storage_.size()) {
-            this->storage_.resize(this->storage_.size() * 2);
-        }
-    }
-
-    if (storage_ptr != 0) {
-        create_T();
-    }
-
-    return false;
-}
-
-template<class T>
-inline FastqParser<T>::FastqParser(gzFile input_file)
-        : Parser<T>(input_file, 4194304) {
-}
-
-template<class T>
-inline FastqParser<T>::~FastqParser() {
-}
-
-template<class T>
-inline bool FastqParser<T>::parse(std::vector<std::unique_ptr<T>>& dst,
-    std::uint64_t max_bytes, bool trim) {
-
-    auto input_file = this->input_file_.get();
-    bool is_eof = false;
-
-    std::uint64_t total_bytes = 0;
-    std::uint32_t line_num = 0;
-    std::uint32_t storage_ptr = 0;
-    std::uint32_t data_ptr = 0;
-    std::uint32_t comm_ptr = 0;
-    std::uint32_t qual_ptr = 0;
-
-    auto create_T = [&] () -> void {
-
-        if (data_ptr == 0 || comm_ptr == 0 || qual_ptr == 0) {
-            throw std::invalid_argument("[bioparser::FastqParser] error: "
-                "invalid file format!");
-        }
-
-        std::uint32_t name_len = data_ptr;
-        if (trim) {
-            rightStripHard(&(this->storage_[0]), name_len);
-        } else {
-            rightStrip(&(this->storage_[0]), name_len);
-        }
-        std::uint32_t data_len = comm_ptr - data_ptr;
-        rightStrip(&(this->storage_[data_ptr]), data_len);
-        std::uint32_t qual_len = storage_ptr - qual_ptr;
-        rightStrip(&(this->storage_[qual_ptr]), qual_len);
-
-        if (name_len == 0 || this->storage_[0] != '@' || data_len == 0 ||
-            qual_len == 0 || data_len != qual_len) {
-            throw std::invalid_argument("[bioparser::FastqParser] error: "
-                "invalid file format!");
-        }
-
-        dst.emplace_back(std::unique_ptr<T>(new T(
-            (const char*) &(this->storage_[1]), name_len - 1,
-            (const char*) &(this->storage_[data_ptr]), data_len,
-            (const char*) &(this->storage_[qual_ptr]), qual_len)));
-
-        total_bytes += storage_ptr;
-        storage_ptr = 0;
-        data_ptr = 0;
-        comm_ptr = 0;
-        qual_ptr = 0;
-    };
-
-    while (true) {
-
-        std::uint32_t begin_ptr = this->buffer_ptr_;
-        for (; this->buffer_ptr_ < this->buffer_bytes_; ++this->buffer_ptr_) {
-            auto c = this->buffer_[this->buffer_ptr_];
-            if (c == '\n') {
-                std::memcpy(&(this->storage_[storage_ptr]),
-                    &(this->buffer_)[begin_ptr],
-                    this->buffer_ptr_ - begin_ptr);
-                storage_ptr += this->buffer_ptr_ - begin_ptr;
-                begin_ptr = this->buffer_ptr_ + 1;
-                if (line_num == 0) {
-                    data_ptr = storage_ptr;
-                    line_num = 1;
-                } else if (line_num == 2) {
-                    qual_ptr = storage_ptr;
-                    line_num = 3;
-                } else if (line_num == 3 && storage_ptr - qual_ptr == comm_ptr - data_ptr) {
-                    line_num = 0;
-                    create_T();
-                    if (total_bytes >= max_bytes) {
-                        ++this->buffer_ptr_;
-                        return true;
-                    }
-                }
-            } else if (line_num == 1 && c == '+') {
-                comm_ptr = storage_ptr;
-                line_num = 2;
-            }
-        }
-        if (begin_ptr < this->buffer_ptr_) {
-            std::memcpy(&(this->storage_[storage_ptr]),
-                &(this->buffer_[begin_ptr]),
-                this->buffer_ptr_ - begin_ptr);
-            storage_ptr += this->buffer_ptr_ - begin_ptr;
-        }
-        this->buffer_ptr_ = 0;
-
-        if (is_eof) {
-            break;
-        }
-
-        this->buffer_bytes_ = gzread(input_file, this->buffer_.data(),
-            this->buffer_.size());
-        is_eof = this->buffer_bytes_ < this->buffer_.size();
-
-        if (storage_ptr + this->buffer_bytes_ > this->storage_.size()) {
-            this->storage_.resize(this->storage_.size() * 2);
-        }
-    }
-
-    if (storage_ptr != 0) {
-        create_T();
-    }
-
-    return false;
-}
-
-template<class T>
-inline MhapParser<T>::MhapParser(gzFile input_file)
-        : Parser<T>(input_file, 65536) {
-}
-
-template<class T>
-inline MhapParser<T>::~MhapParser() {
-}
-
-template<class T>
-inline bool MhapParser<T>::parse(std::vector<std::unique_ptr<T>>& dst,
-    std::uint64_t max_bytes, bool) {
-
-    auto input_file = this->input_file_.get();
-    bool is_eof = false;
-
-    std::uint64_t total_bytes = 0;
-    std::uint32_t storage_ptr = 0;
-
-    std::uint64_t a_id = 0, b_id = 0;
-    std::uint32_t a_rc = 0, a_begin = 0, a_end = 0, a_length = 0, b_rc = 0,
-        b_begin = 0, b_end = 0, b_length = 0, minmers = 0;
-    double error = 0;
-
-    auto create_T = [&] () -> void {
-        this->storage_[storage_ptr] = 0;
-        rightStrip(&(this->storage_[0]), storage_ptr);
-
-        std::uint32_t num_values = 0, begin = 0;
-        while (true) {
-            std::uint32_t end = begin;
-            for (std::uint32_t j = begin; j < storage_ptr; ++j) {
-                if (this->storage_[j] == ' ') {
-                    end = j;
-                    break;
-                }
-            }
-            if (end == begin) {
-                end = storage_ptr;
-            }
-            this->storage_[end] = 0;
-
-            switch (num_values) {
-                case 0: a_id = atoll(&(this->storage_[begin])); break;
-                case 1: b_id = atoll(&(this->storage_[begin])); break;
-                case 2: error = atof(&(this->storage_[begin])); break;
-                case 3: minmers = atoi(&(this->storage_[begin])); break;
-                case 4: a_rc = atoi(&(this->storage_[begin])); break;
-                case 5: a_begin = atoi(&(this->storage_[begin])); break;
-                case 6: a_end = atoi(&(this->storage_[begin])); break;
-                case 7: a_length = atoi(&(this->storage_[begin])); break;
-                case 8: b_rc = atoi(&(this->storage_[begin])); break;
-                case 9: b_begin = atoi(&(this->storage_[begin])); break;
-                case 10: b_end = atoi(&(this->storage_[begin])); break;
-                case 11: b_length = atoi(&(this->storage_[begin])); break;
-                default: break;
-            }
-            num_values++;
-            if (end == storage_ptr || num_values == 12) {
-                break;
-            }
-            begin = end + 1;
-        }
-
-        if (num_values != 12) {
-            throw std::invalid_argument("[bioparser::MhapParser] error: "
-                "invalid file format!");
-        }
-
-        dst.emplace_back(std::unique_ptr<T>(new T(a_id, b_id, error,
-            minmers, a_rc, a_begin, a_end, a_length, b_rc, b_begin,
-            b_end, b_length)));
-
-        total_bytes += storage_ptr;
-        storage_ptr = 0;
-    };
-
-    while (true) {
-
-        std::uint32_t begin_ptr = this->buffer_ptr_;
-        for (; this->buffer_ptr_ < this->buffer_bytes_; ++this->buffer_ptr_) {
-            auto c = this->buffer_[this->buffer_ptr_];
-            if (c == '\n') {
-                std::memcpy(&this->storage_[storage_ptr],
-                    &this->buffer_[begin_ptr],
-                    this->buffer_ptr_ - begin_ptr);
-                storage_ptr += this->buffer_ptr_ - begin_ptr;
-                begin_ptr = this->buffer_ptr_ + 1;
-                create_T();
-                if (total_bytes >= max_bytes) {
-                    ++this->buffer_ptr_;
-                    return true;
-                }
-            }
-        }
-        if (begin_ptr < this->buffer_ptr_) {
-            std::memcpy(&this->storage_[storage_ptr],
-                &this->buffer_[begin_ptr],
-                this->buffer_ptr_ - begin_ptr);
-            storage_ptr += this->buffer_ptr_ - begin_ptr;
-        }
-        this->buffer_ptr_ = 0;
-
-        if (is_eof) {
-            break;
-        }
-
-        this->buffer_bytes_ = gzread(input_file, this->buffer_.data(),
-            this->buffer_.size());
-        is_eof = this->buffer_bytes_ < this->buffer_.size();
-
-        if (storage_ptr + this->buffer_bytes_ > this->storage_.size()) {
-            this->storage_.resize(this->storage_.size() * 2);
-        }
-    }
-
-    if (storage_ptr != 0) {
-        create_T();
-    }
-
-    return false;
-}
-
-template<class T>
-inline PafParser<T>::PafParser(gzFile input_file)
-        : Parser<T>(input_file, 65536) {
-}
-
-template<class T>
-inline PafParser<T>::~PafParser() {
-}
-
-template<class T>
-inline bool PafParser<T>::parse(std::vector<std::unique_ptr<T>>& dst,
-    std::uint64_t max_bytes, bool trim) {
-
-    auto input_file = this->input_file_.get();
-    bool is_eof = false;
-
-    std::uint64_t total_bytes = 0;
-    std::uint32_t storage_ptr = 0;
-
-    const char* q_name = nullptr, * t_name = nullptr;
-    std::uint32_t q_name_length = 0, q_length = 0, q_begin = 0, q_end = 0,
-        t_name_length = 0, t_length = 0, t_begin = 0, t_end = 0,
-        matching_bases = 0, overlap_length = 0, mapping_quality = 0;
-    char orientation = '\0';
-
-    auto create_T = [&] () -> void {
-        this->storage_[storage_ptr] = 0;
-        rightStrip(&(this->storage_[0]), storage_ptr);
-
-        std::uint32_t num_values = 0, begin = 0;
-        while (true) {
-            std::uint32_t end = begin;
-            for (std::uint32_t j = begin; j < storage_ptr; ++j) {
-                if (this->storage_[j] == '\t') {
-                    end = j;
-                    break;
-                }
-            }
-            if (end == begin) {
-                end = storage_ptr;
-            }
-            this->storage_[end] = 0;
-
-            switch (num_values) {
-                case 0:
-                    q_name = &(this->storage_[begin]);
-                    q_name_length = end - begin;
-                    break;
-                case 1: q_length = atoi(&(this->storage_[begin])); break;
-                case 2: q_begin = atoi(&(this->storage_[begin])); break;
-                case 3: q_end = atoi(&(this->storage_[begin])); break;
-                case 4: orientation = this->storage_[begin]; break;
-                case 5:
-                    t_name = &(this->storage_[begin]);
-                    t_name_length = end - begin;
-                    break;
-                case 6: t_length = atoi(&(this->storage_[begin])); break;
-                case 7: t_begin = atoi(&(this->storage_[begin])); break;
-                case 8: t_end = atoi(&(this->storage_[begin])); break;
-                case 9: matching_bases = atoi(&(this->storage_[begin])); break;
-                case 10: overlap_length = atoi(&(this->storage_[begin])); break;
-                case 11: mapping_quality = atoi(&(this->storage_[begin])); break;
-                default: break;
-            }
-            num_values++;
-            if (end == storage_ptr || num_values == 12) {
-                break;
-            }
-            begin = end + 1;
-        }
-
-        if (num_values != 12) {
-            throw std::invalid_argument("[bioparser::PafParser] error: "
-                "invalid file format!");
-        }
-
-        if (trim) {
-            rightStripHard(q_name, q_name_length);
-            rightStripHard(t_name, t_name_length);
-        } else {
-            rightStrip(q_name, q_name_length);
-            rightStrip(t_name, t_name_length);
-        }
-
-        if (q_name_length == 0 || t_name_length == 0) {
-            throw std::invalid_argument("[bioparser::PafParser] error: "
-                "invalid file format!");
-        }
-
-        dst.emplace_back(std::unique_ptr<T>(new T(q_name, q_name_length,
-            q_length, q_begin, q_end, orientation, t_name, t_name_length,
-            t_length, t_begin, t_end, matching_bases, overlap_length,
-            mapping_quality)));
-
-        total_bytes += storage_ptr;
-        storage_ptr = 0;
-    };
-
-    while (true) {
-
-        std::uint32_t begin_ptr = this->buffer_ptr_;
-        for (; this->buffer_ptr_ < this->buffer_bytes_; ++this->buffer_ptr_) {
-            auto c = this->buffer_[this->buffer_ptr_];
-            if (c == '\n') {
-                std::memcpy(&this->storage_[storage_ptr],
-                    &this->buffer_[begin_ptr],
-                    this->buffer_ptr_ - begin_ptr);
-                storage_ptr += this->buffer_ptr_ - begin_ptr;
-                begin_ptr = this->buffer_ptr_ + 1;
-                create_T();
-                if (total_bytes >= max_bytes) {
-                    ++this->buffer_ptr_;
-                    return true;
-                }
-            }
-        }
-        if (begin_ptr < this->buffer_ptr_) {
-            std::memcpy(&this->storage_[storage_ptr],
-                &this->buffer_[begin_ptr],
-                this->buffer_ptr_ - begin_ptr);
-            storage_ptr += this->buffer_ptr_ - begin_ptr;
-        }
-        this->buffer_ptr_ = 0;
-
-        if (is_eof) {
-            break;
-        }
-
-        this->buffer_bytes_ = gzread(input_file, this->buffer_.data(),
-            this->buffer_.size());
-        is_eof = this->buffer_bytes_ < this->buffer_.size();
-
-        if (storage_ptr + this->buffer_bytes_ > this->storage_.size()) {
-            this->storage_.resize(this->storage_.size() * 2);
-        }
-    }
-
-    if (storage_ptr != 0) {
-        create_T();
-    }
-
-    return false;
-}
-
-template<class T>
-inline SamParser<T>::SamParser(gzFile input_file)
-        : Parser<T>(input_file, 65536) {
-}
-
-template<class T>
-inline SamParser<T>::~SamParser() {
-}
-
-template<class T>
-inline bool SamParser<T>::parse(std::vector<std::unique_ptr<T>>& dst,
-    std::uint64_t max_bytes, bool trim) {
-
-    auto input_file = this->input_file_.get();
-    bool is_eof = false;
-
-    std::uint64_t total_bytes = 0;
-    std::uint32_t storage_ptr = 0;
-
-    const char* q_name = nullptr, * t_name = nullptr, * cigar = nullptr,
-        * t_next_name = nullptr, * sequence = nullptr, * quality = nullptr;
-    std::uint32_t q_name_length = 0, flag = 0, t_name_length = 0, t_begin = 0,
-        mapping_quality = 0, cigar_length = 0, t_next_name_length = 0,
-        t_next_begin = 0, template_length = 0, sequence_length = 0,
-        quality_length = 0;
-
-    auto create_T = [&] () -> void {
-        if (this->storage_[0] == '@') { // header
-            storage_ptr = 0;
-            return;
-        }
-
-        this->storage_[storage_ptr] = 0;
-        rightStrip(&(this->storage_[0]), storage_ptr);
-
-        std::uint32_t num_values = 0, begin = 0;
-        while (true) {
-            std::uint32_t end = begin;
-            for (std::uint32_t j = begin; j < storage_ptr; ++j) {
-                if (this->storage_[j] == '\t') {
-                    end = j;
-                    break;
-                }
-            }
-            if (end == begin) {
-                end = storage_ptr;
-            }
-            this->storage_[end] = 0;
-
-            switch (num_values) {
-                case 0:
-                    q_name = &(this->storage_[begin]);
-                    q_name_length = end - begin;
-                    break;
-                case 1: flag = atoi(&(this->storage_[begin])); break;
-                case 2:
-                    t_name = &(this->storage_[begin]);
-                    t_name_length = end - begin;
-                    break;
-                case 3: t_begin = atoi(&(this->storage_[begin])); break;
-                case 4: mapping_quality = atoi(&(this->storage_[begin])); break;
-                case 5:
-                    cigar = &(this->storage_[begin]);
-                    cigar_length = end - begin;
-                    break;
-                case 6:
-                    t_next_name = &(this->storage_[begin]);
-                    t_next_name_length = end - begin;
-                    break;
-                case 7: t_next_begin = atoi(&(this->storage_[begin])); break;
-                case 8: template_length = atoi(&(this->storage_[begin])); break;
-                case 9:
-                    sequence = &(this->storage_[begin]);
-                    sequence_length = end - begin;
-                    break;
-                case 10:
-                    quality = &(this->storage_[begin]);
-                    quality_length = end - begin;
-                    break;
-                default: break;
-            }
-            num_values++;
-            if (end == storage_ptr || num_values == 11) {
-                break;
-            }
-            begin = end + 1;
-        }
-
-        if (num_values != 11) {
-            throw std::invalid_argument("[bioparser::SamParser] error: "
-                "invalid file format!");
-        }
-
-        if (trim) {
-            rightStripHard(q_name, q_name_length);
-            rightStripHard(t_name, t_name_length);
-            rightStripHard(t_next_name, t_next_name_length);
-        } else {
-            rightStrip(q_name, q_name_length);
-            rightStrip(t_name, t_name_length);
-            rightStrip(t_next_name, t_next_name_length);
-        }
-
-        rightStrip(cigar, cigar_length);
-        rightStrip(sequence, sequence_length);
-        rightStrip(quality, quality_length);
-
-        if (q_name_length == 0 || t_name_length == 0 ||
-            cigar_length == 0 || t_next_name_length == 0 ||
-            sequence_length == 0 || quality_length == 0 ||
-            (sequence_length > 1 && quality_length > 1 &&
-            sequence_length != quality_length)) {
-
-            throw std::invalid_argument("[bioparser::SamParser] error: "
-                "invalid file format!");
-        }
-
-        dst.emplace_back(std::unique_ptr<T>(new T(q_name, q_name_length,
-            flag, t_name, t_name_length, t_begin, mapping_quality,
-            cigar, cigar_length, t_next_name, t_next_name_length,
-            t_next_begin, template_length, sequence, sequence_length,
-            quality, quality_length)));
-
-        total_bytes += storage_ptr;
-        storage_ptr = 0;
-    };
-
-    while (true) {
-
-        std::uint32_t begin_ptr = this->buffer_ptr_;
-        for (; this->buffer_ptr_ < this->buffer_bytes_; ++this->buffer_ptr_) {
-            auto c = this->buffer_[this->buffer_ptr_];
-            if (c == '\n') {
-                std::memcpy(&this->storage_[storage_ptr],
-                    &this->buffer_[begin_ptr],
-                    this->buffer_ptr_ - begin_ptr);
-                storage_ptr += this->buffer_ptr_ - begin_ptr;
-                begin_ptr = this->buffer_ptr_ + 1;
-                create_T();
-                if (total_bytes >= max_bytes) {
-                    ++this->buffer_ptr_;
-                    return true;
-                }
-            }
-        }
-        if (begin_ptr < this->buffer_ptr_) {
-            std::memcpy(&this->storage_[storage_ptr],
-                &this->buffer_[begin_ptr],
-                this->buffer_ptr_ - begin_ptr);
-            storage_ptr += this->buffer_ptr_ - begin_ptr;
-        }
-        this->buffer_ptr_ = 0;
-
-        if (is_eof) {
-            break;
-        }
-
-        this->buffer_bytes_ = gzread(input_file, this->buffer_.data(),
-            this->buffer_.size());
-        is_eof = this->buffer_bytes_ < this->buffer_.size();
-
-        if (storage_ptr + this->buffer_bytes_ > this->storage_.size()) {
-            this->storage_.resize(this->storage_.size() * 2);
-        }
-    }
-
-    if (storage_ptr != 0) {
-        create_T();
-    }
-
-    return false;
-}
-
-}


=====================================
include/bioparser/fasta_parser.hpp
=====================================
@@ -0,0 +1,106 @@
+// Copyright (c) 2020 Robert Vaser
+
+#ifndef BIOPARSER_FASTA_PARSER_HPP_
+#define BIOPARSER_FASTA_PARSER_HPP_
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+#include <stdexcept>
+#include <string>
+
+#include "bioparser/parser.hpp"
+
+namespace bioparser {
+
+template<class T>
+class FastaParser: public Parser<T> {
+ public:
+  FastaParser(const FastaParser&) = delete;
+  FastaParser& operator=(const FastaParser&) = delete;
+
+  FastaParser(FastaParser&&) = delete;
+  FastaParser& operator=(FastaParser&&) = delete;
+
+  ~FastaParser() {}
+
+  std::vector<std::unique_ptr<T>> Parse(
+      std::uint64_t bytes, bool shorten_names = true) override {
+    std::vector<std::unique_ptr<T>> dst;
+    std::uint64_t parsed_bytes = 0;
+    std::uint32_t data_ptr = 0;
+
+    auto create_T = [&] () -> void {
+      if (data_ptr == 0) {
+        throw std::invalid_argument(
+            "[bioparser::FastaParser] error: invalid file format");
+      }
+
+      auto name_len = shorten_names ?
+          this->Shorten(this->storage().data(), data_ptr) :
+          this->RightStrip(this->storage().data(), data_ptr);
+
+      auto data_len = this->storage_ptr() - data_ptr;
+
+      if (name_len == 0 || this->storage()[0] != '>' || data_len == 0) {
+        throw std::invalid_argument(
+            "[bioparser::FastaParser] error: invalid file format");
+      }
+
+      dst.emplace_back(std::unique_ptr<T>(new T(
+          static_cast<const char*>(this->storage().data() + 1), name_len - 1,
+          static_cast<const char*>(this->storage().data() + data_ptr), data_len)));  // NOLINT
+
+      parsed_bytes += this->storage_ptr();
+      data_ptr = 0;
+      this->Clear();
+    };
+
+    bool is_eof = false;
+    bool is_name = true;
+
+    while (true) {
+      auto buffer_ptr = this->buffer_ptr();
+      for (; buffer_ptr < this->buffer_bytes(); ++buffer_ptr) {
+        auto c = this->buffer()[buffer_ptr];
+        if (c == '\n') {
+          this->Store(buffer_ptr - this->buffer_ptr(), !is_name);
+          if (is_name) {
+            data_ptr = this->storage_ptr();
+            is_name = false;
+          }
+        } else if (!is_name && c == '>') {
+          is_name = true;
+          create_T();
+          if (parsed_bytes >= bytes) {
+            return dst;
+          }
+        }
+      }
+      if (this->buffer_ptr() < buffer_ptr) {
+        this->Store(buffer_ptr - this->buffer_ptr(), !is_name);
+      }
+
+      if (is_eof) {
+        break;
+      }
+      is_eof = this->Read();
+    }
+
+    if (this->storage_ptr() != 0) {
+      create_T();
+    }
+
+    return dst;
+  }
+
+ private:
+  explicit FastaParser(gzFile file)
+      : Parser<T>(file, 4194304) {}  // 4 MB
+
+  friend Parser<T>;
+};
+
+}  // namespace bioparser
+
+#endif  // BIOPARSER_FASTA_PARSER_HPP_


=====================================
include/bioparser/fastq_parser.hpp
=====================================
@@ -0,0 +1,128 @@
+// Copyright (c) 2020 Robert Vaser
+
+#ifndef BIOPARSER_FASTQ_PARSER_HPP_
+#define BIOPARSER_FASTQ_PARSER_HPP_
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+#include <stdexcept>
+#include <string>
+
+#include "bioparser/parser.hpp"
+
+namespace bioparser {
+
+template<class T>
+class FastqParser: public Parser<T> {
+ public:
+  FastqParser(const FastqParser&) = delete;
+  FastqParser& operator=(const FastqParser&) = delete;
+
+  FastqParser(FastqParser&&) = delete;
+  FastqParser& operator=(FastqParser&&) = delete;
+
+  ~FastqParser() {}
+
+  std::vector<std::unique_ptr<T>> Parse(
+      std::uint64_t bytes, bool shorten_names = true) override {
+    std::vector<std::unique_ptr<T>> dst;
+    std::uint64_t parsed_bytes = 0;
+    std::uint32_t data_ptr = 0;
+    std::uint32_t comment_ptr = 0;
+    std::uint32_t quality_ptr = 0;
+
+    auto create_T = [&] () -> void {
+      if (data_ptr == 0 || comment_ptr == 0 || quality_ptr == 0) {
+        throw std::invalid_argument(
+            "[bioparser::FastqParser] error: invalid file format");
+      }
+
+      auto name_len = shorten_names ?
+          this->Shorten(this->storage().data(), data_ptr) :
+          this->RightStrip(this->storage().data(), data_ptr);
+
+      auto data_len = comment_ptr - data_ptr;
+
+      auto quality_len = this->storage_ptr() - quality_ptr;
+
+      if (name_len == 0 || this->storage()[0] != '@' || data_len == 0 ||
+          quality_len == 0 || data_len != quality_len) {
+        throw std::invalid_argument(
+            "[bioparser::FastqParser] error: invalid file format");
+      }
+
+      dst.emplace_back(std::unique_ptr<T>(new T(
+          static_cast<const char*>(this->storage().data() + 1), name_len - 1,
+          static_cast<const char*>(this->storage().data() + data_ptr), data_len,
+          static_cast<const char*>(this->storage().data() + quality_ptr), quality_len)));  // NOLINT
+
+      parsed_bytes += this->storage_ptr();
+      data_ptr = 0;
+      comment_ptr = 0;
+      quality_ptr = 0;
+      this->Clear();
+    };
+
+    bool is_eof = false;
+    bool is_name = true;
+    bool is_data = false;
+    bool is_comment = false;
+    bool is_quality = false;
+
+    while (true) {
+      auto buffer_ptr = this->buffer_ptr();
+      for (; buffer_ptr < this->buffer_bytes(); ++buffer_ptr) {
+        auto c = this->buffer()[buffer_ptr];
+        if (c == '\n') {
+          this->Store(buffer_ptr - this->buffer_ptr(), !is_name);
+          if (is_name) {
+            is_name = false;
+            is_data = true;
+            data_ptr = this->storage_ptr();
+          } else if (is_comment) {
+            is_comment = false;
+            is_quality = true;
+            quality_ptr = this->storage_ptr();
+          } else if (is_quality &&
+                this->storage_ptr() - quality_ptr == comment_ptr - data_ptr) {
+            is_quality = false;
+            is_name = true;
+            create_T();
+            if (parsed_bytes >= bytes) {
+              return dst;
+            }
+          }
+        } else if (is_data && c == '+') {
+          is_data = false;
+          is_comment = true;
+          comment_ptr = this->storage_ptr();
+        }
+      }
+      if (this->buffer_ptr() < buffer_ptr) {
+        this->Store(buffer_ptr - this->buffer_ptr(), !is_name);
+      }
+
+      if (is_eof) {
+        break;
+      }
+      is_eof = this->Read();
+    }
+
+    if (this->storage_ptr() != 0) {
+      create_T();
+    }
+
+    return dst;
+  }
+
+ private:
+  explicit FastqParser(gzFile file)
+      : Parser<T>(file, 4194304) {}  // 4 MB
+
+  friend Parser<T>;
+};
+
+}  // namespace bioparser
+
+#endif  // BIOPARSER_FASTQ_PARSER_HPP_


=====================================
include/bioparser/mhap_parser.hpp
=====================================
@@ -0,0 +1,139 @@
+// Copyright (c) 2020 Robert Vaser
+
+#ifndef BIOPARSER_MHAP_PARSER_HPP_
+#define BIOPARSER_MHAP_PARSER_HPP_
+
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+#include <vector>
+#include <stdexcept>
+
+#include "bioparser/parser.hpp"
+
+namespace bioparser {
+
+template<class T>
+class MhapParser: public Parser<T> {
+ public:
+  MhapParser(const MhapParser&) = delete;
+  MhapParser& operator=(const MhapParser&) = delete;
+
+  MhapParser(MhapParser&&) = delete;
+  MhapParser& operator=(MhapParser&&) = delete;
+
+  ~MhapParser() {}
+
+  std::vector<std::unique_ptr<T>> Parse(
+      std::uint64_t bytes, bool = true) override {
+    std::vector<std::unique_ptr<T>> dst;
+    std::uint64_t parsed_bytes = 0;
+
+    std::uint64_t lhs_id = 0;
+    std::uint64_t rhs_id = 0;
+    double error = 0;
+    std::uint32_t num_minmers = 0;
+    std::uint32_t lhs_strand = 0;
+    std::uint32_t lhs_begin = 0;
+    std::uint32_t lhs_end = 0;
+    std::uint32_t lhs_len = 0;
+    std::uint32_t rhs_strand = 0;
+    std::uint32_t rhs_begin = 0;
+    std::uint32_t rhs_end = 0;
+    std::uint32_t rhs_len = 0;
+
+    auto create_T = [&] () -> void {
+      auto storage_ptr = this->RightStrip(
+          this->storage().data(),
+          this->storage_ptr());
+      this->Terminate(storage_ptr);
+
+      std::uint32_t num_values = 0;
+      std::uint32_t begin_ptr = 0;
+      while (true) {
+        auto end_ptr = begin_ptr;
+        while (end_ptr < storage_ptr && this->storage()[end_ptr] != ' ') {
+          ++end_ptr;
+        }
+        this->Terminate(end_ptr);
+
+        switch (num_values) {
+          case 0: lhs_id = std::atoll(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 1: rhs_id = std::atoll(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 2: error = std::atof(this->storage().data() + begin_ptr); break;
+          case 3: num_minmers = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 4: lhs_strand = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 5: lhs_begin = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 6: lhs_end = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 7: lhs_len = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 8: rhs_strand = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 9: rhs_begin = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 10: rhs_end = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 11: rhs_len = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          default: break;
+        }
+
+        ++num_values;
+        if (end_ptr == storage_ptr || num_values == 12) {
+          break;
+        }
+        begin_ptr = end_ptr + 1;
+      }
+
+      if (num_values != 12) {
+        throw std::invalid_argument(
+            "[bioparser::MhapParser] error: invalid file format");
+      }
+
+      dst.emplace_back(std::unique_ptr<T>(new T(
+          lhs_id, rhs_id,
+          error,
+          num_minmers,
+          lhs_strand, lhs_begin, lhs_end, lhs_len,
+          rhs_strand, rhs_begin, rhs_end, rhs_len)));
+
+      parsed_bytes += this->storage_ptr();
+      this->Clear();
+    };
+
+    bool is_eof = false;
+
+    while (true) {
+      auto buffer_ptr = this->buffer_ptr();
+      for (; buffer_ptr < this->buffer_bytes(); ++buffer_ptr) {
+        auto c = this->buffer()[buffer_ptr];
+        if (c == '\n') {
+          this->Store(buffer_ptr - this->buffer_ptr());
+          create_T();
+          if (parsed_bytes >= bytes) {
+            return dst;
+          }
+        }
+      }
+      if (this->buffer_ptr() < buffer_ptr) {
+        this->Store(buffer_ptr - this->buffer_ptr());
+      }
+
+      if (is_eof) {
+        break;
+      }
+      is_eof = this->Read();
+    }
+
+    if (this->storage_ptr() != 0) {
+      create_T();
+    }
+
+    return dst;
+  }
+
+ private:
+  explicit MhapParser(gzFile file)
+      : Parser<T>(file, 65536) {}  // 64 kB
+
+  friend Parser<T>;
+};
+
+}  // namespace bioparser
+
+#endif  // BIOPARSER_MHAP_PARSER_HPP_


=====================================
include/bioparser/paf_parser.hpp
=====================================
@@ -0,0 +1,161 @@
+// Copyright (c) 2020 Robert Vaser
+
+#ifndef BIOPARSER_PAF_PARSER_HPP_
+#define BIOPARSER_PAF_PARSER_HPP_
+
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+#include <vector>
+#include <stdexcept>
+
+#include "bioparser/parser.hpp"
+
+namespace bioparser {
+
+template<class T>
+class PafParser: public Parser<T> {
+ public:
+  PafParser(const PafParser&) = delete;
+  PafParser& operator=(const PafParser&) = delete;
+
+  PafParser(PafParser&&) = delete;
+  PafParser& operator=(PafParser&&) = delete;
+
+  ~PafParser() {}
+
+  std::vector<std::unique_ptr<T>> Parse(
+      std::uint64_t bytes, bool shorten_names = true) override {
+    std::vector<std::unique_ptr<T>> dst;
+    std::uint64_t parsed_bytes = 0;
+
+    const char* q_name = nullptr;
+    std::uint32_t q_name_len = 0;
+    std::uint32_t q_len = 0;
+    std::uint32_t q_begin = 0;
+    std::uint32_t q_end = 0;
+    const char* t_name = nullptr;
+    std::uint32_t t_name_len = 0;
+    std::uint32_t t_len = 0;
+    std::uint32_t t_begin = 0;
+    std::uint32_t t_end = 0;
+    std::uint32_t num_matches = 0;
+    std::uint32_t overlap_len = 0;
+    std::uint32_t quality = 0;
+    char orientation = '\0';
+
+    auto create_T = [&] () -> void {
+      auto storage_ptr = this->RightStrip(
+          this->storage().data(),
+          this->storage_ptr());
+      this->Terminate(storage_ptr);
+
+      std::uint32_t num_values = 0;
+      std::uint32_t begin_ptr = 0;
+      while (true) {
+        auto end_ptr = begin_ptr;
+        while (end_ptr < storage_ptr && this->storage()[end_ptr] != '\t') {
+          ++end_ptr;
+        }
+        this->Terminate(end_ptr);
+
+        switch (num_values) {
+          case 0:
+            q_name = this->storage().data() + begin_ptr;
+            q_name_len = end_ptr - begin_ptr;
+            break;
+          case 1: q_len = std::atoi(this->storage().data() + begin_ptr); break;
+          case 2: q_begin = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 3: q_end = std::atoi(this->storage().data() + begin_ptr); break;
+          case 4: orientation = this->storage()[begin_ptr]; break;
+          case 5:
+            t_name = this->storage().data() + begin_ptr;
+            t_name_len = end_ptr - begin_ptr;
+            break;
+          case 6: t_len = std::atoi(this->storage().data() + begin_ptr); break;
+          case 7: t_begin = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 8: t_end = std::atoi(this->storage().data() + begin_ptr); break;
+          case 9: num_matches = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 10: overlap_len = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 11: quality = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          default: break;
+        }
+
+        ++num_values;
+        if (end_ptr == storage_ptr || num_values == 12) {
+          break;
+        }
+        begin_ptr = end_ptr + 1;
+      }
+
+      if (num_values != 12) {
+        throw std::invalid_argument(
+            "[bioparser::PafParser] error: invalid file format");
+      }
+
+      q_name_len = shorten_names ?
+          this->Shorten(q_name, q_name_len) :
+          this->RightStrip(q_name, q_name_len);
+
+      t_name_len = shorten_names ?
+          this->Shorten(t_name, t_name_len) :
+          this->RightStrip(t_name, t_name_len);
+
+      if (q_name_len == 0 || t_name_len == 0) {
+        throw std::invalid_argument(
+            "[bioparser::PafParser] error: invalid file format");
+      }
+
+      dst.emplace_back(std::unique_ptr<T>(new T(
+          q_name, q_name_len, q_len, q_begin, q_end,
+          orientation,
+          t_name, t_name_len, t_len, t_begin, t_end,
+          num_matches,
+          overlap_len,
+          quality)));
+
+      parsed_bytes += this->storage_ptr();
+      this->Clear();
+    };
+
+    bool is_eof = false;
+
+    while (true) {
+      auto buffer_ptr = this->buffer_ptr();
+      for (; buffer_ptr < this->buffer_bytes(); ++buffer_ptr) {
+        auto c = this->buffer()[buffer_ptr];
+        if (c == '\n') {
+          this->Store(buffer_ptr - this->buffer_ptr());
+          create_T();
+          if (parsed_bytes >= bytes) {
+            return dst;
+          }
+        }
+      }
+      if (this->buffer_ptr() < buffer_ptr) {
+        this->Store(buffer_ptr - this->buffer_ptr());
+      }
+
+      if (is_eof) {
+        break;
+      }
+      is_eof = this->Read();
+    }
+
+    if (this->storage_ptr() != 0) {
+      create_T();
+    }
+
+    return dst;
+  }
+
+ private:
+  explicit PafParser(gzFile file)
+      : Parser<T>(file, 65536) {}  // 64 kB
+
+  friend Parser<T>;
+};
+
+}  // namespace bioparser
+
+#endif  // BIOPARSER_PAF_PARSER_HPP_


=====================================
include/bioparser/parser.hpp
=====================================
@@ -0,0 +1,133 @@
+// Copyright (c) 2020 Robert Vaser
+
+#ifndef BIOPARSER_PARSER_HPP_
+#define BIOPARSER_PARSER_HPP_
+
+#include <algorithm>
+#include <cctype>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include "zlib.h"  // NOLINT
+
+namespace bioparser {
+
+template<class T>
+class Parser {  // Parser factory
+ public:
+  Parser(const Parser&) = delete;
+  Parser& operator=(const Parser&) = delete;
+
+  Parser(Parser&&) = delete;
+  Parser& operator=(Parser&&) = delete;
+
+  virtual ~Parser() {}
+
+  template<template<class> class P>
+  static std::unique_ptr<Parser<T>> Create(const std::string& path) {
+    auto file = gzopen(path.c_str(), "r");
+    if (file == nullptr) {
+      throw std::invalid_argument(
+          "[bioparser::Parser::Create] error: unable to open file " + path);
+    }
+    return std::unique_ptr<Parser<T>>(new P<T>(file));
+  }
+
+  // by default, all parsers shrink sequence names to the first white space
+  virtual std::vector<std::unique_ptr<T>> Parse(
+      std::uint64_t bytes, bool shorten_names = true) = 0;
+
+  void Reset() {
+    gzseek(file_.get(), 0, SEEK_SET);
+    buffer_ptr_ = 0;
+    buffer_bytes_ = 0;
+  }
+
+ protected:
+  Parser(gzFile file, std::uint32_t storage_size)
+      : file_(file, gzclose),
+        buffer_(65536, 0),  // 64 kB
+        buffer_ptr_(0),
+        buffer_bytes_(0),
+        storage_(storage_size, 0),
+        storage_ptr_(0) {}
+
+  const std::vector<char>& buffer() const {
+    return buffer_;
+  }
+
+  std::uint32_t buffer_ptr() const {
+    return buffer_ptr_;
+  }
+
+  std::uint32_t buffer_bytes() const {
+    return buffer_bytes_;
+  }
+
+  const std::vector<char>& storage() const {
+    return storage_;
+  }
+
+  std::uint32_t storage_ptr() const {
+    return storage_ptr_;
+  }
+
+  bool Read() {
+    buffer_ptr_ = 0;
+    buffer_bytes_ = gzread(file_.get(), buffer_.data(), buffer_.size());
+    return buffer_bytes_ < buffer_.size();
+  }
+
+  void Store(std::uint32_t count, bool strip = false) {
+    if (buffer_ptr_ + count > buffer_.size()) {
+      throw std::invalid_argument(
+          "[bioparser::Parser::Store] error: buffer overflow");
+    }
+    if (storage_ptr_ + count > storage_.size()) {
+      storage_.resize(2 * storage_.size());
+    }
+    std::memcpy(&storage_[storage_ptr_], &buffer_[buffer_ptr_], count);
+    storage_ptr_ += strip ? RightStrip(&storage_[storage_ptr_], count) : count;
+    buffer_ptr_ += count + 1;  // ignore sought character
+  }
+
+  void Terminate(std::uint32_t i) {
+    storage_[i] = '\0';
+  }
+
+  void Clear() {
+    storage_ptr_ = 0;
+  }
+
+  static std::uint32_t RightStrip(const char* str, std::uint32_t str_len) {
+    while (str_len > 0 && std::isspace(str[str_len - 1])) {
+      --str_len;
+    }
+    return str_len;
+  }
+
+  static std::uint32_t Shorten(const char* str, std::uint32_t str_len) {
+    for (std::uint32_t i = 0; i < str_len; ++i) {
+      if (std::isspace(str[i])) {
+        return i;
+      }
+    }
+    return str_len;
+  }
+
+ private:
+  std::unique_ptr<gzFile_s, int(*)(gzFile)> file_;
+  std::vector<char> buffer_;
+  std::uint32_t buffer_ptr_;
+  std::uint32_t buffer_bytes_;
+  std::vector<char> storage_;
+  std::uint32_t storage_ptr_;
+};
+
+}  // namespace bioparser
+
+#endif  // BIOPARSER_PARSER_HPP_


=====================================
include/bioparser/sam_parser.hpp
=====================================
@@ -0,0 +1,193 @@
+// Copyright (c) 2020 Robert Vaser
+
+#ifndef BIOPARSER_SAM_PARSER_HPP_
+#define BIOPARSER_SAM_PARSER_HPP_
+
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+#include <vector>
+#include <stdexcept>
+
+#include "bioparser/parser.hpp"
+
+namespace bioparser {
+
+template<class T>
+class SamParser: public Parser<T> {
+ public:
+  SamParser(const SamParser&) = delete;
+  SamParser& operator=(const SamParser&) = delete;
+
+  SamParser(SamParser&&) = delete;
+  SamParser& operator=(SamParser&&) = delete;
+
+  ~SamParser() {}
+
+  std::vector<std::unique_ptr<T>> Parse(
+      std::uint64_t bytes, bool shorten_names = true) override {
+    std::vector<std::unique_ptr<T>> dst;
+    std::uint64_t parsed_bytes = 0;
+
+    const char* q_name = nullptr;
+    std::uint32_t q_name_len = 0;
+    std::uint32_t flag = 0;
+    const char* t_name = nullptr;
+    std::uint32_t t_name_len = 0;
+    std::uint32_t t_begin = 0;
+    std::uint32_t map_quality = 0;
+    const char* cigar = nullptr;
+    std::uint32_t cigar_len = 0;
+    const char* t_next_name = nullptr;
+    std::uint32_t t_next_name_len = 0;
+    std::uint32_t t_next_begin = 0;
+    std::uint32_t template_len = 0;
+    const char* data = nullptr;
+    std::uint32_t data_len = 0;
+    const char* quality = nullptr;
+    std::uint32_t quality_len = 0;
+
+    auto create_T = [&] () -> void {
+      if (this->storage()[0] == '@') {  // file header
+        this->Clear();
+        return;
+      }
+      auto storage_ptr = this->RightStrip(
+          this->storage().data(),
+          this->storage_ptr());
+      this->Terminate(storage_ptr);
+
+      std::uint32_t num_values = 0;
+      std::uint32_t begin_ptr = 0;
+      while (true) {
+        auto end_ptr = begin_ptr;
+        while (end_ptr < storage_ptr && this->storage()[end_ptr] != '\t') {
+          ++end_ptr;
+        }
+        this->Terminate(end_ptr);
+
+        switch (num_values) {
+          case 0:
+            q_name = this->storage().data() + begin_ptr;
+            q_name_len = end_ptr - begin_ptr;
+            break;
+          case 1: flag = std::atoi(this->storage().data() + begin_ptr); break;
+          case 2:
+            t_name = this->storage().data() + begin_ptr;
+            t_name_len = end_ptr - begin_ptr;
+            break;
+          case 3: t_begin = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 4: map_quality = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 5:
+            cigar = this->storage().data() + begin_ptr;
+            cigar_len = end_ptr - begin_ptr;
+            break;
+          case 6:
+            t_next_name = this->storage().data() + begin_ptr;
+            t_next_name_len = end_ptr - begin_ptr;
+            break;
+          case 7: t_next_begin = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 8: template_len = std::atoi(this->storage().data() + begin_ptr); break;  // NOLINT
+          case 9:
+            data = this->storage().data() + begin_ptr;
+            data_len = end_ptr - begin_ptr;
+            break;
+          case 10:
+            quality = this->storage().data() + begin_ptr;
+            quality_len = end_ptr - begin_ptr;
+            break;
+          default: break;
+        }
+
+        ++num_values;
+        if (end_ptr == storage_ptr || num_values == 11) {
+          break;
+        }
+        begin_ptr = end_ptr + 1;
+      }
+
+      if (num_values != 11) {
+        throw std::invalid_argument(
+            "[bioparser::SamParser] error: invalid file format");
+      }
+
+      q_name_len = shorten_names ?
+          this->Shorten(q_name, q_name_len) :
+          this->RightStrip(q_name, q_name_len);
+
+      t_name_len = shorten_names ?
+          this->Shorten(t_name, t_name_len) :
+          this->RightStrip(t_name, t_name_len);
+
+      cigar_len = this->RightStrip(cigar, cigar_len);
+
+      t_next_name_len = shorten_names ?
+          this->Shorten(t_next_name, t_next_name_len) :
+          this->RightStrip(t_next_name, t_next_name_len);
+
+      data_len = this->RightStrip(data, data_len);
+      quality_len = this->RightStrip(quality, quality_len);
+
+      if (q_name_len == 0 || t_name_len == 0 || cigar_len == 0 ||
+          t_next_name_len == 0 || data_len == 0 || quality_len == 0 ||
+          (data_len > 1 && quality_len > 1 && data_len != quality_len)) {
+        throw std::invalid_argument(
+            "[bioparser::SamParser] error: invalid file format");
+      }
+
+      dst.emplace_back(std::unique_ptr<T>(new T(
+          q_name, q_name_len,
+          flag,
+          t_name, t_name_len, t_begin,
+          map_quality,
+          cigar, cigar_len,
+          t_next_name, t_next_name_len, t_next_begin,
+          template_len,
+          data, data_len,
+          quality, quality_len)));
+
+      parsed_bytes += this->storage_ptr();
+      this->Clear();
+    };
+
+    bool is_eof = false;
+
+    while (true) {
+      auto buffer_ptr = this->buffer_ptr();
+      for (; buffer_ptr < this->buffer_bytes(); ++buffer_ptr) {
+        auto c = this->buffer()[buffer_ptr];
+        if (c == '\n') {
+          this->Store(buffer_ptr - this->buffer_ptr());
+          create_T();
+          if (parsed_bytes >= bytes) {
+            return dst;
+          }
+        }
+      }
+      if (this->buffer_ptr() < buffer_ptr) {
+        this->Store(buffer_ptr - this->buffer_ptr());
+      }
+
+      if (is_eof) {
+        break;
+      }
+      is_eof = this->Read();
+    }
+
+    if (this->storage_ptr() != 0) {
+      create_T();
+    }
+
+    return dst;
+  }
+
+ private:
+  explicit SamParser(gzFile file)
+      : Parser<T>(file, 65536) {}  // 64 kB
+
+  friend Parser<T>;
+};
+
+}  // namespace bioparser
+
+#endif  // BIOPARSER_SAM_PARSER_HPP_


=====================================
test/bioparser_test.cpp deleted
=====================================
@@ -1,805 +0,0 @@
-/*!
- * @file bioparser_test.cpp
- *
- * @brief Bioparser unit test source file
- */
-
-#include "bioparser_test_config.h"
-
-#include "bioparser/bioparser.hpp"
-#include "gtest/gtest.h"
-
-class Read {
-public:
-    Read(const char* name, std::uint32_t name_length,
-        const char* sequence, std::uint32_t sequence_length)
-            : name_(name, name_length), sequence_(sequence, sequence_length),
-            quality_() {
-    }
-
-    Read(const char* name, std::uint32_t name_length,
-        const char* sequence, std::uint32_t sequence_length,
-        const char* quality, std::uint32_t quality_length)
-            : name_(name, name_length), sequence_(sequence, sequence_length),
-            quality_(quality, quality_length) {
-    }
-
-    ~Read() {}
-
-    std::string name_;
-    std::string sequence_;
-    std::string quality_;
-};
-
-void reads_summary(std::uint32_t& name_size, std::uint32_t& sequence_size,
-    std::uint32_t& quality_size, const std::vector<std::unique_ptr<Read>>& reads) {
-
-    name_size = 0;
-    sequence_size = 0;
-    quality_size = 0;
-    for (const auto& it: reads) {
-        name_size += it->name_.size();
-        sequence_size += it->sequence_.size();
-        quality_size += it->quality_.size();
-    }
-}
-
-class Overlap {
-public:
-    Overlap(std::uint64_t a_id,
-        std::uint64_t b_id,
-        double error,
-        std::uint32_t minmers,
-        std::uint32_t a_rc,
-        std::uint32_t a_begin,
-        std::uint32_t a_end,
-        std::uint32_t a_length,
-        std::uint32_t b_rc,
-        std::uint32_t b_begin,
-        std::uint32_t b_end,
-        std::uint32_t b_length)
-            : q_name_(),
-            q_id_(a_id - 1),
-            q_begin_(a_begin),
-            q_end_(a_end),
-            q_length_(a_length),
-            t_name_(),
-            t_id_(b_id - 1),
-            t_begin_(b_begin),
-            t_end_(b_end),
-            t_length_(b_length),
-            orientation_(a_rc == b_rc ? '+' : '-'),
-            error_(error * 10000),
-            minmers_(minmers),
-            matching_bases_(),
-            overlap_length_(),
-            mapping_quality_() {
-    }
-
-    Overlap(const char* q_name, std::uint32_t q_name_length,
-        std::uint32_t q_length,
-        std::uint32_t q_begin,
-        std::uint32_t q_end,
-        char orientation,
-        const char* t_name, std::uint32_t t_name_length,
-        std::uint32_t t_length,
-        std::uint32_t t_begin,
-        std::uint32_t t_end,
-        std::uint32_t matching_bases,
-        std::uint32_t overlap_length,
-        std::uint32_t quality)
-            : q_name_(q_name, q_name_length),
-            q_id_(),
-            q_begin_(q_begin),
-            q_end_(q_end),
-            q_length_(q_length),
-            t_name_(t_name, t_name_length),
-            t_id_(),
-            t_begin_(t_begin),
-            t_end_(t_end),
-            t_length_(t_length),
-            orientation_(orientation),
-            error_(),
-            minmers_(),
-            matching_bases_(matching_bases),
-            overlap_length_(overlap_length),
-            mapping_quality_(quality) {
-    }
-
-    ~Overlap() {}
-
-    std::string q_name_;
-    std::uint64_t q_id_;
-    std::uint32_t q_begin_;
-    std::uint32_t q_end_;
-    std::uint32_t q_length_;
-    std::string t_name_;
-    std::uint64_t t_id_;
-    std::uint32_t t_begin_;
-    std::uint32_t t_end_;
-    std::uint32_t t_length_;
-    char orientation_;
-    std::uint32_t error_;
-    std::uint32_t minmers_;
-    std::uint32_t matching_bases_;
-    std::uint32_t overlap_length_;
-    std::uint32_t mapping_quality_;
-};
-
-void overlaps_summary(std::uint32_t& name_size, std::uint32_t& total_value,
-    const std::vector<std::unique_ptr<Overlap>>& overlaps) {
-
-    name_size = 0;
-    total_value = 0;
-    for (const auto& it: overlaps) {
-        name_size += it->q_name_.size();
-        name_size += it->t_name_.size();
-        total_value += it->q_id_;
-        total_value += it->q_begin_;
-        total_value += it->q_end_;
-        total_value += it->q_length_;
-        total_value += it->t_id_;
-        total_value += it->t_begin_;
-        total_value += it->t_end_;
-        total_value += it->t_length_;
-        total_value += it->orientation_;
-        total_value += it->error_;
-        total_value += it->minmers_;
-        total_value += it->matching_bases_;
-        total_value += it->overlap_length_;
-        total_value += it->mapping_quality_;
-    }
-}
-
-class Alignment {
-public:
-    Alignment(const char* q_name, std::uint32_t q_name_length,
-        std::uint32_t flag,
-        const char* t_name, std::uint32_t t_name_length,
-        std::uint32_t t_begin,
-        std::uint32_t mapping_quality,
-        const char* cigar, std::uint32_t cigar_length,
-        const char* t_next_name, std::uint32_t t_next_name_length,
-        std::uint32_t t_next_begin,
-        std::uint32_t template_length,
-        const char* sequence, std::uint32_t sequence_length,
-        const char* quality, std::uint32_t quality_length)
-            : q_name_(q_name, q_name_length),
-            flag_(flag),
-            t_name_(t_name, t_name_length),
-            t_begin_(t_begin),
-            mapping_quality_(mapping_quality),
-            cigar_(cigar, cigar_length),
-            t_next_name_(t_next_name, t_next_name_length),
-            t_next_begin_(t_next_begin),
-            template_length_(template_length),
-            sequence_(sequence, sequence_length),
-            quality_(quality, quality_length) {
-    }
-
-    ~Alignment() {}
-
-    std::string q_name_;
-    std::uint32_t flag_;
-    std::string t_name_;
-    std::uint32_t t_begin_;
-    std::uint32_t mapping_quality_;
-    std::string cigar_;
-    std::string t_next_name_;
-    std::uint32_t t_next_begin_;
-    std::uint32_t template_length_;
-    std::string sequence_;
-    std::string quality_;
-};
-
-void alignments_summary(std::uint32_t& string_size, std::uint32_t& total_value,
-    const std::vector<std::unique_ptr<Alignment>>& alignments) {
-
-    string_size = 0;
-    total_value = 0;
-    for (const auto& it: alignments) {
-        string_size += it->q_name_.size();
-        string_size += it->t_name_.size();
-        string_size += it->cigar_.size();
-        string_size += it->t_next_name_.size();
-        string_size += it->sequence_.size();
-        string_size += it->quality_.size();
-        total_value += it->flag_;
-        total_value += it->t_begin_;
-        total_value += it->mapping_quality_;
-        total_value += it->t_next_begin_;
-        total_value += it->template_length_;
-    }
-}
-
-class BioparserFastaTest: public ::testing::Test {
-public:
-    void SetUp(const std::string& file_name) {
-        parser = bioparser::createParser<bioparser::FastaParser, Read>(file_name);
-    }
-
-    void TearDown() {}
-
-    std::unique_ptr<bioparser::Parser<Read>> parser;
-};
-
-class BioparserFastqTest: public ::testing::Test {
-public:
-    void SetUp(const std::string& file_name) {
-        parser = bioparser::createParser<bioparser::FastqParser, Read>(file_name);
-    }
-
-    void TearDown() {}
-
-    std::unique_ptr<bioparser::Parser<Read>> parser;
-};
-
-class BioparserMhapTest: public ::testing::Test {
-public:
-    void SetUp(const std::string& file_name) {
-        parser = bioparser::createParser<bioparser::MhapParser, Overlap>(file_name);
-    }
-
-    void TearDown() {}
-
-    std::unique_ptr<bioparser::Parser<Overlap>> parser;
-};
-
-class BioparserPafTest: public ::testing::Test {
-public:
-    void SetUp(const std::string& file_name) {
-        parser = bioparser::createParser<bioparser::PafParser, Overlap>(file_name);
-    }
-
-    void TearDown() {}
-
-    std::unique_ptr<bioparser::Parser<Overlap>> parser;
-};
-
-class BioparserSamTest: public ::testing::Test {
-public:
-    void SetUp(const std::string& file_name) {
-        parser = bioparser::createParser<bioparser::SamParser, Alignment>(file_name);
-    }
-
-    void TearDown() {}
-
-    std::unique_ptr<bioparser::Parser<Alignment>> parser;
-};
-
-TEST(BioparserTest, CreateParserError) {
-    try {
-        auto parser = bioparser::createParser<bioparser::FastaParser, Read>("");
-    } catch (std::invalid_argument& exception) {
-        EXPECT_STREQ(exception.what(), "[bioparser::createParser] error: "
-            "unable to open file !");
-    }
-}
-
-TEST_F(BioparserFastaTest, ParseWhole) {
-
-    SetUp(bioparser_test_data_path + "sample.fasta");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    parser->parse(reads, -1);
-
-    std::uint32_t name_size = 0, sequence_size = 0, quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    EXPECT_EQ(14U, reads.size());
-    EXPECT_EQ(65U, name_size);
-    EXPECT_EQ(109117U, sequence_size);
-    EXPECT_EQ(0U, quality_size);
-}
-
-TEST_F(BioparserFastaTest, ParseWholeWithoutTrimming) {
-
-    SetUp(bioparser_test_data_path + "sample.fasta");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    parser->parse(reads, -1, false);
-
-    std::uint32_t name_size = 0, sequence_size = 0, quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    EXPECT_EQ(14U, reads.size());
-    EXPECT_EQ(75U, name_size);
-    EXPECT_EQ(109117U, sequence_size);
-    EXPECT_EQ(0U, quality_size);
-}
-
-TEST_F(BioparserFastaTest, CompressedParseWhole) {
-
-    SetUp(bioparser_test_data_path + "sample.fasta.gz");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    parser->parse(reads, -1);
-
-    std::uint32_t name_size = 0, sequence_size = 0, quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    EXPECT_EQ(14U, reads.size());
-    EXPECT_EQ(65U, name_size);
-    EXPECT_EQ(109117U, sequence_size);
-    EXPECT_EQ(0U, quality_size);
-}
-
-TEST_F(BioparserFastaTest, CompressedParseWholeWithoutTrimming) {
-
-    SetUp(bioparser_test_data_path + "sample.fasta.gz");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    parser->parse(reads, -1, false);
-
-    std::uint32_t name_size = 0, sequence_size = 0, quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    EXPECT_EQ(14U, reads.size());
-    EXPECT_EQ(75U, name_size);
-    EXPECT_EQ(109117U, sequence_size);
-    EXPECT_EQ(0U, quality_size);
-}
-
-TEST_F(BioparserFastaTest, ParseInChunks) {
-
-    SetUp(bioparser_test_data_path + "sample.fasta");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    while (parser->parse(reads, 65536));
-
-    std::uint32_t name_size = 0, sequence_size = 0, quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    EXPECT_EQ(14U, reads.size());
-    EXPECT_EQ(65U, name_size);
-    EXPECT_EQ(109117U, sequence_size);
-    EXPECT_EQ(0U, quality_size);
-}
-
-TEST_F(BioparserFastaTest, CompressedParseInChunks) {
-
-    SetUp(bioparser_test_data_path + "sample.fasta.gz");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    while (parser->parse(reads, 65536));
-
-    std::uint32_t name_size = 0, sequence_size = 0, quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    EXPECT_EQ(14U, reads.size());
-    EXPECT_EQ(65U, name_size);
-    EXPECT_EQ(109117U, sequence_size);
-    EXPECT_EQ(0U, quality_size);
-}
-
-TEST_F(BioparserFastaTest, FormatError) {
-
-    SetUp(bioparser_test_data_path + "sample.fastq");
-    std::vector<std::unique_ptr<Read>> reads;
-
-    try {
-        parser->parse(reads, -1);
-    } catch (std::invalid_argument& exception) {
-        EXPECT_STREQ(exception.what(), "[bioparser::FastaParser] error: "
-            "invalid file format!");
-    }
-}
-
-TEST_F(BioparserFastaTest, CompressedFormatError) {
-
-    SetUp(bioparser_test_data_path + "sample.fastq.gz");
-    std::vector<std::unique_ptr<Read>> reads;
-
-    try {
-        parser->parse(reads, -1);
-    } catch (std::invalid_argument& exception) {
-        EXPECT_STREQ(exception.what(), "[bioparser::FastaParser] error: "
-            "invalid file format!");
-    }
-}
-
-TEST_F(BioparserFastaTest, ParseAndReset) {
-
-    SetUp(bioparser_test_data_path + "sample.fasta");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    parser->parse(reads, -1);
-
-    std::uint32_t num_reads = reads.size(), name_size = 0, sequence_size = 0,
-        quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    reads.clear();
-    parser->reset();
-    while (parser->parse(reads, 65536));
-
-    std::uint32_t num_reads_new = reads.size(), name_size_new = 0,
-        sequence_size_new = 0, quality_size_new = 0;
-    reads_summary(name_size_new, sequence_size_new, quality_size_new, reads);
-
-    EXPECT_EQ(num_reads_new, num_reads);
-    EXPECT_EQ(name_size_new, name_size);
-    EXPECT_EQ(sequence_size_new, sequence_size);
-    EXPECT_EQ(quality_size_new, quality_size);
-}
-
-TEST_F(BioparserFastaTest, CompressedParseAndReset) {
-
-    SetUp(bioparser_test_data_path + "sample.fasta.gz");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    parser->parse(reads, -1);
-
-    std::uint32_t num_reads = reads.size(), name_size = 0, sequence_size = 0,
-        quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    reads.clear();
-    parser->reset();
-    while (parser->parse(reads, 65536));
-
-    std::uint32_t num_reads_new = reads.size(), name_size_new = 0,
-        sequence_size_new = 0, quality_size_new = 0;
-    reads_summary(name_size_new, sequence_size_new, quality_size_new, reads);
-
-    EXPECT_EQ(num_reads_new, num_reads);
-    EXPECT_EQ(name_size_new, name_size);
-    EXPECT_EQ(sequence_size_new, sequence_size);
-    EXPECT_EQ(quality_size_new, quality_size);
-}
-
-TEST_F(BioparserFastqTest, ParseWhole) {
-
-    SetUp(bioparser_test_data_path + "sample.fastq");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    parser->parse(reads, -1);
-
-    std::uint32_t name_size = 0, sequence_size = 0, quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    EXPECT_EQ(13U, reads.size());
-    EXPECT_EQ(17U, name_size);
-    EXPECT_EQ(108140U, sequence_size);
-    EXPECT_EQ(108140U, quality_size);
-}
-
-TEST_F(BioparserFastqTest, CompressedParseWhole) {
-
-    SetUp(bioparser_test_data_path + "sample.fastq.gz");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    parser->parse(reads, -1);
-
-    std::uint32_t name_size = 0, sequence_size = 0, quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    EXPECT_EQ(13U, reads.size());
-    EXPECT_EQ(17U, name_size);
-    EXPECT_EQ(108140U, sequence_size);
-    EXPECT_EQ(108140U, quality_size);
-}
-
-TEST_F(BioparserFastqTest, ParseInChunks) {
-
-    SetUp(bioparser_test_data_path + "sample.fastq");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    while (parser->parse(reads, 65536));
-
-    std::uint32_t name_size = 0, sequence_size = 0, quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    EXPECT_EQ(13U, reads.size());
-    EXPECT_EQ(17U, name_size);
-    EXPECT_EQ(108140U, sequence_size);
-    EXPECT_EQ(108140U, quality_size);
-}
-
-TEST_F(BioparserFastqTest, CompressedParseInChunks) {
-
-    SetUp(bioparser_test_data_path + "sample.fastq.gz");
-
-    std::vector<std::unique_ptr<Read>> reads;
-    while (parser->parse(reads, 65536));
-
-    std::uint32_t name_size = 0, sequence_size = 0, quality_size = 0;
-    reads_summary(name_size, sequence_size, quality_size, reads);
-
-    EXPECT_EQ(13U, reads.size());
-    EXPECT_EQ(17U, name_size);
-    EXPECT_EQ(108140U, sequence_size);
-    EXPECT_EQ(108140U, quality_size);
-}
-
-TEST_F(BioparserFastqTest, FormatError) {
-
-    SetUp(bioparser_test_data_path + "sample.fasta");
-
-    std::vector<std::unique_ptr<Read>> reads;
-
-    try {
-        parser->parse(reads, -1);
-    } catch (std::invalid_argument& exception) {
-        EXPECT_STREQ(exception.what(), "[bioparser::FastqParser] error: "
-            "invalid file format!");
-    }
-}
-
-TEST_F(BioparserFastqTest, CompressedFormatError) {
-
-    SetUp(bioparser_test_data_path + "sample.fasta.gz");
-
-    std::vector<std::unique_ptr<Read>> reads;
-
-    try {
-        parser->parse(reads, -1);
-    } catch (std::invalid_argument& exception) {
-        EXPECT_STREQ(exception.what(), "[bioparser::FastqParser] error: "
-            "invalid file format!");
-    }
-}
-
-TEST_F(BioparserMhapTest, ParseWhole) {
-
-    SetUp(bioparser_test_data_path + "sample.mhap");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-    parser->parse(overlaps, -1);
-
-    std::uint32_t name_size = 0, total_value = 0;
-    overlaps_summary(name_size, total_value, overlaps);
-
-    EXPECT_EQ(150U, overlaps.size());
-    EXPECT_EQ(0U, name_size);
-    EXPECT_EQ(7822873U, total_value);
-}
-
-TEST_F(BioparserMhapTest, CompressedParseWhole) {
-
-    SetUp(bioparser_test_data_path + "sample.mhap.gz");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-    parser->parse(overlaps, -1);
-
-    std::uint32_t name_size = 0, total_value = 0;
-    overlaps_summary(name_size, total_value, overlaps);
-
-    EXPECT_EQ(150U, overlaps.size());
-    EXPECT_EQ(0U, name_size);
-    EXPECT_EQ(7822873U, total_value);
-}
-
-TEST_F(BioparserMhapTest, ParseInChunks) {
-
-    SetUp(bioparser_test_data_path + "sample.mhap");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-    while (parser->parse(overlaps, 1024));
-
-    std::uint32_t name_size = 0, total_value = 0;
-    overlaps_summary(name_size, total_value, overlaps);
-
-    EXPECT_EQ(150U, overlaps.size());
-    EXPECT_EQ(0U, name_size);
-    EXPECT_EQ(7822873U, total_value);
-}
-
-TEST_F(BioparserMhapTest, CompressedParseInChunks) {
-
-    SetUp(bioparser_test_data_path + "sample.mhap.gz");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-    while (parser->parse(overlaps, 1024));
-
-    std::uint32_t name_size = 0, total_value = 0;
-    overlaps_summary(name_size, total_value, overlaps);
-
-    EXPECT_EQ(150U, overlaps.size());
-    EXPECT_EQ(0U, name_size);
-    EXPECT_EQ(7822873U, total_value);
-}
-
-TEST_F(BioparserMhapTest, FormatError) {
-
-    SetUp(bioparser_test_data_path + "sample.paf");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-
-    try {
-        parser->parse(overlaps, -1);
-    } catch (std::invalid_argument& exception) {
-        EXPECT_STREQ(exception.what(), "[bioparser::MhapParser] error: "
-            "invalid file format!");
-    }
-}
-
-TEST_F(BioparserMhapTest, CompressedFormatError) {
-
-    SetUp(bioparser_test_data_path + "sample.paf.gz");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-
-    try {
-        parser->parse(overlaps, -1);
-    } catch (std::invalid_argument& exception) {
-        EXPECT_STREQ(exception.what(), "[bioparser::MhapParser] error: "
-            "invalid file format!");
-    }
-}
-
-TEST_F(BioparserPafTest, ParseWhole) {
-
-    SetUp(bioparser_test_data_path + "sample.paf");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-    parser->parse(overlaps, -1);
-
-    std::uint32_t name_size = 0, total_value = 0;
-    overlaps_summary(name_size, total_value, overlaps);
-
-    EXPECT_EQ(500U, overlaps.size());
-    EXPECT_EQ(96478U, name_size);
-    EXPECT_EQ(18494208U, total_value);
-}
-
-TEST_F(BioparserPafTest, CompressedParseWhole) {
-
-    SetUp(bioparser_test_data_path + "sample.paf.gz");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-    parser->parse(overlaps, -1);
-
-    std::uint32_t name_size = 0, total_value = 0;
-    overlaps_summary(name_size, total_value, overlaps);
-
-    EXPECT_EQ(500U, overlaps.size());
-    EXPECT_EQ(96478U, name_size);
-    EXPECT_EQ(18494208U, total_value);
-}
-
-TEST_F(BioparserPafTest, ParseInChunks) {
-
-    SetUp(bioparser_test_data_path + "sample.paf");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-    while (parser->parse(overlaps, 1024));
-
-    std::uint32_t name_size = 0, total_value = 0;
-    overlaps_summary(name_size, total_value, overlaps);
-
-    EXPECT_EQ(500U, overlaps.size());
-    EXPECT_EQ(96478U, name_size);
-    EXPECT_EQ(18494208U, total_value);
-}
-
-TEST_F(BioparserPafTest, CompressedParseInChunks) {
-
-    SetUp(bioparser_test_data_path + "sample.paf.gz");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-    while (parser->parse(overlaps, 1024));
-
-    std::uint32_t name_size = 0, total_value = 0;
-    overlaps_summary(name_size, total_value, overlaps);
-
-    EXPECT_EQ(500U, overlaps.size());
-    EXPECT_EQ(96478U, name_size);
-    EXPECT_EQ(18494208U, total_value);
-}
-
-TEST_F(BioparserPafTest, FormatError) {
-
-    SetUp(bioparser_test_data_path + "sample.mhap");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-
-    try {
-        parser->parse(overlaps, -1);
-    } catch (std::invalid_argument& exception) {
-        EXPECT_STREQ(exception.what(), "[bioparser::PafParser] error: "
-            "invalid file format!");
-    }
-}
-
-TEST_F(BioparserPafTest, CompressedFormatError) {
-
-    SetUp(bioparser_test_data_path + "sample.mhap.gz");
-
-    std::vector<std::unique_ptr<Overlap>> overlaps;
-
-    try {
-        parser->parse(overlaps, -1);
-    } catch (std::invalid_argument& exception) {
-        EXPECT_STREQ(exception.what(), "[bioparser::PafParser] error: "
-            "invalid file format!");
-    }
-}
-
-TEST_F(BioparserSamTest, ParseWhole) {
-
-    SetUp(bioparser_test_data_path + "sample.sam");
-
-    std::vector<std::unique_ptr<Alignment>> alignments;
-    parser->parse(alignments, -1);
-
-    std::uint32_t string_size = 0, total_value = 0;
-    alignments_summary(string_size, total_value, alignments);
-
-    EXPECT_EQ(48U, alignments.size());
-    EXPECT_EQ(795237U, string_size);
-    EXPECT_EQ(639677U, total_value);
-}
-
-TEST_F(BioparserSamTest, CompressedParseWhole) {
-
-    SetUp(bioparser_test_data_path + "sample.sam.gz");
-
-    std::vector<std::unique_ptr<Alignment>> alignments;
-    parser->parse(alignments, -1);
-
-    std::uint32_t string_size = 0, total_value = 0;
-    alignments_summary(string_size, total_value, alignments);
-
-    EXPECT_EQ(48U, alignments.size());
-    EXPECT_EQ(795237U, string_size);
-    EXPECT_EQ(639677U, total_value);
-}
-
-TEST_F(BioparserSamTest, ParseInChunks) {
-
-    SetUp(bioparser_test_data_path + "sample.sam");
-
-    std::vector<std::unique_ptr<Alignment>> alignments;
-    while (parser->parse(alignments, 1024));
-
-    std::uint32_t string_size = 0, total_value = 0;
-    alignments_summary(string_size, total_value, alignments);
-
-    EXPECT_EQ(48U, alignments.size());
-    EXPECT_EQ(795237U, string_size);
-    EXPECT_EQ(639677U, total_value);
-}
-
-TEST_F(BioparserSamTest, CompressedParseInChunks) {
-
-    SetUp(bioparser_test_data_path + "sample.sam.gz");
-
-    std::vector<std::unique_ptr<Alignment>> alignments;
-    while (parser->parse(alignments, 1024));
-
-    std::uint32_t string_size = 0, total_value = 0;
-    alignments_summary(string_size, total_value, alignments);
-
-    EXPECT_EQ(48U, alignments.size());
-    EXPECT_EQ(795237U, string_size);
-    EXPECT_EQ(639677U, total_value);
-}
-
-TEST_F(BioparserSamTest, FormatError) {
-
-    SetUp(bioparser_test_data_path + "sample.paf");
-
-    std::vector<std::unique_ptr<Alignment>> alignments;
-
-    try {
-        parser->parse(alignments, -1);
-    } catch (std::invalid_argument& exception) {
-        EXPECT_STREQ(exception.what(), "[bioparser::SamParser] error: "
-            "invalid file format!");
-    }
-}
-
-TEST_F(BioparserSamTest, CompressedFormatError) {
-
-    SetUp(bioparser_test_data_path + "sample.paf.gz");
-
-    std::vector<std::unique_ptr<Alignment>> alignments;
-
-    try {
-        parser->parse(alignments, -1);
-    } catch (std::invalid_argument& exception) {
-        EXPECT_STREQ(exception.what(), "[bioparser::SamParser] error: "
-            "invalid file format!");
-    }
-}


=====================================
test/bioparser_test_config.h.in deleted
=====================================
@@ -1,9 +0,0 @@
-/*!
- * @file bioparser_test_config.h.in
- *
- * @brief Bioparser test configuration file
- */
-
-#include <string>
-
-const std::string bioparser_test_data_path = "@bioparser_test_data_path@";


=====================================
test/fasta_parser_test.cpp
=====================================
@@ -0,0 +1,138 @@
+// Copyright (c) 2020 Robert Vaser
+
+#include "bioparser/fasta_parser.hpp"
+
+#include <numeric>
+
+#include "biosoup/sequence.hpp"
+#include "gtest/gtest.h"
+
+std::atomic<std::uint32_t> biosoup::Sequence::num_objects{0};
+
+namespace bioparser {
+namespace test {
+
+class BioparserFastaTest: public ::testing::Test {
+ public:
+  void Setup(const std::string& file) {
+    p = Parser<biosoup::Sequence>::Create<FastaParser>(BIOPARSER_DATA_PATH + file);  // NOLINT
+  }
+
+  void Check(bool is_trimmed = true) {
+    EXPECT_EQ(14, s.size());
+    EXPECT_EQ(65 + !is_trimmed * 10, std::accumulate(s.begin(), s.end(), 0,
+        [] (std::uint32_t s, const std::unique_ptr<biosoup::Sequence>& it) {
+          return s + it->name.size();
+        }));
+    EXPECT_EQ(109117, std::accumulate(s.begin(), s.end(), 0,
+        [] (std::uint32_t s, const std::unique_ptr<biosoup::Sequence>& it) {
+          return s + it->data.size();
+        }));
+    EXPECT_EQ(0, std::accumulate(s.begin(), s.end(), 0,
+        [] (std::uint32_t s, const std::unique_ptr<biosoup::Sequence>& it) {
+          return s + it->quality.size();
+        }));
+  }
+
+  std::unique_ptr<Parser<biosoup::Sequence>> p;
+  std::vector<std::unique_ptr<biosoup::Sequence>> s;
+};
+
+TEST_F(BioparserFastaTest, ParseWhole) {
+  Setup("sample.fasta");
+  s = p->Parse(-1);
+  Check();
+}
+
+TEST_F(BioparserFastaTest, ParseWholeWithoutTrimming) {
+  Setup("sample.fasta");
+  s = p->Parse(-1, false);
+  Check(false);
+}
+
+TEST_F(BioparserFastaTest, ParseInChunks) {
+  Setup("sample.fasta");
+  for (auto t = p->Parse(65536); !t.empty(); t = p->Parse(65536)) {
+    s.insert(
+        s.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserFastaTest, FormatError) {
+  Setup("sample.fastq");
+  try {
+    s = p->Parse(-1);
+  } catch (std::invalid_argument& exception) {
+    EXPECT_STREQ(
+        exception.what(),
+        "[bioparser::FastaParser] error: invalid file format");
+  }
+}
+
+TEST_F(BioparserFastaTest, ParseAndReset) {
+  Setup("sample.fasta");
+  s = p->Parse(-1);
+  p->Reset();
+  s.clear();
+  for (auto t = p->Parse(65536); !t.empty(); t = p->Parse(65536)) {
+    s.insert(
+        s.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserFastaTest, CompressedParseWhole) {
+  Setup("sample.fasta.gz");
+  s = p->Parse(-1);
+  Check();
+}
+
+TEST_F(BioparserFastaTest, CompressedParseWholeWithoutTrimming) {
+  Setup("sample.fasta.gz");
+  s = p->Parse(-1, false);
+  Check(false);
+}
+
+TEST_F(BioparserFastaTest, CompressedParseInChunks) {
+  Setup("sample.fasta.gz");
+  for (auto t = p->Parse(65536); !t.empty(); t = p->Parse(65536)) {
+    s.insert(
+        s.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserFastaTest, CompressedParseAndReset) {
+  Setup("sample.fasta.gz");
+  s = p->Parse(-1);
+  p->Reset();
+  s.clear();
+  for (auto t = p->Parse(65536); !t.empty(); t = p->Parse(65536)) {
+    s.insert(
+        s.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserFastaTest, CompressedFormatError) {
+  Setup("sample.fastq.gz");
+  try {
+    s = p->Parse(-1);
+  } catch (std::invalid_argument& exception) {
+    EXPECT_STREQ(
+        exception.what(),
+        "[bioparser::FastaParser] error: invalid file format");
+  }
+}
+
+}  // namespace test
+}  // namespace bioparser


=====================================
test/fastq_parser_test.cpp
=====================================
@@ -0,0 +1,96 @@
+// Copyright (c) 2020 Robert Vaser
+
+#include "bioparser/fastq_parser.hpp"
+
+#include <numeric>
+
+#include "biosoup/sequence.hpp"
+#include "gtest/gtest.h"
+
+namespace bioparser {
+namespace test {
+
+class BioparserFastqTest: public ::testing::Test {
+ public:
+  void Setup(const std::string& file) {
+    p = Parser<biosoup::Sequence>::Create<FastqParser>(BIOPARSER_DATA_PATH + file);  // NOLINT
+  }
+
+  void Check() {
+    EXPECT_EQ(13, s.size());
+    EXPECT_EQ(17, std::accumulate(s.begin(), s.end(), 0,
+        [] (std::uint32_t s, const std::unique_ptr<biosoup::Sequence>& it) {
+          return s + it->name.size();
+        }));
+    EXPECT_EQ(108140, std::accumulate(s.begin(), s.end(), 0,
+        [] (std::uint32_t s, const std::unique_ptr<biosoup::Sequence>& it) {
+          return s + it->data.size();
+        }));
+    EXPECT_EQ(108140, std::accumulate(s.begin(), s.end(), 0,
+        [] (std::uint32_t s, const std::unique_ptr<biosoup::Sequence>& it) {
+          return s + it->quality.size();
+        }));
+  }
+
+  std::unique_ptr<Parser<biosoup::Sequence>> p;
+  std::vector<std::unique_ptr<biosoup::Sequence>> s;
+};
+
+TEST_F(BioparserFastqTest, ParseWhole) {
+  Setup("sample.fastq");
+  s = p->Parse(-1);
+  Check();
+}
+
+TEST_F(BioparserFastqTest, ParseInChunks) {
+  Setup("sample.fastq");
+  for (auto t = p->Parse(65536); !t.empty(); t = p->Parse(65536)) {
+    s.insert(
+        s.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserFastqTest, FormatError) {
+  Setup("sample.fasta");
+  try {
+    s = p->Parse(-1);
+  } catch (std::invalid_argument& exception) {
+    EXPECT_STREQ(
+        exception.what(),
+        "[bioparser::FastqParser] error: invalid file format");
+  }
+}
+
+TEST_F(BioparserFastqTest, CompressedParseWhole) {
+  Setup("sample.fastq.gz");
+  s = p->Parse(-1);
+  Check();
+}
+
+TEST_F(BioparserFastqTest, CompressedParseInChunks) {
+  Setup("sample.fastq.gz");
+  for (auto t = p->Parse(65536); !t.empty(); t = p->Parse(65536)) {
+    s.insert(
+        s.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserFastqTest, CompressedFormatError) {
+  Setup("sample.fasta.gz");
+  try {
+    s = p->Parse(-1);
+  } catch (std::invalid_argument& exception) {
+    EXPECT_STREQ(
+        exception.what(),
+        "[bioparser::FastqParser] error: invalid file format");
+  }
+}
+
+}  // namespace test
+}  // namespace bioparser


=====================================
test/mhap_parser_test.cpp
=====================================
@@ -0,0 +1,123 @@
+// Copyright (c) 2020 Robert Vaser
+
+#include "bioparser/mhap_parser.hpp"
+
+#include <numeric>
+#include <string>
+
+#include "biosoup/overlap.hpp"
+#include "gtest/gtest.h"
+
+namespace bioparser {
+namespace test {
+
+struct MhapOverlap: public biosoup::Overlap {
+ public:
+  MhapOverlap(
+      std::uint64_t lhs_id,
+      std::uint64_t rhs_id,
+      double error,
+      std::uint32_t num_minmers,
+      std::uint32_t lhs_strand,
+      std::uint32_t lhs_begin,
+      std::uint32_t lhs_end,
+      std::uint32_t lhs_len,
+      std::uint32_t rhs_strand,
+      std::uint32_t rhs_begin,
+      std::uint32_t rhs_end,
+      std::uint32_t rhs_len)
+      : biosoup::Overlap(
+          lhs_id, lhs_begin, lhs_end,
+          rhs_id, rhs_begin, rhs_end,
+          num_minmers,
+          lhs_strand == rhs_strand),
+        error(error * 10000),
+        lhs_len(lhs_len),
+        rhs_len(rhs_len) {}
+
+  std::uint32_t error;
+  std::uint32_t lhs_len;
+  std::uint32_t rhs_len;
+};
+
+class BioparserMhapTest: public ::testing::Test {
+ public:
+  void Setup(const std::string& file) {
+    p = Parser<MhapOverlap>::Create<MhapParser>(BIOPARSER_DATA_PATH + file);
+  }
+
+  void Check() {
+    EXPECT_EQ(150, o.size());
+    EXPECT_EQ(7816660, std::accumulate(o.begin(), o.end(), 0,
+        [] (std::uint32_t s, const std::unique_ptr<MhapOverlap>& it) {
+          return s +
+              it->lhs_id + it->lhs_begin + it->lhs_end + it->lhs_len +
+              it->rhs_id + it->rhs_begin + it->rhs_end + it->rhs_len +
+              it->score +
+              it->strand +
+              it->error;
+        }));
+  }
+
+  std::unique_ptr<Parser<MhapOverlap>> p;
+  std::vector<std::unique_ptr<MhapOverlap>> o;
+};
+
+TEST_F(BioparserMhapTest, ParseWhole) {
+  Setup("sample.mhap");
+  o = p->Parse(-1);
+  Check();
+}
+
+TEST_F(BioparserMhapTest, ParseInChunks) {
+  Setup("sample.mhap");
+  for (auto t = p->Parse(1024); !t.empty(); t = p->Parse(1024)) {
+    o.insert(
+        o.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserMhapTest, FormatError) {
+  Setup("sample.paf");
+  try {
+    o = p->Parse(-1);
+  } catch (std::invalid_argument& exception) {
+    EXPECT_STREQ(
+        exception.what(),
+        "[bioparser::MhapParser] error: invalid file format");
+  }
+}
+
+TEST_F(BioparserMhapTest, CompressedParseWhole) {
+  Setup("sample.mhap.gz");
+  o = p->Parse(-1);
+  Check();
+}
+
+TEST_F(BioparserMhapTest, CompressedParseInChunks) {
+  Setup("sample.mhap.gz");
+  for (auto t = p->Parse(1024); !t.empty(); t = p->Parse(1024)) {
+    o.insert(
+        o.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserMhapTest, CompressedFormatError) {
+  Setup("sample.paf.gz");
+  try {
+    o = p->Parse(-1);
+  } catch (std::invalid_argument& exception) {
+    EXPECT_STREQ(
+        exception.what(),
+        "[bioparser::MhapParser] error: invalid file format");
+  }
+}
+
+}  // namespace test
+}  // namespace bioparser


=====================================
test/paf_parser_test.cpp
=====================================
@@ -0,0 +1,134 @@
+// Copyright (c) 2020 Robert Vaser
+
+#include "bioparser/paf_parser.hpp"
+
+#include <numeric>
+#include <string>
+
+#include "biosoup/overlap.hpp"
+#include "gtest/gtest.h"
+
+namespace bioparser {
+namespace test {
+
+struct PafOverlap: public biosoup::Overlap {
+ public:
+  PafOverlap(
+      const char* q_name, std::uint32_t q_name_len,
+      std::uint32_t q_len,
+      std::uint32_t q_begin,
+      std::uint32_t q_end,
+      char orientation,
+      const char* t_name, std::uint32_t t_name_len,
+      std::uint32_t t_len,
+      std::uint32_t t_begin,
+      std::uint32_t t_end,
+      std::uint32_t score,
+      std::uint32_t overlap_len,
+      std::uint32_t quality)
+      : biosoup::Overlap(
+          0, q_begin, q_end,
+          0, t_begin, t_end,
+          score,
+          orientation == '+'),
+        q_name(q_name, q_name_len),
+        q_len(q_len),
+        t_name(t_name, t_name_len),
+        t_len(t_len),
+        overlap_len(overlap_len),
+        quality(quality) {}
+
+  std::string q_name;
+  std::uint32_t q_len;
+  std::string t_name;
+  std::uint32_t t_len;
+  std::uint32_t overlap_len;
+  std::uint32_t quality;
+};
+
+class BioparserPafTest: public ::testing::Test {
+ public:
+  void Setup(const std::string& file) {
+    p = Parser<PafOverlap>::Create<PafParser>(BIOPARSER_DATA_PATH + file);
+  }
+
+  void Check() {
+    EXPECT_EQ(500, o.size());
+    EXPECT_EQ(96478, std::accumulate(o.begin(), o.end(), 0,
+        [] (std::uint32_t s, const std::unique_ptr<PafOverlap>& it) {
+          return s + it->q_name.size() + it->t_name.size();
+        }));
+    EXPECT_EQ(18472506, std::accumulate(o.begin(), o.end(), 0,
+        [] (std::uint32_t s, const std::unique_ptr<PafOverlap>& it) {
+          return s +
+              it->lhs_id + it->lhs_begin + it->lhs_end + it->q_len +
+              it->rhs_id + it->rhs_begin + it->rhs_end + it->t_len +
+              it->score +
+              it->strand +
+              it->overlap_len +
+              it->quality;
+        }));
+  }
+
+  std::unique_ptr<Parser<PafOverlap>> p;
+  std::vector<std::unique_ptr<PafOverlap>> o;
+};
+
+TEST_F(BioparserPafTest, ParseWhole) {
+  Setup("sample.paf");
+  o = p->Parse(-1);
+  Check();
+}
+
+TEST_F(BioparserPafTest, ParseInChunks) {
+  Setup("sample.paf");
+  for (auto t = p->Parse(1024); !t.empty(); t = p->Parse(1024)) {
+    o.insert(
+        o.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserPafTest, FormatError) {
+  Setup("sample.mhap");
+  try {
+    o = p->Parse(-1);
+  } catch (std::invalid_argument& exception) {
+    EXPECT_STREQ(
+        exception.what(),
+        "[bioparser::PafParser] error: invalid file format");
+  }
+}
+
+TEST_F(BioparserPafTest, CompressedParseWhole) {
+  Setup("sample.paf.gz");
+  o = p->Parse(-1);
+  Check();
+}
+
+TEST_F(BioparserPafTest, CompressedParseInChunks) {
+  Setup("sample.paf.gz");
+  for (auto t = p->Parse(1024); !t.empty(); t = p->Parse(1024)) {
+    o.insert(
+        o.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserPafTest, CompressedFormatError) {
+  Setup("sample.mhap.gz");
+  try {
+    o = p->Parse(-1);
+  } catch (std::invalid_argument& exception) {
+    EXPECT_STREQ(
+        exception.what(),
+        "[bioparser::PafParser] error: invalid file format");
+  }
+}
+
+}  // namespace test
+}  // namespace bioparser


=====================================
test/parser_test.cpp
=====================================
@@ -0,0 +1,24 @@
+// Copyright (c) 2020 Robert Vaser
+
+#include "bioparser/parser.hpp"
+
+#include "biosoup/sequence.hpp"
+#include "gtest/gtest.h"
+
+#include "bioparser/fasta_parser.hpp"
+
+namespace bioparser {
+namespace test {
+
+TEST(BioparserParserTest, Create) {
+  try {
+    auto p = Parser<biosoup::Sequence>::Create<FastaParser>("");
+  } catch (std::invalid_argument& exception) {
+    EXPECT_STREQ(
+        exception.what(),
+        "[bioparser::Parser::Create] error: unable to open file ");
+  }
+}
+
+}  // namespace test
+}  // namespace bioparser


=====================================
test/sam_parser_test.cpp
=====================================
@@ -0,0 +1,144 @@
+// Copyright (c) 2020 Robert Vaser
+
+#include "bioparser/sam_parser.hpp"
+
+#include <numeric>
+#include <string>
+
+#include "biosoup/overlap.hpp"
+#include "gtest/gtest.h"
+
+namespace bioparser {
+namespace test {
+
+struct SamOverlap: public biosoup::Overlap {
+ public:
+  SamOverlap(
+      const char* q_name, std::uint32_t q_name_len,
+      std::uint32_t flag,
+      const char* t_name, std::uint32_t t_name_len,
+      std::uint32_t t_begin,
+      std::uint32_t map_quality,
+      const char* cigar, std::uint32_t cigar_len,
+      const char* t_next_name, std::uint32_t t_next_name_len,
+      std::uint32_t t_next_begin,
+      std::uint32_t template_len,
+      const char* data, std::uint32_t data_len,
+      const char* quality, std::uint32_t quality_len)
+      : biosoup::Overlap(
+          0, 0, 0,
+          0, t_begin, 0,
+          0,
+          cigar, cigar_len),
+        q_name(q_name, q_name_len),
+        flag(flag),
+        t_name(t_name, t_name_len),
+        map_quality(map_quality),
+        t_next_name(t_next_name, t_next_name_len),
+        t_next_begin(t_next_begin),
+        template_len(template_len),
+        data(data, data_len),
+        quality(quality, quality_len) {}
+
+  std::string q_name;
+  std::uint32_t flag;
+  std::string t_name;
+  std::uint32_t map_quality;
+  std::string t_next_name;
+  std::uint32_t t_next_begin;
+  std::uint32_t template_len;
+  std::string data;
+  std::string quality;
+};
+
+class BioparserSamTest: public ::testing::Test {
+ public:
+  void Setup(const std::string& file) {
+    p = Parser<SamOverlap>::Create<SamParser>(BIOPARSER_DATA_PATH + file);
+  }
+
+  void Check() {
+    EXPECT_EQ(48, o.size());
+    EXPECT_EQ(795237, std::accumulate(o.begin(), o.end(), 0,
+        [] (std::uint32_t s, const std::unique_ptr<SamOverlap>& it) {
+          return s +
+              it->q_name.size() +
+              it->t_name.size() +
+              it->alignment.size() +
+              it->t_next_name.size() +
+              it->data.size() +
+              it->quality.size();
+        }));
+    EXPECT_EQ(639677, std::accumulate(o.begin(), o.end(), 0,
+        [] (std::uint32_t s, const std::unique_ptr<SamOverlap>& it) {
+          return s +
+              it->flag +
+              it->rhs_begin +
+              it->map_quality +
+              it->t_next_begin +
+              it->template_len;
+        }));
+  }
+
+  std::unique_ptr<Parser<SamOverlap>> p;
+  std::vector<std::unique_ptr<SamOverlap>> o;
+};
+
+TEST_F(BioparserSamTest, ParseWhole) {
+  Setup("sample.sam");
+  o = p->Parse(-1);
+  Check();
+}
+
+TEST_F(BioparserSamTest, ParseInChunks) {
+  Setup("sample.sam");
+  for (auto t = p->Parse(1024); !t.empty(); t = p->Parse(1024)) {
+    o.insert(
+        o.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserSamTest, FormatError) {
+  Setup("sample.paf");
+  try {
+    o = p->Parse(-1);
+  } catch (std::invalid_argument& exception) {
+    EXPECT_STREQ(
+        exception.what(),
+        "[bioparser::SamParser] error: invalid file format");
+  }
+}
+
+TEST_F(BioparserSamTest, CompressedParseWhole) {
+  Setup("sample.sam.gz");
+  o = p->Parse(-1);
+  Check();
+}
+
+TEST_F(BioparserSamTest, CompressedParseInChunks) {
+  Setup("sample.sam.gz");
+  for (auto t = p->Parse(1024); !t.empty(); t = p->Parse(1024)) {
+    o.insert(
+        o.end(),
+        std::make_move_iterator(t.begin()),
+        std::make_move_iterator(t.end()));
+  }
+  Check();
+}
+
+TEST_F(BioparserSamTest, CompressedFormatError) {
+  Setup("sample.paf.gz");
+  try {
+    o = p->Parse(-1);
+  } catch (std::invalid_argument& exception) {
+    EXPECT_STREQ(
+        exception.what(),
+        "[bioparser::SamParser] error: invalid file format");
+  }
+}
+
+}  // namespace test
+}  // namespace bioparser



View it on GitLab: https://salsa.debian.org/med-team/libbioparser-dev/-/commit/77b7ed91567ac15b088686def08b5144d73f3e50

-- 
View it on GitLab: https://salsa.debian.org/med-team/libbioparser-dev/-/commit/77b7ed91567ac15b088686def08b5144d73f3e50
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20210101/8007aeda/attachment-0001.html>


More information about the debian-med-commit mailing list