Author: brane
Date: Tue Feb 3 14:49:38 2026
New Revision: 1931688
Log:
Add an LZ4 compressed stream.
* subversion/include/private/svn_io_private.h
(svn_stream__lz4_compressed): New; stream constructor prototype.
* subversion/libsvn_subr/stream.c
(zbaton): Unrelated change: make substrem an svn_stream_t*.
(lz4_baton,
read_handler_lz4,
ensure_write_buffer_lz4,
write_handler_lz4,
close_handler_lz4): New LZ4 stream helpers.
(svn_stream__lz4_compressed): Implement the stream constructor.
* subversion/tests/libsvn_subr/stream-test.c
(do_test_stream_compressed): Renamed and refactored from
test_stream_compressed().
(test_stream_compressed,
test_stream_compressed_lz4): Test drivers for the above.
(test_funcs): Register the new test.
* build.conf
(slz4): New tool, simulates lz4/unlz4.
(tools/lz4/slz4.c): Implement the lz4/unlz4 simulator.
Added:
subversion/branches/better-pristines/tools/lz4/
subversion/branches/better-pristines/tools/lz4/slz4.c (contents, props
changed)
Modified:
subversion/branches/better-pristines/build.conf
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
Modified: subversion/branches/better-pristines/build.conf
==============================================================================
--- subversion/branches/better-pristines/build.conf Tue Feb 3 12:45:02
2026 (r1931687)
+++ subversion/branches/better-pristines/build.conf Tue Feb 3 14:49:38
2026 (r1931688)
@@ -1699,6 +1699,13 @@ sources = diff4.c
install = tools
libs = libsvn_diff libsvn_subr apriconv apr
+[slz4]
+type = exe
+path = tools/lz4
+sources = slz4.c
+install = tools
+libs = libsvn_subr apriconv apr
+
[svnbench]
description = Benchmarking and diagnostics tool for the network layer
type = exe
Modified:
subversion/branches/better-pristines/subversion/include/private/svn_io_private.h
==============================================================================
---
subversion/branches/better-pristines/subversion/include/private/svn_io_private.h
Tue Feb 3 12:45:02 2026 (r1931687)
+++
subversion/branches/better-pristines/subversion/include/private/svn_io_private.h
Tue Feb 3 14:49:38 2026 (r1931688)
@@ -171,6 +171,12 @@ svn_stream__adler32(svn_stream_t *stream
svn_boolean_t read_all,
apr_pool_t *result_pool);
+/* Similar to svn_stream_compressed() but uses LZ4 instead of zlib.
+ See: svn_lz4__compress_create() etc. in svn_subr_private.h.
+*/
+svn_stream_t *
+svn_stream__lz4_compressed(svn_stream_t *stream, apr_pool_t *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
Tue Feb 3 12:45:02 2026 (r1931687)
+++ subversion/branches/better-pristines/subversion/libsvn_subr/stream.c
Tue Feb 3 14:49:38 2026 (r1931688)
@@ -1148,13 +1148,10 @@ svn_stream__aprfile(svn_stream_t *stream
struct zbaton {
z_stream *in; /* compressed stream for reading */
z_stream *out; /* compressed stream for writing */
- void *substream; /* The substream */
- void *read_buffer; /* buffer used for reading from
- substream */
- int read_flush; /* what flush mode to use while
- reading */
- apr_pool_t *pool; /* The pool this baton is allocated
- on */
+ svn_stream_t *substream; /* The substream */
+ void *read_buffer; /* buffer used for reading from substream */
+ int read_flush; /* what flush mode to use while reading */
+ apr_pool_t *pool; /* The pool this baton is allocated in */
};
/* zlib alloc function. opaque is the pool we need. */
@@ -1368,6 +1365,139 @@ svn_stream_compressed(svn_stream_t *stre
return zstream;
}
+
+
+struct lz4_baton {
+ svn_lz4__decompress_ctx_t *dctx; /* Decompression context for reading */
+ svn_lz4__compress_ctx_t *cctx; /* Compression context for writing */
+ svn_stream_t *substream;
+ svn_stringbuf_t *write_buffer;
+ char *read_buffer;
+ apr_size_t read_pos;
+ apr_size_t read_size;
+ apr_size_t size_hint;
+ apr_pool_t *pool; /* The pool this baton is allocated in */
+};
+
+/* Read data from the substream and decompress it. */
+static svn_error_t *
+read_handler_lz4(void *baton, char *buffer, apr_size_t *len)
+{
+ struct lz4_baton *const ctx = baton;
+ apr_size_t read_total = 0;
+
+ if (!ctx->dctx)
+ {
+ SVN_ERR(svn_lz4__decompress_create(&ctx->dctx, FALSE, ctx->pool));
+ ctx->read_buffer = apr_palloc(ctx->pool, ZBUFFER_SIZE);
+ ctx->read_pos = ctx->read_size = 0;
+ ctx->size_hint = 1;
+ }
+
+ while (read_total < *len && ctx->size_hint > 0)
+ {
+ apr_size_t input_size;
+ apr_size_t output_size;
+
+ if (ctx->read_pos >= ctx->read_size)
+ {
+ ctx->read_size = ZBUFFER_SIZE;
+ SVN_ERR(svn_stream_read_full(ctx->substream,
+ ctx->read_buffer, &ctx->read_size));
+ ctx->read_pos = 0;
+ }
+
+ input_size = ctx->read_size - ctx->read_pos;
+ output_size = *len - read_total;
+ SVN_ERR(svn_lz4__decompress(&ctx->size_hint, ctx->dctx,
+ buffer + read_total, &output_size,
+ ctx->read_buffer + ctx->read_pos,
+ &input_size));
+ ctx->read_pos += input_size;
+
+ read_total += output_size;
+ }
+
+ *len = read_total;
+ return SVN_NO_ERROR;
+}
+
+/* Ensure we have enough space in the write buffer */
+static void
+ensure_write_buffer_lz4(struct lz4_baton *ctx, apr_size_t size)
+{
+ if (!ctx->write_buffer)
+ ctx->write_buffer = svn_stringbuf_create_ensure(size, ctx->pool);
+ else
+ svn_stringbuf_ensure(ctx->write_buffer, size);
+}
+
+/* Compress data and write it to the substream. */
+static svn_error_t *
+write_handler_lz4(void *baton, const char *buffer, apr_size_t *len)
+{
+ struct lz4_baton *const ctx = baton;
+
+ apr_size_t input_size = *len;
+ apr_size_t output_size = 0;
+
+ if (!ctx->cctx)
+ {
+ SVN_ERR(svn_lz4__compress_create(&ctx->cctx, FALSE, ctx->pool));
+ output_size = svn_lz4__header_size_max();
+ }
+
+ output_size += svn_lz4__compress_bound(ctx->cctx, input_size);
+ ensure_write_buffer_lz4(ctx, output_size);
+ SVN_ERR(svn_lz4__compress_update(&output_size, ctx->cctx,
+ ctx->write_buffer->data, output_size,
+ buffer, input_size));
+ return svn_error_trace(svn_stream_write(ctx->substream,
+ ctx->write_buffer->data,
+ &output_size));
+}
+
+/* Handle flushing and closing the stream */
+static svn_error_t *
+close_handler_lz4(void *baton)
+{
+ struct lz4_baton *const ctx = baton;
+ apr_size_t output_size;
+
+ /* If we haven't written anything, there's nothing to flush. */
+ if (!ctx->cctx)
+ return SVN_NO_ERROR;
+
+ output_size = svn_lz4__compress_bound(ctx->cctx, 0);
+ ensure_write_buffer_lz4(ctx, output_size);
+ SVN_ERR(svn_lz4__compress_end(&output_size, ctx->cctx,
+ ctx->write_buffer->data, output_size));
+ return svn_error_trace(svn_stream_write(ctx->substream,
+ ctx->write_buffer->data,
+ &output_size));
+ return svn_error_trace(svn_stream_close(ctx->substream));
+}
+
+svn_stream_t *
+svn_stream__lz4_compressed(svn_stream_t *stream, apr_pool_t *pool)
+{
+ struct svn_stream_t *zstream;
+ struct lz4_baton *ctx;
+
+ assert(stream != NULL);
+
+ ctx = apr_pcalloc(pool, sizeof(*ctx));
+ ctx->substream = stream;
+ ctx->pool = pool;
+
+ zstream = svn_stream_create(ctx, pool);
+ svn_stream_set_read2(zstream, NULL /* only full read support */,
+ read_handler_lz4);
+ svn_stream_set_write(zstream, write_handler_lz4);
+ svn_stream_set_close(zstream, close_handler_lz4);
+
+ return zstream;
+}
/* Checksummed stream support */
Modified:
subversion/branches/better-pristines/subversion/tests/libsvn_subr/stream-test.c
==============================================================================
---
subversion/branches/better-pristines/subversion/tests/libsvn_subr/stream-test.c
Tue Feb 3 12:45:02 2026 (r1931687)
+++
subversion/branches/better-pristines/subversion/tests/libsvn_subr/stream-test.c
Tue Feb 3 14:49:38 2026 (r1931688)
@@ -150,7 +150,8 @@ generate_test_bytes(int num_bytes, apr_p
static svn_error_t *
-test_stream_compressed(apr_pool_t *pool)
+do_test_stream_compressed(svn_stream_t *(*ctor)(svn_stream_t *, apr_pool_t *),
+ apr_pool_t *pool)
{
#define NUM_TEST_STRINGS 5
#define TEST_BUF_SIZE 10
@@ -194,16 +195,12 @@ test_stream_compressed(apr_pool_t *pool)
inbuf = svn_stringbuf_create_empty(subpool);
outbuf = svn_stringbuf_create_empty(subpool);
- stream = svn_stream_compressed(svn_stream_from_stringbuf(outbuf,
- subpool),
- subpool);
+ stream = ctor(svn_stream_from_stringbuf(outbuf, subpool), subpool);
len = origbuf->len;
SVN_ERR(svn_stream_write(stream, origbuf->data, &len));
SVN_ERR(svn_stream_close(stream));
- stream = svn_stream_compressed(svn_stream_from_stringbuf(outbuf,
- subpool),
- subpool);
+ stream = ctor(svn_stream_from_stringbuf(outbuf, subpool), subpool);
len = TEST_BUF_SIZE;
while (len >= TEST_BUF_SIZE)
{
@@ -231,6 +228,19 @@ test_stream_compressed(apr_pool_t *pool)
}
static svn_error_t *
+test_stream_compressed(apr_pool_t *pool)
+{
+ return do_test_stream_compressed(svn_stream_compressed, pool);
+}
+
+static svn_error_t *
+test_stream_compressed_lz4(apr_pool_t *pool)
+{
+ return do_test_stream_compressed(svn_stream__lz4_compressed, pool);
+}
+
+
+static svn_error_t *
test_stream_tee(apr_pool_t *pool)
{
svn_stringbuf_t *test_bytes = generate_test_bytes(100, pool);
@@ -1162,7 +1172,9 @@ static struct svn_test_descriptor_t test
SVN_TEST_PASS2(test_stream_from_string,
"test svn_stream_from_string"),
SVN_TEST_PASS2(test_stream_compressed,
- "test compressed streams"),
+ "test zlib compressed streams"),
+ SVN_TEST_PASS2(test_stream_compressed_lz4,
+ "test LZ4 compressed streams"),
SVN_TEST_PASS2(test_stream_tee,
"test 'tee' streams"),
SVN_TEST_PASS2(test_stream_seek_file,
Added: subversion/branches/better-pristines/tools/lz4/slz4.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ subversion/branches/better-pristines/tools/lz4/slz4.c Tue Feb 3
14:49:38 2026 (r1931688)
@@ -0,0 +1,102 @@
+/* slz4.c -- test driver for LZ4 de/compression
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+
+/*
+ * This compression driver can create create files that can be
+ * read by 'unlz4' and decmpress files created by 'lz4'.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <apr.h>
+
+#include "svn_error.h"
+#include "svn_io.h"
+#include "svn_pools.h"
+#include "private/svn_io_private.h"
+
+
+static void handle_error(svn_error_t *svn_err, int exitcode)
+{
+ if (svn_err)
+ {
+ svn_handle_error2(svn_err, stdout, FALSE, "slz4: ");
+ exit(exitcode);
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ apr_pool_t *pool;
+ svn_stream_t *istream;
+ svn_stream_t *ostream;
+ int compress = 1;
+
+ if (argc >= 2)
+ {
+ int usage = 0;
+
+ if (0 == strcmp(argv[1], "-d"))
+ compress = 0;
+ else if (0 == strcmp(argv[1], "-c"))
+ compress = 1;
+ else
+ {
+ fprintf(stderr, "error: unknown option: %s\n", argv[1]);
+ usage = 1;
+ }
+ if (argc > 2)
+ {
+ fprintf(stderr, "error: too many arguments\n");
+ usage = 1;
+ }
+
+ if (usage)
+ {
+ fprintf(stderr,
+ "Usage: %s [-c|-d] <{input} >{output}\n"
+ " -c compress {input} to {output} [DEFAULT]\n"
+ " -d decompress {output} from {input}\n",
+ argv[0]);
+ exit(2);
+ }
+ }
+
+ apr_initialize();
+ atexit(apr_terminate);
+
+ pool = svn_pool_create(NULL);
+ handle_error(svn_stream_for_stdin2(&istream, TRUE, pool), 2);
+ handle_error(svn_stream_for_stdout(&ostream, pool), 2);
+
+ if (compress)
+ ostream = svn_stream__lz4_compressed(ostream, pool);
+ else
+ istream = svn_stream__lz4_compressed(istream, pool);
+
+ handle_error(svn_stream_copy3(istream, ostream, NULL, NULL, pool), 1);
+ return 0;
+}