summaryrefslogtreecommitdiff
path: root/subversion/libsvn_subr/stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_subr/stream.c')
-rw-r--r--subversion/libsvn_subr/stream.c655
1 files changed, 398 insertions, 257 deletions
diff --git a/subversion/libsvn_subr/stream.c b/subversion/libsvn_subr/stream.c
index 2f803b87219f4..609d7af72c942 100644
--- a/subversion/libsvn_subr/stream.c
+++ b/subversion/libsvn_subr/stream.c
@@ -42,6 +42,7 @@
#include "svn_checksum.h"
#include "svn_path.h"
#include "svn_private_config.h"
+#include "svn_sorts.h"
#include "private/svn_atomic.h"
#include "private/svn_error_private.h"
#include "private/svn_eol_private.h"
@@ -60,7 +61,7 @@ struct svn_stream_t {
svn_stream_mark_fn_t mark_fn;
svn_stream_seek_fn_t seek_fn;
svn_stream_data_available_fn_t data_available_fn;
- svn_stream__is_buffered_fn_t is_buffered_fn;
+ svn_stream_readline_fn_t readline_fn;
apr_file_t *file; /* Maybe NULL */
};
@@ -138,10 +139,10 @@ svn_stream_set_data_available(svn_stream_t *stream,
}
void
-svn_stream__set_is_buffered(svn_stream_t *stream,
- svn_stream__is_buffered_fn_t is_buffered_fn)
+svn_stream_set_readline(svn_stream_t *stream,
+ svn_stream_readline_fn_t readline_fn)
{
- stream->is_buffered_fn = is_buffered_fn;
+ stream->readline_fn = readline_fn;
}
/* Standard implementation for svn_stream_read_full() based on
@@ -197,8 +198,15 @@ svn_error_t *
svn_stream_skip(svn_stream_t *stream, apr_size_t len)
{
if (stream->skip_fn == NULL)
- return svn_error_trace(
- skip_default_handler(stream->baton, len, stream->read_full_fn));
+ {
+ svn_read_fn_t read_fn = stream->read_full_fn ? stream->read_full_fn
+ : stream->read_fn;
+ if (read_fn == NULL)
+ return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL);
+
+ return svn_error_trace(skip_default_handler(stream->baton, len,
+ read_fn));
+ }
return svn_error_trace(stream->skip_fn(stream->baton, len));
}
@@ -227,6 +235,12 @@ svn_stream_supports_mark(svn_stream_t *stream)
return stream->mark_fn != NULL;
}
+svn_boolean_t
+svn_stream_supports_reset(svn_stream_t *stream)
+{
+ return stream->seek_fn != NULL;
+}
+
svn_error_t *
svn_stream_mark(svn_stream_t *stream, svn_stream_mark_t **mark,
apr_pool_t *pool)
@@ -257,15 +271,6 @@ svn_stream_data_available(svn_stream_t *stream,
data_available));
}
-svn_boolean_t
-svn_stream__is_buffered(svn_stream_t *stream)
-{
- if (stream->is_buffered_fn == NULL)
- return FALSE;
-
- return stream->is_buffered_fn(stream->baton);
-}
-
svn_error_t *
svn_stream_close(svn_stream_t *stream)
{
@@ -320,11 +325,9 @@ svn_stream_printf_from_utf8(svn_stream_t *stream,
return svn_error_trace(svn_stream_puts(stream, translated));
}
-/* Guts of svn_stream_readline().
+/* Default implementation for svn_stream_readline().
* Returns the line read from STREAM in *STRINGBUF, and indicates
- * end-of-file in *EOF. If DETECT_EOL is TRUE, the end-of-line indicator
- * is detected automatically and returned in *EOL.
- * If DETECT_EOL is FALSE, *EOL must point to the desired end-of-line
+ * end-of-file in *EOF. EOL must point to the desired end-of-line
* indicator. STRINGBUF is allocated in POOL. */
static svn_error_t *
stream_readline_bytewise(svn_stringbuf_t **stringbuf,
@@ -373,161 +376,27 @@ stream_readline_bytewise(svn_stringbuf_t **stringbuf,
return SVN_NO_ERROR;
}
-static svn_error_t *
-stream_readline_chunky(svn_stringbuf_t **stringbuf,
- svn_boolean_t *eof,
- const char *eol,
- svn_stream_t *stream,
- apr_pool_t *pool)
-{
- /* Read larger chunks of data at once into this buffer and scan
- * that for EOL. A good chunk size should be about 80 chars since
- * most text lines will be shorter. However, don't use a much
- * larger value because filling the buffer from the stream takes
- * time as well.
- */
- char buffer[SVN__LINE_CHUNK_SIZE+1];
-
- /* variables */
- svn_stream_mark_t *mark;
- apr_size_t numbytes;
- const char *eol_pos;
- apr_size_t total_parsed = 0;
-
- /* invariant for this call */
- const size_t eol_len = strlen(eol);
-
- /* Remember the line start so this plus the line length will be
- * the position to move to at the end of this function.
- */
- SVN_ERR(svn_stream_mark(stream, &mark, pool));
-
- /* Read the first chunk. */
- numbytes = SVN__LINE_CHUNK_SIZE;
- SVN_ERR(svn_stream_read_full(stream, buffer, &numbytes));
- buffer[numbytes] = '\0';
-
- /* Look for the EOL in this first chunk. If we find it, we are done here.
- */
- eol_pos = strstr(buffer, eol);
- if (eol_pos != NULL)
- {
- *stringbuf = svn_stringbuf_ncreate(buffer, eol_pos - buffer, pool);
- total_parsed = eol_pos - buffer + eol_len;
- }
- else if (numbytes < SVN__LINE_CHUNK_SIZE)
- {
- /* We hit EOF but not EOL.
- */
- *stringbuf = svn_stringbuf_ncreate(buffer, numbytes, pool);
- *eof = TRUE;
- return SVN_NO_ERROR;
- }
- else
- {
- /* A larger buffer for the string is needed. */
- svn_stringbuf_t *str;
- str = svn_stringbuf_create_ensure(2*SVN__LINE_CHUNK_SIZE, pool);
- svn_stringbuf_appendbytes(str, buffer, numbytes);
- *stringbuf = str;
-
- /* Loop reading chunks until an EOL was found. If we hit EOF, fall
- * back to the standard implementation. */
- do
- {
- /* Append the next chunk to the string read so far.
- */
- svn_stringbuf_ensure(str, str->len + SVN__LINE_CHUNK_SIZE);
- numbytes = SVN__LINE_CHUNK_SIZE;
- SVN_ERR(svn_stream_read_full(stream, str->data + str->len, &numbytes));
- str->len += numbytes;
- str->data[str->len] = '\0';
-
- /* Look for the EOL in the new data plus the last part of the
- * previous chunk because the EOL may span over the boundary
- * between both chunks.
- */
- eol_pos = strstr(str->data + str->len - numbytes - (eol_len-1), eol);
-
- if ((numbytes < SVN__LINE_CHUNK_SIZE) && (eol_pos == NULL))
- {
- /* We hit EOF instead of EOL. */
- *eof = TRUE;
- return SVN_NO_ERROR;
- }
- }
- while (eol_pos == NULL);
-
- /* Number of bytes we actually consumed (i.e. line + EOF).
- * We need to "return" the rest to the stream by moving its
- * read pointer.
- */
- total_parsed = eol_pos - str->data + eol_len;
-
- /* Terminate the string at the EOL postion and return it. */
- str->len = eol_pos - str->data;
- str->data[str->len] = 0;
- }
-
- /* Move the stream read pointer to the first position behind the EOL.
- */
- SVN_ERR(svn_stream_seek(stream, mark));
- return svn_error_trace(svn_stream_skip(stream, total_parsed));
-}
-
-/* Guts of svn_stream_readline().
- * Returns the line read from STREAM in *STRINGBUF, and indicates
- * end-of-file in *EOF. EOL must point to the desired end-of-line
- * indicator. STRINGBUF is allocated in POOL. */
-static svn_error_t *
-stream_readline(svn_stringbuf_t **stringbuf,
- svn_boolean_t *eof,
- const char *eol,
- svn_stream_t *stream,
- apr_pool_t *pool)
+svn_error_t *
+svn_stream_readline(svn_stream_t *stream,
+ svn_stringbuf_t **stringbuf,
+ const char *eol,
+ svn_boolean_t *eof,
+ apr_pool_t *pool)
{
- *eof = FALSE;
-
- /* Often, we operate on APR file or string-based streams and know what
- * EOL we are looking for. Optimize that common case.
- */
- if (svn_stream_supports_mark(stream) &&
- svn_stream__is_buffered(stream))
+ if (stream->readline_fn)
{
- /* We can efficiently read chunks speculatively and reposition the
- * stream pointer to the end of the line once we found that.
- */
- SVN_ERR(stream_readline_chunky(stringbuf,
- eof,
- eol,
- stream,
- pool));
+ /* Use the specific implementation when it's available. */
+ SVN_ERR(stream->readline_fn(stream->baton, stringbuf, eol, eof, pool));
}
else
{
- /* Use the standard byte-byte implementation.
- */
- SVN_ERR(stream_readline_bytewise(stringbuf,
- eof,
- eol,
- stream,
- pool));
+ /* Use the default implementation. */
+ SVN_ERR(stream_readline_bytewise(stringbuf, eof, eol, stream, pool));
}
return SVN_NO_ERROR;
}
-svn_error_t *
-svn_stream_readline(svn_stream_t *stream,
- svn_stringbuf_t **stringbuf,
- const char *eol,
- svn_boolean_t *eof,
- apr_pool_t *pool)
-{
- return svn_error_trace(stream_readline(stringbuf, eof, eol, stream,
- pool));
-}
-
svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to,
svn_cancel_func_t cancel_func,
void *cancel_baton,
@@ -656,11 +525,6 @@ seek_handler_empty(void *baton, const svn_stream_mark_t *mark)
return SVN_NO_ERROR;
}
-static svn_boolean_t
-is_buffered_handler_empty(void *baton)
-{
- return FALSE;
-}
svn_stream_t *
@@ -673,7 +537,6 @@ svn_stream_empty(apr_pool_t *pool)
svn_stream_set_write(stream, write_handler_empty);
svn_stream_set_mark(stream, mark_handler_empty);
svn_stream_set_seek(stream, seek_handler_empty);
- svn_stream__set_is_buffered(stream, is_buffered_handler_empty);
return stream;
}
@@ -780,10 +643,15 @@ data_available_disown(void *baton, svn_boolean_t *data_available)
return svn_error_trace(svn_stream_data_available(baton, data_available));
}
-static svn_boolean_t
-is_buffered_handler_disown(void *baton)
+static svn_error_t *
+readline_handler_disown(void *baton,
+ svn_stringbuf_t **stringbuf,
+ const char *eol,
+ svn_boolean_t *eof,
+ apr_pool_t *pool)
{
- return svn_stream__is_buffered(baton);
+ return svn_error_trace(svn_stream_readline(baton, stringbuf, eol,
+ eof, pool));
}
svn_stream_t *
@@ -797,7 +665,7 @@ svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool)
svn_stream_set_mark(s, mark_handler_disown);
svn_stream_set_seek(s, seek_handler_disown);
svn_stream_set_data_available(s, data_available_disown);
- svn_stream__set_is_buffered(s, is_buffered_handler_disown);
+ svn_stream_set_readline(s, readline_handler_disown);
return s;
}
@@ -808,6 +676,7 @@ svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool)
struct baton_apr {
apr_file_t *file;
apr_pool_t *pool;
+ svn_boolean_t truncate_on_seek;
};
/* svn_stream_mark_t for streams backed by APR files. */
@@ -917,8 +786,7 @@ mark_handler_apr(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
struct mark_apr *mark_apr;
mark_apr = apr_palloc(pool, sizeof(*mark_apr));
- mark_apr->off = 0;
- SVN_ERR(svn_io_file_seek(btn->file, APR_CUR, &mark_apr->off, btn->pool));
+ SVN_ERR(svn_io_file_get_offset(&mark_apr->off, btn->file, btn->pool));
*mark = (svn_stream_mark_t *)mark_apr;
return SVN_NO_ERROR;
}
@@ -929,7 +797,16 @@ seek_handler_apr(void *baton, const svn_stream_mark_t *mark)
struct baton_apr *btn = baton;
apr_off_t offset = (mark != NULL) ? ((const struct mark_apr *)mark)->off : 0;
- SVN_ERR(svn_io_file_seek(btn->file, APR_SET, &offset, btn->pool));
+ if (btn->truncate_on_seek)
+ {
+ /* The apr_file_trunc() function always does seek + trunc,
+ * and this is documented, so don't seek when truncating. */
+ SVN_ERR(svn_io_file_trunc(btn->file, offset, btn->pool));
+ }
+ else
+ {
+ SVN_ERR(svn_io_file_seek(btn->file, APR_SET, &offset, btn->pool));
+ }
return SVN_NO_ERROR;
}
@@ -990,11 +867,146 @@ data_available_handler_apr(void *baton, svn_boolean_t *data_available)
#endif
}
-static svn_boolean_t
-is_buffered_handler_apr(void *baton)
+static svn_error_t *
+readline_apr_lf(apr_file_t *file,
+ svn_stringbuf_t **stringbuf,
+ svn_boolean_t *eof,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *buf;
+
+ buf = svn_stringbuf_create_ensure(SVN__LINE_CHUNK_SIZE, pool);
+ while (1)
+ {
+ apr_status_t status;
+
+ status = apr_file_gets(buf->data + buf->len,
+ (int) (buf->blocksize - buf->len),
+ file);
+ buf->len += strlen(buf->data + buf->len);
+
+ if (APR_STATUS_IS_EOF(status))
+ {
+ /* apr_file_gets() keeps the newline; strip it if necessary. */
+ if (buf->len > 0 && buf->data[buf->len - 1] == '\n')
+ svn_stringbuf_chop(buf, 1);
+
+ *eof = TRUE;
+ *stringbuf = buf;
+ return SVN_NO_ERROR;
+ }
+ else if (status != APR_SUCCESS)
+ {
+ const char *fname;
+ svn_error_t *err = svn_io_file_name_get(&fname, file, pool);
+ if (err)
+ fname = NULL;
+ svn_error_clear(err);
+
+ if (fname)
+ return svn_error_wrap_apr(status,
+ _("Can't read a line from file '%s'"),
+ svn_dirent_local_style(fname, pool));
+ else
+ return svn_error_wrap_apr(status,
+ _("Can't read a line from stream"));
+ }
+
+ /* Do we have the EOL? If yes, strip it and return. */
+ if (buf->len > 0 && buf->data[buf->len - 1] == '\n')
+ {
+ svn_stringbuf_chop(buf, 1);
+ *eof = FALSE;
+ *stringbuf = buf;
+ return SVN_NO_ERROR;
+ }
+
+ /* Otherwise, prepare to read the next chunk. */
+ svn_stringbuf_ensure(buf, buf->blocksize + SVN__LINE_CHUNK_SIZE);
+ }
+}
+
+static svn_error_t *
+readline_apr_generic(apr_file_t *file,
+ svn_stringbuf_t **stringbuf,
+ const char *eol,
+ svn_boolean_t *eof,
+ apr_pool_t *pool)
+{
+ apr_size_t eol_len = strlen(eol);
+ apr_off_t offset;
+ svn_stringbuf_t *buf;
+
+ SVN_ERR(svn_io_file_get_offset(&offset, file, pool));
+
+ buf = svn_stringbuf_create_ensure(SVN__LINE_CHUNK_SIZE, pool);
+ while (1)
+ {
+ apr_size_t bytes_read;
+ svn_boolean_t hit_eof;
+ const char *search_start;
+ const char *eol_pos;
+
+ /* We look for the EOL in the new data plus the last part of the
+ previous chunk because the EOL may span over the boundary
+ between both chunks. */
+ if (buf->len < eol_len)
+ search_start = buf->data;
+ else
+ search_start = buf->data + buf->len - eol_len;
+
+ SVN_ERR(svn_io_file_read_full2(file, buf->data + buf->len,
+ buf->blocksize - buf->len - 1,
+ &bytes_read, &hit_eof, pool));
+ buf->len += bytes_read;
+ buf->data[buf->len] = '\0';
+
+ /* Do we have the EOL now? */
+ eol_pos = strstr(search_start, eol);
+ if (eol_pos)
+ {
+ svn_stringbuf_chop(buf, buf->data + buf->len - eol_pos);
+ /* Seek to the first position behind the EOL. */
+ offset += (buf->len + eol_len);
+ SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool));
+
+ *eof = FALSE;
+ *stringbuf = buf;
+ return SVN_NO_ERROR;
+ }
+ else if (eol_pos == NULL && hit_eof)
+ {
+ *eof = TRUE;
+ *stringbuf = buf;
+ return SVN_NO_ERROR;
+ }
+
+ /* Prepare to read the next chunk. */
+ svn_stringbuf_ensure(buf, buf->blocksize + SVN__LINE_CHUNK_SIZE);
+ }
+}
+
+static svn_error_t *
+readline_handler_apr(void *baton,
+ svn_stringbuf_t **stringbuf,
+ const char *eol,
+ svn_boolean_t *eof,
+ apr_pool_t *pool)
{
struct baton_apr *btn = baton;
- return (apr_file_flags_get(btn->file) & APR_BUFFERED) != 0;
+
+ if (eol[0] == '\n' && eol[1] == '\0')
+ {
+ /* Optimize the common case when we're looking for an LF ("\n")
+ end-of-line sequence by using apr_file_gets(). */
+ return svn_error_trace(readline_apr_lf(btn->file, stringbuf,
+ eof, pool));
+ }
+ else
+ {
+ return svn_error_trace(readline_apr_generic(btn->file, stringbuf,
+ eol, eof, pool));
+ }
}
svn_error_t *
@@ -1056,6 +1068,7 @@ static svn_stream_t *
make_stream_from_apr_file(apr_file_t *file,
svn_boolean_t disown,
svn_boolean_t supports_seek,
+ svn_boolean_t truncate_on_seek,
apr_pool_t *pool)
{
struct baton_apr *baton;
@@ -1067,6 +1080,7 @@ make_stream_from_apr_file(apr_file_t *file,
baton = apr_palloc(pool, sizeof(*baton));
baton->file = file;
baton->pool = pool;
+ baton->truncate_on_seek = truncate_on_seek;
stream = svn_stream_create(baton, pool);
svn_stream_set_read2(stream, read_handler_apr, read_full_handler_apr);
svn_stream_set_write(stream, write_handler_apr);
@@ -1076,10 +1090,10 @@ make_stream_from_apr_file(apr_file_t *file,
svn_stream_set_skip(stream, skip_handler_apr);
svn_stream_set_mark(stream, mark_handler_apr);
svn_stream_set_seek(stream, seek_handler_apr);
+ svn_stream_set_readline(stream, readline_handler_apr);
}
svn_stream_set_data_available(stream, data_available_handler_apr);
- svn_stream__set_is_buffered(stream, is_buffered_handler_apr);
stream->file = file;
if (! disown)
@@ -1089,11 +1103,20 @@ make_stream_from_apr_file(apr_file_t *file,
}
svn_stream_t *
+svn_stream__from_aprfile(apr_file_t *file,
+ svn_boolean_t disown,
+ svn_boolean_t truncate_on_seek,
+ apr_pool_t *pool)
+{
+ return make_stream_from_apr_file(file, disown, TRUE, truncate_on_seek, pool);
+}
+
+svn_stream_t *
svn_stream_from_aprfile2(apr_file_t *file,
svn_boolean_t disown,
apr_pool_t *pool)
{
- return make_stream_from_apr_file(file, disown, TRUE, pool);
+ return make_stream_from_apr_file(file, disown, TRUE, FALSE, pool);
}
apr_file_t *
@@ -1431,6 +1454,31 @@ close_handler_checksum(void *baton)
return svn_error_trace(svn_stream_close(btn->proxy));
}
+static svn_error_t *
+seek_handler_checksum(void *baton, const svn_stream_mark_t *mark)
+{
+ struct checksum_stream_baton *btn = baton;
+
+ /* Only reset support. */
+ if (mark)
+ {
+ return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED,
+ NULL, NULL);
+ }
+ else
+ {
+ if (btn->read_ctx)
+ svn_checksum_ctx_reset(btn->read_ctx);
+
+ if (btn->write_ctx)
+ svn_checksum_ctx_reset(btn->write_ctx);
+
+ SVN_ERR(svn_stream_reset(btn->proxy));
+ }
+
+ return SVN_NO_ERROR;
+}
+
svn_stream_t *
svn_stream_checksummed2(svn_stream_t *stream,
@@ -1468,37 +1516,96 @@ svn_stream_checksummed2(svn_stream_t *stream,
svn_stream_set_write(s, write_handler_checksum);
svn_stream_set_data_available(s, data_available_handler_checksum);
svn_stream_set_close(s, close_handler_checksum);
+ if (svn_stream_supports_reset(stream))
+ svn_stream_set_seek(s, seek_handler_checksum);
return s;
}
+/* Helper for svn_stream_contents_checksum() to compute checksum of
+ * KIND of STREAM. This function doesn't close source stream. */
+static svn_error_t *
+compute_stream_checksum(svn_checksum_t **checksum,
+ svn_stream_t *stream,
+ svn_checksum_kind_t kind,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_checksum_ctx_t *ctx = svn_checksum_ctx_create(kind, scratch_pool);
+ char *buf = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
+
+ while (1)
+ {
+ apr_size_t len = SVN__STREAM_CHUNK_SIZE;
+
+ SVN_ERR(svn_stream_read_full(stream, buf, &len));
+
+ if (len > 0)
+ SVN_ERR(svn_checksum_update(ctx, buf, len));
+
+ if (len != SVN__STREAM_CHUNK_SIZE)
+ break;
+ }
+ SVN_ERR(svn_checksum_final(checksum, ctx, result_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_stream_contents_checksum(svn_checksum_t **checksum,
+ svn_stream_t *stream,
+ svn_checksum_kind_t kind,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+
+ err = compute_stream_checksum(checksum, stream, kind,
+ result_pool, scratch_pool);
+
+ /* Close source stream in all cases. */
+ return svn_error_compose_create(err, svn_stream_close(stream));
+}
+
/* Miscellaneous stream functions. */
+/*
+ * [JAF] By considering the buffer size doubling algorithm we use, I think
+ * the performance characteristics of this implementation are as follows:
+ *
+ * When the effective hint is big enough for the actual data, it uses
+ * minimal time and allocates space roughly equal to the effective hint.
+ * Otherwise, it incurs a time overhead for copying an additional 1x to 2x
+ * the actual length of data, and a space overhead of an additional 2x to
+ * 3x the actual length.
+ */
svn_error_t *
-svn_stringbuf_from_stream(svn_stringbuf_t **str,
+svn_stringbuf_from_stream(svn_stringbuf_t **result,
svn_stream_t *stream,
apr_size_t len_hint,
apr_pool_t *result_pool)
{
#define MIN_READ_SIZE 64
-
- apr_size_t to_read = 0;
svn_stringbuf_t *text
- = svn_stringbuf_create_ensure(len_hint ? len_hint : MIN_READ_SIZE,
+ = svn_stringbuf_create_ensure(MAX(len_hint + 1, MIN_READ_SIZE),
result_pool);
- do
+ while(TRUE)
{
- to_read = text->blocksize - 1 - text->len;
- SVN_ERR(svn_stream_read_full(stream, text->data + text->len, &to_read));
- text->len += to_read;
+ apr_size_t to_read = text->blocksize - 1 - text->len;
+ apr_size_t actually_read = to_read;
+
+ SVN_ERR(svn_stream_read_full(stream, text->data + text->len, &actually_read));
+ text->len += actually_read;
- if (to_read && text->blocksize < text->len + MIN_READ_SIZE)
+ if (actually_read < to_read)
+ break;
+
+ if (text->blocksize - text->len < MIN_READ_SIZE)
svn_stringbuf_ensure(text, text->blocksize * 2);
}
- while (to_read);
text->data[text->len] = '\0';
- *str = text;
+ *result = text;
return SVN_NO_ERROR;
}
@@ -1587,10 +1694,35 @@ data_available_handler_stringbuf(void *baton, svn_boolean_t *data_available)
return SVN_NO_ERROR;
}
-static svn_boolean_t
-is_buffered_handler_stringbuf(void *baton)
+static svn_error_t *
+readline_handler_stringbuf(void *baton,
+ svn_stringbuf_t **stringbuf,
+ const char *eol,
+ svn_boolean_t *eof,
+ apr_pool_t *pool)
{
- return TRUE;
+ struct stringbuf_stream_baton *btn = baton;
+ const char *pos = btn->str->data + btn->amt_read;
+ const char *eol_pos;
+
+ eol_pos = strstr(pos, eol);
+ if (eol_pos)
+ {
+ apr_size_t eol_len = strlen(eol);
+
+ *eof = FALSE;
+ *stringbuf = svn_stringbuf_ncreate(pos, eol_pos - pos, pool);
+ btn->amt_read += (eol_pos - pos + eol_len);
+ }
+ else
+ {
+ *eof = TRUE;
+ *stringbuf = svn_stringbuf_ncreate(pos, btn->str->len - btn->amt_read,
+ pool);
+ btn->amt_read = btn->str->len;
+ }
+
+ return SVN_NO_ERROR;
}
svn_stream_t *
@@ -1613,7 +1745,7 @@ svn_stream_from_stringbuf(svn_stringbuf_t *str,
svn_stream_set_mark(stream, mark_handler_stringbuf);
svn_stream_set_seek(stream, seek_handler_stringbuf);
svn_stream_set_data_available(stream, data_available_handler_stringbuf);
- svn_stream__set_is_buffered(stream, is_buffered_handler_stringbuf);
+ svn_stream_set_readline(stream, readline_handler_stringbuf);
return stream;
}
@@ -1692,10 +1824,35 @@ data_available_handler_string(void *baton, svn_boolean_t *data_available)
return SVN_NO_ERROR;
}
-static svn_boolean_t
-is_buffered_handler_string(void *baton)
+static svn_error_t *
+readline_handler_string(void *baton,
+ svn_stringbuf_t **stringbuf,
+ const char *eol,
+ svn_boolean_t *eof,
+ apr_pool_t *pool)
{
- return TRUE;
+ struct string_stream_baton *btn = baton;
+ const char *pos = btn->str->data + btn->amt_read;
+ const char *eol_pos;
+
+ eol_pos = strstr(pos, eol);
+ if (eol_pos)
+ {
+ apr_size_t eol_len = strlen(eol);
+
+ *eof = FALSE;
+ *stringbuf = svn_stringbuf_ncreate(pos, eol_pos - pos, pool);
+ btn->amt_read += (eol_pos - pos + eol_len);
+ }
+ else
+ {
+ *eof = TRUE;
+ *stringbuf = svn_stringbuf_ncreate(pos, btn->str->len - btn->amt_read,
+ pool);
+ btn->amt_read = btn->str->len;
+ }
+
+ return SVN_NO_ERROR;
}
svn_stream_t *
@@ -1717,18 +1874,21 @@ svn_stream_from_string(const svn_string_t *str,
svn_stream_set_seek(stream, seek_handler_string);
svn_stream_set_skip(stream, skip_handler_string);
svn_stream_set_data_available(stream, data_available_handler_string);
- svn_stream__set_is_buffered(stream, is_buffered_handler_string);
+ svn_stream_set_readline(stream, readline_handler_string);
return stream;
}
svn_error_t *
-svn_stream_for_stdin(svn_stream_t **in, apr_pool_t *pool)
+svn_stream_for_stdin2(svn_stream_t **in,
+ svn_boolean_t buffered,
+ apr_pool_t *pool)
{
apr_file_t *stdin_file;
apr_status_t apr_err;
- apr_err = apr_file_open_stdin(&stdin_file, pool);
+ apr_uint32_t flags = buffered ? APR_BUFFERED : 0;
+ apr_err = apr_file_open_flags_stdin(&stdin_file, flags, pool);
if (apr_err)
return svn_error_wrap_apr(apr_err, "Can't open stdin");
@@ -1736,7 +1896,7 @@ svn_stream_for_stdin(svn_stream_t **in, apr_pool_t *pool)
it does not, or the behavior is implementation-specific. Hence,
we cannot safely advertise mark(), seek() and non-default skip()
support. */
- *in = make_stream_from_apr_file(stdin_file, TRUE, FALSE, pool);
+ *in = make_stream_from_apr_file(stdin_file, TRUE, FALSE, FALSE, pool);
return SVN_NO_ERROR;
}
@@ -1756,7 +1916,7 @@ svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool)
it does not, or the behavior is implementation-specific. Hence,
we cannot safely advertise mark(), seek() and non-default skip()
support. */
- *out = make_stream_from_apr_file(stdout_file, TRUE, FALSE, pool);
+ *out = make_stream_from_apr_file(stdout_file, TRUE, FALSE, FALSE, pool);
return SVN_NO_ERROR;
}
@@ -1776,39 +1936,25 @@ svn_stream_for_stderr(svn_stream_t **err, apr_pool_t *pool)
it does not, or the behavior is implementation-specific. Hence,
we cannot safely advertise mark(), seek() and non-default skip()
support. */
- *err = make_stream_from_apr_file(stderr_file, TRUE, FALSE, pool);
+ *err = make_stream_from_apr_file(stderr_file, TRUE, FALSE, FALSE, pool);
return SVN_NO_ERROR;
}
svn_error_t *
-svn_string_from_stream(svn_string_t **result,
- svn_stream_t *stream,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+svn_string_from_stream2(svn_string_t **result,
+ svn_stream_t *stream,
+ apr_size_t len_hint,
+ apr_pool_t *result_pool)
{
- svn_stringbuf_t *work = svn_stringbuf_create_ensure(SVN__STREAM_CHUNK_SIZE,
- result_pool);
- char *buffer = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
+ svn_stringbuf_t *buf;
- while (1)
- {
- apr_size_t len = SVN__STREAM_CHUNK_SIZE;
-
- SVN_ERR(svn_stream_read_full(stream, buffer, &len));
- svn_stringbuf_appendbytes(work, buffer, len);
-
- if (len < SVN__STREAM_CHUNK_SIZE)
- break;
- }
+ SVN_ERR(svn_stringbuf_from_stream(&buf, stream, len_hint, result_pool));
+ *result = svn_stringbuf__morph_into_string(buf);
SVN_ERR(svn_stream_close(stream));
- *result = apr_palloc(result_pool, sizeof(**result));
- (*result)->data = work->data;
- (*result)->len = work->len;
-
return SVN_NO_ERROR;
}
@@ -1977,17 +2123,19 @@ data_available_handler_lazyopen(void *baton,
data_available));
}
-/* Implements svn_stream__is_buffered_fn_t */
-static svn_boolean_t
-is_buffered_lazyopen(void *baton)
+/* Implements svn_stream_readline_fn_t */
+static svn_error_t *
+readline_handler_lazyopen(void *baton,
+ svn_stringbuf_t **stringbuf,
+ const char *eol,
+ svn_boolean_t *eof,
+ apr_pool_t *pool)
{
lazyopen_baton_t *b = baton;
- /* No lazy open as we cannot handle an open error. */
- if (!b->real_stream)
- return FALSE;
-
- return svn_stream__is_buffered(b->real_stream);
+ SVN_ERR(lazyopen_if_unopened(b));
+ return svn_error_trace(svn_stream_readline(b->real_stream, stringbuf,
+ eol, eof, pool));
}
svn_stream_t *
@@ -2014,7 +2162,7 @@ svn_stream_lazyopen_create(svn_stream_lazyopen_func_t open_func,
svn_stream_set_mark(stream, mark_handler_lazyopen);
svn_stream_set_seek(stream, seek_handler_lazyopen);
svn_stream_set_data_available(stream, data_available_handler_lazyopen);
- svn_stream__set_is_buffered(stream, is_buffered_lazyopen);
+ svn_stream_set_readline(stream, readline_handler_lazyopen);
return stream;
}
@@ -2096,6 +2244,8 @@ create_tempfile(HANDLE *hFile,
return SVN_NO_ERROR;
}
+#endif /* WIN32 */
+
/* Implements svn_close_fn_t */
static svn_error_t *
install_close(void *baton)
@@ -2108,8 +2258,6 @@ install_close(void *baton)
return SVN_NO_ERROR;
}
-#endif /* WIN32 */
-
svn_error_t *
svn_stream__create_for_install(svn_stream_t **install_stream,
const char *tmp_abspath,
@@ -2132,8 +2280,8 @@ svn_stream__create_for_install(svn_stream_t **install_stream,
/* Wrap as a standard APR file to allow sharing implementation.
But do note that some file functions (such as retrieving the name)
- don't work on this wrapper. */
- /* ### Buffered, or not? */
+ don't work on this wrapper.
+ Use buffered mode to match svn_io_open_unique_file3() behavior. */
status = apr_os_file_put(&file, &hInstall,
APR_WRITE | APR_BINARY | APR_BUFFERED,
result_pool);
@@ -2153,7 +2301,9 @@ svn_stream__create_for_install(svn_stream_t **install_stream,
svn_io_file_del_none,
result_pool, scratch_pool));
#endif
- *install_stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
+ /* Set the temporary file to be truncated on seeks. */
+ *install_stream = svn_stream__from_aprfile(file, FALSE, TRUE,
+ result_pool);
ib = apr_pcalloc(result_pool, sizeof(*ib));
ib->baton_apr = *(struct baton_apr*)(*install_stream)->baton;
@@ -2164,12 +2314,8 @@ svn_stream__create_for_install(svn_stream_t **install_stream,
ib->tmp_path = tmp_path;
-#ifdef WIN32
/* Don't close the file on stream close; flush instead */
svn_stream_set_close(*install_stream, install_close);
-#else
- /* ### Install pool cleanup handler for tempfile? */
-#endif
return SVN_NO_ERROR;
}
@@ -2214,8 +2360,6 @@ svn_stream__install_stream(svn_stream_t *install_stream,
svn_io_file_rename2(). */
svn_error_clear(err);
err = SVN_NO_ERROR;
-
- SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
}
else
{
@@ -2225,7 +2369,10 @@ svn_stream__install_stream(svn_stream_t *install_stream,
}
#endif
- err = svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool);
+ /* Close temporary file. */
+ SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
+
+ err = svn_io_file_rename2(ib->tmp_path, final_abspath, FALSE, scratch_pool);
/* A missing directory is too common to not cover here. */
if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err))
@@ -2243,7 +2390,7 @@ svn_stream__install_stream(svn_stream_t *install_stream,
/* We could create a directory: retry install */
svn_error_clear(err);
- SVN_ERR(svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool));
+ SVN_ERR(svn_io_file_rename2(ib->tmp_path, final_abspath, FALSE, scratch_pool));
}
else
SVN_ERR(err);
@@ -2258,19 +2405,12 @@ svn_stream__install_get_info(apr_finfo_t *finfo,
apr_pool_t *scratch_pool)
{
struct install_baton_t *ib = install_stream->baton;
-
-#ifdef WIN32
- /* On WIN32 the file is still open, so we can obtain the information
- from the handle without race conditions */
apr_status_t status;
status = apr_file_info_get(finfo, wanted, ib->baton_apr.file);
if (status)
return svn_error_wrap_apr(status, NULL);
-#else
- SVN_ERR(svn_io_stat(finfo, ib->tmp_path, wanted, scratch_pool));
-#endif
return SVN_NO_ERROR;
}
@@ -2297,9 +2437,10 @@ svn_stream__install_delete(svn_stream_t *install_stream,
/* Deleting file on close may be unsupported, so ignore errors and
fallback to svn_io_remove_file2(). */
svn_error_clear(err);
- SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
#endif
+ SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
+
return svn_error_trace(svn_io_remove_file2(ib->tmp_path, FALSE,
scratch_pool));
}