Author: brane
Date: Sun Jan 18 18:48:11 2026
New Revision: 1931408
Log:
On the better-pristines branch: Introduce a new stream type that
computes Adler-32 checksums of the data that is written or read
through it.
* subversion/include/private/svn_io_private.h
(svn_stream__adler32): New prototype.
* subversion/libsvn_subr/stream.c: Include private/svn_adler32.h.
(struct adler32_stream_context,
read_handler_adler32,
read_full_handler_adler32,
write_handler_adler32,
data_available_handler_adler32,
close_handler_adler32,
seek_handler_adler32,
svn_stream__adler32): The Adler-32 checksumming stream implementation.
* subversion/tests/svn_test.h
(svn_test_make_random_data): New prototype for a helper function ...
* subversion/tests/svn_test_main.c
(svn_test_make_random_data): ... implemented here.
* subversion/tests/libsvn_subr/stream-test.c: Include private/svn_adler32.h.
(do_test_stream_adler32, test_stream_adler32): A new testcase.
(test_funcs): Register the new test.
Modified:
subversion/branches/better-pristines/subversion/include/private/svn_io_private.h
subversion/branches/better-pristines/subversion/libsvn_subr/stream.c
subversion/branches/better-pristines/subversion/tests/libsvn_subr/stream-test.c
subversion/branches/better-pristines/subversion/tests/svn_test.h
subversion/branches/better-pristines/subversion/tests/svn_test_main.c
Modified: subversion/branches/better-pristines/subversion/include/private/svn_io_private.h
==============================================================================
--- subversion/branches/better-pristines/subversion/include/private/svn_io_private.h Sun Jan 18 18:45:02 2026 (r1931407)
+++ subversion/branches/better-pristines/subversion/include/private/svn_io_private.h Sun Jan 18 18:48:11 2026 (r1931408)
@@ -157,6 +157,20 @@ svn_stream__from_aprfile(apr_file_t *fil
svn_boolean_t truncate_on_seek,
apr_pool_t *pool);
+/* Similar to svn_stream_checksummed2(), but calculates the Adler-32
+ checksum of the data that was read and written. All parameters have
+ the same semantics, but of course there is no CHECKSUM_KIND parameter.
+
+ The initial values of *READ_CHECKSUM and *WRITE_CHECKSUM are set to 0
+ when the stream is created or reset.
+*/
+svn_stream_t *
+svn_stream__adler32(svn_stream_t *stream,
+ apr_uint32_t *read_checksum,
+ apr_uint32_t *write_checksum,
+ svn_boolean_t read_all,
+ apr_pool_t *result_pool);
+
#if defined(WIN32)
/* ### Move to something like io.h or subr.h, to avoid making it
Modified: subversion/branches/better-pristines/subversion/libsvn_subr/stream.c
==============================================================================
--- subversion/branches/better-pristines/subversion/libsvn_subr/stream.c Sun Jan 18 18:45:02 2026 (r1931407)
+++ subversion/branches/better-pristines/subversion/libsvn_subr/stream.c Sun Jan 18 18:48:11 2026 (r1931408)
@@ -43,6 +43,7 @@
#include "svn_path.h"
#include "svn_private_config.h"
#include "svn_sorts.h"
+#include "private/svn_adler32.h"
#include "private/svn_atomic.h"
#include "private/svn_error_private.h"
#include "private/svn_eol_private.h"
@@ -1578,6 +1579,151 @@ svn_stream_contents_checksum(svn_checksu
return svn_error_compose_create(err, svn_stream_close(stream));
}
+
+struct adler32_stream_context
+{
+ apr_uint32_t *read_checksum; /* Output value. */
+ apr_uint32_t *write_checksum; /* Output value. */
+ svn_stream_t *proxy;
+
+ /* True if more data should be read when closing the stream. */
+ svn_boolean_t read_more;
+
+ /* Pool to allocate the read buffer. */
+ apr_pool_t *pool;
+};
+
+static svn_error_t *
+read_handler_adler32(void *baton, char *buffer, apr_size_t *len)
+{
+ struct adler32_stream_context *const ctx = baton;;
+
+ SVN_ERR(svn_stream_read2(ctx->proxy, buffer, len));
+
+ if (ctx->read_checksum)
+ *ctx->read_checksum = svn__adler32(*ctx->read_checksum, buffer, *len);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+read_full_handler_adler32(void *baton, char *buffer, apr_size_t *len)
+{
+ struct adler32_stream_context *const ctx = baton;;
+ const apr_size_t saved_len = *len;
+
+ SVN_ERR(svn_stream_read_full(ctx->proxy, buffer, len));
+
+ if (ctx->read_checksum)
+ *ctx->read_checksum = svn__adler32(*ctx->read_checksum, buffer, *len);
+
+ if (saved_len != *len)
+ ctx->read_more = FALSE;
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+write_handler_adler32(void *baton, const char *buffer, apr_size_t *len)
+{
+ struct adler32_stream_context *const ctx = baton;;
+
+ if (ctx->write_checksum && *len > 0)
+ *ctx->write_checksum = svn__adler32(*ctx->write_checksum, buffer, *len);
+
+ return svn_error_trace(svn_stream_write(ctx->proxy, buffer, len));
+}
+
+static svn_error_t *
+data_available_handler_adler32(void *baton, svn_boolean_t *data_available)
+{
+ struct adler32_stream_context *const ctx = baton;;
+
+ return svn_error_trace(svn_stream_data_available(ctx->proxy,
+ data_available));
+}
+
+static svn_error_t *
+close_handler_adler32(void *baton)
+{
+ struct adler32_stream_context *const ctx = baton;;
+
+ /* Drain the stream if required. */
+ if (ctx->read_more)
+ {
+ char *const buf = apr_palloc(ctx->pool, SVN__STREAM_CHUNK_SIZE);
+ apr_size_t len = SVN__STREAM_CHUNK_SIZE;
+
+ do
+ {
+ SVN_ERR(read_full_handler_checksum(baton, buf, &len));
+ }
+ while (ctx->read_more);
+ }
+
+ return svn_error_trace(svn_stream_close(ctx->proxy));
+}
+
+static svn_error_t *
+seek_handler_adler32(void *baton, const svn_stream_mark_t *mark)
+{
+ struct adler32_stream_context *const ctx = baton;;
+
+ /* Only reset support. */
+ if (mark)
+ return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED,
+ NULL, NULL);
+
+ /* Reset to initial checksum values. */
+ if (ctx->read_checksum)
+ *ctx->read_checksum = 0;
+ if (ctx->write_checksum)
+ *ctx->write_checksum = 0;
+
+ return svn_error_trace(svn_stream_reset(ctx->proxy));
+}
+
+svn_stream_t *
+svn_stream__adler32(svn_stream_t *stream,
+ apr_uint32_t *read_checksum,
+ apr_uint32_t *write_checksum,
+ svn_boolean_t read_all,
+ apr_pool_t *result_pool)
+{
+ svn_stream_t *s;
+ struct adler32_stream_context *ctx;
+
+ if (read_checksum == NULL && write_checksum == NULL)
+ return stream;
+
+ ctx = apr_palloc(result_pool, sizeof(*ctx));
+ ctx->read_checksum = read_checksum;
+ ctx->write_checksum = write_checksum;
+ ctx->proxy = stream;
+ ctx->read_more = read_all;
+ ctx->pool = result_pool;
+
+ s = svn_stream_create(ctx, result_pool);
+ svn_stream_set_read2(s,
+ svn_stream_supports_partial_read(stream)
+ ? read_handler_adler32 : NULL,
+ read_full_handler_adler32);
+ svn_stream_set_write(s, write_handler_adler32);
+ svn_stream_set_data_available(s, data_available_handler_adler32);
+ svn_stream_set_close(s, close_handler_adler32);
+ if (svn_stream_supports_reset(stream))
+ svn_stream_set_seek(s, seek_handler_adler32);
+
+ /* Set initial checksum values. */
+ if (read_checksum)
+ *read_checksum = 0;
+ if (write_checksum)
+ *write_checksum = 0;
+
+ return s;
+}
+
+
/* Miscellaneous stream functions. */
/*
Modified: subversion/branches/better-pristines/subversion/tests/libsvn_subr/stream-test.c
==============================================================================
--- subversion/branches/better-pristines/subversion/tests/libsvn_subr/stream-test.c Sun Jan 18 18:45:02 2026 (r1931407)
+++ subversion/branches/better-pristines/subversion/tests/libsvn_subr/stream-test.c Sun Jan 18 18:48:11 2026 (r1931408)
@@ -29,6 +29,7 @@
#include <apr_general.h>
#include "private/svn_io_private.h"
+#include "private/svn_adler32.h"
#include "../svn_test.h"
@@ -871,6 +872,92 @@ test_stream_checksum(apr_pool_t *pool)
}
static svn_error_t *
+do_test_stream_adler32(apr_pool_t *pool, apr_uint32_t seed)
+{
+ const apr_off_t length = 11 * SVN__STREAM_CHUNK_SIZE + 37;
+ void *const data = svn_test_make_random_data(&seed, length, pool);
+ const apr_uint32_t expected_checksum = svn__adler32(0, data, length);
+ apr_off_t size;
+ char *ptr;
+ int i;
+
+ svn_stringbuf_t *outbuf = svn_stringbuf_create_empty(pool);
+ svn_stream_t *buffer = svn_stream_from_stringbuf(outbuf, pool);
+
+ apr_uint32_t read_checksum;
+ apr_uint32_t write_checksum;
+ svn_stream_t *adler = svn_stream__adler32(buffer,
+ &read_checksum,
+ &write_checksum,
+ FALSE, pool);
+
+ SVN_TEST_ASSERT(read_checksum == 0);
+ SVN_TEST_ASSERT(write_checksum == 0);
+
+ ptr = data;
+ size = length;
+ while (size > 0)
+ {
+ const apr_size_t chunk = (size <= SVN__STREAM_CHUNK_SIZE
+ ? size : SVN__STREAM_CHUNK_SIZE);
+ apr_size_t len = chunk;
+
+ SVN_ERR(svn_stream_write(adler, ptr, &len));
+ SVN_TEST_ASSERT(len == chunk);
+
+ ptr += chunk;
+ size -= chunk;
+ }
+ SVN_TEST_ASSERT(size == 0);
+ SVN_TEST_ASSERT(write_checksum == expected_checksum);
+
+ ptr = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
+ for (i = 0; ; ++i)
+ {
+ size = 0;
+ for (;;)
+ {
+ apr_size_t len = SVN__STREAM_CHUNK_SIZE;
+ SVN_ERR(svn_stream_read_full(adler, ptr, &len));
+ size += len;
+ if (len < SVN__STREAM_CHUNK_SIZE)
+ break;
+ }
+ SVN_TEST_ASSERT(size == length);
+ SVN_TEST_ASSERT(read_checksum == expected_checksum);
+
+ /* In the second read iteration after the reset, the write
+ checksum should be 0, because we only read from the stream. */
+ if (i > 0)
+ {
+ SVN_TEST_ASSERT(write_checksum == 0);
+ break;
+ }
+
+ SVN_TEST_ASSERT(write_checksum == expected_checksum);
+
+ SVN_ERR(svn_stream_reset(adler));
+ SVN_TEST_ASSERT(read_checksum == 0);
+ SVN_TEST_ASSERT(write_checksum == 0);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_stream_adler32(apr_pool_t *pool)
+{
+ const apr_uint32_t seed = (apr_uint32_t)apr_time_now();
+ svn_error_t *err = do_test_stream_adler32(pool, seed);
+
+ if (err)
+ fprintf(stderr, "SEED: %lu\n", (unsigned long)seed);
+
+ return err;
+}
+
+
+static svn_error_t *
test_stream_readline_file(const char *testname,
const char *eol,
apr_pool_t *pool)
@@ -1098,6 +1185,8 @@ static struct svn_test_descriptor_t test
"test compression for streams without partial read"),
SVN_TEST_PASS2(test_stream_checksum,
"test svn_stream_contents_checksum()"),
+ SVN_TEST_PASS2(test_stream_adler32,
+ "test the adler32 checksumming stream"),
SVN_TEST_PASS2(test_stream_readline_file_lf,
"test reading LF-terminated lines from file"),
SVN_TEST_PASS2(test_stream_readline_file_crlf,
Modified: subversion/branches/better-pristines/subversion/tests/svn_test.h
==============================================================================
--- subversion/branches/better-pristines/subversion/tests/svn_test.h Sun Jan 18 18:45:02 2026 (r1931407)
+++ subversion/branches/better-pristines/subversion/tests/svn_test.h Sun Jan 18 18:48:11 2026 (r1931408)
@@ -349,6 +349,14 @@ int svn_test_main(int argc, const char *
*/
apr_uint32_t svn_test_rand(apr_uint32_t *seed);
+/* Allocate a buffer of size LEN from POOL and fill it with pseudo-random
+ data. In fact, the size of the buffer will be rounded up to the next
+ multiple of sizeof(apr_uint32_t).
+ Uses and updates *seed. */
+void *
+svn_test_make_random_data(apr_uint32_t *seed,
+ apr_off_t len,
+ apr_pool_t *pool);
/* Add PATH to the test cleanup list. */
void svn_test_add_dir_cleanup(const char *path);
Modified: subversion/branches/better-pristines/subversion/tests/svn_test_main.c
==============================================================================
--- subversion/branches/better-pristines/subversion/tests/svn_test_main.c Sun Jan 18 18:45:02 2026 (r1931407)
+++ subversion/branches/better-pristines/subversion/tests/svn_test_main.c Sun Jan 18 18:48:11 2026 (r1931408)
@@ -288,6 +288,25 @@ svn_test_rand(apr_uint32_t *seed)
return *seed;
}
+/* Allocate a buffer of size LEN from POOL and fill it with pseudo-random
+ data. In fact, the size of the buffer will be rounded up to the next
+ multiple of sizeof(apr_uint32_t). */
+void *
+svn_test_make_random_data(apr_uint32_t *seed,
+ apr_off_t len,
+ apr_pool_t *pool)
+{
+ const apr_off_t count = (len / sizeof(apr_uint32_t)
+ + (len % sizeof(apr_uint32_t) ? 1 : 0));
+ apr_uint32_t *data = apr_palloc(pool, count * sizeof(*data));
+ apr_off_t i;
+
+ for (i = 0; i < count; ++i)
+ data[i] = svn_test_rand(seed);
+
+ return data;
+}
+
/* ================================================================= */
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.