GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
zfstream.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2005-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 /*
27 
28  This file is adapted from the zlib 1.2.2 contrib/iostream3 code,
29  written by
30 
31  Ludwig Schwardt <schwardt@sun.ac.za>
32  original version by Kevin Ruland <kevin@rodin.wustl.edu>
33 
34 */
35 
36 #if defined (HAVE_CONFIG_H)
37 # include "config.h"
38 #endif
39 
40 #include <iomanip>
41 #include <istream>
42 #include <ostream>
43 
44 #include "zfstream.h"
45 
46 #if defined (HAVE_ZLIB)
47 
48 // For strcpy, strcat, strlen (mode strings).
49 #include <cstring>
50 // For BUFSIZ.
51 #include <cstdio>
52 
53 // Internal buffer sizes (default and "unbuffered" versions)
54 #define STASHED_CHARACTERS 16
55 #define BIGBUFSIZE (256 * 1024 + STASHED_CHARACTERS)
56 #define SMALLBUFSIZE 1
57 
58 // Default constructor
60  : file(nullptr), io_mode(std::ios_base::openmode(0)), own_fd(false),
61  buffer(nullptr), buffer_size(BIGBUFSIZE), own_buffer(true)
62 {
63  // No buffers to start with
64  this->disable_buffer ();
65 }
66 
67 // Destructor
69 {
70  // Sync output buffer and close only if responsible for file
71  // (i.e., attached streams should be left open at this stage)
72  this->sync ();
73  if (own_fd)
74  this->close ();
75  // Make sure internal buffer is deallocated
76  this->disable_buffer ();
77 }
78 
79 // Set compression level and strategy
80 int
81 gzfilebuf::setcompression (int comp_level, int comp_strategy)
82 {
83  return gzsetparams (file, comp_level, comp_strategy);
84 }
85 
86 // Open gzipped file
87 gzfilebuf*
88 gzfilebuf::open (const char *name, std::ios_base::openmode mode)
89 {
90  // Fail if file already open
91  if (this->is_open ())
92  return nullptr;
93  // Don't support simultaneous read/write access (yet)
94  if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
95  return nullptr;
96 
97  // Build mode string for gzopen and check it [27.8.1.3.2]
98  char char_mode[6] = "\0\0\0\0\0";
99  if (! this->open_mode (mode, char_mode))
100  return nullptr;
101 
102  // Attempt to open file
103  if ((file = gzopen (name, char_mode)) == nullptr)
104  return nullptr;
105 
106  // On success, allocate internal buffer and set flags
107  this->enable_buffer ();
108  io_mode = mode;
109  own_fd = true;
110  return this;
111 }
112 
113 // Attach to gzipped file
114 gzfilebuf*
115 gzfilebuf::attach (int fd, std::ios_base::openmode mode)
116 {
117  // Fail if file already open
118  if (this->is_open ())
119  return nullptr;
120  // Don't support simultaneous read/write access (yet)
121  if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
122  return nullptr;
123 
124  // Build mode string for gzdopen and check it [27.8.1.3.2]
125  char char_mode[6] = "\0\0\0\0\0";
126  if (! this->open_mode (mode, char_mode))
127  return nullptr;
128 
129  // Attempt to attach to file
130  if ((file = gzdopen (fd, char_mode)) == nullptr)
131  return nullptr;
132 
133  // On success, allocate internal buffer and set flags
134  this->enable_buffer ();
135  io_mode = mode;
136  own_fd = false;
137  return this;
138 }
139 
140 // Close gzipped file
141 gzfilebuf*
143 {
144  // Fail immediately if no file is open
145  if (! this->is_open ())
146  return nullptr;
147  // Assume success
148  gzfilebuf *retval = this;
149  // Attempt to sync and close gzipped file
150  if (this->sync () == -1)
151  retval = nullptr;
152  if (gzclose (file) < 0)
153  retval = nullptr;
154  // File is now gone anyway (postcondition [27.8.1.3.8])
155  file = nullptr;
156  own_fd = false;
157  // Destroy internal buffer if it exists
158  this->disable_buffer ();
159  return retval;
160 }
161 
162 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
163 
164 // Convert int open mode to mode string
165 bool
166 gzfilebuf::open_mode (std::ios_base::openmode mode, char *c_mode) const
167 {
168  // FIXME: do we need testb?
169  // bool testb = mode & std::ios_base::binary;
170  bool testi = mode & std::ios_base::in;
171  bool testo = mode & std::ios_base::out;
172  bool testt = mode & std::ios_base::trunc;
173  bool testa = mode & std::ios_base::app;
174 
175  // Check for valid flag combinations - see [27.8.1.3.2] (Table 92)
176  // Original zfstream hardcoded the compression level to maximum here...
177  // Double the time for less than 1% size improvement seems
178  // excessive though - keeping it at the default level
179  // To change back, just append "9" to the next three mode strings
180  if (! testi && testo && ! testt && ! testa)
181  strcpy (c_mode, "w");
182  if (! testi && testo && ! testt && testa)
183  strcpy (c_mode, "a");
184  if (! testi && testo && testt && ! testa)
185  strcpy (c_mode, "w");
186  if (testi && ! testo && ! testt && ! testa)
187  strcpy (c_mode, "r");
188  // No read/write mode yet
189  // if (testi && testo && ! testt && ! testa)
190  // strcpy(c_mode, "r+");
191  // if (testi && testo && testt && ! testa)
192  // strcpy(c_mode, "w+");
193 
194  // Mode string should be empty for invalid combination of flags
195  if (strlen (c_mode) == 0)
196  return false;
197 
198  strcat (c_mode, "b");
199 
200  return true;
201 }
202 
203 // Determine number of characters in internal get buffer
204 std::streamsize
206 {
207  // Calls to underflow will fail if file not opened for reading
208  if (! this->is_open () || !(io_mode & std::ios_base::in))
209  return -1;
210  // Make sure get area is in use
211  if (this->gptr () && (this->gptr () < this->egptr ()))
212  return std::streamsize (this->egptr () - this->gptr ());
213  else
214  return 0;
215 }
216 
217 // Puts back a character to the stream in two cases. Firstly, when there
218 // is no putback position available, and secondly when the character putback
219 // differs from the one in the file. We can only support the first case
220 // with gzipped files.
221 gzfilebuf::int_type
222 gzfilebuf::pbackfail (gzfilebuf::int_type c)
223 {
224  if (this->is_open ())
225  {
226  if (gzseek (file, this->gptr () - this->egptr () - 1, SEEK_CUR) < 0)
227  return traits_type::eof ();
228 
229  // Invalidates contents of the buffer
230  enable_buffer ();
231 
232  // Attempt to fill internal buffer from gzipped file
233  // (buffer must be guaranteed to exist...)
234  int bytes_read = gzread (file, buffer, buffer_size);
235  // Indicates error or EOF
236  if (bytes_read <= 0)
237  {
238  // Reset get area
239  this->setg (buffer, buffer, buffer);
240  return traits_type::eof ();
241  }
242 
243  // Make all bytes read from file available as get area
244  this->setg (buffer, buffer, buffer + bytes_read);
245 
246  // If next character in get area differs from putback character
247  // flag a failure
248  gzfilebuf::int_type ret = traits_type::to_int_type (*(this->gptr ()));
249  if (ret != c)
250  return traits_type::eof ();
251  else
252  return ret;
253  }
254  else
255  return traits_type::eof ();
256 }
257 
258 // Fill get area from gzipped file
259 gzfilebuf::int_type
261 {
262  // If something is left in the get area by chance, return it
263  // (this shouldn't normally happen, as underflow is only supposed
264  // to be called when gptr >= egptr, but it serves as error check)
265  if (this->gptr () && (this->gptr () < this->egptr ()))
266  return traits_type::to_int_type (*(this->gptr ()));
267 
268  // If the file hasn't been opened for reading, produce error
269  if (! this->is_open () || !(io_mode & std::ios_base::in))
270  return traits_type::eof ();
271 
272  // Copy the final characters to the front of the buffer
273  int stash = 0;
274  if (this->eback () && buffer && buffer_size > STASHED_CHARACTERS)
275  {
276  char_type *ptr1 = buffer;
277  char_type *ptr2 = this->egptr () - STASHED_CHARACTERS + 1;
278  if (ptr2 > this->eback ())
279  while (stash++ <= STASHED_CHARACTERS)
280  *ptr1++ = *ptr2++;
281  }
282 
283  // Attempt to fill internal buffer from gzipped file
284  // (buffer must be guaranteed to exist...)
285  int bytes_read = gzread (file, buffer + stash, buffer_size - stash);
286 
287  // Indicates error or EOF
288  if (bytes_read <= 0)
289  {
290  // Reset get area
291  this->setg (buffer, buffer, buffer);
292  return traits_type::eof ();
293  }
294  // Make all bytes read from file plus the stash available as get area
295  this->setg (buffer, buffer + stash, buffer + bytes_read + stash);
296 
297  // Return next character in get area
298  return traits_type::to_int_type (*(this->gptr ()));
299 }
300 
301 // Write put area to gzipped file
302 gzfilebuf::int_type
304 {
305  // Determine whether put area is in use
306  if (this->pbase ())
307  {
308  // Double-check pointer range
309  if (this->pptr () > this->epptr () || this->pptr () < this->pbase ())
310  return traits_type::eof ();
311  // Add extra character to buffer if not EOF
312  if (! traits_type::eq_int_type (c, traits_type::eof ()))
313  {
314  *(this->pptr ()) = traits_type::to_char_type (c);
315  this->pbump (1);
316  }
317  // Number of characters to write to file
318  int bytes_to_write = this->pptr () - this->pbase ();
319  // Overflow doesn't fail if nothing is to be written
320  if (bytes_to_write > 0)
321  {
322  // If the file hasn't been opened for writing, produce error
323  if (! this->is_open () || !(io_mode & std::ios_base::out))
324  return traits_type::eof ();
325  // If gzipped file won't accept all bytes written to it, fail
326  if (gzwrite (file, this->pbase (), bytes_to_write) != bytes_to_write)
327  return traits_type::eof ();
328  // Reset next pointer to point to pbase on success
329  this->pbump (-bytes_to_write);
330  }
331  }
332  // Write extra character to file if not EOF
333  else if (! traits_type::eq_int_type (c, traits_type::eof ()))
334  {
335  // If the file hasn't been opened for writing, produce error
336  if (! this->is_open () || !(io_mode & std::ios_base::out))
337  return traits_type::eof ();
338  // Impromptu char buffer (allows "unbuffered" output)
339  char_type last_char = traits_type::to_char_type (c);
340  // If gzipped file won't accept this character, fail
341  if (gzwrite (file, &last_char, 1) != 1)
342  return traits_type::eof ();
343  }
344 
345  // If you got here, you have succeeded (even if c was EOF)
346  // The return value should therefore be non-EOF
347  if (traits_type::eq_int_type (c, traits_type::eof ()))
348  return traits_type::not_eof (c);
349  else
350  return c;
351 }
352 
353 // Assign new buffer
354 std::streambuf*
355 gzfilebuf::setbuf (char_type *p, std::streamsize n)
356 {
357  // First make sure stuff is sync'ed, for safety
358  if (this->sync () == -1)
359  return nullptr;
360  // If buffering is turned off on purpose via setbuf(0,0), still allocate one.
361  // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at
362  // least a buffer of size 1 (very inefficient though, therefore make it
363  // bigger?). This follows from [27.5.2.4.3]/12 (gptr needs to point at
364  // something, it seems).
365  if (! p || ! n)
366  {
367  // Replace existing buffer (if any) with small internal buffer
368  this->disable_buffer ();
369  buffer = nullptr;
370  buffer_size = 0;
371  own_buffer = true;
372  this->enable_buffer ();
373  }
374  else
375  {
376  // Replace existing buffer (if any) with external buffer
377  this->disable_buffer ();
378  buffer = p;
379  buffer_size = n;
380  own_buffer = false;
381  this->enable_buffer ();
382  }
383  return this;
384 }
385 
386 // Write put area to gzipped file (i.e., ensures that put area is empty)
387 int
389 {
390  return traits_type::eq_int_type (this->overflow (),
391  traits_type::eof ()) ? -1 : 0;
392 }
393 
394 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
395 
396 // Allocate internal buffer
397 void
399 {
400  // If internal buffer required, allocate one
401  if (own_buffer && ! buffer)
402  {
403  // Check for buffered vs. "unbuffered"
404  if (buffer_size > 0)
405  {
406  // Allocate internal buffer
407  buffer = new char_type [buffer_size];
408  // Get area starts empty and will be expanded by underflow as needed
409  this->setg (buffer, buffer, buffer);
410  // Setup entire internal buffer as put area.
411  // The one-past-end pointer actually points to the last element of
412  // the buffer, so that overflow(c) can safely add the extra character
413  // c to the sequence. These pointers remain in place for the
414  // duration of the buffer
415  this->setp (buffer, buffer + buffer_size - 1);
416  }
417  else
418  {
419  // Even in "unbuffered" case, (small?) get buffer is still required
421  buffer = new char_type [buffer_size];
422  this->setg (buffer, buffer, buffer);
423  // "Unbuffered" means no put buffer
424  this->setp (nullptr, nullptr);
425  }
426  }
427  else
428  {
429  // If buffer already allocated, reset buffer pointers just to make sure no
430  // stale chars are lying around
431  this->setg (buffer, buffer, buffer);
432  this->setp (buffer, buffer + buffer_size - 1);
433  }
434 }
435 
436 // Destroy internal buffer
437 void
439 {
440  // If internal buffer exists, deallocate it
441  if (own_buffer && buffer)
442  {
443  // Preserve unbuffered status by zeroing size
444  if (! this->pbase ())
445  buffer_size = 0;
446  delete[] buffer;
447  buffer = nullptr;
448  this->setg (nullptr, nullptr, nullptr);
449  this->setp (nullptr, nullptr);
450  }
451  else
452  {
453  // Reset buffer pointers to initial state if external buffer exists
454  this->setg (buffer, buffer, buffer);
455  if (buffer)
456  this->setp (buffer, buffer + buffer_size - 1);
457  else
458  this->setp (nullptr, nullptr);
459  }
460 }
461 
462 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
463 
464 // Seek functions
465 gzfilebuf::pos_type
466 gzfilebuf::seekoff (off_type off, std::ios_base::seekdir way,
467  std::ios_base::openmode)
468 {
469  pos_type ret = pos_type (off_type (-1));
470 
471  if (this->is_open ())
472  {
473  off_type computed_off = off;
474 
475  if ((io_mode & std::ios_base::in) && way == std::ios_base::cur)
476  computed_off += this->gptr () - this->egptr ();
477 
478  // Handle tellg/tellp as a special case up front, no need to seek
479  // or invalidate get/put buffers
480  if (off == 0 && way == std::ios_base::cur)
481  return pos_type (gztell (file) + computed_off);
482 
483  if (way == std::ios_base::beg)
484  ret = pos_type (gzseek (file, computed_off, SEEK_SET));
485  else if (way == std::ios_base::cur)
486  ret = pos_type (gzseek (file, computed_off, SEEK_CUR));
487  else
488  // Can't seek from end of a gzipped file, so this will give -1
489  ret = pos_type (gzseek (file, computed_off, SEEK_END));
490 
491  if (io_mode & std::ios_base::in)
492  // Invalidates contents of the buffer
493  enable_buffer ();
494  else
495  // flush contents of buffer to file
496  overflow ();
497  }
498 
499  return ret;
500 }
501 
502 gzfilebuf::pos_type
503 gzfilebuf::seekpos (pos_type sp, std::ios_base::openmode)
504 {
505  pos_type ret = pos_type (off_type (-1));
506 
507  if (this->is_open ())
508  {
509  ret = pos_type (gzseek (file, sp, SEEK_SET));
510 
511  if (io_mode & std::ios_base::in)
512  // Invalidates contents of the buffer
513  enable_buffer ();
514  else
515  // flush contents of buffer to file
516  overflow ();
517  }
518 
519  return ret;
520 }
521 
522 // Default constructor initializes stream buffer
524  : std::istream (nullptr), sb ()
525 { this->init (&sb); }
526 
527 // Initialize stream buffer and open file
528 gzifstream::gzifstream (const char *name, std::ios_base::openmode mode)
529  : std::istream (nullptr), sb ()
530 {
531  this->init (&sb);
532  this->open (name, mode);
533 }
534 
535 // Initialize stream buffer and attach to file
536 gzifstream::gzifstream (int fd, std::ios_base::openmode mode)
537  : std::istream (nullptr), sb ()
538 {
539  this->init (&sb);
540  this->attach (fd, mode);
541 }
542 
543 // Open file and go into fail() state if unsuccessful
544 void
545 gzifstream::open (const char *name, std::ios_base::openmode mode)
546 {
547  if (! sb.open (name, mode | std::ios_base::in))
548  this->setstate (std::ios_base::failbit);
549  else
550  this->clear ();
551 }
552 
553 // Attach to file and go into fail() state if unsuccessful
554 void
555 gzifstream::attach (int fd, std::ios_base::openmode mode)
556 {
557  if (! sb.attach (fd, mode | std::ios_base::in))
558  this->setstate (std::ios_base::failbit);
559  else
560  this->clear ();
561 }
562 
563 // Close file
564 void
566 {
567  if (! sb.close ())
568  this->setstate (std::ios_base::failbit);
569 }
570 
571 // Default constructor initializes stream buffer
573  : std::ostream (nullptr), sb ()
574 { this->init (&sb); }
575 
576 // Initialize stream buffer and open file
577 gzofstream::gzofstream (const char *name, std::ios_base::openmode mode)
578  : std::ostream (nullptr), sb ()
579 {
580  this->init (&sb);
581  this->open (name, mode);
582 }
583 
584 // Initialize stream buffer and attach to file
585 gzofstream::gzofstream (int fd, std::ios_base::openmode mode)
586  : std::ostream (nullptr), sb ()
587 {
588  this->init (&sb);
589  this->attach (fd, mode);
590 }
591 
592 // Open file and go into fail() state if unsuccessful
593 void
594 gzofstream::open (const char *name, std::ios_base::openmode mode)
595 {
596  if (! sb.open (name, mode | std::ios_base::out))
597  this->setstate (std::ios_base::failbit);
598  else
599  this->clear ();
600 }
601 
602 // Attach to file and go into fail() state if unsuccessful
603 void
604 gzofstream::attach (int fd, std::ios_base::openmode mode)
605 {
606  if (! sb.attach (fd, mode | std::ios_base::out))
607  this->setstate (std::ios_base::failbit);
608  else
609  this->clear ();
610 }
611 
612 // Close file
613 void
615 {
616  if (! sb.close ())
617  this->setstate (std::ios_base::failbit);
618 }
619 
620 #endif
#define SEEK_SET
#define SEEK_CUR
#define SEEK_END
Gzipped file stream buffer class.
Definition: zfstream.h:56
std::streamsize buffer_size
Stream buffer size.
Definition: zfstream.h:264
gzfilebuf * close()
Close gzipped file.
Definition: zfstream.cc:142
gzFile file
Underlying file pointer.
Definition: zfstream.h:235
bool is_open() const
Check if file is open.
Definition: zfstream.h:90
virtual int_type underflow()
Fill get area from gzipped file.
Definition: zfstream.cc:260
std::ios_base::openmode io_mode
Mode in which file was opened.
Definition: zfstream.h:240
bool own_fd
True if this object owns file descriptor.
Definition: zfstream.h:248
virtual int_type overflow(int_type c=traits_type::eof())
Write put area to gzipped file.
Definition: zfstream.cc:303
virtual ~gzfilebuf()
Definition: zfstream.cc:68
virtual pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)
Alters the stream positions.
Definition: zfstream.cc:466
virtual int_type pbackfail(int_type c=traits_type::eof())
Definition: zfstream.cc:222
virtual std::streambuf * setbuf(char_type *p, std::streamsize n)
Installs external stream buffer.
Definition: zfstream.cc:355
void disable_buffer()
Destroy internal buffer.
Definition: zfstream.cc:438
gzfilebuf * open(const char *name, std::ios_base::openmode mode)
Open gzipped file.
Definition: zfstream.cc:88
char_type * buffer
Stream buffer.
Definition: zfstream.h:256
gzfilebuf()
Definition: zfstream.cc:59
virtual pos_type seekpos(pos_type sp, std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)
Alters the stream positions.
Definition: zfstream.cc:503
bool open_mode(std::ios_base::openmode mode, char *c_mode) const
Convert ios open mode int to mode string used by zlib.
Definition: zfstream.cc:166
bool own_buffer
True if this object owns stream buffer.
Definition: zfstream.h:272
int setcompression(int comp_level, int comp_strategy=Z_DEFAULT_STRATEGY)
Set compression level and strategy on the fly.
Definition: zfstream.cc:81
void enable_buffer()
Allocate internal buffer.
Definition: zfstream.cc:398
virtual int sync()
Flush stream buffer to file.
Definition: zfstream.cc:388
gzfilebuf * attach(int fd, std::ios_base::openmode mode)
Attach to already open gzipped file.
Definition: zfstream.cc:115
virtual std::streamsize showmanyc()
Number of characters available in stream buffer.
Definition: zfstream.cc:205
void close()
Close gzipped file.
Definition: zfstream.cc:565
void attach(int fd, std::ios_base::openmode mode=std::ios_base::in)
Attach to already open gzipped file.
Definition: zfstream.cc:555
gzfilebuf sb
Underlying stream buffer.
Definition: zfstream.h:359
void open(const char *name, std::ios_base::openmode mode=std::ios_base::in)
Open gzipped file.
Definition: zfstream.cc:545
gzfilebuf sb
Underlying stream buffer.
Definition: zfstream.h:446
void close()
Close gzipped file.
Definition: zfstream.cc:614
void attach(int fd, std::ios_base::openmode mode=std::ios_base::out)
Attach to already open gzipped file.
Definition: zfstream.cc:604
void open(const char *name, std::ios_base::openmode mode=std::ios_base::out)
Open gzipped file.
Definition: zfstream.cc:594
QString name
octave_idx_type n
Definition: mx-inlines.cc:753
std::complex< T > trunc(const std::complex< T > &x)
Definition: lo-mappers.h:111
T::size_type strlen(const typename T::value_type *str)
Definition: oct-string.cc:85
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
#define SMALLBUFSIZE
Definition: zfstream.cc:56
#define STASHED_CHARACTERS
Definition: zfstream.cc:54
#define BIGBUFSIZE
Definition: zfstream.cc:55