annotate src/zlib-1.2.8/contrib/iostream3/zfstream.cc @ 83:ae30d91d2ffe

Replace these with versions built using an older toolset (so as to avoid ABI compatibilities when linking on Ubuntu 14.04 for packaging purposes)
author Chris Cannam
date Fri, 07 Feb 2020 11:51:13 +0000
parents 5ea0608b923f
children
rev   line source
Chris@43 1 /*
Chris@43 2 * A C++ I/O streams interface to the zlib gz* functions
Chris@43 3 *
Chris@43 4 * by Ludwig Schwardt <schwardt@sun.ac.za>
Chris@43 5 * original version by Kevin Ruland <kevin@rodin.wustl.edu>
Chris@43 6 *
Chris@43 7 * This version is standard-compliant and compatible with gcc 3.x.
Chris@43 8 */
Chris@43 9
Chris@43 10 #include "zfstream.h"
Chris@43 11 #include <cstring> // for strcpy, strcat, strlen (mode strings)
Chris@43 12 #include <cstdio> // for BUFSIZ
Chris@43 13
Chris@43 14 // Internal buffer sizes (default and "unbuffered" versions)
Chris@43 15 #define BIGBUFSIZE BUFSIZ
Chris@43 16 #define SMALLBUFSIZE 1
Chris@43 17
Chris@43 18 /*****************************************************************************/
Chris@43 19
Chris@43 20 // Default constructor
Chris@43 21 gzfilebuf::gzfilebuf()
Chris@43 22 : file(NULL), io_mode(std::ios_base::openmode(0)), own_fd(false),
Chris@43 23 buffer(NULL), buffer_size(BIGBUFSIZE), own_buffer(true)
Chris@43 24 {
Chris@43 25 // No buffers to start with
Chris@43 26 this->disable_buffer();
Chris@43 27 }
Chris@43 28
Chris@43 29 // Destructor
Chris@43 30 gzfilebuf::~gzfilebuf()
Chris@43 31 {
Chris@43 32 // Sync output buffer and close only if responsible for file
Chris@43 33 // (i.e. attached streams should be left open at this stage)
Chris@43 34 this->sync();
Chris@43 35 if (own_fd)
Chris@43 36 this->close();
Chris@43 37 // Make sure internal buffer is deallocated
Chris@43 38 this->disable_buffer();
Chris@43 39 }
Chris@43 40
Chris@43 41 // Set compression level and strategy
Chris@43 42 int
Chris@43 43 gzfilebuf::setcompression(int comp_level,
Chris@43 44 int comp_strategy)
Chris@43 45 {
Chris@43 46 return gzsetparams(file, comp_level, comp_strategy);
Chris@43 47 }
Chris@43 48
Chris@43 49 // Open gzipped file
Chris@43 50 gzfilebuf*
Chris@43 51 gzfilebuf::open(const char *name,
Chris@43 52 std::ios_base::openmode mode)
Chris@43 53 {
Chris@43 54 // Fail if file already open
Chris@43 55 if (this->is_open())
Chris@43 56 return NULL;
Chris@43 57 // Don't support simultaneous read/write access (yet)
Chris@43 58 if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
Chris@43 59 return NULL;
Chris@43 60
Chris@43 61 // Build mode string for gzopen and check it [27.8.1.3.2]
Chris@43 62 char char_mode[6] = "\0\0\0\0\0";
Chris@43 63 if (!this->open_mode(mode, char_mode))
Chris@43 64 return NULL;
Chris@43 65
Chris@43 66 // Attempt to open file
Chris@43 67 if ((file = gzopen(name, char_mode)) == NULL)
Chris@43 68 return NULL;
Chris@43 69
Chris@43 70 // On success, allocate internal buffer and set flags
Chris@43 71 this->enable_buffer();
Chris@43 72 io_mode = mode;
Chris@43 73 own_fd = true;
Chris@43 74 return this;
Chris@43 75 }
Chris@43 76
Chris@43 77 // Attach to gzipped file
Chris@43 78 gzfilebuf*
Chris@43 79 gzfilebuf::attach(int fd,
Chris@43 80 std::ios_base::openmode mode)
Chris@43 81 {
Chris@43 82 // Fail if file already open
Chris@43 83 if (this->is_open())
Chris@43 84 return NULL;
Chris@43 85 // Don't support simultaneous read/write access (yet)
Chris@43 86 if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
Chris@43 87 return NULL;
Chris@43 88
Chris@43 89 // Build mode string for gzdopen and check it [27.8.1.3.2]
Chris@43 90 char char_mode[6] = "\0\0\0\0\0";
Chris@43 91 if (!this->open_mode(mode, char_mode))
Chris@43 92 return NULL;
Chris@43 93
Chris@43 94 // Attempt to attach to file
Chris@43 95 if ((file = gzdopen(fd, char_mode)) == NULL)
Chris@43 96 return NULL;
Chris@43 97
Chris@43 98 // On success, allocate internal buffer and set flags
Chris@43 99 this->enable_buffer();
Chris@43 100 io_mode = mode;
Chris@43 101 own_fd = false;
Chris@43 102 return this;
Chris@43 103 }
Chris@43 104
Chris@43 105 // Close gzipped file
Chris@43 106 gzfilebuf*
Chris@43 107 gzfilebuf::close()
Chris@43 108 {
Chris@43 109 // Fail immediately if no file is open
Chris@43 110 if (!this->is_open())
Chris@43 111 return NULL;
Chris@43 112 // Assume success
Chris@43 113 gzfilebuf* retval = this;
Chris@43 114 // Attempt to sync and close gzipped file
Chris@43 115 if (this->sync() == -1)
Chris@43 116 retval = NULL;
Chris@43 117 if (gzclose(file) < 0)
Chris@43 118 retval = NULL;
Chris@43 119 // File is now gone anyway (postcondition [27.8.1.3.8])
Chris@43 120 file = NULL;
Chris@43 121 own_fd = false;
Chris@43 122 // Destroy internal buffer if it exists
Chris@43 123 this->disable_buffer();
Chris@43 124 return retval;
Chris@43 125 }
Chris@43 126
Chris@43 127 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Chris@43 128
Chris@43 129 // Convert int open mode to mode string
Chris@43 130 bool
Chris@43 131 gzfilebuf::open_mode(std::ios_base::openmode mode,
Chris@43 132 char* c_mode) const
Chris@43 133 {
Chris@43 134 bool testb = mode & std::ios_base::binary;
Chris@43 135 bool testi = mode & std::ios_base::in;
Chris@43 136 bool testo = mode & std::ios_base::out;
Chris@43 137 bool testt = mode & std::ios_base::trunc;
Chris@43 138 bool testa = mode & std::ios_base::app;
Chris@43 139
Chris@43 140 // Check for valid flag combinations - see [27.8.1.3.2] (Table 92)
Chris@43 141 // Original zfstream hardcoded the compression level to maximum here...
Chris@43 142 // Double the time for less than 1% size improvement seems
Chris@43 143 // excessive though - keeping it at the default level
Chris@43 144 // To change back, just append "9" to the next three mode strings
Chris@43 145 if (!testi && testo && !testt && !testa)
Chris@43 146 strcpy(c_mode, "w");
Chris@43 147 if (!testi && testo && !testt && testa)
Chris@43 148 strcpy(c_mode, "a");
Chris@43 149 if (!testi && testo && testt && !testa)
Chris@43 150 strcpy(c_mode, "w");
Chris@43 151 if (testi && !testo && !testt && !testa)
Chris@43 152 strcpy(c_mode, "r");
Chris@43 153 // No read/write mode yet
Chris@43 154 // if (testi && testo && !testt && !testa)
Chris@43 155 // strcpy(c_mode, "r+");
Chris@43 156 // if (testi && testo && testt && !testa)
Chris@43 157 // strcpy(c_mode, "w+");
Chris@43 158
Chris@43 159 // Mode string should be empty for invalid combination of flags
Chris@43 160 if (strlen(c_mode) == 0)
Chris@43 161 return false;
Chris@43 162 if (testb)
Chris@43 163 strcat(c_mode, "b");
Chris@43 164 return true;
Chris@43 165 }
Chris@43 166
Chris@43 167 // Determine number of characters in internal get buffer
Chris@43 168 std::streamsize
Chris@43 169 gzfilebuf::showmanyc()
Chris@43 170 {
Chris@43 171 // Calls to underflow will fail if file not opened for reading
Chris@43 172 if (!this->is_open() || !(io_mode & std::ios_base::in))
Chris@43 173 return -1;
Chris@43 174 // Make sure get area is in use
Chris@43 175 if (this->gptr() && (this->gptr() < this->egptr()))
Chris@43 176 return std::streamsize(this->egptr() - this->gptr());
Chris@43 177 else
Chris@43 178 return 0;
Chris@43 179 }
Chris@43 180
Chris@43 181 // Fill get area from gzipped file
Chris@43 182 gzfilebuf::int_type
Chris@43 183 gzfilebuf::underflow()
Chris@43 184 {
Chris@43 185 // If something is left in the get area by chance, return it
Chris@43 186 // (this shouldn't normally happen, as underflow is only supposed
Chris@43 187 // to be called when gptr >= egptr, but it serves as error check)
Chris@43 188 if (this->gptr() && (this->gptr() < this->egptr()))
Chris@43 189 return traits_type::to_int_type(*(this->gptr()));
Chris@43 190
Chris@43 191 // If the file hasn't been opened for reading, produce error
Chris@43 192 if (!this->is_open() || !(io_mode & std::ios_base::in))
Chris@43 193 return traits_type::eof();
Chris@43 194
Chris@43 195 // Attempt to fill internal buffer from gzipped file
Chris@43 196 // (buffer must be guaranteed to exist...)
Chris@43 197 int bytes_read = gzread(file, buffer, buffer_size);
Chris@43 198 // Indicates error or EOF
Chris@43 199 if (bytes_read <= 0)
Chris@43 200 {
Chris@43 201 // Reset get area
Chris@43 202 this->setg(buffer, buffer, buffer);
Chris@43 203 return traits_type::eof();
Chris@43 204 }
Chris@43 205 // Make all bytes read from file available as get area
Chris@43 206 this->setg(buffer, buffer, buffer + bytes_read);
Chris@43 207
Chris@43 208 // Return next character in get area
Chris@43 209 return traits_type::to_int_type(*(this->gptr()));
Chris@43 210 }
Chris@43 211
Chris@43 212 // Write put area to gzipped file
Chris@43 213 gzfilebuf::int_type
Chris@43 214 gzfilebuf::overflow(int_type c)
Chris@43 215 {
Chris@43 216 // Determine whether put area is in use
Chris@43 217 if (this->pbase())
Chris@43 218 {
Chris@43 219 // Double-check pointer range
Chris@43 220 if (this->pptr() > this->epptr() || this->pptr() < this->pbase())
Chris@43 221 return traits_type::eof();
Chris@43 222 // Add extra character to buffer if not EOF
Chris@43 223 if (!traits_type::eq_int_type(c, traits_type::eof()))
Chris@43 224 {
Chris@43 225 *(this->pptr()) = traits_type::to_char_type(c);
Chris@43 226 this->pbump(1);
Chris@43 227 }
Chris@43 228 // Number of characters to write to file
Chris@43 229 int bytes_to_write = this->pptr() - this->pbase();
Chris@43 230 // Overflow doesn't fail if nothing is to be written
Chris@43 231 if (bytes_to_write > 0)
Chris@43 232 {
Chris@43 233 // If the file hasn't been opened for writing, produce error
Chris@43 234 if (!this->is_open() || !(io_mode & std::ios_base::out))
Chris@43 235 return traits_type::eof();
Chris@43 236 // If gzipped file won't accept all bytes written to it, fail
Chris@43 237 if (gzwrite(file, this->pbase(), bytes_to_write) != bytes_to_write)
Chris@43 238 return traits_type::eof();
Chris@43 239 // Reset next pointer to point to pbase on success
Chris@43 240 this->pbump(-bytes_to_write);
Chris@43 241 }
Chris@43 242 }
Chris@43 243 // Write extra character to file if not EOF
Chris@43 244 else if (!traits_type::eq_int_type(c, traits_type::eof()))
Chris@43 245 {
Chris@43 246 // If the file hasn't been opened for writing, produce error
Chris@43 247 if (!this->is_open() || !(io_mode & std::ios_base::out))
Chris@43 248 return traits_type::eof();
Chris@43 249 // Impromptu char buffer (allows "unbuffered" output)
Chris@43 250 char_type last_char = traits_type::to_char_type(c);
Chris@43 251 // If gzipped file won't accept this character, fail
Chris@43 252 if (gzwrite(file, &last_char, 1) != 1)
Chris@43 253 return traits_type::eof();
Chris@43 254 }
Chris@43 255
Chris@43 256 // If you got here, you have succeeded (even if c was EOF)
Chris@43 257 // The return value should therefore be non-EOF
Chris@43 258 if (traits_type::eq_int_type(c, traits_type::eof()))
Chris@43 259 return traits_type::not_eof(c);
Chris@43 260 else
Chris@43 261 return c;
Chris@43 262 }
Chris@43 263
Chris@43 264 // Assign new buffer
Chris@43 265 std::streambuf*
Chris@43 266 gzfilebuf::setbuf(char_type* p,
Chris@43 267 std::streamsize n)
Chris@43 268 {
Chris@43 269 // First make sure stuff is sync'ed, for safety
Chris@43 270 if (this->sync() == -1)
Chris@43 271 return NULL;
Chris@43 272 // If buffering is turned off on purpose via setbuf(0,0), still allocate one...
Chris@43 273 // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at
Chris@43 274 // least a buffer of size 1 (very inefficient though, therefore make it bigger?)
Chris@43 275 // This follows from [27.5.2.4.3]/12 (gptr needs to point at something, it seems)
Chris@43 276 if (!p || !n)
Chris@43 277 {
Chris@43 278 // Replace existing buffer (if any) with small internal buffer
Chris@43 279 this->disable_buffer();
Chris@43 280 buffer = NULL;
Chris@43 281 buffer_size = 0;
Chris@43 282 own_buffer = true;
Chris@43 283 this->enable_buffer();
Chris@43 284 }
Chris@43 285 else
Chris@43 286 {
Chris@43 287 // Replace existing buffer (if any) with external buffer
Chris@43 288 this->disable_buffer();
Chris@43 289 buffer = p;
Chris@43 290 buffer_size = n;
Chris@43 291 own_buffer = false;
Chris@43 292 this->enable_buffer();
Chris@43 293 }
Chris@43 294 return this;
Chris@43 295 }
Chris@43 296
Chris@43 297 // Write put area to gzipped file (i.e. ensures that put area is empty)
Chris@43 298 int
Chris@43 299 gzfilebuf::sync()
Chris@43 300 {
Chris@43 301 return traits_type::eq_int_type(this->overflow(), traits_type::eof()) ? -1 : 0;
Chris@43 302 }
Chris@43 303
Chris@43 304 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Chris@43 305
Chris@43 306 // Allocate internal buffer
Chris@43 307 void
Chris@43 308 gzfilebuf::enable_buffer()
Chris@43 309 {
Chris@43 310 // If internal buffer required, allocate one
Chris@43 311 if (own_buffer && !buffer)
Chris@43 312 {
Chris@43 313 // Check for buffered vs. "unbuffered"
Chris@43 314 if (buffer_size > 0)
Chris@43 315 {
Chris@43 316 // Allocate internal buffer
Chris@43 317 buffer = new char_type[buffer_size];
Chris@43 318 // Get area starts empty and will be expanded by underflow as need arises
Chris@43 319 this->setg(buffer, buffer, buffer);
Chris@43 320 // Setup entire internal buffer as put area.
Chris@43 321 // The one-past-end pointer actually points to the last element of the buffer,
Chris@43 322 // so that overflow(c) can safely add the extra character c to the sequence.
Chris@43 323 // These pointers remain in place for the duration of the buffer
Chris@43 324 this->setp(buffer, buffer + buffer_size - 1);
Chris@43 325 }
Chris@43 326 else
Chris@43 327 {
Chris@43 328 // Even in "unbuffered" case, (small?) get buffer is still required
Chris@43 329 buffer_size = SMALLBUFSIZE;
Chris@43 330 buffer = new char_type[buffer_size];
Chris@43 331 this->setg(buffer, buffer, buffer);
Chris@43 332 // "Unbuffered" means no put buffer
Chris@43 333 this->setp(0, 0);
Chris@43 334 }
Chris@43 335 }
Chris@43 336 else
Chris@43 337 {
Chris@43 338 // If buffer already allocated, reset buffer pointers just to make sure no
Chris@43 339 // stale chars are lying around
Chris@43 340 this->setg(buffer, buffer, buffer);
Chris@43 341 this->setp(buffer, buffer + buffer_size - 1);
Chris@43 342 }
Chris@43 343 }
Chris@43 344
Chris@43 345 // Destroy internal buffer
Chris@43 346 void
Chris@43 347 gzfilebuf::disable_buffer()
Chris@43 348 {
Chris@43 349 // If internal buffer exists, deallocate it
Chris@43 350 if (own_buffer && buffer)
Chris@43 351 {
Chris@43 352 // Preserve unbuffered status by zeroing size
Chris@43 353 if (!this->pbase())
Chris@43 354 buffer_size = 0;
Chris@43 355 delete[] buffer;
Chris@43 356 buffer = NULL;
Chris@43 357 this->setg(0, 0, 0);
Chris@43 358 this->setp(0, 0);
Chris@43 359 }
Chris@43 360 else
Chris@43 361 {
Chris@43 362 // Reset buffer pointers to initial state if external buffer exists
Chris@43 363 this->setg(buffer, buffer, buffer);
Chris@43 364 if (buffer)
Chris@43 365 this->setp(buffer, buffer + buffer_size - 1);
Chris@43 366 else
Chris@43 367 this->setp(0, 0);
Chris@43 368 }
Chris@43 369 }
Chris@43 370
Chris@43 371 /*****************************************************************************/
Chris@43 372
Chris@43 373 // Default constructor initializes stream buffer
Chris@43 374 gzifstream::gzifstream()
Chris@43 375 : std::istream(NULL), sb()
Chris@43 376 { this->init(&sb); }
Chris@43 377
Chris@43 378 // Initialize stream buffer and open file
Chris@43 379 gzifstream::gzifstream(const char* name,
Chris@43 380 std::ios_base::openmode mode)
Chris@43 381 : std::istream(NULL), sb()
Chris@43 382 {
Chris@43 383 this->init(&sb);
Chris@43 384 this->open(name, mode);
Chris@43 385 }
Chris@43 386
Chris@43 387 // Initialize stream buffer and attach to file
Chris@43 388 gzifstream::gzifstream(int fd,
Chris@43 389 std::ios_base::openmode mode)
Chris@43 390 : std::istream(NULL), sb()
Chris@43 391 {
Chris@43 392 this->init(&sb);
Chris@43 393 this->attach(fd, mode);
Chris@43 394 }
Chris@43 395
Chris@43 396 // Open file and go into fail() state if unsuccessful
Chris@43 397 void
Chris@43 398 gzifstream::open(const char* name,
Chris@43 399 std::ios_base::openmode mode)
Chris@43 400 {
Chris@43 401 if (!sb.open(name, mode | std::ios_base::in))
Chris@43 402 this->setstate(std::ios_base::failbit);
Chris@43 403 else
Chris@43 404 this->clear();
Chris@43 405 }
Chris@43 406
Chris@43 407 // Attach to file and go into fail() state if unsuccessful
Chris@43 408 void
Chris@43 409 gzifstream::attach(int fd,
Chris@43 410 std::ios_base::openmode mode)
Chris@43 411 {
Chris@43 412 if (!sb.attach(fd, mode | std::ios_base::in))
Chris@43 413 this->setstate(std::ios_base::failbit);
Chris@43 414 else
Chris@43 415 this->clear();
Chris@43 416 }
Chris@43 417
Chris@43 418 // Close file
Chris@43 419 void
Chris@43 420 gzifstream::close()
Chris@43 421 {
Chris@43 422 if (!sb.close())
Chris@43 423 this->setstate(std::ios_base::failbit);
Chris@43 424 }
Chris@43 425
Chris@43 426 /*****************************************************************************/
Chris@43 427
Chris@43 428 // Default constructor initializes stream buffer
Chris@43 429 gzofstream::gzofstream()
Chris@43 430 : std::ostream(NULL), sb()
Chris@43 431 { this->init(&sb); }
Chris@43 432
Chris@43 433 // Initialize stream buffer and open file
Chris@43 434 gzofstream::gzofstream(const char* name,
Chris@43 435 std::ios_base::openmode mode)
Chris@43 436 : std::ostream(NULL), sb()
Chris@43 437 {
Chris@43 438 this->init(&sb);
Chris@43 439 this->open(name, mode);
Chris@43 440 }
Chris@43 441
Chris@43 442 // Initialize stream buffer and attach to file
Chris@43 443 gzofstream::gzofstream(int fd,
Chris@43 444 std::ios_base::openmode mode)
Chris@43 445 : std::ostream(NULL), sb()
Chris@43 446 {
Chris@43 447 this->init(&sb);
Chris@43 448 this->attach(fd, mode);
Chris@43 449 }
Chris@43 450
Chris@43 451 // Open file and go into fail() state if unsuccessful
Chris@43 452 void
Chris@43 453 gzofstream::open(const char* name,
Chris@43 454 std::ios_base::openmode mode)
Chris@43 455 {
Chris@43 456 if (!sb.open(name, mode | std::ios_base::out))
Chris@43 457 this->setstate(std::ios_base::failbit);
Chris@43 458 else
Chris@43 459 this->clear();
Chris@43 460 }
Chris@43 461
Chris@43 462 // Attach to file and go into fail() state if unsuccessful
Chris@43 463 void
Chris@43 464 gzofstream::attach(int fd,
Chris@43 465 std::ios_base::openmode mode)
Chris@43 466 {
Chris@43 467 if (!sb.attach(fd, mode | std::ios_base::out))
Chris@43 468 this->setstate(std::ios_base::failbit);
Chris@43 469 else
Chris@43 470 this->clear();
Chris@43 471 }
Chris@43 472
Chris@43 473 // Close file
Chris@43 474 void
Chris@43 475 gzofstream::close()
Chris@43 476 {
Chris@43 477 if (!sb.close())
Chris@43 478 this->setstate(std::ios_base::failbit);
Chris@43 479 }