Mercurial > dovecot > original-hg > dovecot-1.2
annotate src/plugins/zlib/istream-zlib.c @ 4891:6ab2712f1a93 HEAD
Only imap binary was actually working.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 10 Dec 2006 14:35:02 +0200 |
parents | 55df57c028d4 |
children | 81394e71f92a |
rev | line source |
---|---|
3800 | 1 /* Copyright (C) 2005 Timo Sirainen */ |
2 | |
3 #include "lib.h" | |
4 #include "istream-internal.h" | |
5 #include "istream-zlib.h" | |
6 | |
7 #include <zlib.h> | |
8 | |
9 /* Default maximum buffer size. Seeking backwards is very expensive, so keep | |
10 this pretty large */ | |
11 #define DEFAULT_MAX_BUFFER_SIZE (1024*1024) | |
12 | |
13 #define I_STREAM_MIN_SIZE 4096 | |
14 | |
15 struct zlib_istream { | |
16 struct _istream istream; | |
17 | |
18 size_t max_buffer_size; | |
19 | |
20 int fd; | |
21 gzFile *file; | |
22 uoff_t cached_size; | |
23 uoff_t seek_offset; | |
24 | |
25 unsigned int marked:1; | |
26 }; | |
27 | |
28 static void _close(struct _iostream *stream) | |
29 { | |
30 struct zlib_istream *zstream = (struct zlib_istream *)stream; | |
31 | |
32 if (zstream->file != NULL) { | |
33 gzclose(zstream->file); | |
34 zstream->file = NULL; | |
35 } | |
36 } | |
37 | |
38 static void _destroy(struct _iostream *stream __attr_unused__) | |
39 { | |
40 struct _istream *_stream = (struct _istream *) stream; | |
41 | |
42 p_free(_stream->iostream.pool, _stream->w_buffer); | |
43 } | |
44 | |
45 static void _set_max_buffer_size(struct _iostream *stream, size_t max_size) | |
46 { | |
47 struct zlib_istream *zstream = (struct zlib_istream *)stream; | |
48 | |
49 zstream->max_buffer_size = max_size; | |
50 } | |
51 | |
52 static void i_stream_grow_buffer(struct _istream *stream, size_t bytes) | |
53 { | |
54 struct zlib_istream *zstream = (struct zlib_istream *)stream; | |
55 size_t old_size; | |
56 | |
57 old_size = stream->buffer_size; | |
58 | |
59 stream->buffer_size = stream->pos + bytes; | |
60 if (stream->buffer_size <= I_STREAM_MIN_SIZE) | |
61 stream->buffer_size = I_STREAM_MIN_SIZE; | |
62 else { | |
63 stream->buffer_size = | |
64 pool_get_exp_grown_size(stream->iostream.pool, | |
65 old_size, stream->buffer_size); | |
66 } | |
67 | |
68 if (zstream->max_buffer_size > 0 && | |
69 stream->buffer_size > zstream->max_buffer_size) | |
70 stream->buffer_size = zstream->max_buffer_size; | |
71 | |
72 stream->buffer = stream->w_buffer = | |
73 p_realloc(stream->iostream.pool, stream->w_buffer, | |
74 old_size, stream->buffer_size); | |
75 } | |
76 | |
77 static void i_stream_compress(struct _istream *stream) | |
78 { | |
79 memmove(stream->w_buffer, stream->w_buffer + stream->skip, | |
80 stream->pos - stream->skip); | |
81 stream->pos -= stream->skip; | |
82 | |
83 stream->skip = 0; | |
84 } | |
85 | |
86 static ssize_t _read(struct _istream *stream) | |
87 { | |
88 struct zlib_istream *zstream = (struct zlib_istream *)stream; | |
89 size_t size; | |
90 int ret; | |
91 | |
92 if (stream->istream.closed) | |
93 return -1; | |
94 | |
95 stream->istream.stream_errno = 0; | |
96 | |
97 if (stream->pos == stream->buffer_size) { | |
98 if (!zstream->marked && stream->skip > 0) { | |
99 /* don't try to keep anything cached if we don't | |
100 have a seek mark. */ | |
101 i_stream_compress(stream); | |
102 } | |
103 | |
104 if (zstream->max_buffer_size == 0 || | |
105 stream->buffer_size < zstream->max_buffer_size) { | |
106 /* buffer is full - grow it */ | |
107 i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE); | |
108 } | |
109 | |
110 if (stream->pos == stream->buffer_size) { | |
111 if (stream->skip > 0) { | |
112 /* lose our buffer cache */ | |
113 i_stream_compress(stream); | |
114 } | |
115 | |
116 if (stream->pos == stream->buffer_size) | |
117 return -2; /* buffer full */ | |
118 } | |
119 } | |
120 | |
121 size = stream->buffer_size - stream->pos; | |
122 | |
123 ret = -1; | |
124 | |
125 i_assert(zstream->seek_offset == stream->istream.v_offset + | |
126 (stream->pos - stream->skip)); | |
127 ret = gzread(zstream->file, stream->w_buffer + stream->pos, size); | |
128 if (ret == 0) { | |
129 /* EOF */ | |
130 stream->istream.eof = TRUE; | |
131 return -1; | |
132 } | |
133 | |
134 if (ret < 0) { | |
135 if (errno == EINTR || errno == EAGAIN) | |
136 ret = 0; | |
137 else { | |
138 stream->istream.eof = TRUE; | |
139 stream->istream.stream_errno = errno; | |
140 return -1; | |
141 } | |
142 } | |
143 | |
144 zstream->seek_offset += ret; | |
145 stream->pos += ret; | |
146 i_assert(ret != 0); | |
147 return ret; | |
148 } | |
149 | |
3863
55df57c028d4
Added "bool" type and changed all ints that were used as booleans to bool.
Timo Sirainen <tss@iki.fi>
parents:
3800
diff
changeset
|
150 static void _seek(struct _istream *stream, uoff_t v_offset, bool mark) |
3800 | 151 { |
152 struct zlib_istream *zstream = (struct zlib_istream *) stream; | |
153 uoff_t start_offset = stream->istream.v_offset - stream->skip; | |
154 | |
155 stream->istream.stream_errno = 0; | |
156 | |
157 if (v_offset < start_offset) { | |
158 /* have to seek backwards */ | |
159 gzseek(zstream->file, v_offset, SEEK_SET); | |
160 zstream->seek_offset = v_offset; | |
161 | |
162 stream->skip = stream->pos = 0; | |
163 stream->istream.v_offset = v_offset; | |
164 } else if (v_offset <= start_offset + stream->pos) { | |
165 /* seeking backwards within what's already cached */ | |
166 stream->skip = v_offset - start_offset; | |
167 stream->istream.v_offset = v_offset; | |
168 } else { | |
169 /* read and cache forward */ | |
170 do { | |
171 size_t avail = stream->pos - stream->skip; | |
172 if (stream->istream.v_offset + avail >= v_offset) { | |
173 i_stream_skip(&stream->istream, | |
174 v_offset - | |
175 stream->istream.v_offset); | |
176 break; | |
177 } | |
178 | |
179 i_stream_skip(&stream->istream, avail); | |
180 } while (_read(stream) >= 0); | |
181 | |
182 if (stream->istream.v_offset != v_offset) { | |
183 /* some failure, we've broken it */ | |
184 if (stream->istream.stream_errno != 0) { | |
185 i_error("zlib_istream.seek() failed: %s", | |
186 strerror(stream->istream.stream_errno)); | |
187 i_stream_close(&stream->istream); | |
188 } else { | |
189 /* unexpected EOF. allow it since we may just | |
190 want to check if there's anything.. */ | |
191 i_assert(stream->istream.eof); | |
192 } | |
193 } | |
194 } | |
195 | |
196 if (mark) { | |
197 i_stream_compress(stream); | |
198 zstream->marked = TRUE; | |
199 } | |
200 } | |
201 | |
3863
55df57c028d4
Added "bool" type and changed all ints that were used as booleans to bool.
Timo Sirainen <tss@iki.fi>
parents:
3800
diff
changeset
|
202 static const struct stat *_stat(struct _istream *stream, bool exact) |
3800 | 203 { |
204 struct zlib_istream *zstream = (struct zlib_istream *) stream; | |
205 size_t size; | |
206 | |
207 if (fstat(zstream->fd, &stream->statbuf) < 0) { | |
208 i_error("zlib_istream.fstat() failed: %m"); | |
209 return NULL; | |
210 } | |
211 | |
212 if (!exact) | |
213 return &stream->statbuf; | |
214 | |
215 if (zstream->cached_size == (uoff_t)-1) { | |
216 uoff_t old_offset = stream->istream.v_offset; | |
217 do { | |
218 (void)i_stream_get_data(&stream->istream, &size); | |
219 i_stream_skip(&stream->istream, size); | |
220 } while (_read(stream) > 0); | |
221 | |
222 zstream->cached_size = stream->istream.v_offset; | |
223 i_stream_seek(&stream->istream, old_offset); | |
224 } | |
225 stream->statbuf.st_size = zstream->cached_size; | |
226 return &stream->statbuf; | |
227 } | |
228 | |
229 static void _sync(struct _istream *stream) | |
230 { | |
231 struct zlib_istream *zstream = (struct zlib_istream *) stream; | |
232 | |
233 zstream->cached_size = (uoff_t)-1; | |
234 } | |
235 | |
236 struct istream *i_stream_create_zlib(int fd, pool_t pool) | |
237 { | |
238 struct zlib_istream *zstream; | |
239 | |
240 zstream = p_new(pool, struct zlib_istream, 1); | |
241 zstream->fd = fd; | |
242 zstream->file = gzdopen(fd, "r"); | |
243 zstream->cached_size = (uoff_t)-1; | |
244 zstream->max_buffer_size = DEFAULT_MAX_BUFFER_SIZE; | |
245 | |
246 zstream->istream.iostream.close = _close; | |
247 zstream->istream.iostream.destroy = _destroy; | |
248 zstream->istream.iostream.set_max_buffer_size = _set_max_buffer_size; | |
249 | |
250 zstream->istream.read = _read; | |
251 zstream->istream.seek = _seek; | |
252 zstream->istream.stat = _stat; | |
253 zstream->istream.sync = _sync; | |
254 | |
255 zstream->istream.istream.seekable = TRUE; | |
256 return _i_stream_create(&zstream->istream, pool, fd, 0); | |
257 } |