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

Reply via email to