7 #define BLOBSIZE (1<<BLOBBITS)
9 #define WINDOWSIZE (1<<(WINDOWBITS-1))
14 uint8_t window[WINDOWSIZE];
19 // FIXME: replace this with a not-stupid rolling checksum algorithm,
20 // such as the one used in rsync (Adler32?)
21 static void rollsum_add(Rollsum *r, uint8_t drop, uint8_t add)
23 r->sum = ((r->sum<<1) | (r->sum>>31)) ^ drop ^ add;
27 static void rollsum_init(Rollsum *r)
30 memset(r->window, 0, WINDOWSIZE);
34 // For some reason, gcc 4.3 (at least) optimizes badly if find_ofs()
35 // is static and rollsum_roll is an inline function. Let's use a macro
36 // here instead to help out the optimizer.
37 #define rollsum_roll(r, ch) do { \
38 rollsum_add((r), (r)->window[(r)->wofs], (ch)); \
39 (r)->window[(r)->wofs] = (ch); \
40 (r)->wofs = ((r)->wofs + 1) % WINDOWSIZE; \
44 static uint32_t rollsum_sum(uint8_t *buf, size_t ofs, size_t len)
49 for (count = ofs; count < len; count++)
50 rollsum_roll(&r, buf[count]);
55 static PyObject *selftest(PyObject *self, PyObject *args)
58 uint32_t sum1a, sum1b, sum2a, sum2b, sum3a, sum3b;
61 if (!PyArg_ParseTuple(args, ""))
65 for (count = 0; count < sizeof(buf); count++)
66 buf[count] = random();
68 sum1a = rollsum_sum(buf, 0, sizeof(buf));
69 sum1b = rollsum_sum(buf, 1, sizeof(buf));
70 sum2a = rollsum_sum(buf, sizeof(buf) - WINDOWSIZE*5/2,
71 sizeof(buf) - WINDOWSIZE);
72 sum2b = rollsum_sum(buf, 0, sizeof(buf) - WINDOWSIZE);
73 sum3a = rollsum_sum(buf, 0, WINDOWSIZE+3);
74 sum3b = rollsum_sum(buf, 3, WINDOWSIZE+3);
76 fprintf(stderr, "sum1a = 0x%08x\n", sum1a);
77 fprintf(stderr, "sum1b = 0x%08x\n", sum1b);
78 fprintf(stderr, "sum2a = 0x%08x\n", sum2a);
79 fprintf(stderr, "sum2b = 0x%08x\n", sum2b);
80 fprintf(stderr, "sum3a = 0x%08x\n", sum3a);
81 fprintf(stderr, "sum3b = 0x%08x\n", sum3b);
83 return Py_BuildValue("i", sum1a==sum1b && sum2a==sum2b && sum3a==sum3b);
87 static int find_ofs(const unsigned char *buf, int len, int *bits)
93 for (count = 0; count < len; count++)
95 rollsum_roll(&r, buf[count]);
96 if ((r.sum & (BLOBSIZE-1)) == ((~0) & (BLOBSIZE-1)))
102 for (*bits = BLOBBITS; (r.sum >>= 1) & 1; (*bits)++)
112 static PyObject *blobbits(PyObject *self, PyObject *args)
114 if (!PyArg_ParseTuple(args, ""))
116 return Py_BuildValue("i", BLOBBITS);
120 static PyObject *splitbuf(PyObject *self, PyObject *args)
122 unsigned char *buf = NULL;
123 int len = 0, out = 0, bits = -1;
125 if (!PyArg_ParseTuple(args, "t#", &buf, &len))
127 out = find_ofs(buf, len, &bits);
128 return Py_BuildValue("ii", out, bits);
132 static PyObject *bitmatch(PyObject *self, PyObject *args)
134 unsigned char *buf1 = NULL, *buf2 = NULL;
135 int len1 = 0, len2 = 0;
138 if (!PyArg_ParseTuple(args, "t#t#", &buf1, &len1, &buf2, &len2))
142 for (byte = 0; byte < len1 && byte < len2; byte++)
144 int b1 = buf1[byte], b2 = buf2[byte];
147 for (bit = 0; bit < 8; bit++)
148 if ( (b1 & (0x80 >> bit)) != (b2 & (0x80 >> bit)) )
154 return Py_BuildValue("i", byte*8 + bit);
158 // I would have made this a lower-level function that just fills in a buffer
159 // with random values, and then written those values from python. But that's
160 // about 20% slower in my tests, and since we typically generate random
161 // numbers for benchmarking other parts of bup, any slowness in generating
162 // random bytes will make our benchmarks inaccurate. Plus nobody wants
163 // pseudorandom bytes much except for this anyway.
164 static PyObject *write_random(PyObject *self, PyObject *args)
166 uint32_t buf[1024/4];
167 int fd = -1, seed = 0;
169 long long len = 0, kbytes = 0, written = 0;
171 if (!PyArg_ParseTuple(args, "iLi", &fd, &len, &seed))
176 for (kbytes = 0; kbytes < len/1024; kbytes++)
179 for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
181 ret = write(fd, buf, sizeof(buf));
185 if (ret < sizeof(buf))
187 if (kbytes/1024 > 0 && !(kbytes%1024))
188 fprintf(stderr, "Random: %lld Mbytes\r", kbytes/1024);
191 // handle non-multiples of 1024
195 for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
197 ret = write(fd, buf, len % 1024);
204 fprintf(stderr, "Random: %lld Mbytes, done.\n", kbytes/1024);
205 return Py_BuildValue("L", written);
209 static PyObject *open_noatime(PyObject *self, PyObject *args)
211 char *filename = NULL;
212 int attrs, attrs_noatime, fd;
213 if (!PyArg_ParseTuple(args, "s", &filename))
220 attrs |= O_LARGEFILE;
222 attrs_noatime = attrs;
224 attrs_noatime |= O_NOATIME;
226 fd = open(filename, attrs_noatime);
227 if (fd < 0 && errno == EPERM)
229 // older Linux kernels would return EPERM if you used O_NOATIME
230 // and weren't the file's owner. This pointless restriction was
231 // relaxed eventually, but we have to handle it anyway.
232 // (VERY old kernels didn't recognized O_NOATIME, but they would
233 // just harmlessly ignore it, so this branch won't trigger)
234 fd = open(filename, attrs);
237 return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
238 return Py_BuildValue("i", fd);
242 static PyObject *fadvise_done(PyObject *self, PyObject *args)
246 if (!PyArg_ParseTuple(args, "iL", &fd, &ofs))
248 #ifdef POSIX_FADV_DONTNEED
249 posix_fadvise(fd, 0, ofs, POSIX_FADV_DONTNEED);
251 return Py_BuildValue("");
255 static PyMethodDef hashsplit_methods[] = {
256 { "selftest", selftest, METH_VARARGS,
257 "Check that the rolling checksum rolls correctly (for unit tests)." },
258 { "blobbits", blobbits, METH_VARARGS,
259 "Return the number of bits in the rolling checksum." },
260 { "splitbuf", splitbuf, METH_VARARGS,
261 "Split a list of strings based on a rolling checksum." },
262 { "bitmatch", bitmatch, METH_VARARGS,
263 "Count the number of matching prefix bits between two strings." },
264 { "write_random", write_random, METH_VARARGS,
265 "Write random bytes to the given file descriptor" },
266 { "open_noatime", open_noatime, METH_VARARGS,
267 "open() the given filename for read with O_NOATIME if possible" },
268 { "fadvise_done", fadvise_done, METH_VARARGS,
269 "Inform the kernel that we're finished with earlier parts of a file" },
270 { NULL, NULL, 0, NULL }, // sentinel
273 PyMODINIT_FUNC init_hashsplit(void)
275 Py_InitModule("_hashsplit", hashsplit_methods);