comparison src/zfstream.cc @ 5269:a90ce2dc8b1e

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