diff options
Diffstat (limited to 'subversion/libsvn_subr/stream.c')
-rw-r--r-- | subversion/libsvn_subr/stream.c | 655 |
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)); } |