_speedups.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. #include <Python.h>
  2. #include <structmember.h>
  3. #include <stdint.h>
  4. /* Cyclic polynomial / buzhash: https://en.wikipedia.org/wiki/Rolling_hash */
  5. static uint32_t table_base[] =
  6. {
  7. 0xe7f831ec, 0xf4026465, 0xafb50cae, 0x6d553c7a, 0xd639efe3, 0x19a7b895, 0x9aba5b21, 0x5417d6d4,
  8. 0x35fd2b84, 0xd1f6a159, 0x3f8e323f, 0xb419551c, 0xf444cebf, 0x21dc3b80, 0xde8d1e36, 0x84a32436,
  9. 0xbeb35a9d, 0xa36f24aa, 0xa4e60186, 0x98d18ffe, 0x3f042f9e, 0xdb228bcd, 0x096474b7, 0x5c20c2f7,
  10. 0xf9eec872, 0xe8625275, 0xb9d38f80, 0xd48eb716, 0x22a950b4, 0x3cbaaeaa, 0xc37cddd3, 0x8fea6f6a,
  11. 0x1d55d526, 0x7fd6d3b3, 0xdaa072ee, 0x4345ac40, 0xa077c642, 0x8f2bd45b, 0x28509110, 0x55557613,
  12. 0xffc17311, 0xd961ffef, 0xe532c287, 0xaab95937, 0x46d38365, 0xb065c703, 0xf2d91d0f, 0x92cd4bb0,
  13. 0x4007c712, 0xf35509dd, 0x505b2f69, 0x557ead81, 0x310f4563, 0xbddc5be8, 0x9760f38c, 0x701e0205,
  14. 0x00157244, 0x14912826, 0xdc4ca32b, 0x67b196de, 0x5db292e8, 0x8c1b406b, 0x01f34075, 0xfa2520f7,
  15. 0x73bc37ab, 0x1e18bc30, 0xfe2c6cb3, 0x20c522d0, 0x5639e3db, 0x942bda35, 0x899af9d1, 0xced44035,
  16. 0x98cc025b, 0x255f5771, 0x70fefa24, 0xe928fa4d, 0x2c030405, 0xb9325590, 0x20cb63bd, 0xa166305d,
  17. 0x80e52c0a, 0xa8fafe2f, 0x1ad13f7d, 0xcfaf3685, 0x6c83a199, 0x7d26718a, 0xde5dfcd9, 0x79cf7355,
  18. 0x8979d7fb, 0xebf8c55e, 0xebe408e4, 0xcd2affba, 0xe483be6e, 0xe239d6de, 0x5dc1e9e0, 0x0473931f,
  19. 0x851b097c, 0xac5db249, 0x09c0f9f2, 0xd8d2f134, 0xe6f38e41, 0xb1c71bf1, 0x52b6e4db, 0x07224424,
  20. 0x6cf73e85, 0x4f25d89c, 0x782a7d74, 0x10a68dcd, 0x3a868189, 0xd570d2dc, 0x69630745, 0x9542ed86,
  21. 0x331cd6b2, 0xa84b5b28, 0x07879c9d, 0x38372f64, 0x7185db11, 0x25ba7c83, 0x01061523, 0xe6792f9f,
  22. 0xe5df07d1, 0x4321b47f, 0x7d2469d8, 0x1a3a4f90, 0x48be29a3, 0x669071af, 0x8ec8dd31, 0x0810bfbf,
  23. 0x813a06b4, 0x68538345, 0x65865ddc, 0x43a71b8e, 0x78619a56, 0x5a34451d, 0x5bdaa3ed, 0x71edc7e9,
  24. 0x17ac9a20, 0x78d10bfa, 0x6c1e7f35, 0xd51839d9, 0x240cbc51, 0x33513cc1, 0xd2b4f795, 0xccaa8186,
  25. 0x0babe682, 0xa33cf164, 0x18c643ea, 0xc1ca105f, 0x9959147a, 0x6d3d94de, 0x0b654fbe, 0xed902ca0,
  26. 0x7d835cb5, 0x99ba1509, 0x6445c922, 0x495e76c2, 0xf07194bc, 0xa1631d7e, 0x677076a5, 0x89fffe35,
  27. 0x1a49bcf3, 0x8e6c948a, 0x0144c917, 0x8d93aea1, 0x16f87ddf, 0xc8f25d49, 0x1fb11297, 0x27e750cd,
  28. 0x2f422da1, 0xdee89a77, 0x1534c643, 0x457b7b8b, 0xaf172f7a, 0x6b9b09d6, 0x33573f7f, 0xf14e15c4,
  29. 0x526467d5, 0xaf488241, 0x87c3ee0d, 0x33be490c, 0x95aa6e52, 0x43ec242e, 0xd77de99b, 0xd018334f,
  30. 0x5b78d407, 0x498eb66b, 0xb1279fa8, 0xb38b0ea6, 0x90718376, 0xe325dee2, 0x8e2f2cba, 0xcaa5bdec,
  31. 0x9d652c56, 0xad68f5cb, 0xa77591af, 0x88e37ee8, 0xf8faa221, 0xfcbbbe47, 0x4f407786, 0xaf393889,
  32. 0xf444a1d9, 0x15ae1a2f, 0x40aa7097, 0x6f9486ac, 0x29d232a3, 0xe47609e9, 0xe8b631ff, 0xba8565f4,
  33. 0x11288749, 0x46c9a838, 0xeb1b7cd8, 0xf516bbb1, 0xfb74fda0, 0x010996e6, 0x4c994653, 0x1d889512,
  34. 0x53dcd9a3, 0xdd074697, 0x1e78e17c, 0x637c98bf, 0x930bb219, 0xcf7f75b0, 0xcb9355fb, 0x9e623009,
  35. 0xe466d82c, 0x28f968d3, 0xfeb385d9, 0x238e026c, 0xb8ed0560, 0x0c6a027a, 0x3d6fec4b, 0xbb4b2ec2,
  36. 0xe715031c, 0xeded011d, 0xcdc4d3b9, 0xc456fc96, 0xdd0eea20, 0xb3df8ec9, 0x12351993, 0xd9cbb01c,
  37. 0x603147a2, 0xcf37d17d, 0xf7fcd9dc, 0xd8556fa3, 0x104c8131, 0x13152774, 0xb4715811, 0x6a72c2c9,
  38. 0xc5ae37bb, 0xa76ce12a, 0x8150d8f3, 0x2ec29218, 0xa35f0984, 0x48c0647e, 0x0b5ff98c, 0x71893f7b
  39. };
  40. #define BARREL_SHIFT(v, shift) ( ((v) << ((shift) & 0x1f)) | ((v) >> (32 - ((shift) & 0x1f))) )
  41. static uint32_t *
  42. init_buzhash_table(uint32_t seed)
  43. {
  44. int i;
  45. uint32_t *table = malloc(1024);
  46. for(i = 0; i < 256; i++)
  47. {
  48. table[i] = table_base[i] ^ seed;
  49. }
  50. return table;
  51. }
  52. static uint32_t
  53. buzhash(const unsigned char *data, int len, const uint32_t *h)
  54. {
  55. int i;
  56. uint32_t sum = 0;
  57. for(i = len - 1; i > 0; i--)
  58. {
  59. sum ^= BARREL_SHIFT(h[*data], i);
  60. data++;
  61. }
  62. return sum ^ h[*data];
  63. }
  64. static uint32_t
  65. buzhash_update(uint32_t sum, unsigned char remove, unsigned char add, int len, const uint32_t *h)
  66. {
  67. return BARREL_SHIFT(sum, 1) ^ BARREL_SHIFT(h[remove], len) ^ h[add];
  68. }
  69. typedef struct {
  70. PyObject_HEAD
  71. int window_size, last, done, buf_size, remaining, position, chunk_mask, min_size;
  72. size_t bytes_read, bytes_yielded;
  73. uint32_t *h;
  74. PyObject *chunks, *fd;
  75. unsigned char *data;
  76. } ChunkifyIter;
  77. static PyObject*
  78. ChunkifyIter_iter(PyObject *self)
  79. {
  80. ChunkifyIter *c = (ChunkifyIter *)self;
  81. c->remaining = 0;
  82. c->position = 0;
  83. c->done = 0;
  84. c->last = 0;
  85. c->bytes_read = 0;
  86. c->bytes_yielded = 0;
  87. Py_INCREF(self);
  88. return self;
  89. }
  90. static void
  91. ChunkifyIter_dealloc(PyObject *self)
  92. {
  93. ChunkifyIter *c = (ChunkifyIter *)self;
  94. Py_DECREF(c->fd);
  95. free(c->data);
  96. free(c->h);
  97. self->ob_type->tp_free(self);
  98. }
  99. static void
  100. ChunkifyIter_fill(PyObject *self)
  101. {
  102. ChunkifyIter *c = (ChunkifyIter *)self;
  103. memmove(c->data, c->data + c->last, c->position + c->remaining - c->last);
  104. c->position -= c->last;
  105. c->last = 0;
  106. PyObject *data = PyObject_CallMethod(c->fd, "read", "i", c->buf_size - c->position - c->remaining);
  107. int n = PyString_Size(data);
  108. memcpy(c->data + c->position + c->remaining, PyString_AsString(data), n);
  109. c->remaining += n;
  110. c->bytes_read += n;
  111. Py_DECREF(data);
  112. }
  113. static PyObject*
  114. ChunkifyIter_iternext(PyObject *self)
  115. {
  116. ChunkifyIter *c = (ChunkifyIter *)self;
  117. uint32_t sum, chunk_mask = c->chunk_mask, min_size = c->min_size, window_size = c->window_size;
  118. int n = 0;
  119. if(c->done) {
  120. if(c->bytes_read == c->bytes_yielded)
  121. PyErr_SetNone(PyExc_StopIteration);
  122. else
  123. PyErr_SetString(PyExc_Exception, "chunkifier byte count mismatch");
  124. return NULL;
  125. }
  126. if(c->remaining <= window_size) {
  127. ChunkifyIter_fill(self);
  128. }
  129. if(c->remaining < window_size) {
  130. c->done = 1;
  131. if(c->remaining) {
  132. c->bytes_yielded += c->remaining;
  133. return PyBuffer_FromMemory(c->data + c->position, c->remaining);
  134. }
  135. else {
  136. if(c->bytes_read == c->bytes_yielded)
  137. PyErr_SetNone(PyExc_StopIteration);
  138. else
  139. PyErr_SetString(PyExc_Exception, "chunkifier byte count mismatch");
  140. return NULL;
  141. }
  142. }
  143. sum = buzhash(c->data + c->position, window_size, c->h);
  144. while(c->remaining >= c->window_size && ((sum & chunk_mask) || n < min_size)) {
  145. sum = buzhash_update(sum, c->data[c->position],
  146. c->data[c->position + window_size],
  147. window_size, c->h);
  148. c->position++;
  149. c->remaining--;
  150. n++;
  151. if(c->remaining <= window_size) {
  152. ChunkifyIter_fill(self);
  153. }
  154. }
  155. if(c->remaining <= window_size) {
  156. c->position += c->remaining;
  157. c->remaining = 0;
  158. }
  159. int old_last = c->last;
  160. c->last = c->position;
  161. n = c->last - old_last;
  162. c->bytes_yielded += n;
  163. return PyBuffer_FromMemory(c->data + old_last, n);
  164. }
  165. static PyTypeObject ChunkifyIterType = {
  166. PyObject_HEAD_INIT(NULL)
  167. 0, /*ob_size*/
  168. "_chunkifier._ChunkifyIter", /*tp_name*/
  169. sizeof(ChunkifyIter), /*tp_basicsize*/
  170. 0, /*tp_itemsize*/
  171. ChunkifyIter_dealloc, /*tp_dealloc*/
  172. 0, /*tp_print*/
  173. 0, /*tp_getattr*/
  174. 0, /*tp_setattr*/
  175. 0, /*tp_compare*/
  176. 0, /*tp_repr*/
  177. 0, /*tp_as_number*/
  178. 0, /*tp_as_sequence*/
  179. 0, /*tp_as_mapping*/
  180. 0, /*tp_hash */
  181. 0, /*tp_call*/
  182. 0, /*tp_str*/
  183. 0, /*tp_getattro*/
  184. 0, /*tp_setattro*/
  185. 0, /*tp_as_buffer*/
  186. Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,
  187. /* tp_flags: Py_TPFLAGS_HAVE_ITER tells python to
  188. use tp_iter and tp_iternext fields. */
  189. "", /* tp_doc */
  190. 0, /* tp_traverse */
  191. 0, /* tp_clear */
  192. 0, /* tp_richcompare */
  193. 0, /* tp_weaklistoffset */
  194. ChunkifyIter_iter, /* tp_iter: __iter__() method */
  195. ChunkifyIter_iternext /* tp_iternext: next() method */
  196. };
  197. static PyObject *
  198. chunkify(PyObject *self, PyObject *args)
  199. {
  200. PyObject *fd;
  201. int seed, window_size, chunk_mask, min_size;
  202. ChunkifyIter *c;
  203. if (!PyArg_ParseTuple(args, "Oiiii", &fd, &window_size, &chunk_mask, &min_size, &seed))
  204. {
  205. return NULL;
  206. }
  207. if (!(c = PyObject_New(ChunkifyIter, &ChunkifyIterType)))
  208. {
  209. return NULL;
  210. }
  211. PyObject_Init((PyObject *)c, &ChunkifyIterType);
  212. c->buf_size = 10 * 1024 * 1024;
  213. c->data = malloc(c->buf_size);
  214. c->h = init_buzhash_table(seed & 0xffffffff);
  215. c->fd = fd;
  216. c->window_size = window_size;
  217. c->chunk_mask = chunk_mask;
  218. c->min_size = min_size;
  219. Py_INCREF(fd);
  220. return (PyObject *)c;
  221. }
  222. static PyObject *
  223. do_buzhash(PyObject *self, PyObject *args)
  224. {
  225. unsigned char *data;
  226. int len;
  227. unsigned long int seed, sum;
  228. uint32_t *h;
  229. if (!PyArg_ParseTuple(args, "s#k", &data, &len, &seed))
  230. {
  231. return NULL;
  232. }
  233. h = init_buzhash_table(seed & 0xffffffff);
  234. sum = buzhash(data, len, h);
  235. free(h);
  236. return PyLong_FromUnsignedLong(sum);
  237. }
  238. static PyObject *
  239. do_buzhash_update(PyObject *self, PyObject *args)
  240. {
  241. unsigned long int sum, seed;
  242. unsigned char remove, add;
  243. uint32_t *h;
  244. int len;
  245. if (!PyArg_ParseTuple(args, "kbbik", &sum, &remove, &add, &len, &seed))
  246. {
  247. return NULL;
  248. }
  249. h = init_buzhash_table(seed & 0xffffffff);
  250. sum = buzhash_update(sum, remove, add, len, h);
  251. free(h);
  252. return PyLong_FromUnsignedLong(sum);
  253. }
  254. static PyMethodDef ChunkifierMethods[] = {
  255. {"chunkify", chunkify, METH_VARARGS, ""},
  256. {"buzhash", do_buzhash, METH_VARARGS, ""},
  257. {"buzhash_update", do_buzhash_update, METH_VARARGS, ""},
  258. {NULL, NULL, 0, NULL} /* Sentinel */
  259. };
  260. PyMODINIT_FUNC
  261. init_speedups(void)
  262. {
  263. ChunkifyIterType.tp_new = PyType_GenericNew;
  264. if (PyType_Ready(&ChunkifyIterType) < 0) return;
  265. Py_InitModule("_speedups", ChunkifierMethods);
  266. }