Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:01:11

0001 /* This Source Code Form is subject to the terms of the Mozilla Public
0002  * License, v. 2.0. If a copy of the MPL was not distributed with this
0003  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
0004  *
0005  * This file is a part of bxzstr (https://github.com/tmaklin/bxzstr)
0006  * Written by Tommi Mäklin (tommi@maklin.fi) based on the zstr.hpp
0007  * file from the zstr project (https://github.com/mateidavid/zstr)
0008  * written by Matei David (https://github.com/mateidavid). */
0009 
0010 #include "config.hpp"
0011 
0012 #ifndef BXZSTR_BXZSTR_HPP
0013 #define BXZSTR_BXZSTR_HPP
0014 
0015 #include <fstream>
0016 #include <memory>
0017 #include <stdexcept>
0018 
0019 #include "stream_wrapper.hpp"
0020 #include "strict_fstream.hpp"
0021 #include "compression_types.hpp"
0022 
0023 namespace bxz {
0024 class istreambuf : public std::streambuf {
0025   public:
0026     istreambuf(std::streambuf * _sbuf_p, std::size_t _buff_size = default_buff_size,
0027            bool _auto_detect = true)
0028             : sbuf_p(_sbuf_p),
0029           strm_p(nullptr),
0030           buff_size(_buff_size),
0031           auto_detect(_auto_detect),
0032           auto_detect_run(false) {
0033         assert(sbuf_p);
0034         in_buff = new char [buff_size];
0035         in_buff_start = in_buff;
0036         in_buff_end = in_buff;
0037         out_buff = new char [buff_size];
0038         setg(out_buff, out_buff, out_buff);
0039     }
0040     istreambuf(const istreambuf &) = delete;
0041     istreambuf(istreambuf &&) = default;
0042     istreambuf & operator = (const istreambuf &) = delete;
0043     istreambuf & operator = (istreambuf &&) = default;
0044     virtual ~istreambuf() {
0045         delete [] in_buff;
0046         delete [] out_buff;
0047     }
0048 
0049     virtual std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out){
0050         std::streampos pos;
0051 
0052         if (way == std::ios_base::cur)
0053             pos = get_cursor() + off;
0054         else if (way == std::ios_base::end)
0055             throw std::runtime_error("Cannot seek from the end position on a compressed stream (the size is not known in advance).");
0056         else if (way == std::ios_base::beg)
0057             pos = off;
0058         
0059         if(pos == get_cursor()) return get_cursor(); // we are just finding the current position
0060 
0061         return seekpos(pos, which);
0062     }
0063 
0064     virtual std::streampos seekpos(std::streampos pos, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out){
0065         if(pos == 0){
0066             seek_to_zero(); // reset the stream
0067             return 0; // this should not fail
0068         }
0069 
0070         while(pos != get_cursor()){
0071             underflow();
0072             std::streamoff relOff = pos-get_cursor();
0073             if(relOff < 0) {              
0074                 if(eback() <= gptr()+relOff) { // if it is buffered just rewind to the position
0075                     setg(eback(), gptr()+relOff, egptr());
0076                 }else{ // otherwise we have to reset/seek to the zero position and seek forward
0077                     seek_to_zero();
0078                 }
0079             }else{
0080                 if(gptr()+relOff >= egptr()) relOff = egptr()-gptr();
0081                 setg(eback(), gptr()+relOff, egptr());
0082             }
0083         }
0084         
0085         return get_cursor();
0086     }
0087 
0088     virtual std::streambuf::int_type underflow() {
0089         if (this->gptr() == this->egptr()) {
0090             // pointers for free region in output buffer
0091             char * out_buff_free_start = out_buff;
0092             do {
0093                 // read more input if none available
0094                 if (in_buff_start == in_buff_end) {
0095                     // empty input buffer: refill from the start
0096                     in_buff_start = in_buff;
0097                     std::streamsize sz = sbuf_p->sgetn(in_buff, buff_size);
0098                     in_buff_end = in_buff + sz;
0099                     if (in_buff_end == in_buff_start) break; // end of input
0100                 }
0101                 // auto detect if the stream contains text or deflate data
0102                 if (auto_detect && ! auto_detect_run) {
0103             this->type = detect_type(in_buff_start, in_buff_end);
0104             this->auto_detect_run = true;
0105         }
0106                 if (this->type == plaintext) {
0107                     // simply swap in_buff and out_buff, and adjust pointers
0108                     assert(in_buff_start == in_buff);
0109                     std::swap(in_buff, out_buff);
0110                     out_buff_free_start = in_buff_end;
0111                     in_buff_start = in_buff;
0112                     in_buff_end = in_buff;
0113                 } else {
0114                     // run inflate() on input
0115             if (! strm_p) init_stream(this->type, true, &strm_p);
0116             strm_p->set_next_in(reinterpret_cast< decltype(strm_p->next_in()) >(in_buff_start));
0117             strm_p->set_avail_in(in_buff_end - in_buff_start);
0118             strm_p->set_next_out(reinterpret_cast< decltype(strm_p->next_out()) >(out_buff_free_start));
0119             strm_p->set_avail_out((out_buff + buff_size) - out_buff_free_start);
0120             strm_p->decompress();
0121                     // update in&out pointers following inflate()
0122             auto tmp = const_cast< unsigned char* >(strm_p->next_in()); // cast away const qualifiers
0123                     in_buff_start = reinterpret_cast< decltype(in_buff_start) >(tmp);
0124                     in_buff_end = in_buff_start + strm_p->avail_in();
0125                     out_buff_free_start = reinterpret_cast< decltype(out_buff_free_start) >(strm_p->next_out());
0126                     assert(out_buff_free_start + strm_p->avail_out() == out_buff + buff_size);
0127                     // if stream ended, deallocate inflator
0128                     if (strm_p->stream_end()) strm_p.reset();
0129                 }
0130             } while (out_buff_free_start == out_buff);
0131             // 2 exit conditions:
0132             // - end of input: there might or might not be output available
0133             // - out_buff_free_start != out_buff: output available
0134             out_buff_end_abs += out_buff_free_start-out_buff;
0135             this->setg(out_buff, out_buff, out_buff_free_start);
0136         }
0137         return this->gptr() == this->egptr()
0138         ? traits_type::eof() : traits_type::to_int_type(*this->gptr());
0139     }
0140   private:
0141   
0142     std::streampos get_cursor(){
0143         return out_buff_end_abs + gptr() - egptr();
0144     }
0145 
0146     void seek_to_zero(){
0147         in_buff_start = in_buff;
0148         in_buff_end = in_buff;
0149         setg(out_buff, out_buff, out_buff);
0150         if(sbuf_p->pubseekpos(0) != 0) throw std::runtime_error("could not seek underlying stream.");
0151         out_buff_end_abs = 0;
0152         strm_p.reset(); // new one will be created on underflow
0153     }
0154 
0155     std::streambuf* sbuf_p;
0156     char* in_buff;
0157     char* in_buff_start;
0158     char* in_buff_end;
0159     char* out_buff;
0160     std::unique_ptr<detail::stream_wrapper> strm_p;
0161     std::size_t buff_size;
0162     bool auto_detect;
0163     bool auto_detect_run;
0164     Compression type;
0165     std::streampos out_buff_end_abs;
0166 
0167     static const std::size_t default_buff_size = (std::size_t)1 << 20;
0168 }; // class istreambuf
0169 
0170 class ostreambuf : public std::streambuf {
0171   public:
0172     ostreambuf(std::streambuf * _sbuf_p, Compression type, int _level = 6,
0173                std::size_t _buff_size = default_buff_size)
0174             : sbuf_p(_sbuf_p),
0175               buff_size(_buff_size),
0176               type(type),
0177               level(_level) {
0178         assert(sbuf_p);
0179         in_buff = new char [buff_size];
0180         out_buff = new char [buff_size];
0181         setp(in_buff, in_buff + buff_size);
0182     init_stream(this->type, false, this->level, &strm_p);
0183     }
0184     ostreambuf(const ostreambuf &) = delete;
0185     ostreambuf(ostreambuf &&) = default;
0186     ostreambuf & operator = (const ostreambuf &) = delete;
0187     ostreambuf & operator = (ostreambuf &&) = default;
0188 
0189     int deflate_loop(const int action) {
0190         while (true) {
0191             strm_p->set_next_out(reinterpret_cast< decltype(strm_p->next_out()) >(out_buff));
0192             strm_p->set_avail_out(buff_size);
0193         strm_p->compress(action);
0194 
0195             std::streamsize sz = sbuf_p->sputn(out_buff, reinterpret_cast< decltype(out_buff) >(strm_p->next_out()) - out_buff);
0196             if (sz != reinterpret_cast< decltype(out_buff) >(strm_p->next_out()) - out_buff) {
0197                 // there was an error in the sink stream
0198                 return -1;
0199             }
0200         if (strm_p->done() || sz == 0) break;
0201         }
0202         return 0;
0203     }
0204 
0205     virtual ~ostreambuf() {
0206         // flush the lzma stream
0207         //
0208         // NOTE: Errors here (sync() return value not 0) are ignored, because we
0209         // cannot throw in a destructor. This mirrors the behaviour of
0210         // std::basic_filebuf::~basic_filebuf(). To see an exception on error,
0211         // close the ofstream with an explicit call to close(), and do not rely
0212         // on the implicit call in the destructor.
0213         //
0214         sync();
0215         delete [] in_buff;
0216         delete [] out_buff;
0217     }
0218     virtual std::streambuf::int_type overflow(std::streambuf::int_type c = traits_type::eof()) {
0219         strm_p->set_next_in(reinterpret_cast< decltype(strm_p->next_in()) >(pbase()));
0220         strm_p->set_avail_in(pptr() - pbase());
0221         while (strm_p->avail_in() > 0) {
0222             int r = deflate_loop(bxz_run(this->type));
0223             if (r != 0) {
0224                 setp(nullptr, nullptr);
0225                 return traits_type::eof();
0226             }
0227         }
0228         setp(in_buff, in_buff + buff_size);
0229         return traits_type::eq_int_type(c, traits_type::eof()) ? traits_type::eof() : sputc(c);
0230     }
0231     virtual int sync() {
0232         // first, call overflow to clear in_buff
0233         overflow();
0234         if (! pptr()) return -1;
0235         // then, call deflate asking to finish the zlib stream
0236         strm_p->set_next_in(nullptr);
0237         strm_p->set_avail_in(0);
0238         if (deflate_loop(bxz_finish(this->type)) != 0) return -1;
0239     init_stream(this->type, false, this->level, &strm_p);
0240         return 0;
0241     }
0242 
0243   private:
0244     std::streambuf* sbuf_p;
0245     char* in_buff;
0246     char* out_buff;
0247     std::unique_ptr<detail::stream_wrapper> strm_p;
0248     std::size_t buff_size;
0249     Compression type;
0250     int level;
0251 
0252     static const std::size_t default_buff_size = (std::size_t)1 << 20;
0253 }; // class ostreambuf
0254 
0255 class istream : public std::istream {
0256   public:
0257     istream(std::istream & is) : std::istream(new istreambuf(is.rdbuf())) {
0258         exceptions(std::ios_base::badbit);
0259     }
0260     explicit istream(std::streambuf * sbuf_p) : std::istream(new istreambuf(sbuf_p)) {
0261         exceptions(std::ios_base::badbit);
0262     }
0263     virtual ~istream() { delete rdbuf(); }
0264 }; // class istream
0265 
0266 class ostream : public std::ostream {
0267   public:
0268     ostream(std::ostream & os, Compression type = plaintext, int level = 6)
0269         : std::ostream(new ostreambuf(os.rdbuf(), type, level)) {
0270     exceptions(std::ios_base::badbit);
0271     }
0272     explicit ostream(std::streambuf * sbuf_p, Compression type = z, int level = 6)
0273         : std::ostream(new ostreambuf(sbuf_p, type, level)) {
0274     exceptions(std::ios_base::badbit);
0275     }
0276     virtual ~ostream() {
0277         delete rdbuf();
0278     }
0279 }; // class ostream
0280 
0281 namespace detail {
0282 template < typename FStream_Type >
0283 class strict_fstream_holder {
0284   public:
0285     strict_fstream_holder() {};
0286     strict_fstream_holder(const std::string& filename,
0287               std::ios_base::openmode mode = std::ios_base::in)
0288             : _fs(filename, mode) {}
0289 
0290   protected:
0291     FStream_Type _fs;
0292 }; // class strict_fstream_holder
0293 
0294 } // namespace detail
0295 
0296 class ifstream : public detail::strict_fstream_holder< strict_fstream::ifstream >,
0297          public std::istream {
0298   public:
0299     ifstream() : std::istream(new istreambuf(_fs.rdbuf())) {}
0300     explicit ifstream(const std::string& filename,
0301               std::ios_base::openmode mode = std::ios_base::in)
0302             : detail::strict_fstream_holder< strict_fstream::ifstream >(filename, mode),
0303             std::istream(new istreambuf(_fs.rdbuf())),
0304         filename(filename),
0305         mode(mode) {
0306         this->setstate(_fs.rdstate());
0307         exceptions(std::ios_base::badbit);
0308     }
0309     ifstream(const ifstream& other) : ifstream(other.filename, other.mode) {}
0310     virtual ~ifstream() { if (rdbuf()) delete rdbuf(); }
0311 
0312 
0313     void open(const std::string &filename,
0314           std::ios_base::openmode mode = std::ios_base::in) {
0315     this->~ifstream();
0316     new (this) ifstream(filename, mode);
0317     }
0318     void open(const char* filename,
0319           std::ios_base::openmode mode = std::ios_base::in) {
0320     this->~ifstream();
0321     new (this) ifstream(filename, mode);
0322     }
0323     bool is_open() const { return _fs.is_open(); }
0324     void close() { _fs.close(); }
0325 
0326   private:
0327     std::string filename;
0328     std::ios_base::openmode mode;
0329 }; // class ifstream
0330 
0331 class ofstream : public detail::strict_fstream_holder< strict_fstream::ofstream >,
0332          public std::ostream {
0333   public:
0334     explicit ofstream(const std::string& filename,
0335               std::ios_base::openmode mode = std::ios_base::out,
0336               Compression type = z, int level = 6)
0337             : detail::strict_fstream_holder< strict_fstream::ofstream >(filename, mode | std::ios_base::binary),
0338             std::ostream(new ostreambuf(_fs.rdbuf(), type, level)),
0339             filename(filename),
0340             mode(mode),
0341             level(level) {
0342         exceptions(std::ios_base::badbit);
0343     }
0344     explicit ofstream(const std::string& filename, Compression type, int level = 6)
0345               : ofstream(filename, std::ios_base::out, type, level) {}
0346     ofstream(const ofstream& other)
0347             : ofstream(other.filename,
0348         other.mode,
0349             other.type,
0350         other.level) {}
0351     virtual ~ofstream() { if (rdbuf()) delete rdbuf(); }
0352     void open(const std::string &filename,
0353           std::ios_base::openmode mode = std::ios_base::in) {
0354     this->~ofstream();
0355     new (this) ofstream(filename, mode);
0356     }
0357     void open(const char* filename,
0358           std::ios_base::openmode mode = std::ios_base::in) {
0359     this->~ofstream();
0360     new (this) ofstream(filename, mode);
0361     }
0362     bool is_open() const { return _fs.is_open(); }
0363     void close() { _fs.close(); }
0364 
0365   private:
0366     std::string filename;
0367     std::ios_base::openmode mode;
0368     Compression type;
0369     int level;
0370 }; // class ofstream
0371 } // namespace bxz
0372 
0373 #endif