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