GNU Octave 7.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
gzfstream.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2005-2022 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
80int
81gzfilebuf::setcompression (int comp_level, int comp_strategy)
82{
83 return gzsetparams (m_file, comp_level, comp_strategy);
84}
85
86// Open gzipped file
88gzfilebuf::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
114gzfilebuf *
115gzfilebuf::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
141gzfilebuf *
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
165bool
166gzfilebuf::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
204std::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.
221gzfilebuf::int_type
222gzfilebuf::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
259gzfilebuf::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
302gzfilebuf::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
355std::streambuf *
356gzfilebuf::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)
388int
390{
391 return traits_type::eq_int_type (this->overflow (),
392 traits_type::eof ()) ? -1 : 0;
393}
394
395/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
396
397// Allocate internal buffer
398void
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
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
438void
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
466gzfilebuf::pos_type
467gzfilebuf::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
503gzfilebuf::pos_type
504gzfilebuf::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
529gzifstream::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
537gzifstream::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
545void
546gzifstream::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
555void
556gzifstream::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
565void
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
578gzofstream::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
586gzofstream::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
594void
595gzofstream::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
604void
605gzofstream::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
614void
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 m_own_fd
True if this object owns file descriptor.
Definition: gzfstream.h:248
bool is_open() const
Check if file is open.
Definition: gzfstream.h:90
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
char_type * m_buffer
Stream buffer.
Definition: gzfstream.h:256
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
std::ios_base::openmode m_io_mode
Mode in which file was opened.
Definition: gzfstream.h:240
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
bool m_own_buffer
True if this object owns stream buffer.
Definition: gzfstream.h:272
void disable_buffer()
Destroy internal buffer.
Definition: gzfstream.cc:439
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
void enable_buffer()
Allocate internal buffer.
Definition: gzfstream.cc:399
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
gzFile m_file
Underlying file pointer.
Definition: gzfstream.h:235
virtual std::streamsize showmanyc()
Number of characters available in stream buffer.
Definition: gzfstream.cc:205
std::streamsize m_buffer_size
Stream buffer size.
Definition: gzfstream.h:264
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
gzfilebuf m_sb
Underlying stream buffer.
Definition: gzfstream.h:359
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
gzfilebuf m_sb
Underlying stream buffer.
Definition: gzfstream.h:446
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
QString name
#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
STL namespace.
T::size_type strlen(const typename T::value_type *str)
Definition: oct-string.cc:85