GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
gzfstream.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2005-2024 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 "gzfstream.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  : m_file(nullptr), m_io_mode(std::ios_base::openmode(0)), m_own_fd(false),
61  m_buffer(nullptr), m_buffer_size(BIGBUFSIZE), m_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 (m_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 (m_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 ((m_file = gzopen (name, char_mode)) == nullptr)
104  return nullptr;
105 
106  // On success, allocate internal buffer and set flags
107  this->enable_buffer ();
108  m_io_mode = mode;
109  m_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 ((m_file = gzdopen (fd, char_mode)) == nullptr)
131  return nullptr;
132 
133  // On success, allocate internal buffer and set flags
134  this->enable_buffer ();
135  m_io_mode = mode;
136  m_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 (m_file) < 0)
153  retval = nullptr;
154  // File is now gone anyway (postcondition [27.8.1.3.8])
155  m_file = nullptr;
156  m_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 () || ! (m_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 (m_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 (m_file, m_buffer, m_buffer_size);
235  // Indicates error or EOF
236  if (bytes_read <= 0)
237  {
238  // Reset get area
239  this->setg (m_buffer, m_buffer, m_buffer);
240  return traits_type::eof ();
241  }
242 
243  // Make all bytes read from file available as get area
244  this->setg (m_buffer, m_buffer, m_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 () || ! (m_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 () && m_buffer && m_buffer_size > STASHED_CHARACTERS)
275  {
276  char_type *ptr1 = m_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 (m_file, m_buffer + stash, m_buffer_size - stash);
286 
287  // Indicates error or EOF
288  if (bytes_read <= 0)
289  {
290  // Reset get area
291  this->setg (m_buffer, m_buffer, m_buffer);
292  return traits_type::eof ();
293  }
294  // Make all bytes read from file plus the stash available as get area
295  this->setg (m_buffer, m_buffer + stash, m_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 () || ! (m_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 (m_file, this->pbase (), bytes_to_write)
327  != bytes_to_write)
328  return traits_type::eof ();
329  // Reset next pointer to point to pbase on success
330  this->pbump (-bytes_to_write);
331  }
332  }
333  // Write extra character to file if not EOF
334  else if (! traits_type::eq_int_type (c, traits_type::eof ()))
335  {
336  // If the file hasn't been opened for writing, produce error
337  if (! this->is_open () || ! (m_io_mode & std::ios_base::out))
338  return traits_type::eof ();
339  // Impromptu char buffer (allows "unbuffered" output)
340  char_type last_char = traits_type::to_char_type (c);
341  // If gzipped file won't accept this character, fail
342  if (gzwrite (m_file, &last_char, 1) != 1)
343  return traits_type::eof ();
344  }
345 
346  // If you got here, you have succeeded (even if c was EOF)
347  // The return value should therefore be non-EOF
348  if (traits_type::eq_int_type (c, traits_type::eof ()))
349  return traits_type::not_eof (c);
350  else
351  return c;
352 }
353 
354 // Assign new buffer
355 std::streambuf *
356 gzfilebuf::setbuf (char_type *p, std::streamsize n)
357 {
358  // First make sure stuff is sync'ed, for safety
359  if (this->sync () == -1)
360  return nullptr;
361  // If buffering is turned off on purpose via setbuf(0,0), still allocate one.
362  // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at
363  // least a buffer of size 1 (very inefficient though, therefore make it
364  // bigger?). This follows from [27.5.2.4.3]/12 (gptr needs to point at
365  // something, it seems).
366  if (! p || ! n)
367  {
368  // Replace existing buffer (if any) with small internal buffer
369  this->disable_buffer ();
370  m_buffer = nullptr;
371  m_buffer_size = 0;
372  m_own_buffer = true;
373  this->enable_buffer ();
374  }
375  else
376  {
377  // Replace existing buffer (if any) with external buffer
378  this->disable_buffer ();
379  m_buffer = p;
380  m_buffer_size = n;
381  m_own_buffer = false;
382  this->enable_buffer ();
383  }
384  return this;
385 }
386 
387 // Write put area to gzipped file (i.e., ensures that put area is empty)
388 int
390 {
391  return traits_type::eq_int_type (this->overflow (),
392  traits_type::eof ()) ? -1 : 0;
393 }
394 
395 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
396 
397 // Allocate internal buffer
398 void
399 gzfilebuf::enable_buffer ()
400 {
401  // If internal buffer required, allocate one
402  if (m_own_buffer && ! m_buffer)
403  {
404  // Check for buffered vs. "unbuffered"
405  if (m_buffer_size > 0)
406  {
407  // Allocate internal buffer
408  m_buffer = new char_type [m_buffer_size];
409  // Get area starts empty and will be expanded by underflow as needed
410  this->setg (m_buffer, m_buffer, m_buffer);
411  // Setup entire internal buffer as put area.
412  // The one-past-end pointer actually points to the last element of
413  // the buffer, so that overflow(c) can safely add the extra character
414  // c to the sequence. These pointers remain in place for the
415  // duration of the buffer
416  this->setp (m_buffer, m_buffer + m_buffer_size - 1);
417  }
418  else
419  {
420  // Even in "unbuffered" case, (small?) get buffer is still required
421  m_buffer_size = SMALLBUFSIZE;
422  m_buffer = new char_type [m_buffer_size];
423  this->setg (m_buffer, m_buffer, m_buffer);
424  // "Unbuffered" means no put buffer
425  this->setp (nullptr, nullptr);
426  }
427  }
428  else
429  {
430  // If buffer already allocated, reset buffer pointers just to make sure no
431  // stale chars are lying around
432  this->setg (m_buffer, m_buffer, m_buffer);
433  this->setp (m_buffer, m_buffer + m_buffer_size - 1);
434  }
435 }
436 
437 // Destroy internal buffer
438 void
439 gzfilebuf::disable_buffer ()
440 {
441  // If internal buffer exists, deallocate it
442  if (m_own_buffer && m_buffer)
443  {
444  // Preserve unbuffered status by zeroing size
445  if (! this->pbase ())
446  m_buffer_size = 0;
447  delete [] m_buffer;
448  m_buffer = nullptr;
449  this->setg (nullptr, nullptr, nullptr);
450  this->setp (nullptr, nullptr);
451  }
452  else
453  {
454  // Reset buffer pointers to initial state if external buffer exists
455  this->setg (m_buffer, m_buffer, m_buffer);
456  if (m_buffer)
457  this->setp (m_buffer, m_buffer + m_buffer_size - 1);
458  else
459  this->setp (nullptr, nullptr);
460  }
461 }
462 
463 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
464 
465 // Seek functions
466 gzfilebuf::pos_type
467 gzfilebuf::seekoff (off_type off, std::ios_base::seekdir way,
468  std::ios_base::openmode)
469 {
470  pos_type ret = pos_type (off_type (-1));
471 
472  if (this->is_open ())
473  {
474  off_type computed_off = off;
475 
476  if ((m_io_mode & std::ios_base::in) && way == std::ios_base::cur)
477  computed_off += this->gptr () - this->egptr ();
478 
479  // Handle tellg/tellp as a special case up front, no need to seek
480  // or invalidate get/put buffers
481  if (off == 0 && way == std::ios_base::cur)
482  return pos_type (gztell (m_file) + computed_off);
483 
484  if (way == std::ios_base::beg)
485  ret = pos_type (gzseek (m_file, computed_off, SEEK_SET));
486  else if (way == std::ios_base::cur)
487  ret = pos_type (gzseek (m_file, computed_off, SEEK_CUR));
488  else
489  // Can't seek from end of a gzipped file, so this will give -1
490  ret = pos_type (gzseek (m_file, computed_off, SEEK_END));
491 
492  if (m_io_mode & std::ios_base::in)
493  // Invalidates contents of the buffer
494  enable_buffer ();
495  else
496  // flush contents of buffer to file
497  overflow ();
498  }
499 
500  return ret;
501 }
502 
503 gzfilebuf::pos_type
504 gzfilebuf::seekpos (pos_type sp, std::ios_base::openmode)
505 {
506  pos_type ret = pos_type (off_type (-1));
507 
508  if (this->is_open ())
509  {
510  ret = pos_type (gzseek (m_file, sp, SEEK_SET));
511 
512  if (m_io_mode & std::ios_base::in)
513  // Invalidates contents of the buffer
514  enable_buffer ();
515  else
516  // flush contents of buffer to file
517  overflow ();
518  }
519 
520  return ret;
521 }
522 
523 // Default constructor initializes stream buffer
525  : std::istream (nullptr), m_sb ()
526 { this->init (&m_sb); }
527 
528 // Initialize stream buffer and open file
529 gzifstream::gzifstream (const char *name, std::ios_base::openmode mode)
530  : std::istream (nullptr), m_sb ()
531 {
532  this->init (&m_sb);
533  this->open (name, mode);
534 }
535 
536 // Initialize stream buffer and attach to file
537 gzifstream::gzifstream (int fd, std::ios_base::openmode mode)
538  : std::istream (nullptr), m_sb ()
539 {
540  this->init (&m_sb);
541  this->attach (fd, mode);
542 }
543 
544 // Open file and go into fail() state if unsuccessful
545 void
546 gzifstream::open (const char *name, std::ios_base::openmode mode)
547 {
548  if (! m_sb.open (name, mode | std::ios_base::in))
549  this->setstate (std::ios_base::failbit);
550  else
551  this->clear ();
552 }
553 
554 // Attach to file and go into fail() state if unsuccessful
555 void
556 gzifstream::attach (int fd, std::ios_base::openmode mode)
557 {
558  if (! m_sb.attach (fd, mode | std::ios_base::in))
559  this->setstate (std::ios_base::failbit);
560  else
561  this->clear ();
562 }
563 
564 // Close file
565 void
567 {
568  if (! m_sb.close ())
569  this->setstate (std::ios_base::failbit);
570 }
571 
572 // Default constructor initializes stream buffer
574  : std::ostream (nullptr), m_sb ()
575 { this->init (&m_sb); }
576 
577 // Initialize stream buffer and open file
578 gzofstream::gzofstream (const char *name, std::ios_base::openmode mode)
579  : std::ostream (nullptr), m_sb ()
580 {
581  this->init (&m_sb);
582  this->open (name, mode);
583 }
584 
585 // Initialize stream buffer and attach to file
586 gzofstream::gzofstream (int fd, std::ios_base::openmode mode)
587  : std::ostream (nullptr), m_sb ()
588 {
589  this->init (&m_sb);
590  this->attach (fd, mode);
591 }
592 
593 // Open file and go into fail() state if unsuccessful
594 void
595 gzofstream::open (const char *name, std::ios_base::openmode mode)
596 {
597  if (! m_sb.open (name, mode | std::ios_base::out))
598  this->setstate (std::ios_base::failbit);
599  else
600  this->clear ();
601 }
602 
603 // Attach to file and go into fail() state if unsuccessful
604 void
605 gzofstream::attach (int fd, std::ios_base::openmode mode)
606 {
607  if (! m_sb.attach (fd, mode | std::ios_base::out))
608  this->setstate (std::ios_base::failbit);
609  else
610  this->clear ();
611 }
612 
613 // Close file
614 void
616 {
617  if (! m_sb.close ())
618  this->setstate (std::ios_base::failbit);
619 }
620 
621 #endif
#define SEEK_SET
#define SEEK_CUR
#define SEEK_END
Gzipped file stream buffer class.
Definition: gzfstream.h:56
gzfilebuf * close()
Close gzipped file.
Definition: gzfstream.cc:142
bool is_open() const
Check if file is open.
Definition: gzfstream.h:86
virtual int_type underflow()
Fill get area from gzipped file.
Definition: gzfstream.cc:260
virtual int_type overflow(int_type c=traits_type::eof())
Write put area to gzipped file.
Definition: gzfstream.cc:303
virtual ~gzfilebuf()
Definition: gzfstream.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: gzfstream.cc:467
virtual int_type pbackfail(int_type c=traits_type::eof())
Definition: gzfstream.cc:222
virtual std::streambuf * setbuf(char_type *p, std::streamsize n)
Installs external stream buffer.
Definition: gzfstream.cc:356
gzfilebuf * open(const char *name, std::ios_base::openmode mode)
Open gzipped file.
Definition: gzfstream.cc:88
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: gzfstream.cc:504
bool open_mode(std::ios_base::openmode mode, char *c_mode) const
Convert ios open mode int to mode string used by zlib.
Definition: gzfstream.cc:166
int setcompression(int comp_level, int comp_strategy=Z_DEFAULT_STRATEGY)
Set compression level and strategy on the fly.
Definition: gzfstream.cc:81
virtual int sync()
Flush stream buffer to file.
Definition: gzfstream.cc:389
gzfilebuf * attach(int fd, std::ios_base::openmode mode)
Attach to already open gzipped file.
Definition: gzfstream.cc:115
virtual std::streamsize showmanyc()
Number of characters available in stream buffer.
Definition: gzfstream.cc:205
void close()
Close gzipped file.
Definition: gzfstream.cc:566
void attach(int fd, std::ios_base::openmode mode=std::ios_base::in)
Attach to already open gzipped file.
Definition: gzfstream.cc:556
void open(const char *name, std::ios_base::openmode mode=std::ios_base::in)
Open gzipped file.
Definition: gzfstream.cc:546
void close()
Close gzipped file.
Definition: gzfstream.cc:615
void attach(int fd, std::ios_base::openmode mode=std::ios_base::out)
Attach to already open gzipped file.
Definition: gzfstream.cc:605
void open(const char *name, std::ios_base::openmode mode=std::ios_base::out)
Open gzipped file.
Definition: gzfstream.cc:595
#define SMALLBUFSIZE
Definition: gzfstream.cc:56
#define STASHED_CHARACTERS
Definition: gzfstream.cc:54
#define BIGBUFSIZE
Definition: gzfstream.cc:55
std::complex< T > trunc(const std::complex< T > &x)
Definition: lo-mappers.h:111
octave_idx_type n
Definition: mx-inlines.cc:761
T::size_type strlen(const typename T::value_type *str)
Definition: oct-string.cc:88