ident: rename IDENT_ERROR_ON_NO_NAME to IDENT_STRICT
[gitweb.git] / xdiff / xutils.c
index ab6503460f760356a393a3c7b3b8438213e1ee94..1b3b471ac86a30474e3acabea3d2df3b6da072d8 100644 (file)
  *
  */
 
+#include <limits.h>
+#include <assert.h>
 #include "xinclude.h"
 
 
 
-#define XDL_GUESS_NLINES 256
-
-
-
 
 long xdl_bogosqrt(long n) {
        long i;
@@ -71,12 +69,6 @@ void *xdl_mmfile_first(mmfile_t *mmf, long *size)
 }
 
 
-void *xdl_mmfile_next(mmfile_t *mmf, long *size)
-{
-       return NULL;
-}
-
-
 long xdl_mmfile_size(mmfile_t *mmf)
 {
        return mmf->size;
@@ -159,18 +151,12 @@ void *xdl_cha_next(chastore_t *cha) {
 }
 
 
-long xdl_guess_lines(mmfile_t *mf) {
+long xdl_guess_lines(mmfile_t *mf, long sample) {
        long nl = 0, size, tsize = 0;
        char const *data, *cur, *top;
 
        if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {
-               for (top = data + size; nl < XDL_GUESS_NLINES;) {
-                       if (cur >= top) {
-                               tsize += (long) (cur - data);
-                               if (!(cur = data = xdl_mmfile_next(mf, &size)))
-                                       break;
-                               top = data + size;
-                       }
+               for (top = data + size; nl < sample && cur < top; ) {
                        nl++;
                        if (!(cur = memchr(cur, '\n', top - cur)))
                                cur = top;
@@ -292,6 +278,109 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data,
        return ha;
 }
 
+#ifdef XDL_FAST_HASH
+
+#define ONEBYTES       0x0101010101010101ul
+#define NEWLINEBYTES   0x0a0a0a0a0a0a0a0aul
+#define HIGHBITS       0x8080808080808080ul
+
+/* Return the high bit set in the first byte that is a zero */
+static inline unsigned long has_zero(unsigned long a)
+{
+       return ((a - ONEBYTES) & ~a) & HIGHBITS;
+}
+
+static inline long count_masked_bytes(unsigned long mask)
+{
+       if (sizeof(long) == 8) {
+               /*
+                * Jan Achrenius on G+: microoptimized version of
+                * the simpler "(mask & ONEBYTES) * ONEBYTES >> 56"
+                * that works for the bytemasks without having to
+                * mask them first.
+                */
+               return mask * 0x0001020304050608 >> 56;
+       } else {
+               /*
+                * Modified Carl Chatfield G+ version for 32-bit *
+                *
+                * (a) gives us
+                *   -1 (0, ff), 0 (ffff) or 1 (ffffff)
+                * (b) gives us
+                *   0 for 0, 1 for (ff ffff ffffff)
+                * (a+b+1) gives us
+                *   correct 0-3 bytemask count result
+                */
+               long a = (mask - 256) >> 23;
+               long b = mask & 1;
+               return a + b + 1;
+       }
+}
+
+unsigned long xdl_hash_record(char const **data, char const *top, long flags)
+{
+       unsigned long hash = 5381;
+       unsigned long a = 0, mask = 0;
+       char const *ptr = *data;
+       char const *end = top - sizeof(unsigned long) + 1;
+
+       if (flags & XDF_WHITESPACE_FLAGS)
+               return xdl_hash_record_with_whitespace(data, top, flags);
+
+       ptr -= sizeof(unsigned long);
+       do {
+               hash += hash << 5;
+               hash ^= a;
+               ptr += sizeof(unsigned long);
+               if (ptr >= end)
+                       break;
+               a = *(unsigned long *)ptr;
+               /* Do we have any '\n' bytes in this word? */
+               mask = has_zero(a ^ NEWLINEBYTES);
+       } while (!mask);
+
+       if (ptr >= end) {
+               /*
+                * There is only a partial word left at the end of the
+                * buffer. Because we may work with a memory mapping,
+                * we have to grab the rest byte by byte instead of
+                * blindly reading it.
+                *
+                * To avoid problems with masking in a signed value,
+                * we use an unsigned char here.
+                */
+               const char *p;
+               for (p = top - 1; p >= ptr; p--)
+                       a = (a << 8) + *((const unsigned char *)p);
+               mask = has_zero(a ^ NEWLINEBYTES);
+               if (!mask)
+                       /*
+                        * No '\n' found in the partial word.  Make a
+                        * mask that matches what we read.
+                        */
+                       mask = 1UL << (8 * (top - ptr) + 7);
+       }
+
+       /* The mask *below* the first high bit set */
+       mask = (mask - 1) & ~mask;
+       mask >>= 7;
+       hash += hash << 5;
+       hash ^= a & mask;
+
+       /* Advance past the last (possibly partial) word */
+       ptr += count_masked_bytes(mask);
+
+       if (ptr < top) {
+               assert(*ptr == '\n');
+               ptr++;
+       }
+
+       *data = ptr;
+
+       return hash;
+}
+
+#else /* XDL_FAST_HASH */
 
 unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
        unsigned long ha = 5381;
@@ -309,6 +398,7 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
        return ha;
 }
 
+#endif /* XDL_FAST_HASH */
 
 unsigned int xdl_hashbits(unsigned int size) {
        unsigned int val = 1, bits = 0;
@@ -402,3 +492,34 @@ int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
 
        return 0;
 }
+
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+               int line1, int count1, int line2, int count2)
+{
+       /*
+        * This probably does not work outside Git, since
+        * we have a very simple mmfile structure.
+        *
+        * Note: ideally, we would reuse the prepared environment, but
+        * the libxdiff interface does not (yet) allow for diffing only
+        * ranges of lines instead of the whole files.
+        */
+       mmfile_t subfile1, subfile2;
+       xdfenv_t env;
+
+       subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
+       subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
+               diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+       subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
+       subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
+               diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+       if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
+               return -1;
+
+       memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
+       memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+
+       xdl_free_env(&env);
+
+       return 0;
+}