You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2011/02/20 08:45:54 UTC

svn commit: r1072522 [2/9] - in /subversion/branches/performance: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ notes/ notes/wc-ng/ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/org/apache/subversion/javahl/...

Modified: subversion/branches/performance/subversion/include/svn_config.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/include/svn_config.h?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/include/svn_config.h (original)
+++ subversion/branches/performance/subversion/include/svn_config.h Sun Feb 20 07:45:48 2011
@@ -189,10 +189,14 @@ svn_config_get_config(apr_hash_t **cfg_h
 /** Set @a *cfgp to an empty @c svn_config_t structure,
  * allocated in @a result_pool.
  *
+ * Pass TRUE to @a section_names_case_sensitive if 
+ * section names are to be populated case sensitively.
+ *
  * @since New in 1.7.
  */
 svn_error_t *
 svn_config_create(svn_config_t **cfgp,
+                  svn_boolean_t section_names_case_sensitive,
                   apr_pool_t *result_pool);
 
 /** Read configuration data from @a file (a file or registry path) into
@@ -200,7 +204,26 @@ svn_config_create(svn_config_t **cfgp,
  *
  * If @a file does not exist, then if @a must_exist, return an error,
  * otherwise return an empty @c svn_config_t.
+ * 
+ * If @a section_names_case_sensitive is TRUE, populate section name hashes 
+ * case sensitively, except for the default SVN_CONFIG__DEFAULT_SECTION.
+ * 
+ * @since New in 1.7.
  */
+
+svn_error_t *
+svn_config_read2(svn_config_t **cfgp,
+                 const char *file,
+                 svn_boolean_t must_exist,
+                 svn_boolean_t section_names_case_sensitive,
+                 apr_pool_t *pool);
+
+/** Similar to svn_config_read2, but always passes FALSE to 
+ * section_names_case_sensitive.
+ *
+ * @deprecated Provided for backward compatibility with 1.6 API.
+ */
+SVN_DEPRECATED
 svn_error_t *
 svn_config_read(svn_config_t **cfgp,
                 const char *file,

Modified: subversion/branches/performance/subversion/include/svn_delta.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/include/svn_delta.h?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/include/svn_delta.h (original)
+++ subversion/branches/performance/subversion/include/svn_delta.h Sun Feb 20 07:45:48 2011
@@ -49,11 +49,20 @@ extern "C" {
 
 
 
+/** This compression level effectively disables data compression.
+ * However, the data pre-processing costs may still not be zero.
+ */
+#define SVN_NO_COMPRESSION_LEVEL 0
+
+/** This is the maximum compression level we can pass to zlib.
+ */
+#define SVN_BEST_COMPRESSION_LEVEL 9
+
 /** This is the default compression level we pass to zlib.  It
- * should be between 0 and 9, with higher numbers being greater
- * compression.  
+ * should be between 0 and 9, with higher numbers resulting in
+ * better compression rates but slower operation.
  */
-#define SVNDIFF1_COMPRESS_LEVEL 5
+#define SVN_DEFAULT_COMPRESSION_LEVEL 5
 
 /**
  * Get libsvn_delta version information.
@@ -487,12 +496,12 @@ svn_txdelta_to_svndiff3(svn_txdelta_wind
                         int compression_level,
                         apr_pool_t *pool);
 
- /** Similar to svn_txdelta_to_svndiff3, but always using the SVN default
-  * compression level (@ref SVNDIFF1_COMPRESS_LEVEL).
-  *
-  * @since New in 1.4.
-  * @deprecated Provided for backward compatibility with the 1.6 API.
-  */
+/** Similar to svn_txdelta_to_svndiff3, but always using the SVN default
+ * compression level (@ref SVN_DEFAULT_COMPRESSION_LEVEL).
+ *
+ * @since New in 1.4.
+ * @deprecated Provided for backward compatibility with the 1.6 API.
+ */
 SVN_DEPRECATED
 void
 svn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler,

Modified: subversion/branches/performance/subversion/include/svn_diff.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/include/svn_diff.h?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/include/svn_diff.h (original)
+++ subversion/branches/performance/subversion/include/svn_diff.h Sun Feb 20 07:45:48 2011
@@ -106,6 +106,51 @@ typedef enum svn_diff_datasource_e
 
 
 /** A vtable for reading data from the three datasources. */
+typedef struct svn_diff_fns2_t
+{
+  /** Open the datasources of type @a datasources. */
+  svn_error_t *(*datasources_open)(void *diff_baton,
+                                   apr_off_t *prefix_lines,
+                                   apr_off_t *suffix_lines,
+                                   const svn_diff_datasource_e *datasources,
+                                   apr_size_t datasources_len);
+
+  /** Close the datasource of type @a datasource. */
+  svn_error_t *(*datasource_close)(void *diff_baton,
+                                   svn_diff_datasource_e datasource);
+
+  /** Get the next "token" from the datasource of type @a datasource.
+   *  Return a "token" in @a *token.   Return a hash of "token" in @a *hash.
+   *  Leave @a token and @a hash untouched when the datasource is exhausted.
+   */
+  svn_error_t *(*datasource_get_next_token)(apr_uint32_t *hash, void **token,
+                                            void *diff_baton,
+                                            svn_diff_datasource_e datasource);
+
+  /** A function for ordering the tokens, resembling 'strcmp' in functionality.
+   * @a compare should contain the return value of the comparison:
+   * If @a ltoken and @a rtoken are "equal", return 0.  If @a ltoken is
+   * "less than" @a rtoken, return a number < 0.  If @a ltoken  is
+   * "greater than" @a rtoken, return a number > 0.
+   */
+  svn_error_t *(*token_compare)(void *diff_baton,
+                                void *ltoken,
+                                void *rtoken,
+                                int *compare);
+
+  /** Free @a token from memory, the diff algorithm is done with it. */
+  void (*token_discard)(void *diff_baton,
+                        void *token);
+
+  /** Free *all* tokens from memory, they're no longer needed. */
+  void (*token_discard_all)(void *diff_baton);
+} svn_diff_fns2_t;
+
+
+/** A vtable for reading data from the three datasources.
+ *
+ * @deprecated Provided for backward compatibility with the 1.6 API.
+ */
 typedef struct svn_diff_fns_t
 {
   /** Open the datasource of type @a datasource. */
@@ -149,8 +194,23 @@ typedef struct svn_diff_fns_t
 /** Given a vtable of @a diff_fns/@a diff_baton for reading datasources,
  * return a diff object in @a *diff that represents a difference between
  * an "original" and "modified" datasource.  Do all allocation in @a pool.
+ *
+ * @since New in 1.7.
  */
 svn_error_t *
+svn_diff_diff_2(svn_diff_t **diff,
+                void *diff_baton,
+                const svn_diff_fns2_t *diff_fns,
+                apr_pool_t *pool);
+
+/** Given a vtable of @a diff_fns/@a diff_baton for reading datasources,
+ * return a diff object in @a *diff that represents a difference between
+ * an "original" and "modified" datasource.  Do all allocation in @a pool.
+ *
+ * @deprecated Provided for backward compatibility with the 1.6 API.
+ */
+SVN_DEPRECATED
+svn_error_t *
 svn_diff_diff(svn_diff_t **diff,
               void *diff_baton,
               const svn_diff_fns_t *diff_fns,
@@ -160,7 +220,23 @@ svn_diff_diff(svn_diff_t **diff,
  * return a diff object in @a *diff that represents a difference between
  * three datasources: "original", "modified", and "latest".  Do all
  * allocation in @a pool.
+ *
+ * @since New in 1.7.
+ */
+svn_error_t *
+svn_diff_diff3_2(svn_diff_t **diff,
+                 void *diff_baton,
+                 const svn_diff_fns2_t *diff_fns,
+                 apr_pool_t *pool);
+
+/** Given a vtable of @a diff_fns/@a diff_baton for reading datasources,
+ * return a diff object in @a *diff that represents a difference between
+ * three datasources: "original", "modified", and "latest".  Do all
+ * allocation in @a pool.
+ *
+ * @deprecated Provided for backward compatibility with the 1.6 API.
  */
+SVN_DEPRECATED
 svn_error_t *
 svn_diff_diff3(svn_diff_t **diff,
                void *diff_baton,
@@ -172,8 +248,25 @@ svn_diff_diff3(svn_diff_t **diff,
  * two datasources: "original" and "latest", adjusted to become a full
  * difference between "original", "modified" and "latest" using "ancestor".
  * Do all allocation in @a pool.
+ *
+ * @since New in 1.7.
  */
 svn_error_t *
+svn_diff_diff4_2(svn_diff_t **diff,
+                 void *diff_baton,
+                 const svn_diff_fns2_t *diff_fns,
+                 apr_pool_t *pool);
+
+/** Given a vtable of @a diff_fns/@a diff_baton for reading datasources,
+ * return a diff object in @a *diff that represents a difference between
+ * two datasources: "original" and "latest", adjusted to become a full
+ * difference between "original", "modified" and "latest" using "ancestor".
+ * Do all allocation in @a pool.
+ *
+ * @deprecated Provided for backward compatibility with the 1.6 API.
+ */
+SVN_DEPRECATED
+svn_error_t *
 svn_diff_diff4(svn_diff_t **diff,
                void *diff_baton,
                const svn_diff_fns_t *diff_fns,
@@ -837,8 +930,8 @@ typedef struct svn_diff_hunk_t svn_diff_
  * text will be returned in reversed form.
  * The line-terminator is detected automatically and stored in @a *eol
  * if @a eol is not NULL.
- * If EOF is reached and the stream does not end with a newline character,
- * and @a eol is not NULL, @a *eol is set to NULL.
+ * If EOF is reached, set @a *eof to TRUE, and set @a *eol to NULL if the
+ * hunk does not end with a newline character and @a eol is not NULL.
  * Temporary allocations will be performed in @a scratch_pool.
  *
  * @since New in 1.7.
@@ -856,8 +949,8 @@ svn_diff_hunk_readline_diff_text(svn_dif
  * of the original text of @a hunk.
  * The line-terminator is detected automatically and stored in @a *eol
  * if @a eol is not NULL.
- * If EOF is reached and the stream does not end with a newline character,
- * and @a eol is not NULL, @a *eol is set to NULL.
+ * If EOF is reached, set @a *eof to TRUE, and set @a *eol to NULL if the
+ * hunk text does not end with a newline character and @a eol is not NULL.
  * Temporary allocations will be performed in @a scratch_pool.
  *
  * @see svn_diff_hunk_t

Modified: subversion/branches/performance/subversion/include/svn_fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/include/svn_fs.h?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/include/svn_fs.h (original)
+++ subversion/branches/performance/subversion/include/svn_fs.h Sun Feb 20 07:45:48 2011
@@ -1397,8 +1397,9 @@ svn_fs_props_changed(svn_boolean_t *chan
 /** Discover a node's copy ancestry, if any.
  *
  * If the node at @a path in @a root was copied from some other node, set
- * @a *rev_p and @a *path_p to the revision and path of the other node,
- * allocating @a *path_p in @a pool.
+ * @a *rev_p and @a *path_p to the revision and path (expressed as an
+ * absolute filesystem path) of the other node, allocating @a *path_p
+ * in @a pool.
  *
  * Else if there is no copy ancestry for the node, set @a *rev_p to
  * #SVN_INVALID_REVNUM and @a *path_p to NULL.

Modified: subversion/branches/performance/subversion/include/svn_io.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/include/svn_io.h?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/include/svn_io.h (original)
+++ subversion/branches/performance/subversion/include/svn_io.h Sun Feb 20 07:45:48 2011
@@ -763,7 +763,9 @@ typedef svn_error_t *(*svn_write_fn_t)(v
 /** Close handler function for a generic stream.  @see svn_stream_t. */
 typedef svn_error_t *(*svn_close_fn_t)(void *baton);
 
-/** An opaque type which represents a mark on a stream.
+/** An opaque type which represents a mark on a stream.  There is no
+ * concrete definition of this type, it is a named type for stream
+ * implementation specific baton pointers.
  *
  * @see svn_stream_mark().
  * @since New in 1.7.
@@ -785,7 +787,7 @@ typedef svn_error_t *(*svn_io_mark_fn_t)
  * @since New in 1.7.
  */
 typedef svn_error_t *(*svn_io_seek_fn_t)(void *baton,
-                                         svn_stream_mark_t *mark);
+                                         const svn_stream_mark_t *mark);
 
 /** Buffer test handler function for a generic stream. @see svn_stream_t 
  * and svn_stream_buffered().
@@ -959,6 +961,16 @@ svn_stream_t *
 svn_stream_from_aprfile(apr_file_t *file,
                         apr_pool_t *pool);
 
+/** Set @a *err to a generic stream connected to stderr, allocated in
+ * @a pool.  The stream and its underlying APR handle will be closed
+ * when @a pool is cleared or destroyed.
+ *
+ * @since New in 1.7.
+ */
+svn_error_t *
+svn_stream_for_stderr(svn_stream_t **err,
+                      apr_pool_t *pool);
+
 /** Set @a *out to a generic stream connected to stdout, allocated in
  * @a pool.  The stream and its underlying APR handle will be closed
  * when @a pool is cleared or destroyed.
@@ -1045,7 +1057,17 @@ svn_stream_read(svn_stream_t *stream,
                 char *buffer,
                 apr_size_t *len);
 
-/** Skip data from a generic stream. @see svn_stream_t. */
+/**
+ * Skip COUNT bytes from a generic STREAM. If the stream is exhausted
+ * before COUNT bytes have been read, an error will be returned and
+ * COUNT will be changed to the actual number of bytes skipped.
+ *
+ * NOTE. No assumption can be made on the semantics of this function
+ * other than that the stream read pointer will be advanced by *count
+ * bytes. Depending on the capabilities of the underlying stream
+ * implementation, this may for instance be translated into a sequence
+ * of reads or a simple seek operation.
+ */
 svn_error_t *
 svn_stream_skip(svn_stream_t *stream,
                 apr_size_t *count);
@@ -1102,7 +1124,7 @@ svn_stream_mark(svn_stream_t *stream,
  * @since New in 1.7.
  */
 svn_error_t *
-svn_stream_seek(svn_stream_t *stream, svn_stream_mark_t *mark);
+svn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark);
 
 /** Return whether this generic @a stream uses internal buffering.
  * This may be used to work around subtle differences between buffered
@@ -1843,11 +1865,11 @@ svn_io_file_read(apr_file_t *file,
  */
 svn_error_t *
 svn_io_file_read_full2(apr_file_t *file,
-                        void *buf,
-                        apr_size_t nbytes,
-                        apr_size_t *bytes_read,
-                        svn_boolean_t *hit_eof,
-                        apr_pool_t *pool);
+                       void *buf,
+                       apr_size_t nbytes,
+                       apr_size_t *bytes_read,
+                       svn_boolean_t *hit_eof,
+                       apr_pool_t *pool);
 
 
 /** Similar to svn_io_file_read_full2 with hit_eof being set

Modified: subversion/branches/performance/subversion/include/svn_ra_svn.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/include/svn_ra_svn.h?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/include/svn_ra_svn.h (original)
+++ subversion/branches/performance/subversion/include/svn_ra_svn.h Sun Feb 20 07:45:48 2011
@@ -173,8 +173,9 @@ svn_ra_svn_create_conn2(apr_socket_t *so
                         int compression_level,
                         apr_pool_t *pool);
 
-/** Similar to @ref svn_ra_svn_create_conn2() but uses default 
- * compression level (#SVNDIFF1_COMPRESS_LEVEL) for network transmissions.
+/** Similar to @ref svn_ra_svn_create_conn2() but uses default
+ * compression level (@ref SVN_DEFAULT_COMPRESSION_LEVEL) for network
+ * transmissions.
  * 
  * @deprecated Provided for backward compatibility with the 1.6 API.
  */

Modified: subversion/branches/performance/subversion/include/svn_string.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/include/svn_string.h?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/include/svn_string.h (original)
+++ subversion/branches/performance/subversion/include/svn_string.h Sun Feb 20 07:45:48 2011
@@ -372,6 +372,13 @@ svn_cstring_split_append(apr_array_heade
 svn_boolean_t
 svn_cstring_match_glob_list(const char *str, const apr_array_header_t *list);
 
+/** Return @c TRUE iff @a str exactly matches any of the elements of @a list.
+ *
+ * @since new in 1.7
+ */
+svn_boolean_t
+svn_cstring_match_list(const char *str, const apr_array_header_t *list);
+
 /**
  * Return the number of line breaks in @a msg, allowing any kind of newline
  * termination (CR, LF, CRLF, or LFCR), even inconsistent.

Modified: subversion/branches/performance/subversion/include/svn_types.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/include/svn_types.h?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/include/svn_types.h (original)
+++ subversion/branches/performance/subversion/include/svn_types.h Sun Feb 20 07:45:48 2011
@@ -839,6 +839,9 @@ typedef struct svn_log_entry_t
    * value as changed_paths for compatibility with users assuming an older
    * version.
    *
+   * @note See http://svn.haxx.se/dev/archive-2010-08/0362.shtml for
+   * further explanation.
+   *
    * @since New in 1.6.
    */
   apr_hash_t *changed_paths2;

Modified: subversion/branches/performance/subversion/include/svn_wc.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/include/svn_wc.h?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/include/svn_wc.h (original)
+++ subversion/branches/performance/subversion/include/svn_wc.h Sun Feb 20 07:45:48 2011
@@ -200,7 +200,7 @@ typedef struct svn_wc_context_t svn_wc_c
  */
 svn_error_t *
 svn_wc_context_create(svn_wc_context_t **wc_ctx,
-                      svn_config_t *config,
+                      const svn_config_t *config,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool);
 
@@ -2569,9 +2569,9 @@ svn_wc_text_modified_p(svn_boolean_t *mo
  */
 svn_error_t *
 svn_wc_props_modified_p2(svn_boolean_t *modified_p,
-                         svn_wc_context_t* wc_ctx,
+                         svn_wc_context_t *wc_ctx,
                          const char *local_abspath,
-                          apr_pool_t *scratch_pool);
+                         apr_pool_t *scratch_pool);
 
 /** Similar to svn_wc_props_modified_p2(), but with a relative path and
  * adm_access baton.

Modified: subversion/branches/performance/subversion/libsvn_client/commit_util.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/commit_util.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/commit_util.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/commit_util.c Sun Feb 20 07:45:48 2011
@@ -242,10 +242,7 @@ add_lock_token(const char *local_abspath
  * information in its parent dir is the only indication that the node
  * is under version control. This function is necessary for this
  * particular case. In all other cases, this simply bails out a little
- * bit earlier.
- *
- * Note: Tree-conflicts info can only be found in "THIS_DIR" entries,
- * where each THIS_DIR will only hold tree-conflicts on its children. */
+ * bit earlier. */
 static svn_error_t *
 bail_on_tree_conflicted_children(svn_wc_context_t *wc_ctx,
                                  const char *local_abspath,

Modified: subversion/branches/performance/subversion/libsvn_client/export.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/export.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/export.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/export.c Sun Feb 20 07:45:48 2011
@@ -900,9 +900,10 @@ close_file(void *file_baton,
   actual_checksum = svn_checksum__from_digest(fb->text_digest,
                                               svn_checksum_md5, pool);
 
-  SVN_ERR(svn_checksum_mismatch_err(text_checksum, actual_checksum, pool,
-                                    _("Checksum mismatch for '%s'"),
-                                    svn_dirent_local_style(fb->path, pool)));
+  if (!svn_checksum_match(text_checksum, actual_checksum))
+    return svn_checksum_mismatch_err(text_checksum, actual_checksum, pool,
+                                     _("Checksum mismatch for '%s'"),
+                                     svn_dirent_local_style(fb->path, pool));
 
   if ((! fb->eol_style_val) && (! fb->keywords_val) && (! fb->special))
     {

Modified: subversion/branches/performance/subversion/libsvn_client/externals.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/externals.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/externals.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/externals.c Sun Feb 20 07:45:48 2011
@@ -36,6 +36,7 @@
 #include "svn_error.h"
 #include "svn_dirent_uri.h"
 #include "svn_path.h"
+#include "svn_props.h"
 #include "svn_config.h"
 #include "client.h"
 
@@ -593,7 +594,7 @@ resolve_relative_external_url(svn_wc_ext
                         apr_pstrndup(pool, url, num_leading_slashes),
                         svn_relpath_canonicalize(url + num_leading_slashes,
                                                  pool),
-                        NULL);
+                        (char*)NULL);
     }
   else
     {
@@ -696,7 +697,7 @@ resolve_relative_external_url(svn_wc_ext
 
       SVN_ERR(uri_scheme(&scheme, repos_root_url, pool));
       item->url = svn_uri_canonicalize(apr_pstrcat(pool, scheme, ":",
-                                                   url, NULL), pool);
+                                                   url, (char *)NULL), pool);
       return SVN_NO_ERROR;
     }
 
@@ -1424,33 +1425,36 @@ svn_client__external_info_gatherer(void 
 }
 
 
-/* Callback of type svn_wc_external_update_t.  Just squirrels away an
+/* An implementation of svn_wc__proplist_receiver_t. Just squirrels away an
    svn:externals property value into BATON (which is an apr_hash_t *
    keyed on local absolute path).  */
 static svn_error_t *
-externals_update_func(void *baton,
-                      const char *local_abspath,
-                      const svn_string_t *old_val,
-                      const svn_string_t *new_val,
-                      svn_depth_t depth,
-                      apr_pool_t *scratch_pool)
+externals_crawl_proplist_receiver(void *baton,
+                                  const char *local_abspath,
+                                  apr_hash_t *props,
+                                  apr_pool_t *scratch_pool)
 {
-  apr_hash_t *externals_hash = baton;
-  apr_pool_t *hash_pool = apr_hash_pool_get(externals_hash);
+  apr_hash_index_t *hi;
+  apr_hash_t *externals_hash = (apr_hash_t*)baton;
 
-  apr_hash_set(externals_hash, apr_pstrdup(hash_pool, local_abspath),
-               APR_HASH_KEY_STRING, svn_string_dup(new_val, hash_pool));
-  return SVN_NO_ERROR;
-}
+  for (hi = apr_hash_first(scratch_pool, props);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *propname;
 
+      propname = svn__apr_hash_index_key(hi);
+      if (strcmp(propname, SVN_PROP_EXTERNALS) == 0)
+        {
+          apr_pool_t *hash_pool = apr_hash_pool_get(externals_hash);
+          svn_string_t *propval = svn__apr_hash_index_val(hi);
+
+          apr_hash_set(externals_hash, apr_pstrdup(hash_pool, local_abspath),
+                       APR_HASH_KEY_STRING, svn_string_dup(propval, hash_pool));
+          break;
+        }
+    }
 
-/* Callback of type svn_wc_status_func4_t.  Does nothing. */
-static svn_error_t *
-status_noop_func(void *baton,
-                 const char *local_abspath,
-                 const svn_wc_status3_t *status,
-                 apr_pool_t *scratch_pool)
-{
   return SVN_NO_ERROR;
 }
 
@@ -1465,12 +1469,12 @@ svn_client__crawl_for_externals(apr_hash
 {
   apr_hash_t *externals_hash = apr_hash_make(result_pool);
 
-  /* Do a status run just to harvest externals definitions. */
-  SVN_ERR(svn_wc_walk_status(ctx->wc_ctx, local_abspath, depth,
-                             FALSE, FALSE, NULL, status_noop_func, NULL,
-                             externals_update_func, externals_hash,
-                             ctx->cancel_func, ctx->cancel_baton,
-                             scratch_pool));
+  SVN_ERR(svn_wc__prop_list_recursive(ctx->wc_ctx, local_abspath, depth,
+                                      externals_crawl_proplist_receiver,
+                                      externals_hash,
+                                      ctx->cancel_func,
+                                      ctx->cancel_baton,
+                                      scratch_pool));
 
   *externals_p = externals_hash;
   return SVN_NO_ERROR;

Modified: subversion/branches/performance/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/patch.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/patch.c Sun Feb 20 07:45:48 2011
@@ -2744,23 +2744,32 @@ svn_client_patch(const char *patch_abspa
 
   if (svn_path_is_url(local_abspath))
     return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
-                             _("'%s' is not a local path"), local_abspath);
+                             _("'%s' is not a local path"),
+                             svn_dirent_local_style(local_abspath,
+                                                    scratch_pool));
 
   SVN_ERR(svn_io_check_path(patch_abspath, &kind, scratch_pool));
   if (kind == svn_node_none)
     return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
-                             _("'%s' does not exist"), patch_abspath);
+                             _("'%s' does not exist"),
+                             svn_dirent_dirname(patch_abspath, scratch_pool));
   if (kind != svn_node_file)
     return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
-                             _("'%s' is not a file"), patch_abspath);
+                             _("'%s' is not a file"),
+                             svn_dirent_local_style(patch_abspath,
+                                                    scratch_pool));
 
   SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
   if (kind == svn_node_none)
     return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
-                             _("'%s' does not exist"), local_abspath);
+                             _("'%s' does not exist"),
+                             svn_dirent_local_style(local_abspath,
+                                                    scratch_pool));
   if (kind != svn_node_dir)
     return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
-                             _("'%s' is not a directory"), local_abspath);
+                             _("'%s' is not a directory"),
+                             svn_dirent_local_style(local_abspath,
+                                                    scratch_pool));
 
   baton.patch_abspath = patch_abspath;
   baton.abs_wc_path = local_abspath;

Modified: subversion/branches/performance/subversion/libsvn_client/relocate.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/relocate.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/relocate.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/relocate.c Sun Feb 20 07:45:48 2011
@@ -157,6 +157,7 @@ relocate_externals(const char *local_abs
         APR_ARRAY_IDX(ext_desc, i, svn_wc_external_item2_t *);
       const char *target_repos_root_url;
       const char *target_abspath;
+      svn_error_t *err;
 
       svn_pool_clear(iterpool);
 
@@ -181,8 +182,19 @@ relocate_externals(const char *local_abs
                                                       ext_item->target_dir,
                                                       iterpool),
                                       iterpool));
-      SVN_ERR(svn_client_root_url_from_path(&target_repos_root_url,
-                                            target_abspath, ctx, iterpool));
+      err = svn_client_root_url_from_path(&target_repos_root_url,
+                                          target_abspath, ctx, iterpool);
+
+      /* Ignore externals that aren't present in the working copy.
+       * This can happen if an external is deleted from disk accidentally,
+       * or if an external is configured on a locally added directory. */
+      if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+        {
+          svn_error_clear(err);
+          continue;
+        }
+      else
+        SVN_ERR(err);
 
       if (strcmp(target_repos_root_url, old_parent_repos_root_url) == 0)
         SVN_ERR(svn_client_relocate2(target_abspath,

Modified: subversion/branches/performance/subversion/libsvn_client/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_client/update.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_client/update.c (original)
+++ subversion/branches/performance/subversion/libsvn_client/update.c Sun Feb 20 07:45:48 2011
@@ -44,6 +44,58 @@
 
 /*** Code. ***/
 
+/* Helper function to fix issue #2267,
+ * "support svn:externals on locally added directories".
+ *
+ * Crawl all externals beneath ANCHOR_ABSPATH (this is cheap because we're
+ * only crawling the WC DB itself). If there are externals within the
+ * REQUESTED_DEPTH that weren't already picked up while we were crawling
+ * the BASE tree, add them to the EXTERNALS_NEW hash with ambient depth
+ * infinity. Facilitates populating externals in locally added directories.
+ *
+ * ### This is a bit of a hack. We should try to find a better solution
+ * ### to this problem. */
+static svn_error_t *
+gather_externals_in_locally_added_dirs(apr_hash_t *externals_new,
+                                       apr_hash_t *ambient_depths,
+                                       const char *anchor_abspath,
+                                       svn_depth_t requested_depth,
+                                       svn_client_ctx_t *ctx,
+                                       apr_pool_t *scratch_pool)
+{
+  apr_hash_t *all_externals;
+  apr_hash_index_t *hi;
+
+  /* If there was no requested depth for this operation, use infinity.
+   * svn_client__crawl_for_externals() doesn't like depth 'unknown'. */
+  if (requested_depth == svn_depth_unknown)
+    requested_depth = svn_depth_infinity;
+
+  SVN_ERR(svn_client__crawl_for_externals(&all_externals, anchor_abspath,
+                                          requested_depth, ctx, scratch_pool,
+                                          scratch_pool));
+
+  for (hi = apr_hash_first(scratch_pool, all_externals);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *local_abspath = svn__apr_hash_index_key(hi);
+
+      if (! apr_hash_get(externals_new, local_abspath, APR_HASH_KEY_STRING))
+        {
+          apr_pool_t *hash_pool = apr_hash_pool_get(externals_new);
+          svn_string_t *propval = svn__apr_hash_index_val(hi);
+
+          apr_hash_set(externals_new, local_abspath, APR_HASH_KEY_STRING,
+                       apr_pstrdup(hash_pool, propval->data));
+          apr_hash_set(ambient_depths, local_abspath, APR_HASH_KEY_STRING,
+                       svn_depth_to_word(svn_depth_infinity));
+        }
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* This is a helper for svn_client__update_internal(), which see for
    an explanation of most of these parameters.  Some stuff that's
    unique is as follows:
@@ -250,6 +302,10 @@ update_internal(svn_revnum_t *result_rev
      the primary operation.  */
   if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
     {
+      SVN_ERR(gather_externals_in_locally_added_dirs(efb.externals_new,
+                                                     efb.ambient_depths,
+                                                     anchor_abspath,
+                                                     depth, ctx, pool));
       SVN_ERR(svn_client__handle_externals(efb.externals_old,
                                            efb.externals_new,
                                            efb.ambient_depths,

Modified: subversion/branches/performance/subversion/libsvn_delta/svndiff.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_delta/svndiff.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_delta/svndiff.c (original)
+++ subversion/branches/performance/subversion/libsvn_delta/svndiff.c Sun Feb 20 07:45:48 2011
@@ -309,7 +309,7 @@ svn_txdelta_to_svndiff2(svn_txdelta_wind
                         apr_pool_t *pool)
 {
   svn_txdelta_to_svndiff3(handler, handler_baton, output,
-                          svndiff_version, SVNDIFF1_COMPRESS_LEVEL, pool);
+                          svndiff_version, SVN_DEFAULT_COMPRESSION_LEVEL, pool);
 }
 
 void
@@ -319,7 +319,7 @@ svn_txdelta_to_svndiff(svn_stream_t *out
                        void **handler_baton)
 {
   svn_txdelta_to_svndiff3(handler, handler_baton, output,
-                          0, SVNDIFF1_COMPRESS_LEVEL, pool);
+                          0, SVN_DEFAULT_COMPRESSION_LEVEL, pool);
 }
 
 

Modified: subversion/branches/performance/subversion/libsvn_diff/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_diff/deprecated.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_diff/deprecated.c (original)
+++ subversion/branches/performance/subversion/libsvn_diff/deprecated.c Sun Feb 20 07:45:48 2011
@@ -41,6 +41,106 @@
 
 
 /*** Code. ***/
+struct fns_wrapper_baton
+{
+  /* We put the old baton in front of this one, so that we can still use
+     this baton in place of the old.  This prevents us from having to 
+     implement simple wrappers around each member of diff_fns_t. */
+  void *old_baton;
+  const svn_diff_fns_t *vtable;
+};
+
+static svn_error_t *
+datasources_open(void *baton,
+                 apr_off_t *prefix_lines,
+                 apr_off_t *suffix_lines,
+                 const svn_diff_datasource_e *datasources,
+                 apr_size_t datasource_len)
+{
+  struct fns_wrapper_baton *fwb = baton;
+  apr_size_t i;
+
+  /* Just iterate over the datasources, using the old singular version. */
+  for (i = 0; i < datasource_len; i++)
+    {
+      SVN_ERR(fwb->vtable->datasource_open(fwb->old_baton, datasources[i]));
+    }
+
+  /* Don't claim any prefix or suffix matches. */
+  *prefix_lines = 0;
+  *suffix_lines = 0;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+datasource_close(void *baton,
+                 svn_diff_datasource_e datasource)
+{
+  struct fns_wrapper_baton *fwb = baton;
+  return fwb->vtable->datasource_close(fwb->old_baton, datasource);
+}
+
+static svn_error_t *
+datasource_get_next_token(apr_uint32_t *hash,
+                          void **token,
+                          void *baton,
+                          svn_diff_datasource_e datasource)
+{
+  struct fns_wrapper_baton *fwb = baton;
+  return fwb->vtable->datasource_get_next_token(hash, token, fwb->old_baton,
+                                                datasource);
+}
+
+static svn_error_t *
+token_compare(void *baton,
+              void *ltoken,
+              void *rtoken,
+              int *compare)
+{
+  struct fns_wrapper_baton *fwb = baton;
+  return fwb->vtable->token_compare(fwb->old_baton, ltoken, rtoken, compare);
+}
+
+static void
+token_discard(void *baton,
+              void *token)
+{
+  struct fns_wrapper_baton *fwb = baton;
+  fwb->vtable->token_discard(fwb->old_baton, token);
+}
+
+static void
+token_discard_all(void *baton)
+{
+  struct fns_wrapper_baton *fwb = baton;
+  fwb->vtable->token_discard_all(fwb->old_baton);
+}
+
+
+static void
+wrap_diff_fns(svn_diff_fns2_t **diff_fns2,
+              struct fns_wrapper_baton **baton2,
+              const svn_diff_fns_t *diff_fns,
+              void *baton,
+              apr_pool_t *result_pool)
+{
+  /* Initialize the return vtable. */
+  *diff_fns2 = apr_palloc(result_pool, sizeof(**diff_fns2));
+
+  (*diff_fns2)->datasources_open = datasources_open;
+  (*diff_fns2)->datasource_close = datasource_close;
+  (*diff_fns2)->datasource_get_next_token = datasource_get_next_token;
+  (*diff_fns2)->token_compare = token_compare;
+  (*diff_fns2)->token_discard = token_discard;
+  (*diff_fns2)->token_discard_all = token_discard_all;
+
+  /* Initialize the wrapper baton. */
+  *baton2 = apr_palloc(result_pool, sizeof (**baton2));
+  (*baton2)->old_baton = baton;
+  (*baton2)->vtable = diff_fns;
+}
+
 
 /*** From diff_file.c ***/
 svn_error_t *
@@ -142,3 +242,48 @@ svn_diff_file_output_merge(svn_stream_t 
                                      style,
                                      pool);
 }
+
+
+/*** From diff.c ***/
+svn_error_t *
+svn_diff_diff(svn_diff_t **diff,
+              void *diff_baton,
+              const svn_diff_fns_t *vtable,
+              apr_pool_t *pool)
+{
+  svn_diff_fns2_t *diff_fns2;
+  struct fns_wrapper_baton *fwb;
+
+  wrap_diff_fns(&diff_fns2, &fwb, vtable, diff_baton, pool);
+  return svn_diff_diff_2(diff, fwb, diff_fns2, pool);
+}
+
+
+/*** From diff3.c ***/
+svn_error_t *
+svn_diff_diff3(svn_diff_t **diff,
+               void *diff_baton,
+               const svn_diff_fns_t *vtable,
+               apr_pool_t *pool)
+{
+  svn_diff_fns2_t *diff_fns2;
+  struct fns_wrapper_baton *fwb;
+
+  wrap_diff_fns(&diff_fns2, &fwb, vtable, diff_baton, pool);
+  return svn_diff_diff3_2(diff, fwb, diff_fns2, pool);
+}
+
+
+/*** From diff4.c ***/
+svn_error_t *
+svn_diff_diff4(svn_diff_t **diff,
+               void *diff_baton,
+               const svn_diff_fns_t *vtable,
+               apr_pool_t *pool)
+{
+  svn_diff_fns2_t *diff_fns2;
+  struct fns_wrapper_baton *fwb;
+
+  wrap_diff_fns(&diff_fns2, &fwb, vtable, diff_baton, pool);
+  return svn_diff_diff4_2(diff, fwb, diff_fns2, pool);
+}

Modified: subversion/branches/performance/subversion/libsvn_diff/diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_diff/diff.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_diff/diff.c (original)
+++ subversion/branches/performance/subversion/libsvn_diff/diff.c Sun Feb 20 07:45:48 2011
@@ -98,16 +98,20 @@ svn_diff__diff(svn_diff__lcs_t *lcs,
 
 
 svn_error_t *
-svn_diff_diff(svn_diff_t **diff,
-              void *diff_baton,
-              const svn_diff_fns_t *vtable,
-              apr_pool_t *pool)
+svn_diff_diff_2(svn_diff_t **diff,
+                void *diff_baton,
+                const svn_diff_fns2_t *vtable,
+                apr_pool_t *pool)
 {
   svn_diff__tree_t *tree;
   svn_diff__position_t *position_list[2];
+  svn_diff_datasource_e datasource[] = {svn_diff_datasource_original,
+                                        svn_diff_datasource_modified};
   svn_diff__lcs_t *lcs;
   apr_pool_t *subpool;
   apr_pool_t *treepool;
+  apr_off_t prefix_lines = 0;
+  apr_off_t suffix_lines = 0;
 
   *diff = NULL;
 
@@ -116,17 +120,22 @@ svn_diff_diff(svn_diff_t **diff,
 
   svn_diff__tree_create(&tree, treepool);
 
+  SVN_ERR(vtable->datasources_open(diff_baton, &prefix_lines, &suffix_lines,
+                                   datasource, 2));
+
   /* Insert the data into the tree */
   SVN_ERR(svn_diff__get_tokens(&position_list[0],
                                tree,
                                diff_baton, vtable,
                                svn_diff_datasource_original,
+                               prefix_lines,
                                subpool));
 
   SVN_ERR(svn_diff__get_tokens(&position_list[1],
                                tree,
                                diff_baton, vtable,
                                svn_diff_datasource_modified,
+                               prefix_lines,
                                subpool));
 
   /* The cool part is that we don't need the tokens anymore.
@@ -139,7 +148,8 @@ svn_diff_diff(svn_diff_t **diff,
   svn_pool_destroy(treepool);
 
   /* Get the lcs */
-  lcs = svn_diff__lcs(position_list[0], position_list[1], subpool);
+  lcs = svn_diff__lcs(position_list[0], position_list[1], prefix_lines,
+                      suffix_lines, subpool);
 
   /* Produce the diff */
   *diff = svn_diff__diff(lcs, 1, 1, TRUE, pool);

Modified: subversion/branches/performance/subversion/libsvn_diff/diff.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_diff/diff.h?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_diff/diff.h (original)
+++ subversion/branches/performance/subversion/libsvn_diff/diff.h Sun Feb 20 07:45:48 2011
@@ -91,6 +91,8 @@ typedef enum svn_diff__normalize_state_t
 svn_diff__lcs_t *
 svn_diff__lcs(svn_diff__position_t *position_list1, /* pointer to tail (ring) */
               svn_diff__position_t *position_list2, /* pointer to tail (ring) */
+              apr_off_t prefix_lines,
+              apr_off_t suffix_lines,
               apr_pool_t *pool);
 
 
@@ -109,8 +111,9 @@ svn_error_t *
 svn_diff__get_tokens(svn_diff__position_t **position_list,
                      svn_diff__tree_t *tree,
                      void *diff_baton,
-                     const svn_diff_fns_t *vtable,
+                     const svn_diff_fns2_t *vtable,
                      svn_diff_datasource_e datasource,
+                     apr_off_t prefix_lines,
                      apr_pool_t *pool);
 
 

Modified: subversion/branches/performance/subversion/libsvn_diff/diff3.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_diff/diff3.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_diff/diff3.c (original)
+++ subversion/branches/performance/subversion/libsvn_diff/diff3.c Sun Feb 20 07:45:48 2011
@@ -173,7 +173,7 @@ svn_diff__resolve_conflict(svn_diff_t *h
         position[1]->next = start_position[1];
       }
 
-    *lcs_ref = svn_diff__lcs(position[0], position[1],
+    *lcs_ref = svn_diff__lcs(position[0], position[1], 0, 0,
                              subpool);
 
     /* Fix up the EOF lcs element in case one of
@@ -244,17 +244,22 @@ svn_diff__resolve_conflict(svn_diff_t *h
 
 
 svn_error_t *
-svn_diff_diff3(svn_diff_t **diff,
-               void *diff_baton,
-               const svn_diff_fns_t *vtable,
-               apr_pool_t *pool)
+svn_diff_diff3_2(svn_diff_t **diff,
+                 void *diff_baton,
+                 const svn_diff_fns2_t *vtable,
+                 apr_pool_t *pool)
 {
   svn_diff__tree_t *tree;
   svn_diff__position_t *position_list[3];
+  svn_diff_datasource_e datasource[] = {svn_diff_datasource_original,
+                                        svn_diff_datasource_modified,
+                                        svn_diff_datasource_latest};
   svn_diff__lcs_t *lcs_om;
   svn_diff__lcs_t *lcs_ol;
   apr_pool_t *subpool;
   apr_pool_t *treepool;
+  apr_off_t prefix_lines = 0;
+  apr_off_t suffix_lines = 0;
 
   *diff = NULL;
 
@@ -263,22 +268,28 @@ svn_diff_diff3(svn_diff_t **diff,
 
   svn_diff__tree_create(&tree, treepool);
 
+  SVN_ERR(vtable->datasources_open(diff_baton, &prefix_lines, &suffix_lines,
+                                   datasource, 3));
+
   SVN_ERR(svn_diff__get_tokens(&position_list[0],
                                tree,
                                diff_baton, vtable,
                                svn_diff_datasource_original,
+                               prefix_lines,
                                subpool));
 
   SVN_ERR(svn_diff__get_tokens(&position_list[1],
                                tree,
                                diff_baton, vtable,
                                svn_diff_datasource_modified,
+                               prefix_lines,
                                subpool));
 
   SVN_ERR(svn_diff__get_tokens(&position_list[2],
                                tree,
                                diff_baton, vtable,
                                svn_diff_datasource_latest,
+                               prefix_lines,
                                subpool));
 
   /* Get rid of the tokens, we don't need them to calc the diff */
@@ -289,10 +300,10 @@ svn_diff_diff3(svn_diff_t **diff,
   svn_pool_destroy(treepool);
 
   /* Get the lcs for original-modified and original-latest */
-  lcs_om = svn_diff__lcs(position_list[0], position_list[1],
-                         subpool);
-  lcs_ol = svn_diff__lcs(position_list[0], position_list[2],
-                         subpool);
+  lcs_om = svn_diff__lcs(position_list[0], position_list[1], prefix_lines,
+                         suffix_lines, subpool);
+  lcs_ol = svn_diff__lcs(position_list[0], position_list[2], prefix_lines,
+                         suffix_lines, subpool);
 
   /* Produce a merged diff */
   {
@@ -324,7 +335,7 @@ svn_diff_diff3(svn_diff_t **diff,
       }
     else
       {
-        sentinel_position[0].offset = 1;
+        sentinel_position[0].offset = prefix_lines + 1;
         sentinel_position[0].next = NULL;
         position_list[1] = &sentinel_position[0];
       }
@@ -338,7 +349,7 @@ svn_diff_diff3(svn_diff_t **diff,
       }
     else
       {
-        sentinel_position[1].offset = 1;
+        sentinel_position[1].offset = prefix_lines + 1;
         sentinel_position[1].next = NULL;
         position_list[2] = &sentinel_position[1];
       }

Modified: subversion/branches/performance/subversion/libsvn_diff/diff4.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_diff/diff4.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_diff/diff4.c (original)
+++ subversion/branches/performance/subversion/libsvn_diff/diff4.c Sun Feb 20 07:45:48 2011
@@ -166,13 +166,17 @@ adjust_diff(svn_diff_t *diff, svn_diff_t
 }
 
 svn_error_t *
-svn_diff_diff4(svn_diff_t **diff,
-               void *diff_baton,
-               const svn_diff_fns_t *vtable,
-               apr_pool_t *pool)
+svn_diff_diff4_2(svn_diff_t **diff,
+                 void *diff_baton,
+                 const svn_diff_fns2_t *vtable,
+                 apr_pool_t *pool)
 {
   svn_diff__tree_t *tree;
   svn_diff__position_t *position_list[4];
+  svn_diff_datasource_e datasource[] = {svn_diff_datasource_original,
+                                        svn_diff_datasource_modified,
+                                        svn_diff_datasource_latest,
+                                        svn_diff_datasource_ancestor};
   svn_diff__lcs_t *lcs_ol;
   svn_diff__lcs_t *lcs_adjust;
   svn_diff_t *diff_ol;
@@ -181,6 +185,8 @@ svn_diff_diff4(svn_diff_t **diff,
   apr_pool_t *subpool;
   apr_pool_t *subpool2;
   apr_pool_t *subpool3;
+  apr_off_t prefix_lines = 0;
+  apr_off_t suffix_lines = 0;
 
   *diff = NULL;
 
@@ -190,28 +196,35 @@ svn_diff_diff4(svn_diff_t **diff,
 
   svn_diff__tree_create(&tree, subpool3);
 
+  SVN_ERR(vtable->datasources_open(diff_baton, &prefix_lines, &suffix_lines,
+                                   datasource, 4));
+
   SVN_ERR(svn_diff__get_tokens(&position_list[0],
                                tree,
                                diff_baton, vtable,
                                svn_diff_datasource_original,
+                               prefix_lines,
                                subpool2));
 
   SVN_ERR(svn_diff__get_tokens(&position_list[1],
                                tree,
                                diff_baton, vtable,
                                svn_diff_datasource_modified,
+                               prefix_lines,
                                subpool));
 
   SVN_ERR(svn_diff__get_tokens(&position_list[2],
                                tree,
                                diff_baton, vtable,
                                svn_diff_datasource_latest,
+                               prefix_lines,
                                subpool));
 
   SVN_ERR(svn_diff__get_tokens(&position_list[3],
                                tree,
                                diff_baton, vtable,
                                svn_diff_datasource_ancestor,
+                               prefix_lines,
                                subpool2));
 
   /* Get rid of the tokens, we don't need them to calc the diff */
@@ -222,7 +235,8 @@ svn_diff_diff4(svn_diff_t **diff,
   svn_pool_clear(subpool3);
 
   /* Get the lcs for original - latest */
-  lcs_ol = svn_diff__lcs(position_list[0], position_list[2], subpool3);
+  lcs_ol = svn_diff__lcs(position_list[0], position_list[2], prefix_lines,
+                         suffix_lines, subpool3);
   diff_ol = svn_diff__diff(lcs_ol, 1, 1, TRUE, pool);
 
   svn_pool_clear(subpool3);
@@ -243,7 +257,8 @@ svn_diff_diff4(svn_diff_t **diff,
   /* Get the lcs for common ancestor - original
    * Do reverse adjustements
    */
-  lcs_adjust = svn_diff__lcs(position_list[3], position_list[2], subpool3);
+  lcs_adjust = svn_diff__lcs(position_list[3], position_list[2], prefix_lines,
+                             suffix_lines, subpool3);
   diff_adjust = svn_diff__diff(lcs_adjust, 1, 1, FALSE, subpool3);
   adjust_diff(diff_ol, diff_adjust);
 
@@ -252,7 +267,8 @@ svn_diff_diff4(svn_diff_t **diff,
   /* Get the lcs for modified - common ancestor
    * Do forward adjustments
    */
-  lcs_adjust = svn_diff__lcs(position_list[1], position_list[3], subpool3);
+  lcs_adjust = svn_diff__lcs(position_list[1], position_list[3], prefix_lines,
+                             suffix_lines, subpool3);
   diff_adjust = svn_diff__diff(lcs_adjust, 1, 1, FALSE, subpool3);
   adjust_diff(diff_ol, diff_adjust);
 

Modified: subversion/branches/performance/subversion/libsvn_diff/diff_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_diff/diff_file.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_diff/diff_file.c (original)
+++ subversion/branches/performance/subversion/libsvn_diff/diff_file.c Sun Feb 20 07:45:48 2011
@@ -83,6 +83,10 @@ typedef struct svn_diff__file_baton_t
     char *endp;    /* next memory address after the current chunk */
 
     svn_diff__normalize_state_t normalize_state;
+
+    /* Where the identical suffix starts in this datasource */
+    int suffix_start_chunk;
+    apr_off_t suffix_offset_in_chunk;
   } files[4];
 
   /* List of free tokens that may be reused. */
@@ -205,47 +209,593 @@ map_or_read_file(apr_file_t **file,
 }
 
 
-/* Let FILE stand for the file_info struct element of BATON->files that is
- * indexed by DATASOURCE.  BATON's type is (svn_diff__file_baton_t *).
- *
- * Open the file at FILE.path; initialize FILE.file, FILE.size, FILE.buffer,
- * FILE.curp and FILE.endp; allocate a buffer and read the first chunk.
- *
- * Implements svn_diff_fns_t::datasource_open. */
+/* For all files in the FILE array, increment the curp pointer.  If a file
+ * points before the beginning of file, let it point at the first byte again.
+ * If the end of the current chunk is reached, read the next chunk in the
+ * buffer and point curp to the start of the chunk.  If EOF is reached, set
+ * curp equal to endp to indicate EOF. */
+#define INCREMENT_POINTERS(all_files, files_len, pool)                       \
+  do {                                                                       \
+    apr_size_t svn_macro__i;                                                 \
+                                                                             \
+    for (svn_macro__i = 0; svn_macro__i < (files_len); svn_macro__i++)       \
+    {                                                                        \
+      if ((all_files)[svn_macro__i].curp < (all_files)[svn_macro__i].endp - 1)\
+        (all_files)[svn_macro__i].curp++;                                    \
+      else                                                                   \
+        SVN_ERR(increment_chunk(&(all_files)[svn_macro__i], (pool)));        \
+    }                                                                        \
+  } while (0)
+
+
+/* For all files in the FILE array, decrement the curp pointer.  If the
+ * start of a chunk is reached, read the previous chunk in the buffer and
+ * point curp to the last byte of the chunk.  If the beginning of a FILE is
+ * reached, set chunk to -1 to indicate BOF. */
+#define DECREMENT_POINTERS(all_files, files_len, pool)                       \
+  do {                                                                       \
+    apr_size_t svn_macro__i;                                                 \
+                                                                             \
+    for (svn_macro__i = 0; svn_macro__i < (files_len); svn_macro__i++)       \
+    {                                                                        \
+      if ((all_files)[svn_macro__i].curp > (all_files)[svn_macro__i].buffer) \
+        (all_files)[svn_macro__i].curp--;                                    \
+      else                                                                   \
+        SVN_ERR(decrement_chunk(&(all_files)[svn_macro__i], (pool)));        \
+    }                                                                        \
+  } while (0)
+
+
 static svn_error_t *
-datasource_open(void *baton, svn_diff_datasource_e datasource)
+increment_chunk(struct file_info *file, apr_pool_t *pool)
 {
-  svn_diff__file_baton_t *file_baton = baton;
-  struct file_info *file = &file_baton->files[datasource_to_index(datasource)];
-  apr_finfo_t finfo;
   apr_off_t length;
-  char *curp;
-  char *endp;
+  apr_off_t last_chunk = offset_to_chunk(file->size);
 
-  SVN_ERR(svn_io_file_open(&file->file, file->path,
-                           APR_READ, APR_OS_DEFAULT, file_baton->pool));
+  if (file->chunk == -1)
+    {
+      /* We are at BOF (Beginning Of File). Point to first chunk/byte again. */
+      file->chunk = 0;
+      file->curp = file->buffer;
+    }
+  else if (file->chunk == last_chunk)
+    {
+      /* We are at the last chunk. Indicate EOF by setting curp == endp. */
+      file->curp = file->endp;
+    }
+  else
+    {
+      /* There are still chunks left. Read next chunk and reset pointers. */
+      file->chunk++;
+      length = file->chunk == last_chunk ? 
+        offset_in_chunk(file->size) : CHUNK_SIZE;
+      SVN_ERR(read_chunk(file->file, file->path, file->buffer,
+                         length, chunk_to_offset(file->chunk),
+                         pool));
+      file->endp = file->buffer + length;
+      file->curp = file->buffer;
+    }
+  
+  return SVN_NO_ERROR;
+}
 
-  SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE,
-                               file->file, file_baton->pool));
 
-  file->size = finfo.size;
-  length = finfo.size > CHUNK_SIZE ? CHUNK_SIZE : finfo.size;
+static svn_error_t *
+decrement_chunk(struct file_info *file, apr_pool_t *pool)
+{
+  if (file->chunk == 0)
+    {
+      /* We are already at the first chunk. Indicate BOF (Beginning Of File)
+         by setting chunk = -1 and curp = endp - 1. Both conditions are
+         important. They help the increment step to catch the BOF situation
+         in an efficient way. */
+      file->chunk--; 
+      file->curp = file->endp - 1;
+    }
+  else
+    {
+      /* Read previous chunk and reset pointers. */
+      file->chunk--;
+      SVN_ERR(read_chunk(file->file, file->path, file->buffer,
+                         CHUNK_SIZE, chunk_to_offset(file->chunk),
+                         pool));
+      file->endp = file->buffer + CHUNK_SIZE;
+      file->curp = file->endp - 1;
+    }
+  
+  return SVN_NO_ERROR;
+}
 
-  if (length == 0)
-    return SVN_NO_ERROR;
 
-  endp = curp = apr_palloc(file_baton->pool, (apr_size_t) length);
-  endp += length;
+/* Check whether one of the FILEs has its pointers 'before' the beginning of
+ * the file (this can happen while scanning backwards). This is the case if
+ * one of them has chunk == -1. */
+static svn_boolean_t
+is_one_at_bof(struct file_info file[], apr_size_t file_len)
+{
+  apr_size_t i;
+
+  for (i = 0; i < file_len; i++)
+    if (file[i].chunk == -1)
+      return TRUE;
+
+  return FALSE;
+}
+
+/* Check whether one of the FILEs has its pointers at EOF (this is the case if
+ * one of them has curp == endp (this can only happen at the last chunk)) */
+static svn_boolean_t
+is_one_at_eof(struct file_info file[], apr_size_t file_len)
+{
+  apr_size_t i;
+
+  for (i = 0; i < file_len; i++)
+    if (file[i].curp == file[i].endp)
+      return TRUE;
+
+  return FALSE;
+}
+
+/* Quickly determine whether there is a eol char in CHUNK.
+ * (mainly copy-n-paste from eol.c#svn_eol__find_eol_start).
+ */
+#if SVN_UNALIGNED_ACCESS_IS_OK
+#if APR_SIZEOF_VOIDP == 8
+#  define LOWER_7BITS_SET 0x7f7f7f7f7f7f7f7f
+#  define BIT_7_SET       0x8080808080808080
+#  define R_MASK          0x0a0a0a0a0a0a0a0a
+#  define N_MASK          0x0d0d0d0d0d0d0d0d
+#else
+#  define LOWER_7BITS_SET 0x7f7f7f7f
+#  define BIT_7_SET       0x80808080
+#  define R_MASK          0x0a0a0a0a
+#  define N_MASK          0x0d0d0d0d
+#endif
+#endif
+
+#if SVN_UNALIGNED_ACCESS_IS_OK
+static svn_boolean_t contains_eol(apr_size_t chunk)
+{
+  apr_size_t r_test = chunk ^ R_MASK;
+  apr_size_t n_test = chunk ^ N_MASK;
+
+  r_test |= (r_test & LOWER_7BITS_SET) + LOWER_7BITS_SET;
+  n_test |= (n_test & LOWER_7BITS_SET) + LOWER_7BITS_SET;
+
+  return (r_test & n_test & BIT_7_SET) != BIT_7_SET;
+}
+#endif
+
+/* Find the prefix which is identical between all elements of the FILE array.
+ * Return the number of prefix lines in PREFIX_LINES.  REACHED_ONE_EOF will be
+ * set to TRUE if one of the FILEs reached its end while scanning prefix,
+ * i.e. at least one file consisted entirely of prefix.  Otherwise, 
+ * REACHED_ONE_EOF is set to FALSE.
+ *
+ * After this function is finished, the buffers, chunks, curp's and endp's 
+ * of the FILEs are set to point at the first byte after the prefix. */
+static svn_error_t *
+find_identical_prefix(svn_boolean_t *reached_one_eof, apr_off_t *prefix_lines,
+                      struct file_info file[], apr_size_t file_len,
+                      apr_pool_t *pool)
+{
+  svn_boolean_t had_cr = FALSE;
+  svn_boolean_t is_match;
+  apr_off_t lines = 0;
+  apr_size_t i;
+
+  for (i = 1, is_match = TRUE; i < file_len; i++)
+    is_match = is_match && *file[0].curp == *file[i].curp;
+  while (is_match)
+    {
+#if SVN_UNALIGNED_ACCESS_IS_OK
+      apr_ssize_t max_delta, delta;
+#endif /* SVN_UNALIGNED_ACCESS_IS_OK */
+
+      /* ### TODO: see if we can take advantage of 
+         diff options like ignore_eol_style or ignore_space. */
+      /* check for eol, and count */
+      if (*file[0].curp == '\r')
+        {
+          lines++;
+          had_cr = TRUE;
+        }
+      else if (*file[0].curp == '\n' && !had_cr)
+        {
+          lines++;
+        }
+      else 
+        {
+          had_cr = FALSE;
+        }
+
+      INCREMENT_POINTERS(file, file_len, pool);
+
+#if SVN_UNALIGNED_ACCESS_IS_OK
+
+      /* Try to advance as far as possible with machine-word granularity.
+       * Determine how far we may advance with chunky ops without reaching
+       * endp for any of the files.
+       * Signedness is important here if curp gets close to endp.
+       */
+      max_delta = file[0].endp - file[0].curp - sizeof(apr_size_t);
+      for (i = 1; i < file_len; i++)
+        {
+          delta = file[i].endp - file[i].curp - sizeof(apr_size_t);
+          if (delta < max_delta)
+            max_delta = delta;
+        }
+
+      is_match = TRUE;
+      for (delta = 0; delta < max_delta && is_match; delta += sizeof(apr_size_t))
+        {
+          apr_size_t chunk = *(const apr_size_t *)(file[0].curp + delta);
+          if (contains_eol(chunk))
+            break;
+
+          for (i = 1; i < file_len; i++)
+            if (chunk != *(const apr_size_t *)(file[i].curp + delta))
+              {
+                is_match = FALSE;
+                delta -= sizeof(apr_size_t);
+                break;
+              }
+        }
+
+      /* We either found a mismatch or an EOL at or shortly behind curp+delta
+       * or we cannot proceed with chunky ops without exceeding endp.
+       * In any way, everything up to curp + delta is equal and not an EOL.
+       */
+      for (i = 0; i < file_len; i++)
+        file[i].curp += delta;
+
+#endif
+
+      *reached_one_eof = is_one_at_eof(file, file_len);
+      if (*reached_one_eof)
+        break;
+      else
+        for (i = 1, is_match = TRUE; i < file_len; i++)
+          is_match = is_match && *file[0].curp == *file[i].curp;
+    }
+
+  if (had_cr)
+    {
+      /* Check if we ended in the middle of a \r\n for one file, but \r for 
+         another. If so, back up one byte, so the next loop will back up
+         the entire line. Also decrement lines, since we counted one
+         too many for the \r. */
+      svn_boolean_t ended_at_nonmatching_newline = FALSE;
+      for (i = 0; i < file_len; i++)
+        ended_at_nonmatching_newline = ended_at_nonmatching_newline 
+                                       || *file[i].curp == '\n';
+      if (ended_at_nonmatching_newline)
+        {
+          lines--;
+          DECREMENT_POINTERS(file, file_len, pool);
+        }
+    }
+
+  /* Back up one byte, so we point at the last identical byte */
+  DECREMENT_POINTERS(file, file_len, pool);
+
+  /* Back up to the last eol sequence (\n, \r\n or \r) */
+  while (!is_one_at_bof(file, file_len) && 
+         *file[0].curp != '\n' && *file[0].curp != '\r')
+    DECREMENT_POINTERS(file, file_len, pool);
+
+  /* Slide one byte forward, to point past the eol sequence */
+  INCREMENT_POINTERS(file, file_len, pool);
+
+  *prefix_lines = lines;
+
+  return SVN_NO_ERROR;
+}
+
+
+/* The number of identical suffix lines to keep with the middle section. These
+ * lines are not eliminated as suffix, and can be picked up by the token
+ * parsing and lcs steps. This is mainly for backward compatibility with
+ * the previous diff (and blame) output (if there are multiple diff solutions,
+ * our lcs algorithm prefers taking common lines from the start, rather than
+ * from the end. By giving it back some suffix lines, we give it some wiggle
+ * room to find the exact same diff as before).
+ *
+ * The number 50 is more or less arbitrary, based on some real-world tests
+ * with big files (and then doubling the required number to be on the safe
+ * side). This has a negligible effect on the power of the optimization. */
+#ifndef SUFFIX_LINES_TO_KEEP
+#define SUFFIX_LINES_TO_KEEP 50
+#endif
+
+/* Find the suffix which is identical between all elements of the FILE array.
+ * Return the number of suffix lines in SUFFIX_LINES.
+ *
+ * Before this function is called the FILEs' pointers and chunks should be 
+ * positioned right after the identical prefix (which is the case after 
+ * find_identical_prefix), so we can determine where suffix scanning should 
+ * ultimately stop. */
+static svn_error_t *
+find_identical_suffix(apr_off_t *suffix_lines, struct file_info file[],
+                      apr_size_t file_len, apr_pool_t *pool)
+{
+  struct file_info file_for_suffix[4] = { { 0 }  };
+  apr_off_t length[4];
+  apr_off_t suffix_min_chunk0;
+  apr_off_t suffix_min_offset0;
+  apr_off_t min_file_size;
+  int suffix_lines_to_keep = SUFFIX_LINES_TO_KEEP;
+  svn_boolean_t is_match, reached_prefix;
+  apr_off_t lines = 0;
+  svn_boolean_t had_cr, had_nl;
+  apr_size_t i;
+
+  /* Initialize file_for_suffix[].
+     Read last chunk, position curp at last byte. */
+  for (i = 0; i < file_len; i++)
+    {
+      file_for_suffix[i].path = file[i].path;
+      file_for_suffix[i].file = file[i].file;
+      file_for_suffix[i].size = file[i].size;
+      file_for_suffix[i].chunk =
+        (int) offset_to_chunk(file_for_suffix[i].size); /* last chunk */
+      length[i] = offset_in_chunk(file_for_suffix[i].size);
+      if (file_for_suffix[i].chunk == file[i].chunk)
+        {
+          /* Prefix ended in last chunk, so we can reuse the prefix buffer */
+          file_for_suffix[i].buffer = file[i].buffer;
+        }
+      else
+        {
+          /* There is at least more than 1 chunk,
+             so allocate full chunk size buffer */
+          file_for_suffix[i].buffer = apr_palloc(pool, CHUNK_SIZE);
+          SVN_ERR(read_chunk(file_for_suffix[i].file, file_for_suffix[i].path,
+                             file_for_suffix[i].buffer, length[i],
+                             chunk_to_offset(file_for_suffix[i].chunk),
+                             pool));
+        }
+      file_for_suffix[i].endp = file_for_suffix[i].buffer + length[i];
+      file_for_suffix[i].curp = file_for_suffix[i].endp - 1;
+    }
+
+  /* Get the chunk and pointer offset (for file[0]) at which we should stop
+     scanning backward for the identical suffix, i.e. when we reach prefix. */
+  suffix_min_chunk0 = file[0].chunk;
+  suffix_min_offset0 = file[0].curp - file[0].buffer;
+
+  /* Compensate if other files are smaller than file[0] */
+  for (i = 1, min_file_size = file[0].size; i < file_len; i++)
+    if (file[i].size < min_file_size)
+      min_file_size = file[i].size;
+  if (file[0].size > min_file_size)
+    {
+      suffix_min_chunk0 += (file[0].size - min_file_size) / CHUNK_SIZE;
+      suffix_min_offset0 += (file[0].size - min_file_size) % CHUNK_SIZE;
+    }
+
+  /* Scan backwards until mismatch or until we reach the prefix. */
+  for (i = 1, is_match = TRUE; i < file_len; i++)
+    is_match = is_match 
+               && *file_for_suffix[0].curp == *file_for_suffix[i].curp;
+  if (is_match && *file_for_suffix[0].curp != '\r' 
+               && *file_for_suffix[0].curp != '\n')
+    /* Count an extra line for the last line not ending in an eol. */
+    lines++;
+
+  while (is_match)
+    {
+      /* Initialize the minimum pointer positions. */
+      const char *min_curp[4];
+#if SVN_UNALIGNED_ACCESS_IS_OK
+      svn_boolean_t can_read_word;
+#endif /* SVN_UNALIGNED_ACCESS_IS_OK */
+
+      /* ### TODO: see if we can take advantage of 
+         diff options like ignore_eol_style or ignore_space. */
+      /* check for eol, and count */
+      if (*file_for_suffix[0].curp == '\n')
+        {
+          lines++;
+          had_nl = TRUE;
+        }
+      else if (*file_for_suffix[0].curp == '\r' && !had_nl)
+        {
+          lines++;
+        }
+      else 
+        {
+          had_nl = FALSE;
+        }
+
+      DECREMENT_POINTERS(file_for_suffix, file_len, pool);
+
+
+      min_curp[0] = file_for_suffix[0].chunk == suffix_min_chunk0
+                  ? file_for_suffix[0].buffer + suffix_min_offset0 + 1
+                  : file_for_suffix[0].buffer + 1;
+      for (i = 1; i < file_len; i++)
+        min_curp[i] = file_for_suffix[i].buffer + 1;
+
+#if SVN_UNALIGNED_ACCESS_IS_OK
+
+      /* Scan quickly by reading with machine-word granularity. */
+      for (i = 0, can_read_word = TRUE; i < file_len; i++)
+        can_read_word = can_read_word 
+                        && (   file_for_suffix[i].curp - sizeof(apr_size_t)
+                            >= min_curp[i]);
+      if (can_read_word)
+        {
+          do
+            {
+              apr_size_t chunk;
+              for (i = 0; i < file_len; i++)
+                file_for_suffix[i].curp -= sizeof(apr_size_t);
+
+              chunk = *(const apr_size_t *)(file_for_suffix[0].curp + 1);
+              if (contains_eol(chunk))
+                break;
+
+              for (i = 0, can_read_word = TRUE; i < file_len; i++)
+                can_read_word = can_read_word 
+                                && (   file_for_suffix[i].curp - sizeof(apr_size_t)
+                                    >= min_curp[i]);
+              for (i = 1, is_match = TRUE; i < file_len; i++)
+                is_match = is_match
+                           && (   chunk
+                               == *(const apr_size_t *)(file_for_suffix[i].curp + 1));
+            } while (can_read_word && is_match);
+
+            for (i = 0; i < file_len; i++)
+              file_for_suffix[i].curp += sizeof(apr_size_t);
+        }
+
+#endif
+
+      reached_prefix = file_for_suffix[0].chunk == suffix_min_chunk0 
+                       && (file_for_suffix[0].curp - file_for_suffix[0].buffer)
+                          == suffix_min_offset0;
+      if (reached_prefix || is_one_at_bof(file_for_suffix, file_len))
+        break;
+
+      for (i = 1, is_match = TRUE; i < file_len; i++)
+        is_match = is_match 
+                   && *file_for_suffix[0].curp == *file_for_suffix[i].curp;
+    }
+
+  /* Slide one byte forward, to point at the first byte of identical suffix */
+  INCREMENT_POINTERS(file_for_suffix, file_len, pool);
+
+  /* Slide forward until we find an eol sequence to add the rest of the line
+     we're in. Then add SUFFIX_LINES_TO_KEEP more lines. Stop if at least 
+     one file reaches its end. */
+  do
+    {
+      had_cr = FALSE;
+      while (!is_one_at_eof(file_for_suffix, file_len)
+             && *file_for_suffix[0].curp != '\n'
+             && *file_for_suffix[0].curp != '\r')
+        INCREMENT_POINTERS(file_for_suffix, file_len, pool);
+
+      /* Slide one or two more bytes, to point past the eol. */
+      if (!is_one_at_eof(file_for_suffix, file_len)
+          && *file_for_suffix[0].curp == '\r')
+        {
+          lines--;
+          had_cr = TRUE;
+          INCREMENT_POINTERS(file_for_suffix, file_len, pool);
+        }
+      if (!is_one_at_eof(file_for_suffix, file_len)
+          && *file_for_suffix[0].curp == '\n')
+        {
+          if (!had_cr)
+            lines--;
+          INCREMENT_POINTERS(file_for_suffix, file_len, pool);
+        }
+    }
+  while (!is_one_at_eof(file_for_suffix, file_len) 
+         && suffix_lines_to_keep--);
+
+  if (is_one_at_eof(file_for_suffix, file_len))
+    lines = 0;
+
+  /* Save the final suffix information in the original file_info */
+  for (i = 0; i < file_len; i++)
+    {
+      file[i].suffix_start_chunk = file_for_suffix[i].chunk;
+      file[i].suffix_offset_in_chunk = 
+        file_for_suffix[i].curp - file_for_suffix[i].buffer;
+    }
 
-  file->buffer = file->curp = curp;
-  file->endp = endp;
+  *suffix_lines = lines;
 
-  return read_chunk(file->file, file->path,
-                    curp, length, 0, file_baton->pool);
+  return SVN_NO_ERROR;
 }
 
 
-/* Implements svn_diff_fns_t::datasource_close */
+/* Let FILE stand for the array of file_info struct elements of BATON->files
+ * that are indexed by the elements of the DATASOURCE array.
+ * BATON's type is (svn_diff__file_baton_t *).
+ *
+ * For each file in the FILE array, open the file at FILE.path; initialize 
+ * FILE.file, FILE.size, FILE.buffer, FILE.curp and FILE.endp; allocate a 
+ * buffer and read the first chunk.  Then find the prefix and suffix lines
+ * which are identical between all the files.  Return the number of identical
+ * prefix lines in PREFIX_LINES, and the number of identical suffix lines in
+ * SUFFIX_LINES.
+ *
+ * Finding the identical prefix and suffix allows us to exclude those from the
+ * rest of the diff algorithm, which increases performance by reducing the 
+ * problem space.
+ *
+ * Implements svn_diff_fns2_t::datasources_open. */
+static svn_error_t *
+datasources_open(void *baton,
+                 apr_off_t *prefix_lines,
+                 apr_off_t *suffix_lines,
+                 const svn_diff_datasource_e *datasources,
+                 apr_size_t datasources_len)
+{
+  svn_diff__file_baton_t *file_baton = baton;
+  struct file_info files[4];
+  apr_finfo_t finfo[4];
+  apr_off_t length[4];
+  svn_boolean_t reached_one_eof;
+  apr_size_t i;
+
+  /* Make sure prefix_lines and suffix_lines are set correctly, even if we
+   * exit early because one of the files is empty. */
+  *prefix_lines = 0;
+  *suffix_lines = 0;
+
+  /* Open datasources and read first chunk */
+  for (i = 0; i < datasources_len; i++)
+    {
+      struct file_info *file
+          = &file_baton->files[datasource_to_index(datasources[i])];
+      SVN_ERR(svn_io_file_open(&file->file, file->path,
+                               APR_READ, APR_OS_DEFAULT, file_baton->pool));
+      SVN_ERR(svn_io_file_info_get(&finfo[i], APR_FINFO_SIZE,
+                                   file->file, file_baton->pool));
+      file->size = finfo[i].size;
+      length[i] = finfo[i].size > CHUNK_SIZE ? CHUNK_SIZE : finfo[i].size;
+      file->buffer = apr_palloc(file_baton->pool, (apr_size_t) length[i]);
+      SVN_ERR(read_chunk(file->file, file->path, file->buffer,
+                         length[i], 0, file_baton->pool));
+      file->endp = file->buffer + length[i];
+      file->curp = file->buffer;
+      /* Set suffix_start_chunk to a guard value, so if suffix scanning is
+       * skipped because one of the files is empty, or because of
+       * reached_one_eof, we can still easily check for the suffix during
+       * token reading (datasource_get_next_token). */
+      file->suffix_start_chunk = -1;
+
+      files[i] = *file;
+    }
+
+  for (i = 0; i < datasources_len; i++)
+    if (length[i] == 0)
+      /* There will not be any identical prefix/suffix, so we're done. */
+      return SVN_NO_ERROR;
+
+  SVN_ERR(find_identical_prefix(&reached_one_eof, prefix_lines,
+                                files, datasources_len, file_baton->pool));
+
+  if (!reached_one_eof)
+    /* No file consisted totally of identical prefix,
+     * so there may be some identical suffix.  */
+    SVN_ERR(find_identical_suffix(suffix_lines, files, datasources_len,
+                                  file_baton->pool));
+
+  /* Copy local results back to baton. */
+  for (i = 0; i < datasources_len; i++)
+    file_baton->files[datasource_to_index(datasources[i])] = files[i];
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Implements svn_diff_fns2_t::datasource_close */
 static svn_error_t *
 datasource_close(void *baton, svn_diff_datasource_e datasource)
 {
@@ -256,7 +806,7 @@ datasource_close(void *baton, svn_diff_d
   return SVN_NO_ERROR;
 }
 
-/* Implements svn_diff_fns_t::datasource_get_next_token */
+/* Implements svn_diff_fns2_t::datasource_get_next_token */
 static svn_error_t *
 datasource_get_next_token(apr_uint32_t *hash, void **token, void *baton,
                           svn_diff_datasource_e datasource)
@@ -286,7 +836,14 @@ datasource_get_next_token(apr_uint32_t *
       return SVN_NO_ERROR;
     }
 
-  /* Get a new token */
+  /* Stop when we encounter the identical suffix. If suffix scanning was not
+   * performed, suffix_start_chunk will be -1, so this condition will never
+   * be true. */
+  if (file->chunk == file->suffix_start_chunk
+      && curp - file->buffer == file->suffix_offset_in_chunk)
+    return SVN_NO_ERROR;
+
+  /* Allocate a new token, or fetch one from the "reusable tokens" list. */
   file_token = file_baton->tokens;
   if (file_token)
     {
@@ -388,7 +945,7 @@ datasource_get_next_token(apr_uint32_t *
 
 #define COMPARE_CHUNK_SIZE 4096
 
-/* Implements svn_diff_fns_t::token_compare */
+/* Implements svn_diff_fns2_t::token_compare */
 static svn_error_t *
 token_compare(void *baton, void *token1, void *token2, int *compare)
 {
@@ -509,19 +1066,20 @@ token_compare(void *baton, void *token1,
 }
 
 
-/* Implements svn_diff_fns_t::token_discard */
+/* Implements svn_diff_fns2_t::token_discard */
 static void
 token_discard(void *baton, void *token)
 {
   svn_diff__file_baton_t *file_baton = baton;
   svn_diff__file_token_t *file_token = token;
 
+  /* Prepend FILE_TOKEN to FILE_BATON->TOKENS, for reuse. */
   file_token->next = file_baton->tokens;
   file_baton->tokens = file_token;
 }
 
 
-/* Implements svn_diff_fns_t::token_discard_all */
+/* Implements svn_diff_fns2_t::token_discard_all */
 static void
 token_discard_all(void *baton)
 {
@@ -532,9 +1090,9 @@ token_discard_all(void *baton)
 }
 
 
-static const svn_diff_fns_t svn_diff__file_vtable =
+static const svn_diff_fns2_t svn_diff__file_vtable =
 {
-  datasource_open,
+  datasources_open,
   datasource_close,
   datasource_get_next_token,
   token_compare,
@@ -668,15 +1226,14 @@ svn_diff_file_diff_2(svn_diff_t **diff,
                      const svn_diff_file_options_t *options,
                      apr_pool_t *pool)
 {
-  svn_diff__file_baton_t baton;
+  svn_diff__file_baton_t baton = { 0 };
 
-  memset(&baton, 0, sizeof(baton));
   baton.options = options;
   baton.files[0].path = original;
   baton.files[1].path = modified;
   baton.pool = svn_pool_create(pool);
 
-  SVN_ERR(svn_diff_diff(diff, &baton, &svn_diff__file_vtable, pool));
+  SVN_ERR(svn_diff_diff_2(diff, &baton, &svn_diff__file_vtable, pool));
 
   svn_pool_destroy(baton.pool);
   return SVN_NO_ERROR;
@@ -690,16 +1247,15 @@ svn_diff_file_diff3_2(svn_diff_t **diff,
                       const svn_diff_file_options_t *options,
                       apr_pool_t *pool)
 {
-  svn_diff__file_baton_t baton;
+  svn_diff__file_baton_t baton = { 0 };
 
-  memset(&baton, 0, sizeof(baton));
   baton.options = options;
   baton.files[0].path = original;
   baton.files[1].path = modified;
   baton.files[2].path = latest;
   baton.pool = svn_pool_create(pool);
 
-  SVN_ERR(svn_diff_diff3(diff, &baton, &svn_diff__file_vtable, pool));
+  SVN_ERR(svn_diff_diff3_2(diff, &baton, &svn_diff__file_vtable, pool));
 
   svn_pool_destroy(baton.pool);
   return SVN_NO_ERROR;
@@ -714,9 +1270,8 @@ svn_diff_file_diff4_2(svn_diff_t **diff,
                       const svn_diff_file_options_t *options,
                       apr_pool_t *pool)
 {
-  svn_diff__file_baton_t baton;
+  svn_diff__file_baton_t baton = { 0 };
 
-  memset(&baton, 0, sizeof(baton));
   baton.options = options;
   baton.files[0].path = original;
   baton.files[1].path = modified;
@@ -724,7 +1279,7 @@ svn_diff_file_diff4_2(svn_diff_t **diff,
   baton.files[3].path = ancestor;
   baton.pool = svn_pool_create(pool);
 
-  SVN_ERR(svn_diff_diff4(diff, &baton, &svn_diff__file_vtable, pool));
+  SVN_ERR(svn_diff_diff4_2(diff, &baton, &svn_diff__file_vtable, pool));
 
   svn_pool_destroy(baton.pool);
   return SVN_NO_ERROR;

Modified: subversion/branches/performance/subversion/libsvn_diff/diff_memory.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_diff/diff_memory.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_diff/diff_memory.c (original)
+++ subversion/branches/performance/subversion/libsvn_diff/diff_memory.c Sun Feb 20 07:45:48 2011
@@ -88,16 +88,22 @@ datasource_to_index(svn_diff_datasource_
 }
 
 
-/* Implements svn_diff_fns_t::datasource_open */
+/* Implements svn_diff_fns2_t::datasources_open */
 static svn_error_t *
-datasource_open(void *baton, svn_diff_datasource_e datasource)
+datasources_open(void *baton,
+                 apr_off_t *prefix_lines,
+                 apr_off_t *suffix_lines,
+                 const svn_diff_datasource_e *datasources,
+                 apr_size_t datasources_len)
 {
   /* Do nothing: everything is already there and initialized to 0 */
+  *prefix_lines = 0;
+  *suffix_lines = 0;
   return SVN_NO_ERROR;
 }
 
 
-/* Implements svn_diff_fns_t::datasource_close */
+/* Implements svn_diff_fns2_t::datasource_close */
 static svn_error_t *
 datasource_close(void *baton, svn_diff_datasource_e datasource)
 {
@@ -109,7 +115,7 @@ datasource_close(void *baton, svn_diff_d
 }
 
 
-/* Implements svn_diff_fns_t::datasource_get_next_token */
+/* Implements svn_diff_fns2_t::datasource_get_next_token */
 static svn_error_t *
 datasource_get_next_token(apr_uint32_t *hash, void **token, void *baton,
                           svn_diff_datasource_e datasource)
@@ -138,7 +144,7 @@ datasource_get_next_token(apr_uint32_t *
   return SVN_NO_ERROR;
 }
 
-/* Implements svn_diff_fns_t::token_compare */
+/* Implements svn_diff_fns2_t::token_compare */
 static svn_error_t *
 token_compare(void *baton, void *token1, void *token2, int *result)
 {
@@ -167,7 +173,7 @@ token_compare(void *baton, void *token1,
   return SVN_NO_ERROR;
 }
 
-/* Implements svn_diff_fns_t::token_discard */
+/* Implements svn_diff_fns2_t::token_discard */
 static void
 token_discard(void *baton, void *token)
 {
@@ -175,7 +181,7 @@ token_discard(void *baton, void *token)
 }
 
 
-/* Implements svn_diff_fns_t::token_discard_all */
+/* Implements svn_diff_fns2_t::token_discard_all */
 static void
 token_discard_all(void *baton)
 {
@@ -187,9 +193,9 @@ token_discard_all(void *baton)
 }
 
 
-static const svn_diff_fns_t svn_diff__mem_vtable =
+static const svn_diff_fns2_t svn_diff__mem_vtable =
 {
-  datasource_open,
+  datasources_open,
   datasource_close,
   datasource_get_next_token,
   token_compare,
@@ -284,7 +290,7 @@ svn_diff_mem_string_diff(svn_diff_t **di
 
   baton.normalization_options = options;
 
-  return svn_diff_diff(diff, &baton, &svn_diff__mem_vtable, pool);
+  return svn_diff_diff_2(diff, &baton, &svn_diff__mem_vtable, pool);
 }
 
 svn_error_t *
@@ -304,7 +310,7 @@ svn_diff_mem_string_diff3(svn_diff_t **d
 
   baton.normalization_options = options;
 
-  return svn_diff_diff3(diff, &baton, &svn_diff__mem_vtable, pool);
+  return svn_diff_diff3_2(diff, &baton, &svn_diff__mem_vtable, pool);
 }
 
 
@@ -327,7 +333,7 @@ svn_diff_mem_string_diff4(svn_diff_t **d
 
   baton.normalization_options = options;
 
-  return svn_diff_diff4(diff, &baton, &svn_diff__mem_vtable, pool);
+  return svn_diff_diff4_2(diff, &baton, &svn_diff__mem_vtable, pool);
 }
 
 

Modified: subversion/branches/performance/subversion/libsvn_diff/lcs.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_diff/lcs.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_diff/lcs.c (original)
+++ subversion/branches/performance/subversion/libsvn_diff/lcs.c Sun Feb 20 07:45:48 2011
@@ -160,9 +160,36 @@ svn_diff__lcs_reverse(svn_diff__lcs_t *l
 }
 
 
+/* Prepends a new lcs chunk for the amount of LINES at the given positions
+ * POS0_OFFSET and POS1_OFFSET to the given LCS chain, and returns it.
+ * This function assumes LINES > 0. */
+static svn_diff__lcs_t *
+prepend_lcs(svn_diff__lcs_t *lcs, apr_off_t lines,
+            apr_off_t pos0_offset, apr_off_t pos1_offset,
+            apr_pool_t *pool)
+{
+  svn_diff__lcs_t *new_lcs;
+
+  SVN_ERR_ASSERT_NO_RETURN(lines > 0);
+
+  new_lcs = apr_palloc(pool, sizeof(*new_lcs));
+  new_lcs->position[0] = apr_pcalloc(pool, sizeof(*new_lcs->position[0]));
+  new_lcs->position[0]->offset = pos0_offset;
+  new_lcs->position[1] = apr_pcalloc(pool, sizeof(*new_lcs->position[1]));
+  new_lcs->position[1]->offset = pos1_offset;
+  new_lcs->length = lines;
+  new_lcs->refcount = 1;
+  new_lcs->next = lcs;
+
+  return new_lcs;
+}
+
+
 svn_diff__lcs_t *
 svn_diff__lcs(svn_diff__position_t *position_list1, /* pointer to tail (ring) */
               svn_diff__position_t *position_list2, /* pointer to tail (ring) */
+              apr_off_t prefix_lines,
+              apr_off_t suffix_lines,
               apr_pool_t *pool)
 {
   int idx;
@@ -180,15 +207,29 @@ svn_diff__lcs(svn_diff__position_t *posi
    */
   lcs = apr_palloc(pool, sizeof(*lcs));
   lcs->position[0] = apr_pcalloc(pool, sizeof(*lcs->position[0]));
-  lcs->position[0]->offset = position_list1 ? position_list1->offset + 1 : 1;
+  lcs->position[0]->offset = position_list1
+                             ? position_list1->offset + suffix_lines + 1
+                             : prefix_lines + suffix_lines + 1;
   lcs->position[1] = apr_pcalloc(pool, sizeof(*lcs->position[1]));
-  lcs->position[1]->offset = position_list2 ? position_list2->offset + 1 : 1;
+  lcs->position[1]->offset = position_list2
+                             ? position_list2->offset + suffix_lines + 1
+                             : prefix_lines + suffix_lines + 1;
   lcs->length = 0;
   lcs->refcount = 1;
   lcs->next = NULL;
 
   if (position_list1 == NULL || position_list2 == NULL)
-    return lcs;
+    {
+      if (suffix_lines)
+        lcs = prepend_lcs(lcs, suffix_lines, 
+                          lcs->position[0]->offset - suffix_lines,
+                          lcs->position[1]->offset - suffix_lines,
+                          pool);
+      if (prefix_lines)
+        lcs = prepend_lcs(lcs, prefix_lines, 1, 1, pool);
+
+      return lcs;
+    }
 
   /* Calculate length of both sequences to be compared */
   length[0] = position_list1->offset - position_list1->next->offset + 1;
@@ -243,11 +284,21 @@ svn_diff__lcs(svn_diff__position_t *posi
     }
   while (fp[d].position[1] != &sentinel_position[1]);
 
-  lcs->next = fp[d].lcs;
+  if (suffix_lines)
+    lcs->next = prepend_lcs(fp[d].lcs, suffix_lines,
+                            lcs->position[0]->offset - suffix_lines,
+                            lcs->position[1]->offset - suffix_lines,
+                            pool);
+  else
+    lcs->next = fp[d].lcs;
+  
   lcs = svn_diff__lcs_reverse(lcs);
 
   position_list1->next = sentinel_position[idx].next;
   position_list2->next = sentinel_position[abs(1 - idx)].next;
 
-  return lcs;
+  if (prefix_lines)
+    return prepend_lcs(lcs, prefix_lines, 1, 1, pool);
+  else
+    return lcs;
 }

Modified: subversion/branches/performance/subversion/libsvn_diff/token.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_diff/token.c?rev=1072522&r1=1072521&r2=1072522&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_diff/token.c (original)
+++ subversion/branches/performance/subversion/libsvn_diff/token.c Sun Feb 20 07:45:48 2011
@@ -71,7 +71,7 @@ svn_diff__tree_create(svn_diff__tree_t *
 static svn_error_t *
 tree_insert_token(svn_diff__node_t **node, svn_diff__tree_t *tree,
                   void *diff_baton,
-                  const svn_diff_fns_t *vtable,
+                  const svn_diff_fns2_t *vtable,
                   apr_uint32_t hash, void *token)
 {
   svn_diff__node_t *new_node;
@@ -137,8 +137,9 @@ svn_error_t *
 svn_diff__get_tokens(svn_diff__position_t **position_list,
                      svn_diff__tree_t *tree,
                      void *diff_baton,
-                     const svn_diff_fns_t *vtable,
+                     const svn_diff_fns2_t *vtable,
                      svn_diff_datasource_e datasource,
+                     apr_off_t prefix_lines,
                      apr_pool_t *pool)
 {
   svn_diff__position_t *start_position;
@@ -151,11 +152,8 @@ svn_diff__get_tokens(svn_diff__position_
 
   *position_list = NULL;
 
-
-  SVN_ERR(vtable->datasource_open(diff_baton, datasource));
-
   position_ref = &start_position;
-  offset = 0;
+  offset = prefix_lines;
   hash = 0; /* The callback fn doesn't need to touch it per se */
   while (1)
     {