comparison libinterp/corefcn/gzfstream.cc @ 30123:d809b99f1280

maint: rename "zfstream.[h|cc]" to "gzfstream.[h|cc]" to match class name. * gzfstream.cc: Renamed from zfstream.cc * gzfstream.h: Renamed from zfstream.h * libinterp/corefcn/module.mk: Rename files in build system. * load-save.cc: Update #include to renamed file.
author Rik <rik@octave.org>
date Sun, 05 Sep 2021 19:40:02 -0700
parents libinterp/corefcn/zfstream.cc@a7813409b8c6
children 4412f57132c4
comparison
equal deleted inserted replaced
30122:a7813409b8c6 30123:d809b99f1280
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 "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
59 gzfilebuf::gzfilebuf ()
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
68 gzfilebuf::~gzfilebuf ()
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*
142 gzfilebuf::close ()
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
205 gzfilebuf::showmanyc ()
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
260 gzfilebuf::underflow ()
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
303 gzfilebuf::overflow (int_type c)
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
389 gzfilebuf::sync ()
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
524 gzifstream::gzifstream ()
525 : std::istream (nullptr), sb ()
526 { this->init (&sb); }
527
528 // Initialize stream buffer and open file
529 gzifstream::gzifstream (const char *name, std::ios_base::openmode mode)
530 : std::istream (nullptr), sb ()
531 {
532 this->init (&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), sb ()
539 {
540 this->init (&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 (! 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 (! 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
566 gzifstream::close ()
567 {
568 if (! sb.close ())
569 this->setstate (std::ios_base::failbit);
570 }
571
572 // Default constructor initializes stream buffer
573 gzofstream::gzofstream ()
574 : std::ostream (nullptr), sb ()
575 { this->init (&sb); }
576
577 // Initialize stream buffer and open file
578 gzofstream::gzofstream (const char *name, std::ios_base::openmode mode)
579 : std::ostream (nullptr), sb ()
580 {
581 this->init (&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), sb ()
588 {
589 this->init (&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 (! 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 (! 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
615 gzofstream::close ()
616 {
617 if (! sb.close ())
618 this->setstate (std::ios_base::failbit);
619 }
620
621 #endif