]> arthur.barton.de Git - bup.git/commitdiff
Rename _hashsplit.so to _faster.so, and move bupsplit into its own source file.
authorAvery Pennarun <apenwarr@gmail.com>
Sat, 31 Jul 2010 00:23:08 +0000 (20:23 -0400)
committerAvery Pennarun <apenwarr@gmail.com>
Sat, 31 Jul 2010 00:31:29 +0000 (20:31 -0400)
A lot of stuff in _hashsplit.c wasn't actually about hashsplitting; it was
just a catch-all for all our C accelerator functions.  Now the module name
reflects that.

Also move the bupsplit functions into their own non-python-dependent C
source file so they can be used as part of other projects.

Signed-off-by: Avery Pennarun <apenwarr@gmail.com>
DESIGN
Makefile
cmd/margin-cmd.py
cmd/random-cmd.py
lib/bup/_faster.c [new file with mode: 0644]
lib/bup/_hashsplit.c [deleted file]
lib/bup/bupsplit.c [new file with mode: 0644]
lib/bup/bupsplit.h [new file with mode: 0644]
lib/bup/csetup.py
lib/bup/hashsplit.py
lib/bup/t/thashsplit.py

diff --git a/DESIGN b/DESIGN
index 00a56e4647338d16db34c3e9f5ddc2b12fd264d4..c0d9fb7d9fdd8813221c43a90405777d91e80f25 100644 (file)
--- a/DESIGN
+++ b/DESIGN
@@ -144,7 +144,7 @@ we need your help!  Avery's out of control!  Join our mailing list!  Please!
 Save us! ...  oh boy, I sure hope he doesn't read this)
 
 In any case, stupidsum, although stupid, seems to do pretty well at its job. 
-You can find it in _hashsplit.c.  Basically, it converts the last 32 bytes
+You can find it in bupsplit.c.  Basically, it converts the last 32 bytes
 of the file into a 32-bit integer.  What we then do is take the lowest 13
 bits of the checksum, and if they're all 1's, we consider that to be the end
 of a chunk.  This happens on average once every 2^13 = 8192 bytes, so the
index a555114c3a4e097ea3662fd5ed32122f4d964e59..47b03ead01072171baff6aa8e73307bcda5cbf90 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@ default: all
 
 all: bup Documentation/all
 
-bup: lib/bup/_version.py lib/bup/_hashsplit$(SOEXT) cmds
+bup: lib/bup/_version.py lib/bup/_faster$(SOEXT) cmds
 
 Documentation/all: bup
 
@@ -62,10 +62,11 @@ install: all
 %/clean:
        $(MAKE) -C $* clean
 
-lib/bup/_hashsplit$(SOEXT): lib/bup/_hashsplit.c lib/bup/csetup.py
+lib/bup/_faster$(SOEXT): \
+               lib/bup/bupsplit.c lib/bup/_faster.c lib/bup/csetup.py
        @rm -f $@
        cd lib/bup && $(PYTHON) csetup.py build
-       cp lib/bup/build/*/_hashsplit$(SOEXT) lib/bup/
+       cp lib/bup/build/*/_faster$(SOEXT) lib/bup/
 
 .PHONY: lib/bup/_version.py
 lib/bup/_version.py:
index 2564027c1d6363c0fab9adabf568a2ed38c212df..21f711f7e4acf37ab51622c336f161f1b33c804e 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 import sys
-from bup import options, git, _hashsplit
+from bup import options, git, _faster
 from bup.helpers import *
 
 
@@ -23,7 +23,7 @@ for i in mi:
     if i == last:
         continue
     #assert(str(i) >= last)
-    pm = _hashsplit.bitmatch(last, i)
+    pm = _faster.bitmatch(last, i)
     longmatch = max(longmatch, pm)
     last = i
 print longmatch
index 362a19cade569a3ee604ac3b4e07d42ce9145b7a..46fd4f2673db0cfe563f9f97f8eb0945cf0a07e7 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 import sys, mmap
-from bup import options, _hashsplit
+from bup import options, _faster
 from bup.helpers import *
 
 optspec = """
@@ -21,7 +21,7 @@ handle_ctrl_c()
 
 if opt.force or (not os.isatty(1) and
                  not atoi(os.environ.get('BUP_FORCE_TTY')) & 1):
-    _hashsplit.write_random(sys.stdout.fileno(), total, opt.seed)
+    _faster.write_random(sys.stdout.fileno(), total, opt.seed)
 else:
     log('error: not writing binary data to a terminal. Use -f to force.\n')
     sys.exit(1)
diff --git a/lib/bup/_faster.c b/lib/bup/_faster.c
new file mode 100644 (file)
index 0000000..cfab4fb
--- /dev/null
@@ -0,0 +1,180 @@
+#include "bupsplit.h"
+#include <Python.h>
+#include <assert.h>
+#include <stdint.h>
+#include <fcntl.h>
+
+static PyObject *selftest(PyObject *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ""))
+       return NULL;
+    
+    return Py_BuildValue("i", !bupsplit_selftest());
+}
+
+
+static PyObject *blobbits(PyObject *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ""))
+       return NULL;
+    return Py_BuildValue("i", BUP_BLOBBITS);
+}
+
+
+static PyObject *splitbuf(PyObject *self, PyObject *args)
+{
+    unsigned char *buf = NULL;
+    int len = 0, out = 0, bits = -1;
+
+    if (!PyArg_ParseTuple(args, "t#", &buf, &len))
+       return NULL;
+    out = bupsplit_find_ofs(buf, len, &bits);
+    return Py_BuildValue("ii", out, bits);
+}
+
+
+static PyObject *bitmatch(PyObject *self, PyObject *args)
+{
+    unsigned char *buf1 = NULL, *buf2 = NULL;
+    int len1 = 0, len2 = 0;
+    int byte, bit;
+
+    if (!PyArg_ParseTuple(args, "t#t#", &buf1, &len1, &buf2, &len2))
+       return NULL;
+    
+    bit = 0;
+    for (byte = 0; byte < len1 && byte < len2; byte++)
+    {
+       int b1 = buf1[byte], b2 = buf2[byte];
+       if (b1 != b2)
+       {
+           for (bit = 0; bit < 8; bit++)
+               if ( (b1 & (0x80 >> bit)) != (b2 & (0x80 >> bit)) )
+                   break;
+           break;
+       }
+    }
+    
+    return Py_BuildValue("i", byte*8 + bit);
+}
+
+
+// I would have made this a lower-level function that just fills in a buffer
+// with random values, and then written those values from python.  But that's
+// about 20% slower in my tests, and since we typically generate random
+// numbers for benchmarking other parts of bup, any slowness in generating
+// random bytes will make our benchmarks inaccurate.  Plus nobody wants
+// pseudorandom bytes much except for this anyway.
+static PyObject *write_random(PyObject *self, PyObject *args)
+{
+    uint32_t buf[1024/4];
+    int fd = -1, seed = 0;
+    ssize_t ret;
+    long long len = 0, kbytes = 0, written = 0;
+
+    if (!PyArg_ParseTuple(args, "iLi", &fd, &len, &seed))
+       return NULL;
+    
+    srandom(seed);
+    
+    for (kbytes = 0; kbytes < len/1024; kbytes++)
+    {
+       unsigned i;
+       for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
+           buf[i] = random();
+       ret = write(fd, buf, sizeof(buf));
+       if (ret < 0)
+           ret = 0;
+       written += ret;
+       if (ret < (int)sizeof(buf))
+           break;
+       if (kbytes/1024 > 0 && !(kbytes%1024))
+           fprintf(stderr, "Random: %lld Mbytes\r", kbytes/1024);
+    }
+    
+    // handle non-multiples of 1024
+    if (len % 1024)
+    {
+       unsigned i;
+       for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
+           buf[i] = random();
+       ret = write(fd, buf, len % 1024);
+       if (ret < 0)
+           ret = 0;
+       written += ret;
+    }
+    
+    if (kbytes/1024 > 0)
+       fprintf(stderr, "Random: %lld Mbytes, done.\n", kbytes/1024);
+    return Py_BuildValue("L", written);
+}
+
+
+static PyObject *open_noatime(PyObject *self, PyObject *args)
+{
+    char *filename = NULL;
+    int attrs, attrs_noatime, fd;
+    if (!PyArg_ParseTuple(args, "s", &filename))
+       return NULL;
+    attrs = O_RDONLY;
+#ifdef O_NOFOLLOW
+    attrs |= O_NOFOLLOW;
+#endif
+#ifdef O_LARGEFILE
+    attrs |= O_LARGEFILE;
+#endif
+    attrs_noatime = attrs;
+#ifdef O_NOATIME
+    attrs_noatime |= O_NOATIME;
+#endif
+    fd = open(filename, attrs_noatime);
+    if (fd < 0 && errno == EPERM)
+    {
+       // older Linux kernels would return EPERM if you used O_NOATIME
+       // and weren't the file's owner.  This pointless restriction was
+       // relaxed eventually, but we have to handle it anyway.
+       // (VERY old kernels didn't recognized O_NOATIME, but they would
+       // just harmlessly ignore it, so this branch won't trigger)
+       fd = open(filename, attrs);
+    }
+    if (fd < 0)
+       return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
+    return Py_BuildValue("i", fd);
+}
+
+
+static PyObject *fadvise_done(PyObject *self, PyObject *args)
+{
+    int fd = -1;
+    long long ofs = 0;
+    if (!PyArg_ParseTuple(args, "iL", &fd, &ofs))
+       return NULL;
+#ifdef POSIX_FADV_DONTNEED
+    posix_fadvise(fd, 0, ofs, POSIX_FADV_DONTNEED);
+#endif    
+    return Py_BuildValue("");
+}
+
+
+static PyMethodDef faster_methods[] = {
+    { "selftest", selftest, METH_VARARGS,
+       "Check that the rolling checksum rolls correctly (for unit tests)." },
+    { "blobbits", blobbits, METH_VARARGS,
+       "Return the number of bits in the rolling checksum." },
+    { "splitbuf", splitbuf, METH_VARARGS,
+       "Split a list of strings based on a rolling checksum." },
+    { "bitmatch", bitmatch, METH_VARARGS,
+       "Count the number of matching prefix bits between two strings." },
+    { "write_random", write_random, METH_VARARGS,
+       "Write random bytes to the given file descriptor" },
+    { "open_noatime", open_noatime, METH_VARARGS,
+       "open() the given filename for read with O_NOATIME if possible" },
+    { "fadvise_done", fadvise_done, METH_VARARGS,
+       "Inform the kernel that we're finished with earlier parts of a file" },
+    { NULL, NULL, 0, NULL },  // sentinel
+};
+
+PyMODINIT_FUNC init_faster(void)
+{
+    Py_InitModule("_faster", faster_methods);
+}
diff --git a/lib/bup/_hashsplit.c b/lib/bup/_hashsplit.c
deleted file mode 100644 (file)
index 8f7e4d1..0000000
+++ /dev/null
@@ -1,291 +0,0 @@
-#include <Python.h>
-#include <assert.h>
-#include <stdint.h>
-#include <fcntl.h>
-
-#define BLOBBITS (13)
-#define BLOBSIZE (1<<BLOBBITS)
-#define WINDOWBITS (7)
-#define WINDOWSIZE (1<<(WINDOWBITS-1))
-
-// According to librsync/rollsum.h:
-// "We should make this something other than zero to improve the
-// checksum algorithm: tridge suggests a prime number."
-// apenwarr: I unscientifically tried 0 and 7919, and they both ended up
-// slightly worse than the librsync value of 31 for my arbitrary test data.
-#define ROLLSUM_CHAR_OFFSET 31
-
-typedef struct {
-    unsigned s1, s2;
-    uint8_t window[WINDOWSIZE];
-    int wofs;
-} Rollsum;
-
-
-// These formulas are based on rollsum.h in the librsync project.
-static void rollsum_add(Rollsum *r, uint8_t drop, uint8_t add)
-{
-    r->s1 += add - drop;
-    r->s2 += r->s1 - (WINDOWSIZE * (drop + ROLLSUM_CHAR_OFFSET));
-}
-
-
-static void rollsum_init(Rollsum *r)
-{
-    r->s1 = WINDOWSIZE * ROLLSUM_CHAR_OFFSET;
-    r->s2 = WINDOWSIZE * (WINDOWSIZE-1) * ROLLSUM_CHAR_OFFSET;
-    r->wofs = 0;
-    memset(r->window, 0, WINDOWSIZE);
-}
-
-
-// For some reason, gcc 4.3 (at least) optimizes badly if find_ofs()
-// is static and rollsum_roll is an inline function.  Let's use a macro
-// here instead to help out the optimizer.
-#define rollsum_roll(r, ch) do { \
-    rollsum_add((r), (r)->window[(r)->wofs], (ch)); \
-    (r)->window[(r)->wofs] = (ch); \
-    (r)->wofs = ((r)->wofs + 1) % WINDOWSIZE; \
-} while (0)
-
-
-static uint32_t rollsum_digest(Rollsum *r)
-{
-    return (r->s1 << 16) | (r->s2 & 0xffff);
-}
-
-
-static uint32_t rollsum_sum(uint8_t *buf, size_t ofs, size_t len)
-{
-    size_t count;
-    Rollsum r;
-    rollsum_init(&r);
-    for (count = ofs; count < len; count++)
-       rollsum_roll(&r, buf[count]);
-    return rollsum_digest(&r);
-}
-
-
-static PyObject *selftest(PyObject *self, PyObject *args)
-{
-    uint8_t buf[100000];
-    uint32_t sum1a, sum1b, sum2a, sum2b, sum3a, sum3b;
-    unsigned count;
-    
-    if (!PyArg_ParseTuple(args, ""))
-       return NULL;
-    
-    srandom(1);
-    for (count = 0; count < sizeof(buf); count++)
-       buf[count] = random();
-    
-    sum1a = rollsum_sum(buf, 0, sizeof(buf));
-    sum1b = rollsum_sum(buf, 1, sizeof(buf));
-    sum2a = rollsum_sum(buf, sizeof(buf) - WINDOWSIZE*5/2,
-                       sizeof(buf) - WINDOWSIZE);
-    sum2b = rollsum_sum(buf, 0, sizeof(buf) - WINDOWSIZE);
-    sum3a = rollsum_sum(buf, 0, WINDOWSIZE+3);
-    sum3b = rollsum_sum(buf, 3, WINDOWSIZE+3);
-    
-    fprintf(stderr, "sum1a = 0x%08x\n", sum1a);
-    fprintf(stderr, "sum1b = 0x%08x\n", sum1b);
-    fprintf(stderr, "sum2a = 0x%08x\n", sum2a);
-    fprintf(stderr, "sum2b = 0x%08x\n", sum2b);
-    fprintf(stderr, "sum3a = 0x%08x\n", sum3a);
-    fprintf(stderr, "sum3b = 0x%08x\n", sum3b);
-    
-    return Py_BuildValue("i", sum1a==sum1b && sum2a==sum2b && sum3a==sum3b);
-}
-
-
-static int find_ofs(const unsigned char *buf, int len, int *bits)
-{
-    Rollsum r;
-    int count;
-    
-    rollsum_init(&r);
-    for (count = 0; count < len; count++)
-    {
-       rollsum_roll(&r, buf[count]);
-       if ((r.s2 & (BLOBSIZE-1)) == ((~0) & (BLOBSIZE-1)))
-       {
-           if (bits)
-           {
-               unsigned rsum = rollsum_digest(&r);
-               *bits = BLOBBITS;
-               rsum >>= BLOBBITS;
-               for (*bits = BLOBBITS; (rsum >>= 1) & 1; (*bits)++)
-                   ;
-           }
-           return count+1;
-       }
-    }
-    return 0;
-}
-
-
-static PyObject *blobbits(PyObject *self, PyObject *args)
-{
-    if (!PyArg_ParseTuple(args, ""))
-       return NULL;
-    return Py_BuildValue("i", BLOBBITS);
-}
-
-
-static PyObject *splitbuf(PyObject *self, PyObject *args)
-{
-    unsigned char *buf = NULL;
-    int len = 0, out = 0, bits = -1;
-
-    if (!PyArg_ParseTuple(args, "t#", &buf, &len))
-       return NULL;
-    out = find_ofs(buf, len, &bits);
-    return Py_BuildValue("ii", out, bits);
-}
-
-
-static PyObject *bitmatch(PyObject *self, PyObject *args)
-{
-    unsigned char *buf1 = NULL, *buf2 = NULL;
-    int len1 = 0, len2 = 0;
-    int byte, bit;
-
-    if (!PyArg_ParseTuple(args, "t#t#", &buf1, &len1, &buf2, &len2))
-       return NULL;
-    
-    bit = 0;
-    for (byte = 0; byte < len1 && byte < len2; byte++)
-    {
-       int b1 = buf1[byte], b2 = buf2[byte];
-       if (b1 != b2)
-       {
-           for (bit = 0; bit < 8; bit++)
-               if ( (b1 & (0x80 >> bit)) != (b2 & (0x80 >> bit)) )
-                   break;
-           break;
-       }
-    }
-    
-    return Py_BuildValue("i", byte*8 + bit);
-}
-
-
-// I would have made this a lower-level function that just fills in a buffer
-// with random values, and then written those values from python.  But that's
-// about 20% slower in my tests, and since we typically generate random
-// numbers for benchmarking other parts of bup, any slowness in generating
-// random bytes will make our benchmarks inaccurate.  Plus nobody wants
-// pseudorandom bytes much except for this anyway.
-static PyObject *write_random(PyObject *self, PyObject *args)
-{
-    uint32_t buf[1024/4];
-    int fd = -1, seed = 0;
-    ssize_t ret;
-    long long len = 0, kbytes = 0, written = 0;
-
-    if (!PyArg_ParseTuple(args, "iLi", &fd, &len, &seed))
-       return NULL;
-    
-    srandom(seed);
-    
-    for (kbytes = 0; kbytes < len/1024; kbytes++)
-    {
-       unsigned i;
-       for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
-           buf[i] = random();
-       ret = write(fd, buf, sizeof(buf));
-       if (ret < 0)
-           ret = 0;
-       written += ret;
-       if (ret < (int)sizeof(buf))
-           break;
-       if (kbytes/1024 > 0 && !(kbytes%1024))
-           fprintf(stderr, "Random: %lld Mbytes\r", kbytes/1024);
-    }
-    
-    // handle non-multiples of 1024
-    if (len % 1024)
-    {
-       unsigned i;
-       for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
-           buf[i] = random();
-       ret = write(fd, buf, len % 1024);
-       if (ret < 0)
-           ret = 0;
-       written += ret;
-    }
-    
-    if (kbytes/1024 > 0)
-       fprintf(stderr, "Random: %lld Mbytes, done.\n", kbytes/1024);
-    return Py_BuildValue("L", written);
-}
-
-
-static PyObject *open_noatime(PyObject *self, PyObject *args)
-{
-    char *filename = NULL;
-    int attrs, attrs_noatime, fd;
-    if (!PyArg_ParseTuple(args, "s", &filename))
-       return NULL;
-    attrs = O_RDONLY;
-#ifdef O_NOFOLLOW
-    attrs |= O_NOFOLLOW;
-#endif
-#ifdef O_LARGEFILE
-    attrs |= O_LARGEFILE;
-#endif
-    attrs_noatime = attrs;
-#ifdef O_NOATIME
-    attrs_noatime |= O_NOATIME;
-#endif
-    fd = open(filename, attrs_noatime);
-    if (fd < 0 && errno == EPERM)
-    {
-       // older Linux kernels would return EPERM if you used O_NOATIME
-       // and weren't the file's owner.  This pointless restriction was
-       // relaxed eventually, but we have to handle it anyway.
-       // (VERY old kernels didn't recognized O_NOATIME, but they would
-       // just harmlessly ignore it, so this branch won't trigger)
-       fd = open(filename, attrs);
-    }
-    if (fd < 0)
-       return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
-    return Py_BuildValue("i", fd);
-}
-
-
-static PyObject *fadvise_done(PyObject *self, PyObject *args)
-{
-    int fd = -1;
-    long long ofs = 0;
-    if (!PyArg_ParseTuple(args, "iL", &fd, &ofs))
-       return NULL;
-#ifdef POSIX_FADV_DONTNEED
-    posix_fadvise(fd, 0, ofs, POSIX_FADV_DONTNEED);
-#endif    
-    return Py_BuildValue("");
-}
-
-
-static PyMethodDef hashsplit_methods[] = {
-    { "selftest", selftest, METH_VARARGS,
-       "Check that the rolling checksum rolls correctly (for unit tests)." },
-    { "blobbits", blobbits, METH_VARARGS,
-       "Return the number of bits in the rolling checksum." },
-    { "splitbuf", splitbuf, METH_VARARGS,
-       "Split a list of strings based on a rolling checksum." },
-    { "bitmatch", bitmatch, METH_VARARGS,
-       "Count the number of matching prefix bits between two strings." },
-    { "write_random", write_random, METH_VARARGS,
-       "Write random bytes to the given file descriptor" },
-    { "open_noatime", open_noatime, METH_VARARGS,
-       "open() the given filename for read with O_NOATIME if possible" },
-    { "fadvise_done", fadvise_done, METH_VARARGS,
-       "Inform the kernel that we're finished with earlier parts of a file" },
-    { NULL, NULL, 0, NULL },  // sentinel
-};
-
-PyMODINIT_FUNC init_hashsplit(void)
-{
-    Py_InitModule("_hashsplit", hashsplit_methods);
-}
diff --git a/lib/bup/bupsplit.c b/lib/bup/bupsplit.c
new file mode 100644 (file)
index 0000000..532619a
--- /dev/null
@@ -0,0 +1,122 @@
+#include "bupsplit.h"
+#include <assert.h>
+#include <stdint.h>
+#include <memory.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+// According to librsync/rollsum.h:
+// "We should make this something other than zero to improve the
+// checksum algorithm: tridge suggests a prime number."
+// apenwarr: I unscientifically tried 0 and 7919, and they both ended up
+// slightly worse than the librsync value of 31 for my arbitrary test data.
+#define ROLLSUM_CHAR_OFFSET 31
+
+typedef struct {
+    unsigned s1, s2;
+    uint8_t window[BUP_WINDOWSIZE];
+    int wofs;
+} Rollsum;
+
+
+// These formulas are based on rollsum.h in the librsync project.
+static void rollsum_add(Rollsum *r, uint8_t drop, uint8_t add)
+{
+    r->s1 += add - drop;
+    r->s2 += r->s1 - (BUP_WINDOWSIZE * (drop + ROLLSUM_CHAR_OFFSET));
+}
+
+
+static void rollsum_init(Rollsum *r)
+{
+    r->s1 = BUP_WINDOWSIZE * ROLLSUM_CHAR_OFFSET;
+    r->s2 = BUP_WINDOWSIZE * (BUP_WINDOWSIZE-1) * ROLLSUM_CHAR_OFFSET;
+    r->wofs = 0;
+    memset(r->window, 0, BUP_WINDOWSIZE);
+}
+
+
+// For some reason, gcc 4.3 (at least) optimizes badly if find_ofs()
+// is static and rollsum_roll is an inline function.  Let's use a macro
+// here instead to help out the optimizer.
+#define rollsum_roll(r, ch) do { \
+    rollsum_add((r), (r)->window[(r)->wofs], (ch)); \
+    (r)->window[(r)->wofs] = (ch); \
+    (r)->wofs = ((r)->wofs + 1) % BUP_WINDOWSIZE; \
+} while (0)
+
+
+static uint32_t rollsum_digest(Rollsum *r)
+{
+    return (r->s1 << 16) | (r->s2 & 0xffff);
+}
+
+
+static uint32_t rollsum_sum(uint8_t *buf, size_t ofs, size_t len)
+{
+    size_t count;
+    Rollsum r;
+    rollsum_init(&r);
+    for (count = ofs; count < len; count++)
+       rollsum_roll(&r, buf[count]);
+    return rollsum_digest(&r);
+}
+
+
+int bupsplit_find_ofs(const unsigned char *buf, int len, int *bits)
+{
+    Rollsum r;
+    int count;
+    
+    rollsum_init(&r);
+    for (count = 0; count < len; count++)
+    {
+       rollsum_roll(&r, buf[count]);
+       if ((r.s2 & (BUP_BLOBSIZE-1)) == ((~0) & (BUP_BLOBSIZE-1)))
+       {
+           if (bits)
+           {
+               unsigned rsum = rollsum_digest(&r);
+               *bits = BUP_BLOBBITS;
+               rsum >>= BUP_BLOBBITS;
+               for (*bits = BUP_BLOBBITS; (rsum >>= 1) & 1; (*bits)++)
+                   ;
+           }
+           return count+1;
+       }
+    }
+    return 0;
+}
+
+
+#ifndef BUP_NO_SELFTEST
+
+int bupsplit_selftest()
+{
+    uint8_t buf[100000];
+    uint32_t sum1a, sum1b, sum2a, sum2b, sum3a, sum3b;
+    unsigned count;
+    
+    srandom(1);
+    for (count = 0; count < sizeof(buf); count++)
+       buf[count] = random();
+    
+    sum1a = rollsum_sum(buf, 0, sizeof(buf));
+    sum1b = rollsum_sum(buf, 1, sizeof(buf));
+    sum2a = rollsum_sum(buf, sizeof(buf) - BUP_WINDOWSIZE*5/2,
+                       sizeof(buf) - BUP_WINDOWSIZE);
+    sum2b = rollsum_sum(buf, 0, sizeof(buf) - BUP_WINDOWSIZE);
+    sum3a = rollsum_sum(buf, 0, BUP_WINDOWSIZE+3);
+    sum3b = rollsum_sum(buf, 3, BUP_WINDOWSIZE+3);
+    
+    fprintf(stderr, "sum1a = 0x%08x\n", sum1a);
+    fprintf(stderr, "sum1b = 0x%08x\n", sum1b);
+    fprintf(stderr, "sum2a = 0x%08x\n", sum2a);
+    fprintf(stderr, "sum2b = 0x%08x\n", sum2b);
+    fprintf(stderr, "sum3a = 0x%08x\n", sum3a);
+    fprintf(stderr, "sum3b = 0x%08x\n", sum3b);
+    
+    return sum1a!=sum1b || sum2a!=sum2b || sum3a!=sum3b;
+}
+
+#endif // !BUP_NO_SELFTEST
diff --git a/lib/bup/bupsplit.h b/lib/bup/bupsplit.h
new file mode 100644 (file)
index 0000000..2fb6eb7
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __BUPSPLIT_H
+#define __BUPSPLIT_H
+
+#define BUP_BLOBBITS (13)
+#define BUP_BLOBSIZE (1<<BUP_BLOBBITS)
+#define BUP_WINDOWBITS (7)
+#define BUP_WINDOWSIZE (1<<(BUP_WINDOWBITS-1))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    
+int bupsplit_find_ofs(const unsigned char *buf, int len, int *bits);
+int bupsplit_selftest(void);
+
+#ifdef __cplusplus
+}
+#endif
+    
+#endif /* __BUPSPLIT_H */
index b58932c0ae2cf5dcddaf4576b9194b4da0787397..6036e4281ea5e4f956d0c2abe4913be245ee97f4 100644 (file)
@@ -1,8 +1,8 @@
 from distutils.core import setup, Extension
 
-_hashsplit_mod = Extension('_hashsplit', sources=['_hashsplit.c'])
+_faster_mod = Extension('_faster', sources=['_faster.c', 'bupsplit.c'])
 
-setup(name='_hashsplit',
+setup(name='_faster',
       version='0.1',
-      description='hashsplit helper library for bup',
-      ext_modules=[_hashsplit_mod])
+      description='accelerator library for bup',
+      ext_modules=[_faster_mod])
index f0fb7974e8999a0191fd2bfa7a246aa16c47f6b7..28e90d28a676f24aa121e070f3e60fe74d3f27e2 100644 (file)
@@ -1,5 +1,5 @@
 import sys, math
-from bup import _hashsplit
+from bup import _faster
 from bup.helpers import *
 
 BLOB_LWM = 8192*2
@@ -38,7 +38,7 @@ class Buf:
 
 def splitbuf(buf):
     b = buf.peek(buf.used())
-    (ofs, bits) = _hashsplit.splitbuf(b)
+    (ofs, bits) = _faster.splitbuf(b)
     if ofs:
         buf.eat(ofs)
         return (buffer(b, 0, ofs), bits)
@@ -135,7 +135,7 @@ def split_to_shalist(w, files):
             shal.append(('100644', sha, size))
         return _make_shalist(shal)[0]
     else:
-        base_bits = _hashsplit.blobbits()
+        base_bits = _faster.blobbits()
         fanout_bits = int(math.log(fanout, 2))
         def bits_to_idx(n):
             assert(n >= base_bits)
@@ -163,7 +163,7 @@ def split_to_blob_or_tree(w, files):
 
 
 def open_noatime(name):
-    fd = _hashsplit.open_noatime(name)
+    fd = _faster.open_noatime(name)
     try:
         return os.fdopen(fd, 'rb', 1024*1024)
     except:
@@ -177,4 +177,4 @@ def open_noatime(name):
 def fadvise_done(f, ofs):
     assert(ofs >= 0)
     if ofs > 0:
-        _hashsplit.fadvise_done(f.fileno(), ofs)
+        _faster.fadvise_done(f.fileno(), ofs)
index 9f56ca3a9b84a9fd8cb2c88e4aa2628750ce9a08..bd8a015ede8e1910e7cec3e9803440da962fe638 100644 (file)
@@ -1,6 +1,6 @@
-from bup import hashsplit, _hashsplit
+from bup import hashsplit, _faster
 from wvtest import *
 
 @wvtest
 def test_rolling_sums():
-    WVPASS(_hashsplit.selftest())
+    WVPASS(_faster.selftest())