From c1a7a8a4a0c5433ba34db77b4bd3ca447c9c7611 Mon Sep 17 00:00:00 2001 From: Zilong-Li Date: Mon, 11 Mar 2024 10:42:54 +0100 Subject: [PATCH] fix memory leak when modifying records --- test/Makefile | 3 +- vcfpp.h | 202 ++++++++++++++++++++++++++++---------------------- 2 files changed, 114 insertions(+), 91 deletions(-) diff --git a/test/Makefile b/test/Makefile index 2e1733c..f5e598f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,7 +1,6 @@ # detect OS architecture and add flags CXX = g++ -# CXXFLAGS = -std=c++11 -g -Wall -fsanitize=address -CXXFLAGS = -std=c++11 -Wall +CXXFLAGS = -std=c++11 -g -Wall -fsanitize=address HTSINC = /usr/local/include HTSLIB = /usr/local/lib LDFLAGS = -L$(HTSLIB) -Wl,-rpath,$(HTSLIB) diff --git a/vcfpp.h b/vcfpp.h index f441656..7fffd12 100644 --- a/vcfpp.h +++ b/vcfpp.h @@ -139,6 +139,24 @@ inline std::vector split_string(const std::string & s, const std::s return ret; } +// deleter for htsFile +struct htsFile_close +{ + void operator()(htsFile * x) + { + if(x) hts_close(x); + } +}; + +// deleter for variant +struct variant_close +{ + void operator()(bcf1_t * x) + { + if(x) bcf_destroy(x); + } +}; + /** * @class BcfHeader * @brief Object represents header of the VCF, offering methods to access and modify the tags @@ -157,7 +175,11 @@ class BcfHeader public: BcfHeader() {} - ~BcfHeader() {} + ~BcfHeader() + { + if(hdr) bcf_hrec_destroy(hrec); + if(hrec) bcf_hdr_destroy(hdr); + } /** @brief stream out the header */ friend std::ostream & operator<<(std::ostream & out, const BcfHeader & h) @@ -243,7 +265,11 @@ class BcfHeader { kstring_t s = {0, 0, NULL}; // kstring if(bcf_hdr_format(hdr, 0, &s) == 0) // append header string to s.s! append! - return std::string(s.s, s.l); + { + std::string out(s.s, s.l); + free(s.s); + return out; + } else throw std::runtime_error("failed to convert formatted header to string"); } @@ -270,7 +296,7 @@ class BcfHeader { vec.push_back(std::string(seqs[i])); } - // TODO: return uninitialized vec may be undefined. + free(seqs); return vec; } @@ -359,7 +385,7 @@ class BcfRecord private: BcfHeader header; - bcf1_t * line = bcf_init(); // current bcf record + std::shared_ptr line = std::shared_ptr(bcf_init(), variant_close()); // variant bcf_hdr_t * hdr_d; // a dup header by bcf_hdr_dup(header->hdr) bcf_fmt_t * fmt = NULL; bcf_info_t * info = NULL; @@ -379,11 +405,9 @@ class BcfRecord BcfRecord() {} /// constructor with a given BcfHeader object - BcfRecord(const BcfHeader & h) : header(h) + BcfRecord(const BcfHeader & h) { - nsamples = header.nSamples(); - typeOfGT.resize(nsamples); - gtPhase.resize(nsamples, 0); + init(h); } ~BcfRecord() {} @@ -391,6 +415,7 @@ class BcfRecord /// initilize a BcfRecord object using a given BcfHeader object void init(const BcfHeader & h) { + if(h.hdr == NULL) throw std::runtime_error("please initial header first\n"); header = h; nsamples = header.nSamples(); typeOfGT.resize(nsamples); @@ -408,8 +433,12 @@ class BcfRecord inline std::string asString() const { kstring_t s = {0, 0, NULL}; // kstring - if(vcf_format(header.hdr, line, &s) == 0) - return std::string(s.s, s.l); + if(vcf_format(header.hdr, line.get(), &s) == 0) + { + std::string out(s.s, s.l); + free(s.s); + return out; + } else throw std::runtime_error("couldn't format current record into a string.\n"); } @@ -426,7 +455,7 @@ class BcfRecord isValidGT getGenotypes(T & v) { ndst = 0; - ret = bcf_get_genotypes(header.hdr, line, >s, &ndst); + ret = bcf_get_genotypes(header.hdr, line.get(), >s, &ndst); if(ret <= 0) throw std::runtime_error("genotypes not present"); // if nploidy is not set manually. find the max nploidy using the first variant (eg. 2) resize v as // max(nploidy) @@ -446,7 +475,7 @@ class BcfRecord isGenoMissing.assign(nsamples, 0); int i, j, nphased = 0; noneMissing = true; - fmt = bcf_get_fmt(header.hdr, line, "GT"); + fmt = bcf_get_fmt(header.hdr, line.get(), "GT"); int nploidy_cur = ret / nsamples; // requires nploidy_cur <= nploidy for(i = 0; i < nsamples; i++) { @@ -489,7 +518,7 @@ class BcfRecord bool getGenotypes(std::vector & v) { ndst = 0; - ret = bcf_get_genotypes(header.hdr, line, >s, &ndst); + ret = bcf_get_genotypes(header.hdr, line.get(), >s, &ndst); if(ret <= 0) throw std::runtime_error( "genotypes not present. make sure you initilized the variant object first\n"); @@ -539,24 +568,25 @@ class BcfRecord template isFormatVector getFORMAT(std::string tag, T & v) { - fmt = bcf_get_fmt(header.hdr, line, tag.c_str()); + fmt = bcf_get_fmt(header.hdr, line.get(), tag.c_str()); if(!fmt) throw std::runtime_error("there is no " + tag + " in FORMAT of this variant.\n"); nvalues = fmt->n; ndst = 0; S * dst = NULL; int tagid = header.getFormatType(tag); if(tagid == 1) - ret = bcf_get_format_int32(header.hdr, line, tag.c_str(), &dst, &ndst); + ret = bcf_get_format_int32(header.hdr, line.get(), tag.c_str(), &dst, &ndst); else if(tagid == 2) - ret = bcf_get_format_float(header.hdr, line, tag.c_str(), &dst, &ndst); + ret = bcf_get_format_float(header.hdr, line.get(), tag.c_str(), &dst, &ndst); else if(tagid == 3) - ret = bcf_get_format_char(header.hdr, line, tag.c_str(), &dst, &ndst); + ret = bcf_get_format_char(header.hdr, line.get(), tag.c_str(), &dst, &ndst); else throw std::runtime_error("can not find the type of " + tag + " in the header file.\n"); if(ret >= 0) { // user have to check if there is missing in the return v; v = std::vector(dst, dst + ret); + free(dst); return true; } else @@ -573,14 +603,14 @@ class BcfRecord * */ bool getFORMAT(std::string tag, std::vector & v) { - fmt = bcf_get_fmt(header.hdr, line, tag.c_str()); + fmt = bcf_get_fmt(header.hdr, line.get(), tag.c_str()); if(!fmt) throw std::runtime_error("there is no " + tag + " in FORMAT for this variant of ID=" + ID()); nvalues = fmt->n; // if ndst < (fmt->n+1)*nsmpl; then realloc is involved ret = -1, ndst = 0; char ** dst = NULL; if(header.getFormatType(tag) == 3) - ret = bcf_get_format_string(header.hdr, line, tag.c_str(), &dst, &ndst); + ret = bcf_get_format_string(header.hdr, line.get(), tag.c_str(), &dst, &ndst); if(ret > 0) { v.clear(); @@ -589,6 +619,7 @@ class BcfRecord // Ugly: GT field is considered to be a string by the VCF header but BCF represents it as INT. v.emplace_back(dst[i]); }; + free(dst); return true; } else @@ -606,23 +637,24 @@ class BcfRecord template isInfoVector getINFO(std::string tag, T & v) { - info = bcf_get_info(header.hdr, line, tag.c_str()); + info = bcf_get_info(header.hdr, line.get(), tag.c_str()); if(!info) throw std::runtime_error("there is no " + tag + " tag in INFO of this variant.\n"); S * dst = NULL; ndst = 0; ret = -1; if(info->type == BCF_BT_INT8 || info->type == BCF_BT_INT16 || info->type == BCF_BT_INT32) { - ret = bcf_get_info_int32(header.hdr, line, tag.c_str(), &dst, &ndst); + ret = bcf_get_info_int32(header.hdr, line.get(), tag.c_str(), &dst, &ndst); } else if(info->type == BCF_BT_FLOAT) { - ret = bcf_get_info_float(header.hdr, line, tag.c_str(), &dst, &ndst); + ret = bcf_get_info_float(header.hdr, line.get(), tag.c_str(), &dst, &ndst); } if(ret >= 0) v = std::vector(dst, dst + ret); // user have to check if there is missing in the return v; else throw std::runtime_error("couldn't parse the " + tag + " format of this variant.\n"); + free(dst); return true; } @@ -635,7 +667,7 @@ class BcfRecord template isScalar getINFO(std::string tag, T & v) { - info = bcf_get_info(header.hdr, line, tag.c_str()); + info = bcf_get_info(header.hdr, line.get(), tag.c_str()); if(!info) throw std::runtime_error("there is no " + tag + " tag in INFO of this variant.\n"); // scalar value if(info->len == 1) @@ -665,7 +697,7 @@ class BcfRecord template isString getINFO(std::string tag, T & v) { - info = bcf_get_info(header.hdr, line, tag.c_str()); + info = bcf_get_info(header.hdr, line.get(), tag.c_str()); if(!info) throw std::runtime_error("there is no " + tag + " tag in INFO of this variant.\n"); if(info->type == BCF_BT_CHAR) v = std::string(reinterpret_cast(info->vptr), info->vptr_len); @@ -687,11 +719,11 @@ class BcfRecord // bcf_update_info_flag(header.hdr, line, tag.c_str(), NULL, 1); int tag_id = bcf_hdr_id2int(header.hdr, BCF_DT_ID, tag.c_str()); if(bcf_hdr_id2type(header.hdr, BCF_HL_INFO, tag_id) == (BCF_HT_INT & 0xff)) - ret = bcf_update_info_int32(header.hdr, line, tag.c_str(), &v, 1); + ret = bcf_update_info_int32(header.hdr, line.get(), tag.c_str(), &v, 1); else if(bcf_hdr_id2type(header.hdr, BCF_HL_INFO, tag_id) == (BCF_HT_REAL & 0xff)) { float v2 = static_cast(v); - ret = bcf_update_info_float(header.hdr, line, tag.c_str(), &v2, 1); + ret = bcf_update_info_float(header.hdr, line.get(), tag.c_str(), &v2, 1); } if(ret < 0) { @@ -717,11 +749,11 @@ class BcfRecord // bcf_update_info_flag(header.hdr, line, tag.c_str(), NULL, 1); int tag_id = bcf_hdr_id2int(header.hdr, BCF_DT_ID, tag.c_str()); if(bcf_hdr_id2type(header.hdr, BCF_HL_INFO, tag_id) == (BCF_HT_INT & 0xff)) - ret = bcf_update_info_int32(header.hdr, line, tag.c_str(), v.data(), v.size()); + ret = bcf_update_info_int32(header.hdr, line.get(), tag.c_str(), v.data(), v.size()); else if(bcf_hdr_id2type(header.hdr, BCF_HL_INFO, tag_id) == (BCF_HT_REAL & 0xff)) - ret = bcf_update_info_float(header.hdr, line, tag.c_str(), v.data(), v.size()); + ret = bcf_update_info_float(header.hdr, line.get(), tag.c_str(), v.data(), v.size()); else if(bcf_hdr_id2type(header.hdr, BCF_HL_INFO, tag_id) == (BCF_HT_STR & 0xff)) - ret = bcf_update_info_string(header.hdr, line, tag.c_str(), v.data()); + ret = bcf_update_info_string(header.hdr, line.get(), tag.c_str(), v.data()); if(ret < 0) throw std::runtime_error("couldn't set " + tag + " for this variant.\nplease add the tag in header first.\n"); @@ -735,11 +767,11 @@ class BcfRecord ret = -1; int tag_id = bcf_hdr_id2int(header.hdr, BCF_DT_ID, tag.c_str()); if(bcf_hdr_id2type(header.hdr, BCF_HL_INFO, tag_id) == (BCF_HT_INT & 0xff)) - ret = bcf_update_info_int32(header.hdr, line, tag.c_str(), NULL, 0); + ret = bcf_update_info_int32(header.hdr, line.get(), tag.c_str(), NULL, 0); else if(bcf_hdr_id2type(header.hdr, BCF_HL_INFO, tag_id) == (BCF_HT_REAL & 0xff)) - ret = bcf_update_info_float(header.hdr, line, tag.c_str(), NULL, 0); + ret = bcf_update_info_float(header.hdr, line.get(), tag.c_str(), NULL, 0); else if(bcf_hdr_id2type(header.hdr, BCF_HL_INFO, tag_id) == (BCF_HT_STR & 0xff)) - ret = bcf_update_info_string(header.hdr, line, tag.c_str(), NULL); + ret = bcf_update_info_string(header.hdr, line.get(), tag.c_str(), NULL); if(ret < 0) { throw std::runtime_error("couldn't remove " + tag + " for this variant.\n"); @@ -770,10 +802,9 @@ class BcfRecord gts[k] = bcf_gt_unphased(v[k]); } } - if(bcf_update_genotypes(header.hdr, line, gts, v.size()) < 0) + if(bcf_update_genotypes(header.hdr, line.get(), gts, v.size()) < 0) throw std::runtime_error("couldn't set genotypes correctly.\n"); - else - return true; + return true; } /** @@ -792,11 +823,11 @@ class BcfRecord ret = -1; int tag_id = bcf_hdr_id2int(header.hdr, BCF_DT_ID, tag.c_str()); if(bcf_hdr_id2type(header.hdr, BCF_HL_FMT, tag_id) == (BCF_HT_INT & 0xff)) - ret = bcf_update_format_int32(header.hdr, line, tag.c_str(), NULL, 0); + ret = bcf_update_format_int32(header.hdr, line.get(), tag.c_str(), NULL, 0); else if(bcf_hdr_id2type(header.hdr, BCF_HL_FMT, tag_id) == (BCF_HT_STR & 0xff)) - ret = bcf_update_format_char(header.hdr, line, tag.c_str(), NULL, 0); + ret = bcf_update_format_char(header.hdr, line.get(), tag.c_str(), NULL, 0); else if(bcf_hdr_id2type(header.hdr, BCF_HL_FMT, tag_id) == (BCF_HT_REAL & 0xff)) - ret = bcf_update_format_float(header.hdr, line, tag.c_str(), NULL, 0); + ret = bcf_update_format_float(header.hdr, line.get(), tag.c_str(), NULL, 0); if(ret < 0) throw std::runtime_error("couldn't remove " + tag + " correctly.\n"); } @@ -812,11 +843,11 @@ class BcfRecord ret = -1; int tag_id = bcf_hdr_id2int(header.hdr, BCF_DT_ID, tag.c_str()); if(bcf_hdr_id2type(header.hdr, BCF_HL_FMT, tag_id) == (BCF_HT_INT & 0xff)) - ret = bcf_update_format_int32(header.hdr, line, tag.c_str(), v.data(), v.size()); + ret = bcf_update_format_int32(header.hdr, line.get(), tag.c_str(), v.data(), v.size()); else if(bcf_hdr_id2type(header.hdr, BCF_HL_FMT, tag_id) == (BCF_HT_STR & 0xff)) - ret = bcf_update_format_char(header.hdr, line, tag.c_str(), v.data(), v.size()); + ret = bcf_update_format_char(header.hdr, line.get(), tag.c_str(), v.data(), v.size()); else if(bcf_hdr_id2type(header.hdr, BCF_HL_FMT, tag_id) == (BCF_HT_REAL & 0xff)) - ret = bcf_update_format_float(header.hdr, line, tag.c_str(), v.data(), v.size()); + ret = bcf_update_format_float(header.hdr, line.get(), tag.c_str(), v.data(), v.size()); if(ret < 0) throw std::runtime_error("couldn't set format " + tag + " correctly.\n"); return true; } @@ -835,9 +866,9 @@ class BcfRecord float v2 = v; int tag_id = bcf_hdr_id2int(header.hdr, BCF_DT_ID, tag.c_str()); if(bcf_hdr_id2type(header.hdr, BCF_HL_FMT, tag_id) == (BCF_HT_INT & 0xff)) - ret = bcf_update_format_int32(header.hdr, line, tag.c_str(), &v, 1); + ret = bcf_update_format_int32(header.hdr, line.get(), tag.c_str(), &v, 1); else if(bcf_hdr_id2type(header.hdr, BCF_HL_FMT, tag_id) == (BCF_HT_REAL & 0xff)) - ret = bcf_update_format_float(header.hdr, line, tag.c_str(), &v2, 1); + ret = bcf_update_format_float(header.hdr, line.get(), tag.c_str(), &v2, 1); if(ret < 0) throw std::runtime_error("couldn't set format " + tag + " correctly.\n"); return true; } @@ -848,7 +879,7 @@ class BcfRecord std::vector str(vcfline.begin(), vcfline.end()); str.push_back('\0'); // don't forget string has no \0; kstring_t s = {vcfline.length(), vcfline.length(), &str[0]}; // kstring - ret = vcf_parse(&s, header.hdr, line); + ret = vcf_parse(&s, header.hdr, line.get()); if(ret > 0) throw std::runtime_error("error parsing: " + vcfline + "\n"); if(line->errcode == BCF_ERR_CTG_UNDEF) { @@ -861,6 +892,7 @@ class BcfRecord if(ret < 0) throw std::runtime_error("error adding contig " + contig + " to header.\n"); ret = bcf_hdr_sync(header.hdr); } + free(s.s); } /** @brief if all samples have non missing values for the tag in FORMAT */ @@ -872,7 +904,7 @@ class BcfRecord /** @brief return boolean value indicates if current variant is Structual Variant or not */ inline bool isSV() const { - if(bcf_get_info(header.hdr, line, "SVTYPE") == NULL) + if(bcf_get_info(header.hdr, line.get(), "SVTYPE") == NULL) return false; else return true; @@ -933,7 +965,7 @@ class BcfRecord */ inline bool hasSNP() const { - int type = bcf_has_variant_types(line, VCF_SNP, bcf_match_overlap); + int type = bcf_has_variant_types(line.get(), VCF_SNP, bcf_match_overlap); if(type < 0) throw std::runtime_error("something wrong with variant type\n"); if(type == 0) return false; return true; @@ -942,7 +974,7 @@ class BcfRecord /// return boolean value indicates if current variant has INDEL type defined in vcf.h (htslib>=1.16) inline bool hasINDEL() const { - int type = bcf_has_variant_types(line, VCF_INDEL, bcf_match_overlap); + int type = bcf_has_variant_types(line.get(), VCF_INDEL, bcf_match_overlap); if(type < 0) throw std::runtime_error("something wrong with variant type\n"); if(type == 0) return false; return true; @@ -951,7 +983,7 @@ class BcfRecord /// return boolean value indicates if current variant has INS type defined in vcf.h (htslib>=1.16) inline bool hasINS() const { - int type = bcf_has_variant_types(line, VCF_INS, bcf_match_overlap); + int type = bcf_has_variant_types(line.get(), VCF_INS, bcf_match_overlap); if(type < 0) throw std::runtime_error("something wrong with variant type\n"); if(type == 0) return false; return true; @@ -960,7 +992,7 @@ class BcfRecord /// return boolean value indicates if current variant has DEL type defined in vcf.h (htslib>=1.16) inline bool hasDEL() const { - int type = bcf_has_variant_types(line, VCF_DEL, bcf_match_overlap); + int type = bcf_has_variant_types(line.get(), VCF_DEL, bcf_match_overlap); if(type < 0) throw std::runtime_error("something wrong with variant type\n"); if(type == 0) return false; return true; @@ -969,7 +1001,7 @@ class BcfRecord /// return boolean value indicates if current variant has MNP type defined in vcf.h (htslib>=1.16) inline bool hasMNP() const { - int type = bcf_has_variant_types(line, VCF_MNP, bcf_match_overlap); + int type = bcf_has_variant_types(line.get(), VCF_MNP, bcf_match_overlap); if(type < 0) throw std::runtime_error("something wrong with variant type\n"); if(type == 0) return false; return true; @@ -978,7 +1010,7 @@ class BcfRecord /// return boolean value indicates if current variant has BND type defined in vcf.h (htslib>=1.16) inline bool hasBND() const { - int type = bcf_has_variant_types(line, VCF_BND, bcf_match_overlap); + int type = bcf_has_variant_types(line.get(), VCF_BND, bcf_match_overlap); if(type < 0) throw std::runtime_error("something wrong with variant type\n"); if(type == 0) return false; return true; @@ -987,7 +1019,7 @@ class BcfRecord /// return boolean value indicates if current variant has OTHER type defined in vcf.h (htslib>=1.16) inline bool hasOTHER() const { - int type = bcf_has_variant_types(line, VCF_OTHER, bcf_match_overlap); + int type = bcf_has_variant_types(line.get(), VCF_OTHER, bcf_match_overlap); if(type < 0) throw std::runtime_error("something wrong with variant type\n"); if(type == 0) return false; return true; @@ -996,7 +1028,7 @@ class BcfRecord /// return boolean value indicates if current variant has OVERLAP type defined in vcf.h (htslib>=1.16) inline bool hasOVERLAP() const { - int type = bcf_has_variant_types(line, VCF_OVERLAP, bcf_match_overlap); + int type = bcf_has_variant_types(line.get(), VCF_OVERLAP, bcf_match_overlap); if(type < 0) throw std::runtime_error("something wrong with variant type\n"); if(type == 0) return false; return true; @@ -1035,13 +1067,13 @@ class BcfRecord /** @brief update ID */ inline void setID(const char * s) { - bcf_update_id(header.hdr, line, s); + bcf_update_id(header.hdr, line.get(), s); } /** @brief set REF and ALT alleles given a string seperated by comma */ inline void setRefAlt(const char * alleles_string) { - bcf_update_alleles_str(header.hdr, line, alleles_string); + bcf_update_alleles_str(header.hdr, line.get(), alleles_string); } /** @brief return 0-base start of the variant (can be any type) */ @@ -1232,7 +1264,7 @@ class BcfRecord class BcfReader { private: - htsFile * fp = NULL; // hts file + std::shared_ptr fp; // hts file hts_idx_t * hidx = NULL; // hts index file tbx_t * tidx = NULL; // .tbi .csi index file for vcf files hts_itr_t * itr = NULL; // hts records iterator @@ -1299,8 +1331,8 @@ class BcfReader void open(const std::string & file) { fname = file; - fp = hts_open(file.c_str(), "r"); - header.hdr = bcf_hdr_read(fp); + fp = std::shared_ptr(hts_open(file.c_str(), "r"), htsFile_close()); + header.hdr = bcf_hdr_read(fp.get()); nsamples = bcf_hdr_nsamples(header.hdr); SamplesName = header.getSamples(); } @@ -1317,20 +1349,17 @@ class BcfReader /// close the BcfReader object. void close() { - if(fp) hts_close(fp); + free(s.s); + if(fp) fp.reset(); if(itr) hts_itr_destroy(itr); } - ~BcfReader() - { - if(fp) hts_close(fp); - if(itr) hts_itr_destroy(itr); - } + ~BcfReader() {} /** @brief set the number of threads to use */ inline int setThreads(int n) { - return hts_set_threads(fp, n); + return hts_set_threads(fp.get(), n); } /** @brief get the number of records of given region */ @@ -1396,26 +1425,26 @@ class BcfReader { if(isBcf) { - ret = bcf_itr_next(fp, itr, r.line); - bcf_unpack(r.line, BCF_UN_ALL); + ret = bcf_itr_next(fp.get(), itr, r.line.get()); + bcf_unpack(r.line.get(), BCF_UN_ALL); return (ret >= 0); } else { - int slen = tbx_itr_next(fp, tidx, itr, &s); + int slen = tbx_itr_next(fp.get(), tidx, itr, &s); if(slen > 0) { - ret = vcf_parse(&s, r.header.hdr, r.line); // ret > 0, error - bcf_unpack(r.line, BCF_UN_ALL); + ret = vcf_parse(&s, r.header.hdr, r.line.get()); // ret > 0, error + bcf_unpack(r.line.get(), BCF_UN_ALL); } return (ret <= 0) && (slen > 0); } } else { - ret = bcf_read(fp, r.header.hdr, r.line); + ret = bcf_read(fp.get(), r.header.hdr, r.line.get()); // unpack record immediately. not lazy - bcf_unpack(r.line, BCF_UN_ALL); + bcf_unpack(r.line.get(), BCF_UN_ALL); return (ret == 0); } } @@ -1428,12 +1457,11 @@ class BcfReader class BcfWriter { private: - htsFile * fp = NULL; // hts file + std::shared_ptr fp; // hts file + std::shared_ptr b = std::shared_ptr(bcf_init(), variant_close()); // variant int ret; - bcf1_t * b = bcf_init(); kstring_t s = {0, 0, NULL}; // kstring bool isHeaderWritten = false; - bool isClosed = false; public: /// header object initialized by initalHeader @@ -1496,10 +1524,7 @@ class BcfWriter initalHeader(h); } - ~BcfWriter() - { - if(!isClosed) close(); - } + ~BcfWriter() {} /** * @brief Open VCF/BCF file for writing. The format is infered from file's suffix @@ -1511,7 +1536,7 @@ class BcfWriter if(isEndWith(fname, "bcf.gz")) mode += "b"; if(isEndWith(fname, "bcf")) mode += "bu"; if(isEndWith(fname, "vcf.gz")) mode += "z"; - fp = hts_open(fname.c_str(), mode.c_str()); + fp = std::shared_ptr(hts_open(fname.c_str(), mode.c_str()), htsFile_close()); } /** @@ -1525,16 +1550,15 @@ class BcfWriter */ void open(const std::string & fname, const std::string & mode) { - fp = hts_open(fname.c_str(), mode.c_str()); + fp = std::shared_ptr(hts_open(fname.c_str(), mode.c_str()), htsFile_close()); } /// close the BcfWriter object. void close() { if(!isHeaderWritten) writeHeader(); - if(b) bcf_destroy(b); - if(fp) hts_close(fp); // be careful of double free - isClosed = true; + if(b) b.reset(); + if(fp) fp.reset(); } /// initial a VCF header using the internal template given a specific version. VCF4.1 is the default @@ -1554,27 +1578,27 @@ class BcfWriter /// write a string to a vcf line void writeLine(const std::string & vcfline) { - if(!isHeaderWritten && !writeHeader()) throw std::runtime_error("could not write header out\n"); + if(!isHeaderWritten && !writeHeader()) throw std::runtime_error("could not write header\n"); std::vector line(vcfline.begin(), vcfline.end()); line.push_back('\0'); // don't forget string has no \0; s.s = &line[0]; s.l = vcfline.length(); s.m = vcfline.length(); - ret = vcf_parse(&s, header.hdr, b); + ret = vcf_parse(&s, header.hdr, b.get()); if(ret > 0) throw std::runtime_error("error parsing: " + vcfline + "\n"); if(b->errcode == BCF_ERR_CTG_UNDEF) { throw std::runtime_error("contig id " + (std::string)bcf_hdr_id2name(header.hdr, b->rid) + " not found in the header. please run header->AddContig() first.\n"); } - ret = bcf_write(fp, header.hdr, b); + ret = bcf_write(fp.get(), header.hdr, b.get()); if(ret != 0) throw std::runtime_error("error writing: " + vcfline + "\n"); } /// streams out the header bool writeHeader() { - ret = bcf_hdr_write(fp, header.hdr); + ret = bcf_hdr_write(fp.get(), header.hdr); if(ret == 0) return isHeaderWritten = true; else @@ -1585,7 +1609,7 @@ class BcfWriter inline bool writeRecord(BcfRecord & v) { if(!isHeaderWritten) writeHeader(); - if(bcf_write(fp, v.header.hdr, v.line) < 0) + if(bcf_write(fp.get(), v.header.hdr, v.line.get()) < 0) return false; else return true;