1 #define UNICODE
2 #define PY_SSIZE_T_CLEAN
3
4 #include <Python.h>
5 #include <datetime.h>
6 #include <errno.h>
7
8 #include <stdlib.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #define _USE_MATH_DEFINES
12 #include <math.h>
13 #include <string.h>
14
15 #define MIN(x, y) ((x < y) ? x : y)
16 #define MAX(x, y) ((x > y) ? x : y)
17 #define CLAMP(value, lower, upper) ((value > upper) ? upper : ((value < lower) ? lower : value))
18 #define STRIDE(width, r, c) ((width * (r)) + (c))
19
20 #ifdef _MSC_VER
21 #ifndef uint32_t
22 typedef unsigned __int32 uint32_t;
23 #endif
24
25 #ifndef uint8_t
26 typedef unsigned __int8 uint8_t;
27 #endif
28 #else
29 #include <stdint.h>
30 #endif
31
32 static PyObject *
speedup_parse_date(PyObject * self,PyObject * args)33 speedup_parse_date(PyObject *self, PyObject *args) {
34 const char *raw, *orig, *tz;
35 char *end;
36 long year, month, day, hour, minute, second, tzh = 0, tzm = 0, sign = 0;
37 size_t len;
38 if(!PyArg_ParseTuple(args, "s", &raw)) return NULL;
39 while ((*raw == ' ' || *raw == '\t' || *raw == '\n' || *raw == '\r' || *raw == '\f' || *raw == '\v') && *raw != 0) raw++;
40 len = strlen(raw);
41 if (len < 19) Py_RETURN_NONE;
42
43 orig = raw;
44
45 year = strtol(raw, &end, 10);
46 if ((end - raw) != 4) Py_RETURN_NONE;
47 raw += 5;
48
49
50 month = strtol(raw, &end, 10);
51 if ((end - raw) != 2) Py_RETURN_NONE;
52 raw += 3;
53
54 day = strtol(raw, &end, 10);
55 if ((end - raw) != 2) Py_RETURN_NONE;
56 raw += 3;
57
58 hour = strtol(raw, &end, 10);
59 if ((end - raw) != 2) Py_RETURN_NONE;
60 raw += 3;
61
62 minute = strtol(raw, &end, 10);
63 if ((end - raw) != 2) Py_RETURN_NONE;
64 raw += 3;
65
66 second = strtol(raw, &end, 10);
67 if ((end - raw) != 2) Py_RETURN_NONE;
68
69 tz = orig + len - 6;
70
71 if (*tz == '+') sign = +1;
72 if (*tz == '-') sign = -1;
73 if (sign != 0) {
74 // We have TZ info
75 tz += 1;
76
77 tzh = strtol(tz, &end, 10);
78 if ((end - tz) != 2) Py_RETURN_NONE;
79 tz += 3;
80
81 tzm = strtol(tz, &end, 10);
82 if ((end - tz) != 2) Py_RETURN_NONE;
83 }
84
85 return Py_BuildValue("lllllll", year, month, day, hour, minute, second,
86 (tzh*60 + tzm)*sign*60);
87 }
88
89
90 static PyObject*
speedup_pdf_float(PyObject * self,PyObject * args)91 speedup_pdf_float(PyObject *self, PyObject *args) {
92 double f = 0.0, a = 0.0;
93 char *buf = "0", *dot;
94 void *free_buf = NULL;
95 int precision = 6, l = 0;
96 PyObject *ret;
97
98 if(!PyArg_ParseTuple(args, "d", &f)) return NULL;
99
100 a = fabs(f);
101
102 if (a > 1.0e-7) {
103 if(a > 1) precision = MIN(MAX(0, 6-(int)log10(a)), 6);
104 buf = PyOS_double_to_string(f, 'f', precision, 0, NULL);
105 if (buf != NULL) {
106 free_buf = (void*)buf;
107 if (precision > 0) {
108 l = (int)(strlen(buf) - 1);
109 while (l > 0 && buf[l] == '0') l--;
110 if (buf[l] == ',' || buf[l] == '.') buf[l] = 0;
111 else buf[l+1] = 0;
112 if ( (dot = strchr(buf, ',')) ) *dot = '.';
113 }
114 } else if (!PyErr_Occurred()) PyErr_SetString(PyExc_TypeError, "Float->str failed.");
115 }
116
117 ret = PyUnicode_FromString(buf);
118 if (free_buf != NULL) PyMem_Free(free_buf);
119 return ret;
120 }
121
122 static PyObject*
speedup_detach(PyObject * self,PyObject * args)123 speedup_detach(PyObject *self, PyObject *args) {
124 char *devnull = NULL;
125 if (!PyArg_ParseTuple(args, "s", &devnull)) return NULL;
126 if (freopen(devnull, "r", stdin) == NULL) return PyErr_SetFromErrnoWithFilename(PyExc_OSError, devnull);
127 if (freopen(devnull, "w", stdout) == NULL) return PyErr_SetFromErrnoWithFilename(PyExc_OSError, devnull);
128 if (freopen(devnull, "w", stderr) == NULL) return PyErr_SetFromErrnoWithFilename(PyExc_OSError, devnull);
129 Py_RETURN_NONE;
130 }
131
calculate_gaussian_kernel(Py_ssize_t size,double * kernel,double radius)132 static void calculate_gaussian_kernel(Py_ssize_t size, double *kernel, double radius) {
133 const double sqr = radius * radius;
134 const double factor = 1.0 / (2 * M_PI * sqr);
135 const double denom = 2 * sqr;
136 double *t, sum = 0;
137 Py_ssize_t r, c, center = size / 2;
138
139 for (r = 0; r < size; r++) {
140 t = kernel + (r * size);
141 for (c = 0; c < size; c++) {
142 t[c] = factor * pow(M_E, - ( ( (r - center) * (r - center) + (c - center) * (c - center) ) / denom ));
143 }
144 }
145
146 // Normalize matrix
147 for (r = 0; r < size * size; r++) sum += kernel[r];
148 sum = 1 / sum;
149 for (r = 0; r < size * size; r++) kernel[r] *= sum;
150 }
151
152 static PyObject*
speedup_create_texture(PyObject * self,PyObject * args,PyObject * kw)153 speedup_create_texture(PyObject *self, PyObject *args, PyObject *kw) {
154 PyObject *ret = NULL;
155 Py_ssize_t width, height, weight = 3, i, j, r, c, half_weight;
156 double pixel, *mask = NULL, radius = 1, *kernel = NULL, blend_alpha = 0.1;
157 float density = 0.7f;
158 unsigned char base_r, base_g, base_b, blend_r = 0, blend_g = 0, blend_b = 0, *ppm = NULL, *t = NULL;
159 char header[100] = {0};
160 static char* kwlist[] = {"blend_red", "blend_green", "blend_blue", "blend_alpha", "density", "weight", "radius", NULL};
161
162 if (!PyArg_ParseTupleAndKeywords(args, kw, "nnbbb|bbbdfnd", kwlist, &width, &height, &base_r, &base_g, &base_b, &blend_r, &blend_g, &blend_b, &blend_alpha, &density, &weight, &radius)) return NULL;
163 if (weight % 2 != 1 || weight < 1) { PyErr_SetString(PyExc_ValueError, "The weight must be an odd positive number"); return NULL; }
164 if (radius <= 0) { PyErr_SetString(PyExc_ValueError, "The radius must be positive"); return NULL; }
165 if (width > 100000 || height > 10000) { PyErr_SetString(PyExc_ValueError, "The width or height is too large"); return NULL; }
166 if (width < 1 || height < 1) { PyErr_SetString(PyExc_ValueError, "The width or height is too small"); return NULL; }
167 snprintf(header, 99, "P6\n%d %d\n255\n", (int)width, (int)height);
168
169 kernel = (double*)calloc(weight * weight, sizeof(double));
170 if (kernel == NULL) { PyErr_NoMemory(); return NULL; }
171 mask = (double*)calloc(width * height, sizeof(double));
172 if (mask == NULL) { free(kernel); PyErr_NoMemory(); return NULL;}
173 ppm = (unsigned char*)calloc(strlen(header) + (3 * width * height), sizeof(unsigned char));
174 if (ppm == NULL) { free(kernel); free(mask); PyErr_NoMemory(); return NULL; }
175
176 calculate_gaussian_kernel(weight, kernel, radius);
177
178 // Random noise, noisy pixels are blend_alpha, other pixels are 0
179 for (i = 0; i < width * height; i++) {
180 if (((float)(rand()) / RAND_MAX) <= density) mask[i] = blend_alpha;
181 }
182
183 // Blur the noise using the gaussian kernel
184 half_weight = weight / 2;
185 for (r = 0; r < height; r++) {
186 for (c = 0; c < width; c++) {
187 pixel = 0;
188 for (i = -half_weight; i <= half_weight; i++) {
189 for (j = -half_weight; j <= half_weight; j++) {
190 pixel += (*(mask + STRIDE(width, CLAMP(r + i, 0, height - 1), CLAMP(c + j, 0, width - 1)))) * (*(kernel + STRIDE(weight, half_weight + i, half_weight + j)));
191 }
192 }
193 *(mask + STRIDE(width, r, c)) = CLAMP(pixel, 0, 1);
194 }
195 }
196
197 // Create the texture in PPM (P6) format
198 memcpy(ppm, header, strlen(header));
199 t = ppm + strlen(header);
200 for (i = 0, j = 0; j < width * height; i += 3, j += 1) {
201 #define BLEND(src, dest) ( ((unsigned char)(src * mask[j])) + ((unsigned char)(dest * (1 - mask[j]))) )
202 t[i] = BLEND(blend_r, base_r);
203 t[i+1] = BLEND(blend_g, base_g);
204 t[i+2] = BLEND(blend_b, base_b);
205 }
206
207 ret = Py_BuildValue("s", ppm);
208 free(mask); mask = NULL;
209 free(kernel); kernel = NULL;
210 free(ppm); ppm = NULL;
211 return ret;
212 }
213
214 static PyObject*
speedup_websocket_mask(PyObject * self,PyObject * args)215 speedup_websocket_mask(PyObject *self, PyObject *args) {
216 PyObject *data = NULL, *mask = NULL;
217 Py_buffer data_buf = {0}, mask_buf = {0};
218 Py_ssize_t offset = 0, i = 0;
219 char *dbuf = NULL, *mbuf = NULL;
220 int ok = 0;
221
222 if(!PyArg_ParseTuple(args, "OO|n", &data, &mask, &offset)) return NULL;
223
224 if (PyObject_GetBuffer(data, &data_buf, PyBUF_SIMPLE|PyBUF_WRITABLE) != 0) return NULL;
225 if (PyObject_GetBuffer(mask, &mask_buf, PyBUF_SIMPLE) != 0) goto done;
226
227 dbuf = (char*)data_buf.buf; mbuf = (char*)mask_buf.buf;
228 for(i = 0; i < data_buf.len; i++) dbuf[i] ^= mbuf[(i + offset) & 3];
229 ok = 1;
230
231 done:
232 if(data_buf.obj) PyBuffer_Release(&data_buf);
233 if(mask_buf.obj) PyBuffer_Release(&mask_buf);
234 if (ok) { Py_RETURN_NONE; }
235 return NULL;
236 }
237
238 #define UTF8_ACCEPT 0
239 #define UTF8_REJECT 1
240
241 static const uint8_t utf8d[] = {
242 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
243 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
244 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
245 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
246 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
247 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
248 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
249 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
250 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
251 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
252 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
253 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
254 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
255 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
256 };
257
258 #ifdef _MSC_VER
259 static void __inline
260 #else
261 static void inline
262 #endif
utf8_decode_(uint32_t * state,uint32_t * codep,uint8_t byte)263 utf8_decode_(uint32_t* state, uint32_t* codep, uint8_t byte) {
264 /* Comes from http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
265 * Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
266 * Used under license: https://opensource.org/licenses/MIT
267 */
268 uint32_t type = utf8d[byte];
269
270 *codep = (*state != UTF8_ACCEPT) ?
271 (byte & 0x3fu) | (*codep << 6) :
272 (0xff >> type) & (byte);
273
274 *state = utf8d[256 + *state*16 + type];
275 }
276
277 static PyObject*
utf8_decode(PyObject * self,PyObject * args)278 utf8_decode(PyObject *self, PyObject *args) {
279 uint8_t *dbuf = NULL;
280 uint32_t state = UTF8_ACCEPT, codep = 0, *buf = NULL;
281 PyObject *data_obj = NULL, *ans = NULL;
282 Py_buffer pbuf;
283 Py_ssize_t i = 0, pos = 0;
284
285 if(!PyArg_ParseTuple(args, "O|II", &data_obj, &state, &codep)) return NULL;
286 if (PyObject_GetBuffer(data_obj, &pbuf, PyBUF_SIMPLE) != 0) return NULL;
287 buf = (uint32_t*)PyMem_Malloc(sizeof(uint32_t) * pbuf.len);
288 if (buf == NULL) goto error;
289 dbuf = (uint8_t*)pbuf.buf;
290
291 for (i = 0; i < pbuf.len; i++) {
292 utf8_decode_(&state, &codep, dbuf[i]);
293 if (state == UTF8_ACCEPT) buf[pos++] = codep;
294 else if (state == UTF8_REJECT) { PyErr_SetString(PyExc_ValueError, "Invalid byte in UTF-8 string"); goto error; }
295 }
296 ans = PyUnicode_DecodeUTF32((const char*)buf, pos * sizeof(uint32_t), "strict", NULL);
297 error:
298 if (pbuf.obj) PyBuffer_Release(&pbuf);
299 if (buf) { PyMem_Free(buf); buf = NULL; }
300 if (ans == NULL) return ans;
301 return Py_BuildValue("NII", ans, state, codep);
302 }
303
304 static PyObject*
clean_xml_chars(PyObject * self,PyObject * text)305 clean_xml_chars(PyObject *self, PyObject *text) {
306 PyObject *result = NULL;
307 void *result_text = NULL;
308 Py_ssize_t src_i, target_i;
309 enum PyUnicode_Kind text_kind;
310 Py_UCS4 ch;
311
312 if (!PyUnicode_Check(text)) {
313 PyErr_SetString(PyExc_TypeError, "A unicode string is required");
314 return NULL;
315 }
316 if(PyUnicode_READY(text) != 0) {
317 // just return null, an exception is already set by READY()
318 return NULL;
319 }
320 if(PyUnicode_GET_LENGTH(text) == 0) {
321 // make sure that malloc(0) will never happen
322 return text;
323 }
324
325 text_kind = PyUnicode_KIND(text);
326 // Once we've called READY(), our string is in canonical form, which means
327 // it is encoded using UTF-{8,16,32}, such that each codepoint is one
328 // element in the array. The value of the Kind enum is the size of each
329 // character.
330 result_text = malloc(PyUnicode_GET_LENGTH(text) * text_kind);
331 if (result_text == NULL) return PyErr_NoMemory();
332
333 target_i = 0;
334 for (src_i = 0; src_i < PyUnicode_GET_LENGTH(text); src_i++) {
335 ch = PyUnicode_READ(text_kind, PyUnicode_DATA(text), src_i);
336 // based on https://en.wikipedia.org/wiki/Valid_characters_in_XML#Non-restricted_characters
337 // python 3.3+ unicode strings never contain surrogate pairs, since if
338 // they did, they would be represented as UTF-32
339 if ((0x20 <= ch && ch <= 0x7e) ||
340 ch == 0x9 || ch == 0xa || ch == 0xd || ch == 0x85 ||
341 (0x00A0 <= ch && ch <= 0xD7FF) ||
342 (0xE000 <= ch && ch <= 0xFDCF) ||
343 (0xFDF0 <= ch && ch <= 0xFFFD) ||
344 (0xffff < ch && ch <= 0x10ffff)) {
345 PyUnicode_WRITE(text_kind, result_text, target_i, ch);
346 target_i += 1;
347 }
348 }
349
350 // using text_kind here is ok because we don't create any characters that
351 // are larger than might already exist
352 result = PyUnicode_FromKindAndData(text_kind, result_text, target_i);
353 free(result_text);
354 return result;
355 }
356
357 static PyObject *
speedup_iso_8601(PyObject * self,PyObject * args)358 speedup_iso_8601(PyObject *self, PyObject *args) {
359 char *str = NULL, *c = NULL;
360 int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, usecond = 0, i = 0, tzhour = 1000, tzminute = 0, tzsign = 0;
361
362 if (!PyArg_ParseTuple(args, "s", &str)) return NULL;
363 c = str;
364
365 #define RAISE(msg) return PyErr_Format(PyExc_ValueError, "%s is not a valid ISO 8601 datestring: %s", str, msg);
366 #define CHAR_IS_DIGIT(c) (*c >= '0' && *c <= '9')
367 #define READ_DECIMAL_NUMBER(max_digits, x, abort) \
368 for (i = 0; i < max_digits; i++) { \
369 if (CHAR_IS_DIGIT(c)) x = 10 * x + *c++ - '0'; \
370 else { abort; } \
371 }
372 #define OPTIONAL_SEPARATOR(x) if(*c == x) c++;
373
374 // Ignore leading whitespace
375 while(*c == ' ' || *c == '\n' || *c == '\r' || *c == '\t' || *c == '\v' || *c == '\f') c++;
376
377 // Year
378 READ_DECIMAL_NUMBER(4, year, RAISE("No year specified"));
379 OPTIONAL_SEPARATOR('-');
380 // Month (optional)
381 READ_DECIMAL_NUMBER(2, month, break);
382 if (month == 0) month = 1; // YYYY format
383 else {
384 OPTIONAL_SEPARATOR('-');
385
386 // Day (optional)
387 READ_DECIMAL_NUMBER(2, day, break);
388 }
389 if (day == 0) day = 1; // YYYY-MM format
390 if (month > 12) RAISE("month greater than 12");
391
392 if (*c == 'T' || *c == ' ') // Time separator
393 {
394 c++;
395
396 // Hour
397 READ_DECIMAL_NUMBER(2, hour, RAISE("No hour specified"));
398 OPTIONAL_SEPARATOR(':');
399 // Minute (optional)
400 READ_DECIMAL_NUMBER(2, minute, break);
401 OPTIONAL_SEPARATOR(':');
402 // Second (optional)
403 READ_DECIMAL_NUMBER(2, second, break);
404
405 if (*c == '.' || *c == ',') // separator for microseconds
406 {
407 c++;
408 // Parse fraction of second up to 6 places
409 READ_DECIMAL_NUMBER(6, usecond, break);
410 // Omit excessive digits
411 while (CHAR_IS_DIGIT(c)) c++;
412 // If we break early, fully expand the usecond
413 while (i++ < 6) usecond *= 10;
414 }
415 }
416
417 switch(*c) {
418 case 'Z':
419 tzhour = 0; c++; break;
420 case '+':
421 tzsign = 1; c++; break;
422 case '-':
423 tzsign = -1; c++; break;
424 default:
425 break;
426 }
427
428 if (tzsign != 0) {
429 tzhour = 0;
430 READ_DECIMAL_NUMBER(2, tzhour, break);
431 OPTIONAL_SEPARATOR(':');
432 READ_DECIMAL_NUMBER(2, tzminute, break);
433 }
434
435 return Py_BuildValue("NOi", PyDateTime_FromDateAndTime(year, month, day, hour, minute, second, usecond), (tzhour == 1000) ? Py_False : Py_True, tzsign*60*(tzhour*60 + tzminute));
436 }
437
438 #ifndef _MSC_VER
439 #include <pthread.h>
440 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
441 #define FREEBSD_SET_NAME
442 #endif
443 #if defined(__APPLE__)
444 // I can't figure out how to get pthread.h to include this definition on macOS. MACOSX_DEPLOYMENT_TARGET does not work.
445 extern int pthread_setname_np(const char *name);
446 #elif defined(FREEBSD_SET_NAME)
447 // Function has a different name on FreeBSD
448 void pthread_set_name_np(pthread_t tid, const char *name);
449 #elif defined(__NetBSD__)
450 // pthread.h provides the symbol
451 #elif defined(__HAIKU__)
452 // Haiku doesn't support pthread_set_name_np yet
453 #else
454 // Need _GNU_SOURCE for pthread_setname_np on linux and that causes other issues on systems with old glibc
455 extern int pthread_setname_np(pthread_t, const char *name);
456 #endif
457 #endif
458
459
460 static PyObject*
set_thread_name(PyObject * self,PyObject * args)461 set_thread_name(PyObject *self, PyObject *args) {
462 (void)(self); (void)(args);
463 #if defined(_MSC_VER) || defined(__HAIKU__)
464 PyErr_SetString(PyExc_RuntimeError, "Setting thread names not supported on on this platform");
465 return NULL;
466 #else
467 char *name;
468 int ret;
469 if (!PyArg_ParseTuple(args, "s", &name)) return NULL;
470 while (1) {
471 errno = 0;
472 #if defined(__APPLE__)
473 ret = pthread_setname_np(name);
474 #elif defined(FREEBSD_SET_NAME)
475 pthread_set_name_np(pthread_self(), name);
476 ret = 0;
477 #elif defined(__NetBSD__)
478 ret = pthread_setname_np(pthread_self(), "%s", name);
479 #else
480 ret = pthread_setname_np(pthread_self(), name);
481 #endif
482 if (ret != 0 && (errno == EINTR || errno == EAGAIN)) continue;
483 break;
484 }
485 if (ret != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
486 Py_RETURN_NONE;
487 #endif
488 }
489
490 #define char_is_ignored(ch) (ch <= 32)
491
492 static size_t
count_chars_in(PyObject * text)493 count_chars_in(PyObject *text) {
494 size_t ans = 0;
495 if (PyUnicode_READY(text) != 0) return 0;
496 int kind = PyUnicode_KIND(text);
497 void *data = PyUnicode_DATA(text);
498 Py_ssize_t len = PyUnicode_GET_LENGTH(text);
499 ans = len;
500 for (Py_ssize_t i = 0; i < len; i++) {
501 if (char_is_ignored(PyUnicode_READ(kind, data, i))) ans--;
502 }
503 return ans;
504 }
505
506 static PyObject*
get_element_char_length(PyObject * self,PyObject * args)507 get_element_char_length(PyObject *self, PyObject *args) {
508 (void)(self);
509 const char *tag_name;
510 PyObject *text, *tail;
511 if (!PyArg_ParseTuple(args, "sOO", &tag_name, &text, &tail)) return NULL;
512 const char *b = strrchr(tag_name, '}');
513 if (b) tag_name = b + 1;
514 char ltagname[16];
515 const size_t tag_name_len = strnlen(tag_name, sizeof(ltagname)-1);
516 for (size_t i = 0; i < tag_name_len; i++) {
517 if ('A' <= tag_name[i] && tag_name[i] <= 'Z') ltagname[i] = 32 + tag_name[i];
518 else ltagname[i] = tag_name[i];
519 }
520 int is_ignored_tag = 0;
521 size_t ans = 0;
522 #define EQ(x) memcmp(ltagname, #x, sizeof(#x) - 1) == 0
523 if (EQ(script) || EQ(noscript) || EQ(style) || EQ(title)) is_ignored_tag = 1;
524 if (EQ(img) || EQ(svg)) ans += 1000;
525 #undef EQ
526 if (tail != Py_None) ans += count_chars_in(tail);
527 if (text != Py_None && !is_ignored_tag) ans += count_chars_in(text);
528 return PyLong_FromSize_t(ans);
529 }
530
531
532 static PyMethodDef speedup_methods[] = {
533 {"parse_date", speedup_parse_date, METH_VARARGS,
534 "parse_date()\n\nParse ISO dates faster (specialized for dates stored in the calibre db)."
535 },
536
537 {"parse_iso8601", speedup_iso_8601, METH_VARARGS,
538 "parse_iso8601(datestring)\n\nParse ISO 8601 dates faster. More spec compliant than parse_date()"
539 },
540
541 {"pdf_float", speedup_pdf_float, METH_VARARGS,
542 "pdf_float()\n\nConvert float to a string representation suitable for PDF"
543 },
544
545 {"detach", speedup_detach, METH_VARARGS,
546 "detach()\n\nRedirect the standard I/O stream to the specified file (usually os.devnull)"
547 },
548
549 {"create_texture", (PyCFunction)speedup_create_texture, METH_VARARGS | METH_KEYWORDS,
550 "create_texture(width, height, red, green, blue, blend_red=0, blend_green=0, blend_blue=0, blend_alpha=0.1, density=0.7, weight=3, radius=1)\n\n"
551 "Create a texture of the specified width and height from the specified color."
552 " The texture is created by blending in random noise of the specified blend color into a flat image."
553 " All colors are numbers between 0 and 255. 0 <= blend_alpha <= 1 with 0 being fully transparent."
554 " 0 <= density <= 1 is used to control the amount of noise in the texture."
555 " weight and radius control the Gaussian convolution used for blurring of the noise. weight must be an odd positive integer. Increasing the weight will tend to blur out the noise. Decreasing it will make it sharper."
556 " This function returns an image (bytestring) in the PPM format as the texture."
557 },
558
559 {"websocket_mask", speedup_websocket_mask, METH_VARARGS,
560 "websocket_mask(data, mask [, offset=0)\n\nXOR the data (bytestring) with the specified (must be 4-byte bytestring) mask"
561 },
562
563 {"utf8_decode", utf8_decode, METH_VARARGS,
564 "utf8_decode(data, [, state=0, codep=0)\n\nDecode an UTF-8 bytestring, using a strict UTF-8 decoder, that unlike python does not allow orphaned surrogates. Returns a unicode object and the state."
565 },
566
567 {"clean_xml_chars", clean_xml_chars, METH_O,
568 "clean_xml_chars(unicode_object)\n\nRemove codepoints in unicode_object that are not allowed in XML"
569 },
570
571 {"set_thread_name", set_thread_name, METH_VARARGS,
572 "set_thread_name(name)\n\nWrapper for pthread_setname_np"
573 },
574
575 {"get_element_char_length", get_element_char_length, METH_VARARGS,
576 "get_element_char_length(tag_name, text, tail)\n\nGet the number of chars in specified tag"
577 },
578
579 {NULL, NULL, 0, NULL}
580 };
581
582 static int
exec_module(PyObject * module)583 exec_module(PyObject *module) {
584 PyDateTime_IMPORT;
585 #ifndef _WIN32
586 PyModule_AddIntConstant(module, "O_CLOEXEC", O_CLOEXEC);
587 #endif
588 return 0;
589 }
590
591 static PyModuleDef_Slot slots[] = { {Py_mod_exec, exec_module}, {0, NULL} };
592
593 static struct PyModuleDef module_def = {
594 .m_base = PyModuleDef_HEAD_INIT,
595 .m_name = "speedup",
596 .m_doc = "Implementation of methods in C for speed.",
597 .m_methods = speedup_methods,
598 .m_slots = slots,
599 };
600
PyInit_speedup(void)601 CALIBRE_MODINIT_FUNC PyInit_speedup(void) { return PyModuleDef_Init(&module_def); }
602