Fossil SCM

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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button