svn commit: r1925668 - in /subversion/branches/patch-from-stream: ./ subversion/include/ subversion/libsvn_repos/ subversion/libsvn_subr/ subversion/tests/libsvn_subr/

[email protected]
Newsgroups gmane.comp.version-control.subversion.svn
Message-ID <[email protected]>
Author: brane
Date: Sun May 18 13:31:20 2025
New Revision: 1925668

URL: http://svn.apache.org/viewvc?rev=1925668&view=rev
Log:
On the patch-from-stream branch: Add a new stream handler function that
calculates the distance between two stream positions.

* BRANCH-README: Update contents.

* subversion/include/svn_error_codes.h
  (SVN_ERR_STREAM_SPAN_NOT_SUPPORTED,
   SVN_ERR_STREAM_OFFSET_TOO_LARGE): Add two new error codes.
* subversion/include/svn_io.h
  (svn_stream_span_fn_t,
   svn_stream_set_span,
   svn_stream_supports_span,
   svn_stream_span): New functions.
* subversion/libsvn_subr/stream.c
  (svn_stream_t::span_fn): Add new member to the private stream type.
  (svn_stream_set_span,
   svn_stream_supports_span,
   svn_stream_span): Implement the new generic stream functions.
  (span_handler_empty, svn_stream_empty): Support span for empty streams...
  (span_handler_disown, svn_stream_disown): ...and for disowned streams...
  (span_handler_apr, make_stream_from_apr_file): ...and file streams.
  (stringbuf_stream_mark, string_stream_mark): Remove. Replace with...
  (mark_str): ... this. These were identical copy/pasted structures.
  (mark_handler_stringbuf, seek_handler_stringbuf): Use mark_str.
  (mark_handler_string, seek_handler_string): And here, too.
  (off_t_can_not_represent): New helper function for type conversions.
  (span_handler_str,
   svn_stream_from_stringbuf,
   svn_stream_from_string): Support span for string and stringbuf streams...
  (span_handler_lazyopen, svn_stream_lazyopen_create): ...and lazyopen.
* subversion/libsvn_subr/subst.c
  (translated_stream_span, stream_translated): Support span.

* subversion/libsvn_repos/config_file.c
  (span_handler_rep, representation_stream): Support span.

* subversion/tests/libsvn_subr/stream-test.c
  (test_stream_seek_file,
   test_stream_seek_stringbuf,
   test_stream_seek_translated,
   test_stream_compressed_empty_file): Add tests for stream span.

Modified:
    subversion/branches/patch-from-stream/BRANCH-README
    subversion/branches/patch-from-stream/subversion/include/svn_error_codes.h
    subversion/branches/patch-from-stream/subversion/include/svn_io.h
    subversion/branches/patch-from-stream/subversion/libsvn_repos/config_file.c
    subversion/branches/patch-from-stream/subversion/libsvn_subr/stream.c
    subversion/branches/patch-from-stream/subversion/libsvn_subr/subst.c
    subversion/branches/patch-from-stream/subversion/tests/libsvn_subr/stream-test.c

Modified: subversion/branches/patch-from-stream/BRANCH-README
URL: http://svn.apache.org/viewvc/subversion/branches/patch-from-stream/BRANCH-README?rev=1925668&r1=1925667&r2=1925668&view=diff
==============================================================================
--- subversion/branches/patch-from-stream/BRANCH-README (original)
+++ subversion/branches/patch-from-stream/BRANCH-README Sun May 18 13:31:20 2025
@@ -1,6 +1,21 @@
 Use streams instead of APR files to read patch files.
 =====================================================
 
-2025-05-16: The patch parser currently uses apr_file_t to read
-and parse patch files. Convert to usint svn_stream_t instead,
+2025-05-16: The patch parser currently uses APR files to read
+and parse patch files. Convert it to use SVN streams instead,
 as it's more flexible and doesn't strictly rely on on-disk files.
+
+
+ISSUES:
+  - Reading patches currently relies heavily on seeking in the file.
+    This is really supported only in file-based and string-based streams,
+    and not even all of those (e.g., (de)compressed and tee'd streams
+    don't support seek even if there's an underlying file). That makes
+    reading patches from streams less flexible than it could be.
+
+    Still, by converting to reading from streams, we're not reducing
+    existing features; all current use cases for reading patches remain
+    supported, because file-based streams always support mark and seek.
+
+    And now, they also support span, i.e., finding the distance between
+    two stream marks, just like for two file positions.

Modified: subversion/branches/patch-from-stream/subversion/include/svn_error_codes.h
URL: http://svn.apache.org/viewvc/subversion/branches/patch-from-stream/subversion/include/svn_error_codes.h?rev=1925668&r1=1925667&r2=1925668&view=diff
==============================================================================
--- subversion/branches/patch-from-stream/subversion/include/svn_error_codes.h (original)
+++ subversion/branches/patch-from-stream/subversion/include/svn_error_codes.h Sun May 18 13:31:20 2025
@@ -334,6 +334,16 @@ SVN_ERROR_START
              SVN_ERR_STREAM_CATEGORY_START + 4,
              "Stream doesn't support this capability")
 
+  /** @since New in 1.15. */
+  SVN_ERRDEF(SVN_ERR_STREAM_SPAN_NOT_SUPPORTED,
+             SVN_ERR_STREAM_CATEGORY_START + 5,
+             "Stream doesn't support calculating offsets")
+
+  /** @since New in 1.15. */
+  SVN_ERRDEF(SVN_ERR_STREAM_OFFSET_TOO_LARGE,
+             SVN_ERR_STREAM_CATEGORY_START + 6,
+             "The offset between two stream positions is too large")
+
   /* node errors */
 
   SVN_ERRDEF(SVN_ERR_NODE_UNKNOWN_KIND,

Modified: subversion/branches/patch-from-stream/subversion/include/svn_io.h
URL: http://svn.apache.org/viewvc/subversion/branches/patch-from-stream/subversion/include/svn_io.h?rev=1925668&r1=1925667&r2=1925668&view=diff
==============================================================================
--- subversion/branches/patch-from-stream/subversion/include/svn_io.h (original)
+++ subversion/branches/patch-from-stream/subversion/include/svn_io.h Sun May 18 13:31:20 2025
@@ -910,6 +910,16 @@ typedef svn_error_t *(*svn_stream_mark_f
 typedef svn_error_t *(*svn_stream_seek_fn_t)(void *baton,
                                              const svn_stream_mark_t *mark);
 
+/** Span handler function for a generic stream. @see svn_stream_t and
+ * svn_stream_span().
+ *
+ * @since New in 1.15.
+ */
+typedef svn_error_t *(*svn_stream_span_fn_t)(
+    void *baton, apr_off_t *offset,
+    const svn_stream_mark_t *first_mark,
+    const svn_stream_mark_t *second_mark);
+
 /** Poll handler for generic streams that support incomplete reads, @see
  * svn_stream_t and svn_stream_data_available().
  *
@@ -995,6 +1005,14 @@ void
 svn_stream_set_seek(svn_stream_t *stream,
                     svn_stream_seek_fn_t seek_fn);
 
+/** Set @a stream's span function to @a span_fn
+ *
+ * @since New in 1.15.
+ */
+void
+svn_stream_set_span(svn_stream_t *stream,
+                    svn_stream_span_fn_t span_fn);
+
 /** Set @a stream's data available function to @a data_available_fn
  *
  * @since New in 1.9.
@@ -1393,6 +1411,14 @@ svn_stream_supports_mark(svn_stream_t *s
 svn_boolean_t
 svn_stream_supports_seek(svn_stream_t *stream);
 
+/** Returns @c TRUE if the generic @a stream supports svn_stream_span().
+ *
+ * @see svn_stream_span()
+ * @since New in 1.15.
+ */
+svn_boolean_t
+svn_stream_supports_span(svn_stream_t *stream);
+
 /** Returns @c TRUE if the generic @a stream supports svn_stream_reset().
  *
  * @see svn_stream_reset()
@@ -1427,6 +1453,32 @@ svn_stream_mark(svn_stream_t *stream,
 svn_error_t *
 svn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark);
 
+/** Calculate the offset between two positions in a generic @a stream.
+ *
+ * The @a stream must support svn_stream_mark(), but that does not guarantee
+ * that the offset between two positions in the stream is well defined. The
+ * function returns the #SVN_ERR_STREAM_SPAN_NOT_SUPPORTED in this case.
+ *
+ * It's possible for a stream to support svn_stream_mark() and
+ * svn_stream_span() but not svn_stream_seek() or svn_stream_reset().
+ * This is the case, for example, for compressed stream wrappers, where
+ * a seek would have to roll back some magic internal compression state,
+ * but for calculating an offset we only have to keep track of the number
+ * of bytes read from or written to the stream.
+ *
+ * If the offset can be calculated but is too large to represent with an
+ * apr_off_t, this function returns #SVN_ERR_STREAM_OFFSET_TOO_LARGE.
+ *
+ * The returned @a offset can be negative.
+ *
+ * @see svn_stream_mark()
+ * @since New in 1.15
+ */
+svn_error_t *
+svn_stream_span(apr_off_t *offset, svn_stream_t *stream,
+                const svn_stream_mark_t *first_mark,
+                const svn_stream_mark_t *second_mark);
+
 /** When a stream supports polling for available data, obtain a boolean
  * indicating whether data is waiting to be read. If the stream doesn't
  * support polling this function returns a #SVN_ERR_STREAM_NOT_SUPPORTED

Modified: subversion/branches/patch-from-stream/subversion/libsvn_repos/config_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/patch-from-stream/subversion/libsvn_repos/config_file.c?rev=1925668&r1=1925667&r2=1925668&view=diff
==============================================================================
--- subversion/branches/patch-from-stream/subversion/libsvn_repos/config_file.c (original)
+++ subversion/branches/patch-from-stream/subversion/libsvn_repos/config_file.c Sun May 18 13:31:20 2025
@@ -110,6 +110,18 @@ seek_handler_rep(void *baton, const svn_
 }
 
 static svn_error_t *
+span_handler_rep(void *baton, apr_off_t *offset,
+                 const svn_stream_mark_t *first_mark,
+                 const svn_stream_mark_t *second_mark)
+{
+  presentation_stream_baton_t *b = baton;
+  SVN_ERR(auto_open_inner_stream(b));
+
+  return svn_error_trace(svn_stream_span(offset, b->inner,
+                                         first_mark, second_mark));
+}
+
+static svn_error_t *
 skip_handler_rep(void *baton, apr_size_t len)
 {
   presentation_stream_baton_t *b = baton;
@@ -160,6 +172,7 @@ representation_stream(svn_fs_root_t *roo
   svn_stream_set_mark(stream, mark_handler_rep);
   svn_stream_set_seek(stream, seek_handler_rep);
   svn_stream_set_skip(stream, skip_handler_rep);
+  svn_stream_set_span(stream, span_handler_rep);
   svn_stream_set_data_available(stream, data_available_handler_rep);
   svn_stream_set_readline(stream, readline_handler_rep);
   return stream;

Modified: subversion/branches/patch-from-stream/subversion/libsvn_subr/stream.c
URL: http://svn.apache.org/viewvc/subversion/branches/patch-from-stream/subversion/libsvn_subr/stream.c?rev=1925668&r1=1925667&r2=1925668&view=diff
==============================================================================
--- subversion/branches/patch-from-stream/subversion/libsvn_subr/stream.c (original)
+++ subversion/branches/patch-from-stream/subversion/libsvn_subr/stream.c Sun May 18 13:31:20 2025
@@ -63,6 +63,7 @@ struct svn_stream_t {
   svn_stream_data_available_fn_t data_available_fn;
   svn_stream_readline_fn_t readline_fn;
   apr_file_t *file; /* Maybe NULL */
+  svn_stream_span_fn_t span_fn;
 };
 
 
@@ -132,6 +133,13 @@ svn_stream_set_seek(svn_stream_t *stream
 }
 
 void
+svn_stream_set_span(svn_stream_t *stream,
+                    svn_stream_span_fn_t span_fn)
+{
+  stream->span_fn = span_fn;
+}
+
+void
 svn_stream_set_data_available(svn_stream_t *stream,
                               svn_stream_data_available_fn_t data_available_fn)
 {
@@ -242,6 +250,12 @@ svn_stream_supports_seek(svn_stream_t *s
 }
 
 svn_boolean_t
+svn_stream_supports_span(svn_stream_t *stream)
+{
+  return stream->span_fn != NULL;
+}
+
+svn_boolean_t
 svn_stream_supports_reset(svn_stream_t *stream)
 {
   return svn_stream_supports_seek(stream);
@@ -267,6 +281,19 @@ svn_stream_seek(svn_stream_t *stream, co
 }
 
 svn_error_t *
+svn_stream_span(apr_off_t *offset,
+                svn_stream_t *stream,
+                const svn_stream_mark_t *first_mark,
+                const svn_stream_mark_t *second_mark)
+{
+  if (stream->span_fn == NULL)
+    return svn_error_create(SVN_ERR_STREAM_SPAN_NOT_SUPPORTED, NULL, NULL);
+
+  return svn_error_trace(stream->span_fn(stream->baton, offset,
+                                         first_mark, second_mark));
+}
+
+svn_error_t *
 svn_stream_data_available(svn_stream_t *stream,
                           svn_boolean_t *data_available)
 {
@@ -531,7 +558,14 @@ seek_handler_empty(void *baton, const sv
   return SVN_NO_ERROR;
 }
 
-
+static svn_error_t *
+span_handler_empty(void *baton, apr_off_t *offset,
+                   const svn_stream_mark_t *first_mark,
+                   const svn_stream_mark_t *second_mark)
+{
+  *offset = 0;
+  return SVN_NO_ERROR;
+}
 
 svn_stream_t *
 svn_stream_empty(apr_pool_t *pool)
@@ -543,6 +577,7 @@ 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_span(stream, span_handler_empty);
   return stream;
 }
 
@@ -644,6 +679,15 @@ seek_handler_disown(void *baton, const s
 }
 
 static svn_error_t *
+span_handler_disown(void *baton, apr_off_t *offset,
+                    const svn_stream_mark_t *first_mark,
+                    const svn_stream_mark_t *second_mark)
+{
+  return svn_error_trace(svn_stream_span(offset, baton,
+                                         first_mark, second_mark));
+}
+
+static svn_error_t *
 data_available_disown(void *baton, svn_boolean_t *data_available)
 {
   return svn_error_trace(svn_stream_data_available(baton, data_available));
@@ -674,6 +718,8 @@ svn_stream_disown(svn_stream_t *stream,
     svn_stream_set_mark(s, mark_handler_disown);
   if (svn_stream_supports_seek(stream))
     svn_stream_set_seek(s, seek_handler_disown);
+  if (svn_stream_supports_span(stream))
+    svn_stream_set_span(s, span_handler_disown);
   svn_stream_set_data_available(s, data_available_disown);
   svn_stream_set_readline(s, readline_handler_disown);
 
@@ -822,6 +868,20 @@ seek_handler_apr(void *baton, const svn_
 }
 
 static svn_error_t *
+span_handler_apr(void *baton, apr_off_t *offset,
+                 const svn_stream_mark_t *first_mark,
+                 const svn_stream_mark_t *second_mark)
+{
+  apr_off_t first_offset = ((first_mark == NULL) ? 0
+                            : ((const struct mark_apr *)first_mark)->off);
+  apr_off_t second_offset = ((second_mark == NULL) ? 0
+                             : ((const struct mark_apr *)second_mark)->off);
+
+  *offset = second_offset - first_offset;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 data_available_handler_apr(void *baton, svn_boolean_t *data_available)
 {
   struct baton_apr *btn = baton;
@@ -1100,6 +1160,7 @@ make_stream_from_apr_file(apr_file_t *fi
       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_span(stream, span_handler_apr);
       svn_stream_set_readline(stream, readline_handler_apr);
     }
 
@@ -1628,11 +1689,51 @@ struct stringbuf_stream_baton
   apr_size_t amt_read;
 };
 
-/* svn_stream_mark_t for streams backed by stringbufs. */
-struct stringbuf_stream_mark {
+/* svn_stream_mark_t for streams backed by stringbufs and strings. */
+struct mark_str {
     apr_size_t pos;
 };
 
+/* Try to check if an apr_size_t can be safely converted to an apr_off_t. */
+static APR_INLINE svn_boolean_t
+off_t_can_not_represent(apr_size_t size)
+{
+#ifdef APR_OFF_MAX
+  static const apr_off_t off_t_max = APR_OFF_MAX;
+#else
+  static const apr_off_t off_t_max =
+      (sizeof(apr_off_t) == sizeof(apr_int32_t) ? APR_INT32_MAX
+       : (sizeof(apr_off_t) == sizeof(apr_int64_t) ? APR_INT64_MAX
+          : 0 /* Maybe, someday, APR will have an int128_t. */));
+#endif
+
+  /* Any sane compiler should resolve this at compile time. */
+  if (sizeof(off_t_max) > sizeof(size))
+    return FALSE;
+
+  if (SVN__PREDICT_FALSE(off_t_max == 0))
+    SVN_ERR_ASSERT_NO_RETURN(off_t_max != 0);
+  return size > off_t_max;
+}
+
+static svn_error_t* span_handler_str(void *baton, apr_off_t *offset,
+                                     const svn_stream_mark_t *first_mark,
+                                     const svn_stream_mark_t *second_mark)
+{
+  const apr_size_t pos1 = ((first_mark == NULL) ? 0
+                           : ((const struct mark_str*)first_mark)->pos);
+  const apr_size_t pos2 = ((second_mark == NULL) ? 0
+                           : ((const struct mark_str *)second_mark)->pos);
+  const svn_boolean_t negative = pos2 < pos1;
+  const apr_size_t scale = negative ? pos1 - pos2 : pos2 - pos1;
+
+  if (SVN__PREDICT_FALSE(off_t_can_not_represent(scale)))
+    return svn_error_create(SVN_ERR_STREAM_OFFSET_TOO_LARGE, NULL, NULL);
+
+  *offset = negative ? -(apr_off_t)scale : (apr_off_t)scale;
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 read_handler_stringbuf(void *baton, char *buffer, apr_size_t *len)
 {
@@ -1669,13 +1770,13 @@ static svn_error_t *
 mark_handler_stringbuf(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
 {
   struct stringbuf_stream_baton *btn;
-  struct stringbuf_stream_mark *stringbuf_stream_mark;
+  struct mark_str *marker;
 
   btn = baton;
 
-  stringbuf_stream_mark = apr_palloc(pool, sizeof(*stringbuf_stream_mark));
-  stringbuf_stream_mark->pos = btn->amt_read;
-  *mark = (svn_stream_mark_t *)stringbuf_stream_mark;
+  marker = apr_palloc(pool, sizeof(*marker));
+  marker->pos = btn->amt_read;
+  *mark = (svn_stream_mark_t *)marker;
   return SVN_NO_ERROR;
 }
 
@@ -1686,10 +1787,8 @@ seek_handler_stringbuf(void *baton, cons
 
   if (mark != NULL)
     {
-      const struct stringbuf_stream_mark *stringbuf_stream_mark;
-
-      stringbuf_stream_mark = (const struct stringbuf_stream_mark *)mark;
-      btn->amt_read = stringbuf_stream_mark->pos;
+      const struct mark_str *const marker = (const struct mark_str *)mark;
+      btn->amt_read = marker->pos;
     }
   else
     btn->amt_read = 0;
@@ -1756,6 +1855,7 @@ svn_stream_from_stringbuf(svn_stringbuf_
   svn_stream_set_write(stream, write_handler_stringbuf);
   svn_stream_set_mark(stream, mark_handler_stringbuf);
   svn_stream_set_seek(stream, seek_handler_stringbuf);
+  svn_stream_set_span(stream, span_handler_str);
   svn_stream_set_data_available(stream, data_available_handler_stringbuf);
   svn_stream_set_readline(stream, readline_handler_stringbuf);
   return stream;
@@ -1767,11 +1867,6 @@ struct string_stream_baton
   apr_size_t amt_read;
 };
 
-/* svn_stream_mark_t for streams backed by stringbufs. */
-struct string_stream_mark {
-    apr_size_t pos;
-};
-
 static svn_error_t *
 read_handler_string(void *baton, char *buffer, apr_size_t *len)
 {
@@ -1788,7 +1883,7 @@ static svn_error_t *
 mark_handler_string(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
 {
   struct string_stream_baton *btn;
-  struct string_stream_mark *marker;
+  struct mark_str *marker;
 
   btn = baton;
 
@@ -1805,9 +1900,7 @@ seek_handler_string(void *baton, const s
 
   if (mark != NULL)
     {
-      const struct string_stream_mark *marker;
-
-      marker = (const struct string_stream_mark *)mark;
+      const struct mark_str *const marker = (const struct mark_str *)mark;
       btn->amt_read = marker->pos;
     }
   else
@@ -1885,6 +1978,7 @@ svn_stream_from_string(const svn_string_
   svn_stream_set_mark(stream, mark_handler_string);
   svn_stream_set_seek(stream, seek_handler_string);
   svn_stream_set_skip(stream, skip_handler_string);
+  svn_stream_set_span(stream, span_handler_str);
   svn_stream_set_data_available(stream, data_available_handler_string);
   svn_stream_set_readline(stream, readline_handler_string);
   return stream;
@@ -2124,6 +2218,21 @@ seek_handler_lazyopen(void *baton,
   return SVN_NO_ERROR;
 }
 
+/* Implements svn_stream_span_fn_t */
+static svn_error_t *
+span_handler_lazyopen(void *baton, apr_off_t *offset,
+                      const svn_stream_mark_t *first_mark,
+                      const svn_stream_mark_t *second_mark)
+{
+  lazyopen_baton_t *b = baton;
+
+  SVN_ERR(lazyopen_if_unopened(b));
+  SVN_ERR(svn_stream_span(offset, b->real_stream,
+                          first_mark, second_mark));
+
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 data_available_handler_lazyopen(void *baton,
                                 svn_boolean_t *data_available)
@@ -2173,6 +2282,7 @@ svn_stream_lazyopen_create(svn_stream_la
   svn_stream_set_close(stream, close_handler_lazyopen);
   svn_stream_set_mark(stream, mark_handler_lazyopen);
   svn_stream_set_seek(stream, seek_handler_lazyopen);
+  svn_stream_set_span(stream, span_handler_lazyopen);
   svn_stream_set_data_available(stream, data_available_handler_lazyopen);
   svn_stream_set_readline(stream, readline_handler_lazyopen);
 

Modified: subversion/branches/patch-from-stream/subversion/libsvn_subr/subst.c
URL: http://svn.apache.org/viewvc/subversion/branches/patch-from-stream/subversion/libsvn_subr/subst.c?rev=1925668&r1=1925667&r2=1925668&view=diff
==============================================================================
--- subversion/branches/patch-from-stream/subversion/libsvn_subr/subst.c (original)
+++ subversion/branches/patch-from-stream/subversion/libsvn_subr/subst.c Sun May 18 13:31:20 2025
@@ -1432,6 +1432,24 @@ translated_stream_seek(void *baton, cons
   return SVN_NO_ERROR;
 }
 
+/* Implements svn_stream_span_fn_t.
+   NOTE: Returns the distance in the wrapped stream, not in the
+         translated data. */
+static svn_error_t *
+translated_stream_span(void *baton, apr_off_t *offset,
+                       const svn_stream_mark_t *first_mark,
+                       const svn_stream_mark_t *second_mark)
+{
+  struct translated_stream_baton *b = baton;
+  const mark_translated_t *mt1 = (const mark_translated_t *)first_mark;
+  const mark_translated_t *mt2 = (const mark_translated_t *)second_mark;
+  return svn_error_trace(
+      svn_stream_span(offset, b->stream,
+                      mt1 != NULL ? mt1->mark : NULL,
+                      mt2 != NULL ? mt2->mark : NULL));
+}
+
+
 svn_error_t *
 svn_subst_read_specialfile(svn_stream_t **stream,
                            const char *path,
@@ -1545,6 +1563,8 @@ stream_translated(svn_stream_t *stream,
         svn_stream_set_mark(s, translated_stream_mark);
       if (svn_stream_supports_seek(stream))
         svn_stream_set_seek(s, translated_stream_seek);
+      if (svn_stream_supports_span(stream))
+        svn_stream_set_span(s, translated_stream_span);
 
       return s;
     }

Modified: subversion/branches/patch-from-stream/subversion/tests/libsvn_subr/stream-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/patch-from-stream/subversion/tests/libsvn_subr/stream-test.c?rev=1925668&r1=1925667&r2=1925668&view=diff
==============================================================================
--- subversion/branches/patch-from-stream/subversion/tests/libsvn_subr/stream-test.c (original)
+++ subversion/branches/patch-from-stream/subversion/tests/libsvn_subr/stream-test.c Sun May 18 13:31:20 2025
@@ -262,7 +262,8 @@ test_stream_seek_file(apr_pool_t *pool)
   int j;
   apr_status_t status;
   static const char *NL = APR_EOL_STR;
-  svn_stream_mark_t *mark;
+  svn_stream_mark_t *mark, *mark2;
+  apr_off_t offset;
 
   status = apr_file_open(&f, fname, (APR_READ | APR_WRITE | APR_CREATE |
                          APR_TRUNCATE | APR_DELONCLOSE), APR_OS_DEFAULT, pool);
@@ -297,6 +298,11 @@ test_stream_seek_file(apr_pool_t *pool)
   /* Read the second line and then seek back to the mark. */
   SVN_ERR(svn_stream_readline(stream, &line, NL, &eof, pool));
   SVN_TEST_ASSERT(! eof && strcmp(line->data, file_data[1]) == 0);
+  /* Check how far we read before seeking. */
+  SVN_ERR(svn_stream_mark(stream, &mark2, pool));
+  SVN_ERR(svn_stream_span(&offset, stream, mark, mark2));
+  SVN_TEST_ASSERT(offset == strlen(line->data) + strlen(NL));
+  /* Ok, carry on... */
   SVN_ERR(svn_stream_seek(stream, mark));
   /* The next read should return the second line again. */
   SVN_ERR(svn_stream_readline(stream, &line, NL, &eof, pool));
@@ -328,7 +334,8 @@ test_stream_seek_stringbuf(apr_pool_t *p
   svn_stringbuf_t *stringbuf;
   char buf[4];
   apr_size_t len;
-  svn_stream_mark_t *mark;
+  svn_stream_mark_t *mark, *mark2;
+  apr_off_t offset;
 
   stringbuf = svn_stringbuf_create("OneTwo", pool);
   stream = svn_stream_from_stringbuf(stringbuf, pool);
@@ -350,6 +357,10 @@ test_stream_seek_stringbuf(apr_pool_t *p
   /* Go back to the begin of last word and try to skip some of it */
   SVN_ERR(svn_stream_seek(stream, mark));
   SVN_ERR(svn_stream_skip(stream, 2));
+  /* Check that we actually skipped that far. */
+  SVN_ERR(svn_stream_mark(stream, &mark2, pool));
+  SVN_ERR(svn_stream_span(&offset, stream, mark, mark2));
+  SVN_TEST_ASSERT(offset == 2);
   /* The remaining line should be empty */
   len = 3;
   SVN_ERR(svn_stream_read_full(stream, buf, &len));
@@ -369,7 +380,8 @@ test_stream_seek_translated(apr_pool_t *
   svn_stringbuf_t *stringbuf;
   char buf[44]; /* strlen("One$MyKeyword: my keyword was expanded $Two") + \0 */
   apr_size_t len;
-  svn_stream_mark_t *mark;
+  svn_stream_mark_t *mark, *mark2;
+  apr_off_t offset;
   apr_hash_t *keywords;
   svn_string_t *keyword_val;
 
@@ -381,6 +393,7 @@ test_stream_seek_translated(apr_pool_t *
   translated_stream = svn_subst_stream_translated(stream, APR_EOL_STR,
                                                   FALSE, keywords, TRUE, pool);
   /* Seek from outside of keyword to inside of keyword. */
+  SVN_ERR(svn_stream_mark(translated_stream, &mark2, pool));
   len = 25;
   SVN_ERR(svn_stream_read_full(translated_stream, buf, &len));
   SVN_TEST_ASSERT(len == 25);
@@ -388,6 +401,10 @@ test_stream_seek_translated(apr_pool_t *
   SVN_TEST_STRING_ASSERT(buf, "One$MyKeyword: my keyword");
   SVN_ERR(svn_stream_mark(translated_stream, &mark, pool));
   SVN_ERR(svn_stream_reset(translated_stream));
+  SVN_ERR(svn_stream_span(&offset, translated_stream, mark, mark2));
+  /* This is the distance in the untranslated stream, so the length of
+     that "One$MyKeyword$Two" that was read from there. */
+  SVN_TEST_ASSERT(offset == -17);
   SVN_ERR(svn_stream_seek(translated_stream, mark));
   len = 4;
   SVN_ERR(svn_stream_read_full(translated_stream, buf, &len));
@@ -524,6 +541,7 @@ test_stream_compressed_empty_file(apr_po
                                  svn_io_file_del_on_pool_cleanup,
                                  pool, pool));
   stream = svn_stream_compressed(empty_file_stream, pool);
+  SVN_TEST_ASSERT(!svn_stream_supports_span(stream));
   len = sizeof(buf);
   SVN_ERR(svn_stream_read_full(stream, buf, &len));
   if (len > 0)
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.