File indexing completed on 2025-01-18 10:01:11
0001
0002
0003
0004
0005
0006
0007
0008
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();
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();
0067 return 0;
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) {
0075 setg(eback(), gptr()+relOff, egptr());
0076 }else{
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
0091 char * out_buff_free_start = out_buff;
0092 do {
0093
0094 if (in_buff_start == in_buff_end) {
0095
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;
0100 }
0101
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
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
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
0122 auto tmp = const_cast< unsigned char* >(strm_p->next_in());
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
0128 if (strm_p->stream_end()) strm_p.reset();
0129 }
0130 } while (out_buff_free_start == out_buff);
0131
0132
0133
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();
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 };
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
0198 return -1;
0199 }
0200 if (strm_p->done() || sz == 0) break;
0201 }
0202 return 0;
0203 }
0204
0205 virtual ~ostreambuf() {
0206
0207
0208
0209
0210
0211
0212
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
0233 overflow();
0234 if (! pptr()) return -1;
0235
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 };
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 };
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 };
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 };
0293
0294 }
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 };
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 };
0371 }
0372
0373 #endif