Mercurial > dovecot > original-hg > dovecot-1.2
view 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 |
line wrap: on
line source
/* Copyright (C) 2005 Timo Sirainen */ #include "lib.h" #include "istream-internal.h" #include "istream-zlib.h" #include <zlib.h> /* Default maximum buffer size. Seeking backwards is very expensive, so keep this pretty large */ #define DEFAULT_MAX_BUFFER_SIZE (1024*1024) #define I_STREAM_MIN_SIZE 4096 struct zlib_istream { struct _istream istream; size_t max_buffer_size; int fd; gzFile *file; uoff_t cached_size; uoff_t seek_offset; unsigned int marked:1; }; static void _close(struct _iostream *stream) { struct zlib_istream *zstream = (struct zlib_istream *)stream; if (zstream->file != NULL) { gzclose(zstream->file); zstream->file = NULL; } } static void _destroy(struct _iostream *stream __attr_unused__) { struct _istream *_stream = (struct _istream *) stream; p_free(_stream->iostream.pool, _stream->w_buffer); } static void _set_max_buffer_size(struct _iostream *stream, size_t max_size) { struct zlib_istream *zstream = (struct zlib_istream *)stream; zstream->max_buffer_size = max_size; } static void i_stream_grow_buffer(struct _istream *stream, size_t bytes) { struct zlib_istream *zstream = (struct zlib_istream *)stream; size_t old_size; old_size = stream->buffer_size; stream->buffer_size = stream->pos + bytes; if (stream->buffer_size <= I_STREAM_MIN_SIZE) stream->buffer_size = I_STREAM_MIN_SIZE; else { stream->buffer_size = pool_get_exp_grown_size(stream->iostream.pool, old_size, stream->buffer_size); } if (zstream->max_buffer_size > 0 && stream->buffer_size > zstream->max_buffer_size) stream->buffer_size = zstream->max_buffer_size; stream->buffer = stream->w_buffer = p_realloc(stream->iostream.pool, stream->w_buffer, old_size, stream->buffer_size); } static void i_stream_compress(struct _istream *stream) { memmove(stream->w_buffer, stream->w_buffer + stream->skip, stream->pos - stream->skip); stream->pos -= stream->skip; stream->skip = 0; } static ssize_t _read(struct _istream *stream) { struct zlib_istream *zstream = (struct zlib_istream *)stream; size_t size; int ret; if (stream->istream.closed) return -1; stream->istream.stream_errno = 0; if (stream->pos == stream->buffer_size) { if (!zstream->marked && stream->skip > 0) { /* don't try to keep anything cached if we don't have a seek mark. */ i_stream_compress(stream); } if (zstream->max_buffer_size == 0 || stream->buffer_size < zstream->max_buffer_size) { /* buffer is full - grow it */ i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE); } if (stream->pos == stream->buffer_size) { if (stream->skip > 0) { /* lose our buffer cache */ i_stream_compress(stream); } if (stream->pos == stream->buffer_size) return -2; /* buffer full */ } } size = stream->buffer_size - stream->pos; ret = -1; i_assert(zstream->seek_offset == stream->istream.v_offset + (stream->pos - stream->skip)); ret = gzread(zstream->file, stream->w_buffer + stream->pos, size); if (ret == 0) { /* EOF */ stream->istream.eof = TRUE; return -1; } if (ret < 0) { if (errno == EINTR || errno == EAGAIN) ret = 0; else { stream->istream.eof = TRUE; stream->istream.stream_errno = errno; return -1; } } zstream->seek_offset += ret; stream->pos += ret; i_assert(ret != 0); return ret; } static void _seek(struct _istream *stream, uoff_t v_offset, bool mark) { struct zlib_istream *zstream = (struct zlib_istream *) stream; uoff_t start_offset = stream->istream.v_offset - stream->skip; stream->istream.stream_errno = 0; if (v_offset < start_offset) { /* have to seek backwards */ gzseek(zstream->file, v_offset, SEEK_SET); zstream->seek_offset = v_offset; stream->skip = stream->pos = 0; stream->istream.v_offset = v_offset; } else if (v_offset <= start_offset + stream->pos) { /* seeking backwards within what's already cached */ stream->skip = v_offset - start_offset; stream->istream.v_offset = v_offset; } else { /* read and cache forward */ do { size_t avail = stream->pos - stream->skip; if (stream->istream.v_offset + avail >= v_offset) { i_stream_skip(&stream->istream, v_offset - stream->istream.v_offset); break; } i_stream_skip(&stream->istream, avail); } while (_read(stream) >= 0); if (stream->istream.v_offset != v_offset) { /* some failure, we've broken it */ if (stream->istream.stream_errno != 0) { i_error("zlib_istream.seek() failed: %s", strerror(stream->istream.stream_errno)); i_stream_close(&stream->istream); } else { /* unexpected EOF. allow it since we may just want to check if there's anything.. */ i_assert(stream->istream.eof); } } } if (mark) { i_stream_compress(stream); zstream->marked = TRUE; } } static const struct stat *_stat(struct _istream *stream, bool exact) { struct zlib_istream *zstream = (struct zlib_istream *) stream; size_t size; if (fstat(zstream->fd, &stream->statbuf) < 0) { i_error("zlib_istream.fstat() failed: %m"); return NULL; } if (!exact) return &stream->statbuf; if (zstream->cached_size == (uoff_t)-1) { uoff_t old_offset = stream->istream.v_offset; do { (void)i_stream_get_data(&stream->istream, &size); i_stream_skip(&stream->istream, size); } while (_read(stream) > 0); zstream->cached_size = stream->istream.v_offset; i_stream_seek(&stream->istream, old_offset); } stream->statbuf.st_size = zstream->cached_size; return &stream->statbuf; } static void _sync(struct _istream *stream) { struct zlib_istream *zstream = (struct zlib_istream *) stream; zstream->cached_size = (uoff_t)-1; } struct istream *i_stream_create_zlib(int fd, pool_t pool) { struct zlib_istream *zstream; zstream = p_new(pool, struct zlib_istream, 1); zstream->fd = fd; zstream->file = gzdopen(fd, "r"); zstream->cached_size = (uoff_t)-1; zstream->max_buffer_size = DEFAULT_MAX_BUFFER_SIZE; zstream->istream.iostream.close = _close; zstream->istream.iostream.destroy = _destroy; zstream->istream.iostream.set_max_buffer_size = _set_max_buffer_size; zstream->istream.read = _read; zstream->istream.seek = _seek; zstream->istream.stat = _stat; zstream->istream.sync = _sync; zstream->istream.istream.seekable = TRUE; return _i_stream_create(&zstream->istream, pool, fd, 0); }