<commit_before>/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */

#ifdef BZLIB_INCLUDE
#  define BUILD_SOURCE
#else
#  include "lib.h"
#  include "istream-internal.h"
#  include "istream-zlib.h"
#  include <zlib.h>

#  ifdef HAVE_ZLIB
#    define BUILD_SOURCE
#    define HAVE_GZSEEK
#  endif
#endif

#ifdef BUILD_SOURCE
/* Default maximum buffer size. Seeking backwards is very expensive, so keep
   this pretty large */
#define DEFAULT_MAX_BUFFER_SIZE (1024*1024)

struct zlib_istream {
	struct istream_private istream;

	int fd;
	gzFile *file;
	uoff_t cached_size;
	uoff_t seek_offset;

	unsigned int marked:1;
};

static void i_stream_zlib_close(struct iostream_private *stream)
{
	struct zlib_istream *zstream = (struct zlib_istream *)stream;

	if (zstream->file != NULL) {
		gzclose(zstream->file);
		zstream->file = NULL;
	}
}

static void i_stream_zlib_destroy(struct iostream_private *stream ATTR_UNUSED)
{
	struct istream_private *_stream = (struct istream_private *)stream;

	i_free(_stream->w_buffer);
}

static ssize_t i_stream_zlib_read(struct istream_private *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);
		} else if (stream->max_buffer_size == 0 ||
			   stream->buffer_size < stream->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));
	do {
	       ret = gzread(zstream->file, stream->w_buffer + stream->pos,
			    size);
	} while (ret < 0 && errno == EINTR && stream->istream.blocking);

	if (ret == 0) {
		/* EOF */
		stream->istream.eof = TRUE;
		return -1;
	}

	if (ret < 0) {
		if (errno == EAGAIN) {
			i_assert(!stream->istream.blocking);
			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
i_stream_zlib_seek(struct istream_private *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;

#ifndef HAVE_GZSEEK
	if (v_offset < start_offset) {
		gzclose(zstream->file);
		zstream->file = gzdopen(zstream->fd, "r");
	}
#else
	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
#endif
	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 (i_stream_zlib_read(stream) >= 0);<commit_msg>bzip plugin: Seeking streams backwards was broken.<commit_after>/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */

#ifdef BZLIB_INCLUDE
#  define BUILD_SOURCE
#else
#  include "lib.h"
#  include "istream-internal.h"
#  include "istream-zlib.h"
#  include <zlib.h>

#  ifdef HAVE_ZLIB
#    define BUILD_SOURCE
#    define HAVE_GZSEEK
#  endif
#endif

#ifdef BUILD_SOURCE
/* Default maximum buffer size. Seeking backwards is very expensive, so keep
   this pretty large */
#define DEFAULT_MAX_BUFFER_SIZE (1024*1024)

#include <unistd.h>

struct zlib_istream {
	struct istream_private istream;

	int fd;
	gzFile *file;
	uoff_t cached_size;
	uoff_t seek_offset;

	unsigned int marked:1;
};

static void i_stream_zlib_close(struct iostream_private *stream)
{
	struct zlib_istream *zstream = (struct zlib_istream *)stream;

	if (zstream->file != NULL) {
		gzclose(zstream->file);
		zstream->file = NULL;
	}
}

static void i_stream_zlib_destroy(struct iostream_private *stream ATTR_UNUSED)
{
	struct istream_private *_stream = (struct istream_private *)stream;

	i_free(_stream->w_buffer);
}

static ssize_t i_stream_zlib_read(struct istream_private *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);
		} else if (stream->max_buffer_size == 0 ||
			   stream->buffer_size < stream->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));
	do {
	       ret = gzread(zstream->file, stream->w_buffer + stream->pos,
			    size);
	} while (ret < 0 && errno == EINTR && stream->istream.blocking);

	if (ret == 0) {
		/* EOF */
		stream->istream.eof = TRUE;
		return -1;
	}

	if (ret < 0) {
		if (errno == EAGAIN) {
			i_assert(!stream->istream.blocking);
			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
i_stream_zlib_seek(struct istream_private *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;

#ifndef HAVE_GZSEEK
	if (v_offset < start_offset) {
		/* need to reopen, but since closing the file closes the
		   file descriptor we'll have to duplicate it first. */
		int fd = dup(zstream->fd);
		if (fd == -1) {
			stream->istream.stream_errno = errno;
			i_error("zlib istream: dup() failed: %m");
			i_stream_close(&stream->istream);
			return;
		}
		gzclose(zstream->file);
		zstream->fd = fd;
		zstream->file = gzdopen(zstream->fd, "r");
	}
#else
	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
#endif
	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 (i_stream_zlib_read(stream) >= 0);
{'fraction_non_alphanumeric': 0.13430358738669732, 'fraction_numerical': 0.008808885484488702, 'mean_word_length': 3.5947214076246334, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 126, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4489479', 'n_tokens_mistral': 3155, 'n_tokens_neox': 2821, 'n_words': 664}