]> arthur.barton.de Git - bup.git/blob - lib/bup/_helpers.c
Merge branch 'maint'
[bup.git] / lib / bup / _helpers.c
1 #include "bupsplit.h"
2 #include <Python.h>
3 #include <assert.h>
4 #include <stdint.h>
5 #include <fcntl.h>
6 #include <arpa/inet.h>
7
8 static PyObject *selftest(PyObject *self, PyObject *args)
9 {
10     if (!PyArg_ParseTuple(args, ""))
11         return NULL;
12     
13     return Py_BuildValue("i", !bupsplit_selftest());
14 }
15
16
17 static PyObject *blobbits(PyObject *self, PyObject *args)
18 {
19     if (!PyArg_ParseTuple(args, ""))
20         return NULL;
21     return Py_BuildValue("i", BUP_BLOBBITS);
22 }
23
24
25 static PyObject *splitbuf(PyObject *self, PyObject *args)
26 {
27     unsigned char *buf = NULL;
28     int len = 0, out = 0, bits = -1;
29
30     if (!PyArg_ParseTuple(args, "t#", &buf, &len))
31         return NULL;
32     out = bupsplit_find_ofs(buf, len, &bits);
33     return Py_BuildValue("ii", out, bits);
34 }
35
36
37 static PyObject *bitmatch(PyObject *self, PyObject *args)
38 {
39     unsigned char *buf1 = NULL, *buf2 = NULL;
40     int len1 = 0, len2 = 0;
41     int byte, bit;
42
43     if (!PyArg_ParseTuple(args, "t#t#", &buf1, &len1, &buf2, &len2))
44         return NULL;
45     
46     bit = 0;
47     for (byte = 0; byte < len1 && byte < len2; byte++)
48     {
49         int b1 = buf1[byte], b2 = buf2[byte];
50         if (b1 != b2)
51         {
52             for (bit = 0; bit < 8; bit++)
53                 if ( (b1 & (0x80 >> bit)) != (b2 & (0x80 >> bit)) )
54                     break;
55             break;
56         }
57     }
58     
59     return Py_BuildValue("i", byte*8 + bit);
60 }
61
62
63 static PyObject *firstword(PyObject *self, PyObject *args)
64 {
65     unsigned char *buf = NULL;
66     int len = 0;
67     uint32_t v;
68
69     if (!PyArg_ParseTuple(args, "t#", &buf, &len))
70         return NULL;
71     
72     if (len < 4)
73         return NULL;
74     
75     v = ntohl(*(uint32_t *)buf);
76     return Py_BuildValue("I", v);
77 }
78
79
80 static PyObject *extract_bits(PyObject *self, PyObject *args)
81 {
82     unsigned char *buf = NULL;
83     int len = 0, nbits = 0;
84     uint32_t v, mask;
85
86     if (!PyArg_ParseTuple(args, "t#i", &buf, &len, &nbits))
87         return NULL;
88     
89     if (len < 4)
90         return NULL;
91     
92     mask = (1<<nbits) - 1;
93     v = ntohl(*(uint32_t *)buf);
94     v = (v >> (32-nbits)) & mask;
95     return Py_BuildValue("I", v);
96 }
97
98
99 // I would have made this a lower-level function that just fills in a buffer
100 // with random values, and then written those values from python.  But that's
101 // about 20% slower in my tests, and since we typically generate random
102 // numbers for benchmarking other parts of bup, any slowness in generating
103 // random bytes will make our benchmarks inaccurate.  Plus nobody wants
104 // pseudorandom bytes much except for this anyway.
105 static PyObject *write_random(PyObject *self, PyObject *args)
106 {
107     uint32_t buf[1024/4];
108     int fd = -1, seed = 0;
109     ssize_t ret;
110     long long len = 0, kbytes = 0, written = 0;
111
112     if (!PyArg_ParseTuple(args, "iLi", &fd, &len, &seed))
113         return NULL;
114     
115     srandom(seed);
116     
117     for (kbytes = 0; kbytes < len/1024; kbytes++)
118     {
119         unsigned i;
120         for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
121             buf[i] = random();
122         ret = write(fd, buf, sizeof(buf));
123         if (ret < 0)
124             ret = 0;
125         written += ret;
126         if (ret < (int)sizeof(buf))
127             break;
128         if (kbytes/1024 > 0 && !(kbytes%1024))
129             fprintf(stderr, "Random: %lld Mbytes\r", kbytes/1024);
130     }
131     
132     // handle non-multiples of 1024
133     if (len % 1024)
134     {
135         unsigned i;
136         for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
137             buf[i] = random();
138         ret = write(fd, buf, len % 1024);
139         if (ret < 0)
140             ret = 0;
141         written += ret;
142     }
143     
144     if (kbytes/1024 > 0)
145         fprintf(stderr, "Random: %lld Mbytes, done.\n", kbytes/1024);
146     return Py_BuildValue("L", written);
147 }
148
149
150 static PyObject *open_noatime(PyObject *self, PyObject *args)
151 {
152     char *filename = NULL;
153     int attrs, attrs_noatime, fd;
154     if (!PyArg_ParseTuple(args, "s", &filename))
155         return NULL;
156     attrs = O_RDONLY;
157 #ifdef O_NOFOLLOW
158     attrs |= O_NOFOLLOW;
159 #endif
160 #ifdef O_LARGEFILE
161     attrs |= O_LARGEFILE;
162 #endif
163     attrs_noatime = attrs;
164 #ifdef O_NOATIME
165     attrs_noatime |= O_NOATIME;
166 #endif
167     fd = open(filename, attrs_noatime);
168     if (fd < 0 && errno == EPERM)
169     {
170         // older Linux kernels would return EPERM if you used O_NOATIME
171         // and weren't the file's owner.  This pointless restriction was
172         // relaxed eventually, but we have to handle it anyway.
173         // (VERY old kernels didn't recognized O_NOATIME, but they would
174         // just harmlessly ignore it, so this branch won't trigger)
175         fd = open(filename, attrs);
176     }
177     if (fd < 0)
178         return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
179     return Py_BuildValue("i", fd);
180 }
181
182
183 static PyObject *fadvise_done(PyObject *self, PyObject *args)
184 {
185     int fd = -1;
186     long long ofs = 0;
187     if (!PyArg_ParseTuple(args, "iL", &fd, &ofs))
188         return NULL;
189 #ifdef POSIX_FADV_DONTNEED
190     posix_fadvise(fd, 0, ofs, POSIX_FADV_DONTNEED);
191 #endif    
192     return Py_BuildValue("");
193 }
194
195
196 static PyMethodDef faster_methods[] = {
197     { "selftest", selftest, METH_VARARGS,
198         "Check that the rolling checksum rolls correctly (for unit tests)." },
199     { "blobbits", blobbits, METH_VARARGS,
200         "Return the number of bits in the rolling checksum." },
201     { "splitbuf", splitbuf, METH_VARARGS,
202         "Split a list of strings based on a rolling checksum." },
203     { "bitmatch", bitmatch, METH_VARARGS,
204         "Count the number of matching prefix bits between two strings." },
205     { "firstword", firstword, METH_VARARGS,
206         "Return an int corresponding to the first 32 bits of buf." },
207     { "extract_bits", extract_bits, METH_VARARGS,
208         "Take the first 'nbits' bits from 'buf' and return them as an int." },
209     { "write_random", write_random, METH_VARARGS,
210         "Write random bytes to the given file descriptor" },
211     { "open_noatime", open_noatime, METH_VARARGS,
212         "open() the given filename for read with O_NOATIME if possible" },
213     { "fadvise_done", fadvise_done, METH_VARARGS,
214         "Inform the kernel that we're finished with earlier parts of a file" },
215     { NULL, NULL, 0, NULL },  // sentinel
216 };
217
218 PyMODINIT_FUNC init_helpers(void)
219 {
220     Py_InitModule("_helpers", faster_methods);
221 }