All pastes #62828 Raw Edit

svn_rangelist_diff

public text v1 · immutable
#62828 ·published 2006-06-07 03:14 UTC
rendered paste body
Index: subversion/include/svn_mergeinfo.h
===================================================================
--- subversion/include/svn_mergeinfo.h	(revision 19977)
+++ subversion/include/svn_mergeinfo.h	(working copy)
@@ -72,6 +72,18 @@
 svn_mergeinfo_remove(apr_hash_t **output, apr_hash_t *eraser,
                      apr_hash_t *whiteboard, apr_pool_t *pool);
 
+/** Calculate the delta between two rangelists consisting of @c
+ * svn_merge_range_t * elements, @a from and @a to, and place the
+ * result in @a deleted and @a added (neither output argument will
+ * ever be @c NULL).
+ *
+ * @since New in 1.5.
+ */
+svn_error_t *
+svn_rangelist_diff(apr_array_header_t **deleted, apr_array_header_t **added,
+                   apr_array_header_t *from, apr_array_header_t *to,
+                   apr_pool_t *pool);
+
 /** Merge two rangelists consisting of @c svn_merge_range_t *
  * elements, @a in1 and @a in2, and place the result in @a output.
  *
Index: subversion/libsvn_subr/mergeinfo.c
===================================================================
--- subversion/libsvn_subr/mergeinfo.c	(revision 19977)
+++ subversion/libsvn_subr/mergeinfo.c	(working copy)
@@ -375,6 +375,190 @@
 }
 
 
+/* The state necessary for traversing a range list. */
+struct rangelist_iterator
+{
+  /* The list of svn_merge_range_t *'s being traversed. */
+  apr_array_header_t *rangelist;
+
+  /* How far we traveled along RANGELIST. */
+  int index;
+};
+
+/* Return the next range in the list (and increment ITER->INDEX), or
+   return NULL if none remain. */
+static svn_merge_range_t *
+next_range(struct rangelist_iterator *iter)
+{
+    if (iter->index < iter->rangelist->nelts)
+      return APR_ARRAY_IDX(iter->rangelist, iter->index++,
+                           svn_merge_range_t *);
+    else
+      return NULL;
+}
+
+/* The state necessary for traversing FROM and TO range lists in
+   search of differences. */
+struct rangelist_delta_explorer
+{
+  /* The lists of svn_merge_range_t *'s being compared for deltas. */
+  struct rangelist_iterator *from_iter;
+  struct rangelist_iterator *to_iter;
+
+  /* Any differences we've detected between FROM_ITER and TO_ITER. */
+  apr_array_header_t *deletions;
+  apr_array_header_t *additions;
+};
+
+static struct rangelist_delta_explorer *
+rangelist_delta_explorer_create(apr_array_header_t *from,
+                                apr_array_header_t *to, apr_pool_t *pool)
+{
+  struct rangelist_delta_explorer *delta_explorer =
+    apr_pcalloc(pool, sizeof(*delta_explorer));
+
+  delta_explorer->from_iter = 
+    apr_pcalloc(pool, sizeof(*delta_explorer->from_iter));
+  delta_explorer->from_iter->rangelist = from;
+  delta_explorer->to_iter =
+    apr_pcalloc(pool, sizeof(*delta_explorer->to_iter));
+  delta_explorer->to_iter->rangelist = to;
+
+  delta_explorer->deletions =
+    apr_array_make(pool, 0, sizeof(*delta_explorer->deletions));
+  delta_explorer->additions =
+    apr_array_make(pool, 0, sizeof(*delta_explorer->additions));
+
+  return delta_explorer;
+}
+
+/* Return whether FROM and TO overlap at all. */
+static svn_boolean_t
+ranges_overlap_p(svn_merge_range_t *from, svn_merge_range_t *to)
+{
+    /* from range: 1-4, to range: 3-3 */
+    return (from != NULL && to != NULL) &&
+        ((to->start <= from->start && from->start <= to->end) ||
+         (to->start <= from->end && from->end <= to->end) ||
+         (from->start <= to->start && to->end <= from->end));
+}
+
+/* Walk all ranges in DELTA_EXPLORER, recording any deltas
+   encountered. */
+static void
+walk_rangelists(struct rangelist_delta_explorer *delta_explorer,
+                svn_merge_range_t *from, svn_merge_range_t *to,
+                apr_pool_t *pool)
+{
+  /* Walk forward along each rangelist as necessary. */
+  if (from == NULL)
+    from = next_range(delta_explorer->from_iter);
+  if (to == NULL)
+    to = next_range(delta_explorer->to_iter);
+
+  if (from == NULL && to == NULL)
+    /* No ranges remain, we're done. */
+    return;
+  if (from != NULL && to != NULL)
+    printf("'from' range: %d-%d, 'to' range: %d-%d\n",
+           from->start, from->end, to->start, to->end);
+
+  printf("checking whether ranges overlap\n");
+  if (ranges_overlap_p(from, to))
+    {
+      printf("ranges do overlap\n");
+      if (from->start != to->start && from->end != to->end)
+        {
+          /* When the overlapping ranges aren't an exact match,
+             discover and record the delta. */
+          svn_merge_range_t *range = apr_pcalloc(pool, sizeof(*range));
+          printf("no exact match\n");
+
+          /* Handle any delta between left edges of the ranges. */
+          if (from->start < to->start)
+            {
+              /* Lost revs from the left edge of the FROM range. */
+              range->start = from->start;
+              range->end = to->start - 1;
+              APR_ARRAY_PUSH(delta_explorer->deletions,
+                             svn_merge_range_t *) = range;
+              printf("recorded range del: %d-%d\n", range->start, range->end);
+              range = NULL;
+            }
+          else  /* from->start > to->start */
+            {
+              /* Gained revs from the left edge of the TO range. */
+              range->start = to->start;
+              range->end = from->start - 1;
+              APR_ARRAY_PUSH(delta_explorer->additions,
+                             svn_merge_range_t *) = range;
+              printf("recorded range add: %d-%d\n", range->start, range->end);
+              range = NULL;
+            }
+
+          /* Handle any delta between right edges of the ranges. */
+          if (from->end > to->end)
+            {
+              /* Lost revs from the right edge of the FROM range. */
+              if (range == NULL)
+                range = apr_pcalloc(pool, sizeof(*range));
+              range->start = to->end + 1;
+              range->end = from->end;
+              APR_ARRAY_PUSH(delta_explorer->deletions,
+                             svn_merge_range_t *) = range;
+              printf("recorded range del: %d-%d\n", range->start, range->end);
+            }
+          else  /* from->end < to->end */
+            {
+              /* Gained revs from the right edge of the TO range. */
+              if (range == NULL)
+                range = apr_pcalloc(pool, sizeof(*range));
+              range->start = from->end + 1;
+              range->end = to->end;
+              APR_ARRAY_PUSH(delta_explorer->additions,
+                             svn_merge_range_t *) = range;
+              printf("recorded range add: %d-%d\n", range->start, range->end);
+            }
+        }
+      /* else no delta to record */
+
+      /* We'll want new FROM and TO ranges for our next iteration. */
+      from = to = NULL;
+    }
+  else
+    {
+      printf("ranges don't overlap\n");
+      /* A FROM and TO range with no overlap between them indicates a
+         complete removal or addition (respectively) of the first
+         range encountered. */
+      if (from != NULL && (to == NULL || from->end < to->start))
+        {
+          /* FROM range encountered first (a delete). */
+          APR_ARRAY_PUSH(delta_explorer->deletions,
+                         svn_merge_range_t *) = from;
+          printf("recorded range del: %d-%d\n", from->start, from->end);
+
+          /* We'll re-use our TO range, and see if it overlaps with
+             the next FROM range. */
+          from = NULL;
+        }
+      else
+        {
+          /* TO range encountered first (an add). */
+          APR_ARRAY_PUSH(delta_explorer->additions, svn_merge_range_t *) = to;
+          printf("recorded range add: %d-%d\n", to->start, to->end);
+
+          /* We'll re-use our FROM range, and see if it overlaps with
+             the next TO range. */
+          to = NULL;
+        }
+      printf("done dealing with non-overlapping ranges\n");
+    }
+
+  /* ### FIXME: Avoid recursion, which could be very deep. */
+  walk_rangelists(delta_explorer, from, to, pool);
+}
+
 /* Output deltas via *DELETED and *ADDED, which will never be @c NULL.
 
    The following diagrams illustrate some common range delta scenarios:
@@ -384,7 +568,7 @@
     [to]                                    added
 
     (from)           deleted                deleted
-    0 <===========(=========[============]=========)===========> rHEAD
+    r0 <===========(=========[============]=========)===========> rHEAD
     [to]
 
     (from)           deleted
@@ -398,22 +582,31 @@
     (from)
     r0 <===========[=========(============)=========]===========> rHEAD
     [to]             added                  added
+
+    (from)  d                                  d             d
+    r0 <===(=[=)=]=[==]=[=(=)=]=[=]=[=(===|===(=)==|=|==[=(=]=)=> rHEAD
+    [to]        a   a    a   a   a   a                   a
 */
-static svn_error_t *
+svn_error_t *
 svn_rangelist_diff(apr_array_header_t **deleted, apr_array_header_t **added,
                    apr_array_header_t *from, apr_array_header_t *to,
                    apr_pool_t *pool)
 {
-  int i;
+  /* ### Avoid having memory for delta_explorer stick around
+     ### (e.g. thru use of sub-pool)? */
+  struct rangelist_delta_explorer *delta_explorer =
+    rangelist_delta_explorer_create(from, to, pool);
 
-  *deleted = apr_array_make(pool, 0, sizeof(svn_merge_range_t *));
-  *added = apr_array_make(pool, 0, sizeof(svn_merge_range_t *));
+  walk_rangelists(delta_explorer, NULL, NULL, pool);
 
-  /* ### TODO */
-
+  *deleted = delta_explorer->deletions;
+  *added = delta_explorer->additions;
   return SVN_NO_ERROR;
 }
 
+/* Record deletions and additions of entire range lists (by path
+   presence), and delegate to svn_rangelist_diff() for delta
+   calculations on a specific path. */
 /* ### TODO: Merge implementation with
    ### libsvn_subr/sorts.c:svn_prop_diffs().  Factor out a generic
    ### hash diffing function for addition to APR's apr_hash.h API. */