[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] Delta window changes and delta applicator

From: Greg Hudson <ghudson_at_mit.edu>
Date: 2000-09-18 16:28:06 CEST

Here is my proposed delta applicator code. (Since you can't "cvs add"
without commit access, I hand-added the diffs for the new files at the
end.) Some notes:

        * I invented the term "view" to describe the source and target
          substrings accessible to a window. Calling them the "source
          window" and "target window" seemed confusing given how we
          use the term "window" already.

        * Branko is going to have to do a little bit of code reorg
          when it comes time to be able to marshal and unmarshal
          deltas to vcdiff format. The existing text delta interface
          and data structures (independent of this patch) is pretty
          fixed on the idea that deltas are only created from source
          and target files, not read in from an external source.

        * I decided to make the applicator function use a generic
          output function, so I had to add that concept to svn_io.h.
          I also wrote some wrappers for full reads and full writes,
          since generic read and write functions appear to be allowed
          to return short lengths for arbitrary reasons.

        * I clarified the meaning of POOL in the generic read function
          description, and then promptly abused that definition in
          svn_complete_read() when I create an errors. Hopefully that
          problem should go away when we change errors to use malloc().

        * I wrote a test program to generate and apply a diff of a
          random source and target file. It's a little light on
          output, but it tells you what random seed it's using so that
          you can run it again under the debugger if the test fails.
          I found a few bugs this way (mostly in my assertions,
          ironically), and now the code seems to pass that test fine.

        * The vdelta code was already generating instruction offsets
          relative to the source window offset.

        * window->tview_len is kind of redundant; you could compute it
          easily by summing the lengths of all the instructions in the
          window. But I think it makes a good check.

Index: include/svn_delta.h
===================================================================
RCS file: /cvs/subversion/subversion/include/svn_delta.h,v
retrieving revision 1.94
diff -u -r1.94 svn_delta.h
--- include/svn_delta.h 2000/09/15 06:08:50 1.94
+++ include/svn_delta.h 2000/09/18 14:13:53
@@ -91,24 +91,31 @@
 
 
 /* An `svn_txdelta_window_t' object describes how to reconstruct a
- contiguous section of the target string. It contains a series of
- instructions which assemble the new target string text by pulling
- together substrings from:
- - the source file,
- - the target file text so far, and
- - a string of new data (accessible to this window only). */
+ contiguous section of the target string (the "target view") using a
+ specified contiguous region of the source string (the "source
+ view"). It contains a series of instructions which assemble the
+ new target string text by pulling together substrings from:
+ - the source view,
+ - the previously constructed portion of the target view,
+ - a string of new data contained within the window structure
+
+ The source view must always slide forward from one window to the
+ next; that is, neither the beginning nor the end of the source view
+ may move to the left as we read from a window stream. This
+ property allows us to apply deltas to non-seekable source streams
+ without making a full copy of the source stream. */
 
 /* A single text delta instruction. */
 typedef struct svn_txdelta_op_t {
   enum {
- /* Append the LEN bytes at OFFSET in the source string to the
+ /* Append the LEN bytes at OFFSET in the source view to the
        target. It must be the case that 0 <= OFFSET < OFFSET + LEN <=
- size of source string. */
+ size of source view. */
     svn_txdelta_source,
 
- /* Append the LEN bytes at OFFSET in the target file, to the
- target. It must be the case that 0 <= OFFSET < current size of
- the target string.
+ /* Append the LEN bytes at OFFSET in the target view, to the
+ target. It must be the case that 0 <= OFFSET < current
+ position in the target view.
 
        However! OFFSET + LEN may be *beyond* the end of the existing
        target data. "Where the heck does the text come from, then?"
@@ -136,6 +143,14 @@
 /* How to produce the next stretch of the target string. */
 typedef struct svn_txdelta_window_t {
 
+ /* The offset and length of the source view for this window. */
+ apr_off_t sview_offset;
+ apr_off_t sview_len;
+
+ /* The length of the target view for this window, i.e. the number of
+ * bytes which will be reconstructed by the instruction stream. */
+ apr_off_t tview_len;
+
   /* The number of instructions in this window. */
   int num_ops;
 
@@ -188,6 +203,17 @@
                                  svn_read_fn_t *target_fn,
                                  void *target_baton,
                                  apr_pool_t *pool);
+
+
+/* Apply DELTA to a source stream to produce a target stream. Call
+ * SOURCE_FN to gather as much source data as needed and TARGET_FN to
+ * output the generated target data. Do any necessary allocation in a
+ * sub-pool of DELTA's pool. */
+extern svn_error_t *svn_txdelta_apply (svn_txdelta_stream_t *delta,
+ svn_read_fn_t *source_fn,
+ void *source_baton,
+ svn_write_fn_t *target_fn,
+ void *target_baton);
 
 
 /* Free the delta stream STREAM. */
Index: include/svn_io.h
===================================================================
RCS file: /cvs/subversion/subversion/include/svn_io.h,v
retrieving revision 1.3
diff -u -r1.3 svn_io.h
--- include/svn_io.h 2000/09/09 16:11:27 1.3
+++ include/svn_io.h 2000/09/18 14:13:54
@@ -73,10 +73,37 @@
    BUFFER is a buffer to hold the data, and *LEN indicates how many
    bytes to read. Upon return, the function should set *LEN to the
    number of bytes actually read, or zero at the end of the data
- stream. */
+ stream. Any necessary temporary allocation should be done in a
+ sub-pool of POOL. (If the read function needs to perform
+ allocations which last beyond the lifetime of the function, it must
+ use a different pool, e.g. one referenced through BATON.) */
 typedef svn_error_t *svn_read_fn_t (void *baton,
                                     char *buffer,
                                     apr_size_t *len,
                                     apr_pool_t *pool);
+
+/* Similar to svn_read_fn_t, but for writing. */
+typedef svn_error_t *svn_write_fn_t (void *baton,
+ const char *data,
+ apr_size_t *len,
+ apr_pool_t *pool);
+
+/* Wrappers around the above class of functions to do complete reads
+ * and writes. *LEN is still set on return to the actual number of
+ * bytes read or written, but *LEN will only change when an error is
+ * returned. svn_full_read returns an SVN_ERR_INCOMPLETE_DATA error
+ * if the stream runs out of data before *LEN bytes can be read. */
+
+svn_error_t *svn_full_read (svn_read_fn_t read_fn,
+ void *baton,
+ char *buffer,
+ apr_size_t *len,
+ apr_pool_t *pool);
+
+svn_error_t *svn_full_write (svn_write_fn_t write_fn,
+ void *baton,
+ const char *data,
+ apr_size_t *len,
+ apr_pool_t *pool);
 
 #endif /* SVN_IO_H */
Index: libsvn_delta/text_delta.c
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_delta/text_delta.c,v
retrieving revision 1.1
diff -u -r1.1 text_delta.c
--- libsvn_delta/text_delta.c 2000/09/15 06:08:49 1.1
+++ libsvn_delta/text_delta.c 2000/09/18 14:13:58
@@ -50,9 +50,14 @@
 
 #include <assert.h>
 #include "svn_delta.h"
+#include "svn_io.h"
 #include "delta.h"
 
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
 
+
 
 /* Text delta stream descriotor. */
 
@@ -66,6 +71,7 @@
   /* Private data */
   apr_pool_t* pool; /* Pool to allocate stream data from. */
   svn_boolean_t more; /* TRUE if there are more data in the pool. */
+ apr_off_t pos; /* Position in source file. */
 };
 
 
@@ -80,6 +86,9 @@
   assert (pool != NULL);
 
   (*window) = apr_palloc (pool, sizeof (**window));
+ (*window)->sview_offset = 0;
+ (*window)->sview_len = 0;
+ (*window)->tview_len = 0;
   (*window)->num_ops = 0;
   (*window)->ops_size = 0;
   (*window)->ops = NULL;
@@ -165,6 +174,7 @@
   (*stream)->target_baton = target_baton;
   (*stream)->pool = subpool;
   (*stream)->more = TRUE;
+ (*stream)->pos = 0;
   return SVN_NO_ERROR;
 }
 
@@ -191,6 +201,7 @@
   else
     {
       svn_error_t *err = SVN_NO_ERROR;
+ apr_off_t source_offset = stream->pos;
       apr_size_t source_len = svn_txdelta__window_size;
       apr_size_t target_len = svn_txdelta__window_size;
       apr_pool_t *temp_pool = apr_make_sub_pool (stream->pool, NULL);
@@ -201,6 +212,7 @@
                          &source_len, NULL/*FIXME:*/);
       stream->target_fn (stream->target_baton, buffer + source_len,
                          &target_len, NULL/*FIXME:*/);
+ stream->pos += source_len;
 
       /* Forget everything if there's no target data. */
       *window = NULL;
@@ -214,9 +226,14 @@
       /* Create the delta window */
       err = svn_txdelta__init_window (window, stream);
       if (err == SVN_NO_ERROR)
- err = svn_txdelta__vdelta (*window, buffer,
- source_len, target_len,
- temp_pool);
+ {
+ (*window)->sview_offset = source_offset;
+ (*window)->sview_len = source_len;
+ (*window)->tview_len = target_len;
+ err = svn_txdelta__vdelta (*window, buffer,
+ source_len, target_len,
+ temp_pool);
+ }
 
       /* That's it. */
       apr_destroy_pool (temp_pool);
@@ -235,6 +252,156 @@
 {
   if (window)
     apr_destroy_pool (window->pool);
+}
+
+
+
+/* Apply WINDOW to a source view SBUF to produce a target view TBUF.
+ * SBUF is assumed to have window->sview_len bytes of data and TBUF is
+ * assumed to have room for window->tview_len bytes of output. This
+ * is purely a memory operation; nothing can go wrong as long as we
+ * have a valid window. */
+
+static void
+apply_window (svn_txdelta_window_t *window, const char *sbuf, char *tbuf)
+{
+ svn_txdelta_op_t *op;
+ apr_off_t i, tpos = 0;
+
+ for (op = window->ops; op < window->ops + window->num_ops; op++)
+ {
+ /* Check some invariants common to all instructions. */
+ assert (op->offset >= 0 && op->length >= 0);
+ assert (tpos + op->length <= window->tview_len);
+
+ switch (op->action_code)
+ {
+ case svn_txdelta_source:
+ /* Copy from source area. */
+ assert (op->offset + op->length <= window->sview_len);
+ memcpy (tbuf + tpos, sbuf + op->offset, op->length);
+ tpos += op->length;
+ break;
+
+ case svn_txdelta_target:
+ /* Copy from target area. Don't use memcpy() since its
+ semantics aren't guaranteed for overlapping memory areas,
+ and target copies are allowed to overlap to generate
+ repeated data. */
+ assert (op->offset < tpos);
+ for (i = op->offset; i < op->offset + op->length; i++)
+ tbuf[tpos++] = tbuf[i];
+ break;
+
+ case svn_txdelta_new:
+ /* Copy from window new area. */
+ assert (op->offset + op->length <= window->new->len);
+ memcpy (tbuf + tpos, window->new->data + op->offset, op->length);
+ tpos += op->length;
+ break;
+
+ default:
+ assert ("Invalid delta instruction code" == NULL);
+ }
+ }
+
+ /* Check that we produced the right amount of data. */
+ assert (tpos == window->tview_len);
+}
+
+
+/* Apply a text delta to an input stream. */
+
+svn_error_t *
+svn_txdelta_apply (svn_txdelta_stream_t *delta,
+ svn_read_fn_t *source_fn,
+ void *source_baton,
+ svn_write_fn_t *target_fn,
+ void *target_baton)
+{
+ apr_pool_t *temp_pool = apr_make_sub_pool (delta->pool, NULL);
+ char *sbuf = NULL, *tbuf = NULL;
+ apr_size_t sbuf_size = 0, tbuf_size = 0, len;
+ apr_off_t sbuf_offset = 0, sbuf_len = 0;
+ svn_error_t *err;
+ svn_txdelta_window_t *window = NULL, *oldwin;
+
+ while (1)
+ {
+ /* Get a new window. */
+ oldwin = window;
+ err = svn_txdelta_next_window (&window, delta);
+ if (err != SVN_NO_ERROR || window == NULL)
+ break;
+
+ /* Make sure the source view didn't slide backwards. */
+ assert (window->sview_offset >= sbuf_offset
+ && (window->sview_offset + window->sview_len
+ >= sbuf_offset + sbuf_len));
+
+ /* Ensure adequate space in the target buffer. */
+ if (window->tview_len > tbuf_size)
+ {
+ int new_size = MAX (window->tview_len, tbuf_size * 2);
+ tbuf = apr_palloc (temp_pool, new_size);
+ tbuf_size = new_size;
+ }
+
+ /* Prepare the source buffer for reading from the input stream. */
+ if (window->sview_offset != sbuf_offset || window->sview_len > sbuf_size)
+ {
+ char *new_sbuf = sbuf;
+ apr_size_t new_size = sbuf_size;
+
+ /* Make a new source buffer if we don't have enough space in
+ the current one. */
+ if (window->sview_len > sbuf_size)
+ {
+ new_size = MAX (window->sview_len, sbuf_size * 2);
+ new_sbuf = apr_palloc (temp_pool, new_size);
+ sbuf_size = new_size;
+ }
+
+ /* If the existing view overlaps with the new view, copy the
+ * overlap to the beginning of the new buffer. */
+ if (sbuf_offset + sbuf_len > window->sview_offset)
+ {
+ apr_size_t start = window->sview_offset - sbuf_offset;
+ memmove (new_sbuf, sbuf + start, sbuf_len - start);
+ sbuf_len -= start;
+ }
+ else
+ sbuf_len = 0;
+ sbuf_offset = window->sview_offset;
+
+ sbuf = new_sbuf;
+ sbuf_size = new_size;
+ }
+
+ /* Read the remainder of the source view into the buffer. */
+ if (sbuf_len < window->sview_len)
+ {
+ len = window->sview_len - sbuf_len;
+ err = svn_full_read (source_fn, source_baton, sbuf + sbuf_len,
+ &len, temp_pool);
+ if (err != SVN_NO_ERROR)
+ break;
+ sbuf_len = window->sview_len;
+ }
+
+ /* Apply the window instructions to the source view to generate
+ the target view. */
+ apply_window (window, sbuf, tbuf);
+
+ /* Write out the output. */
+ len = window->tview_len;
+ err = svn_full_write (target_fn, target_baton, tbuf, &len, temp_pool);
+ if (err != SVN_NO_ERROR)
+ break;
+ }
+
+ apr_destroy_pool (temp_pool);
+ return err;
 }
 
 
Index: libsvn_delta/tests/.cvsignore
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_delta/tests/.cvsignore,v
retrieving revision 1.4
diff -u -r1.4 .cvsignore
--- libsvn_delta/tests/.cvsignore 2000/09/15 06:15:34 1.4
+++ libsvn_delta/tests/.cvsignore 2000/09/18 14:13:58
@@ -5,3 +5,4 @@
 .libs
 deltaparse-test
 vdelta-test
+random-test
Index: libsvn_delta/tests/Makefile.am
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_delta/tests/Makefile.am,v
retrieving revision 1.4
diff -u -r1.4 Makefile.am
--- libsvn_delta/tests/Makefile.am 2000/09/15 06:08:50 1.4
+++ libsvn_delta/tests/Makefile.am 2000/09/18 14:13:59
@@ -1,8 +1,9 @@
 ## Makefile.in is generated from this by automake.
 
-noinst_PROGRAMS = deltaparse-test vdelta-test
+noinst_PROGRAMS = deltaparse-test vdelta-test random-test
 deltaparse_test_SOURCES = deltaparse-test.c
 vdelta_test_SOURCES = vdelta-test.c
+random_test_SOURCES = random-test.c
 
 ## Flags needed when compiling:
 CFLAGS = -pthread
@@ -18,7 +19,15 @@
 vdelta_test_LDADD = ../libsvn_delta.la \
                     ../../libsvn_subr/libsvn_subr.la \
                     ../../libsvn_string/libsvn_string.la \
- ../../../apr/libapr.a
+ ../../../apr/libapr.a \
+ ../../../expat-lite/libexpat.la
+
+random_test_LDADD = ../libsvn_delta.la \
+ ../../libsvn_subr/libsvn_subr.la \
+ ../../libsvn_string/libsvn_string.la \
+ ../../../apr/libapr.a \
+ ../../../expat-lite/libexpat.la
+
 
 ## Make libtool be quiet
 LIBTOOL = @LIBTOOL@ --silent
Index: libsvn_subr/Makefile.am
===================================================================
RCS file: /cvs/subversion/subversion/libsvn_subr/Makefile.am,v
retrieving revision 1.6
diff -u -r1.6 Makefile.am
--- libsvn_subr/Makefile.am 2000/09/11 09:21:51 1.6
+++ libsvn_subr/Makefile.am 2000/09/18 14:13:59
@@ -8,7 +8,7 @@
 lib_LTLIBRARIES = libsvn_subr.la
 
 ## Sources needed to build each library (names canonicalized)
-libsvn_subr_la_SOURCES = svn_error.c path.c hashdump.c xml.c
+libsvn_subr_la_SOURCES = svn_error.c path.c hashdump.c xml.c io.c
 
 ## Build flags ---------
 
Index: libsvn_delta/tests/random-test.c
===================================================================
--- /dev/null Tue May 5 16:32:27 1998
+++ libsvn_delta/tests/random-test.c Mon Sep 18 10:12:30 2000
@@ -0,0 +1,208 @@
+/*
+ * random-test.c: Test delta generation and application using random data.
+ *
+ * ================================================================
+ * Copyright (c) 2000 CollabNet. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement: "This product includes
+ * software developed by CollabNet (http://www.Collab.Net)."
+ * Alternately, this acknowlegement may appear in the software itself, if
+ * and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The hosted project names must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact info@collab.net.
+ *
+ * 5. Products derived from this software may not use the "Tigris" name
+ * nor may "Tigris" appear in their names without prior written
+ * permission of CollabNet.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL COLLABNET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of CollabNet.
+ */
+
+
+#include <stdio.h>
+#include <assert.h>
+#include "apr_general.h"
+#include "apr_getopt.h"
+#include "svn_delta.h"
+
+#define DEFAULT_MAXLEN (100 * 1024)
+
+/* Generate a temporary file containing random data. */
+static FILE *
+generate_random_file (int maxlen)
+{
+ int len, i;
+ FILE *fp;
+
+ fp = tmpfile ();
+ assert (fp != NULL);
+ len = rand () % maxlen;
+ for (i = 0; i < len; i++)
+ putc (rand () % 256, fp);
+ rewind (fp);
+ return fp;
+}
+
+static FILE *
+copy_tempfile (FILE *fp)
+{
+ FILE *newfp;
+ int c;
+
+ newfp = tmpfile ();
+ assert (newfp != NULL);
+ while ((c = getc (fp)) != EOF)
+ putc (c, newfp);
+ rewind (newfp);
+ return newfp;
+}
+
+/* NOTE: Does no error-checking. */
+static svn_error_t *
+read_from_file (void *baton, char *buffer, apr_size_t *len, apr_pool_t *pool)
+{
+ FILE *fp = baton;
+
+ if (!fp || feof (fp) || ferror (fp))
+ *len = 0;
+ else
+ *len = fread (buffer, 1, *len, fp);
+ return SVN_NO_ERROR;
+}
+
+/* NOTE: Does no error-checking. */
+static svn_error_t *
+write_to_file (void *baton, const char *data, apr_size_t *len,
+ apr_pool_t *pool)
+{
+ FILE *fp = baton;
+
+ *len = fwrite (data, 1, *len, fp);
+ return SVN_NO_ERROR;
+}
+
+int
+main (int argc, char **argv)
+{
+ FILE *source, *source_copy, *target, *target_regen;
+ apr_getopt_t *opt;
+ apr_pool_t *pool;
+ apr_status_t status;
+ char optch;
+ const char *optarg;
+ unsigned int seed;
+ int seed_set = 0, maxlen = DEFAULT_MAXLEN, c1, c2;
+ svn_txdelta_stream_t *stream;
+ svn_error_t *err;
+
+ apr_initialize();
+
+ /* Read options. */
+ pool = apr_make_sub_pool (NULL, NULL);
+ apr_initopt (&opt, NULL, argc, argv);
+ while ((status = apr_getopt (opt, "s:l:", &optch, &optarg)) == APR_SUCCESS)
+ {
+ switch (optch)
+ {
+ case 's':
+ seed = atoi (optarg);
+ seed_set = 1;
+ break;
+ case 'l':
+ maxlen = atoi (optarg);
+ break;
+ }
+ }
+ apr_destroy_pool (pool);
+ if (status != APR_EOF)
+ {
+ fprintf (stderr, "Usage: %s [-s seed]\n", argv[0]);
+ return 1;
+ }
+
+ if (!seed_set)
+ {
+ seed = (unsigned int) apr_now () ^ (unsigned int) getpid ();
+ printf ("Using seed %d\n", seed);
+ srand (seed);
+ }
+
+ /* Generate source and target files for the delta and its application. */
+ source = generate_random_file (maxlen);
+ target = generate_random_file (maxlen);
+ source_copy = copy_tempfile (source);
+ rewind (source);
+ target_regen = tmpfile ();
+
+ /* Create and simultaneously apply a delta between the source and target. */
+ pool = apr_make_sub_pool (NULL, NULL);
+ err = svn_txdelta (&stream,
+ read_from_file, source,
+ read_from_file, target,
+ pool);
+ if (err == SVN_NO_ERROR)
+ err = svn_txdelta_apply (stream,
+ read_from_file, source_copy,
+ write_to_file, target_regen);
+ if (err != SVN_NO_ERROR)
+ {
+ svn_handle_error (err, stderr);
+ exit (1);
+ }
+ apr_destroy_pool (pool);
+
+ /* Compare the two files. */
+ rewind (target);
+ rewind (target_regen);
+ while (1)
+ {
+ c1 = getc (target);
+ c2 = getc (target_regen);
+ if (c1 == EOF && c2 == EOF)
+ break;
+ if (c1 != c2)
+ {
+ printf ("Regenerated files differ; test failed.\n");
+ exit (1);
+ }
+ }
+ printf ("Test succeeded.\n");
+ exit (0);
+}
+
+
+
+/*
+ * local variables:
+ * eval: (load-file "../../svn-dev.el")
+ * end:
+ */
Index: libsvn_subr/io.c
===================================================================
--- /dev/null Tue May 5 16:32:27 1998
+++ libsvn_subr/io.c Mon Sep 18 08:47:41 2000
@@ -0,0 +1,110 @@
+/*
+ * io.c: helpful wrappers for generic read and write functions
+ *
+ * ================================================================
+ * Copyright (c) 2000 CollabNet. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement: "This product includes
+ * software developed by CollabNet (http://www.Collab.Net)."
+ * Alternately, this acknowlegement may appear in the software itself, if
+ * and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The hosted project names must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact info@collab.net.
+ *
+ * 5. Products derived from this software may not use the "Tigris" name
+ * nor may "Tigris" appear in their names without prior written
+ * permission of CollabNet.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL COLLABNET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of CollabNet.
+ */
+
+
+
+#include "svn_error.h"
+#include "svn_io.h"
+
+
+
+svn_error_t *
+svn_full_read (svn_read_fn_t read_fn,
+ void *baton,
+ char *buffer,
+ apr_size_t *len,
+ apr_pool_t *pool)
+{
+ apr_size_t pos = 0, count;
+ svn_error_t *err = SVN_NO_ERROR;
+
+ while (pos < *len)
+ {
+ count = *len - pos;
+ err = read_fn (baton, buffer + pos, &count, pool);
+ pos += count;
+ if (err == SVN_NO_ERROR && count == 0)
+ err = svn_create_error (SVN_ERR_INCOMPLETE_DATA, 0,
+ "Input stream ended unexpectedly", NULL, pool);
+ if (err != SVN_NO_ERROR)
+ break;
+ }
+ *len = count;
+ return err;
+}
+
+
+svn_error_t *svn_full_write (svn_write_fn_t write_fn,
+ void *baton,
+ const char *data,
+ apr_size_t *len,
+ apr_pool_t *pool)
+{
+ apr_size_t pos = 0, count;
+ svn_error_t *err = SVN_NO_ERROR;
+
+ while (pos < *len)
+ {
+ count = *len - pos;
+ err = write_fn (baton, data + pos, &count, pool);
+ pos += count;
+ if (err != SVN_NO_ERROR)
+ break;
+ }
+ *len = count;
+ return err;
+}
+
+
+
+/*
+ * local variables:
+ * eval: (load-file "../svn-dev.el")
+ * end:
+ */
Received on Sat Oct 21 14:36:08 2006

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.