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;
+}
+
/* ================================================================= */